Merge pull request #7 from boltlabs-inc/master

Sync'ing with Bolt Labs - libbolt fork
This commit is contained in:
J. Ayo Akinyele 2019-10-21 14:47:00 -04:00 committed by GitHub
commit 2561ece47f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 24196 additions and 2919 deletions

3
.gitignore vendored
View File

@ -1 +1,4 @@
.idea/
target
Cargo.lock
py/__pycache__/

View File

@ -1,7 +1,7 @@
[package]
name = "bolt"
version = "0.2.0"
authors = ["J. Ayo Akinyele <ayo@yeletech.org>"]
version = "0.3.0"
authors = ["Bolt Labs, Inc <info@boltlabs.io>"]
description = "library for Blind Off-chain Lightweight Transactions (BOLT)"
keywords = ["zcash", "payment channels", "bolt"]
readme = "README.md"
@ -10,26 +10,19 @@ repository = "https://github.com/ZcashFoundation/libbolt"
license = "MIT License"
[dependencies]
rand = "0.5"
rand = "0.6"
rand_core = "0.4.0"
bn = { git = "https://github.com/ZcashFoundation/bn", branch = "master" }
bincode = "0.6.1"
sodiumoxide = "0.0.16"
serde = "1.0"
serde_derive = "1.0"
ff = { git = "https://github.com/boltlabs-inc/ff", branch = "master" }
pairing = { git = "https://github.com/boltlabs-inc/pairing", branch = "master", features = ["serde"] }
libc = "*"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_with = "1.0"
serde_bytes = "0.11.2"
time = "*"
rustc-serialize = "0.3"
secp256k1 = { git = "https://github.com/ZcashFoundation/rust-secp256k1", branch = "master", features = ["rand"] }
curve25519-dalek = "1.0.0-pre.0"
merlin = "1.0.0"
bulletproofs = { git = "https://github.com/dalek-cryptography/bulletproofs", branch = "main" }
secp256k1 = { version = "0.15.0", features = ["rand", "serde"] }
sha2 = { version = "0.8", default-features = false }
[dev-dependencies.bincode]
version = "~0.6.0"
default-features = false
features = ["rustc-serialize"]
[lib]
crate-type = ["lib", "cdylib"]

View File

@ -3,6 +3,7 @@
all:
export RUSTFLAGS=-Awarnings
cargo +nightly build
cargo +nightly test
cargo +nightly run --example bolt_test
debug:
@ -22,12 +23,37 @@ test:
cargo +nightly test --release #-- --nocapture
update:
# updates local git repos (for forked bn lib)
cargo +nightly update
doc:
# generates the documentation
cargo +nightly doc
pythontests:
cargo +nightly clean
cargo +nightly update
cargo +nightly build --release
python py/libbolt.py
python py/libbolt_tests.py
cpptests:
@cargo +nightly build --release
@g++ cpp/libbolt.cpp -L ./target/release/ -lbolt -I ./include -o cpp_test
@LD_LIBRARY_PATH=./target/release/ ./cpp_test
@rm cpp_test
gotests:
cargo +nightly build --release
go test go/libbolt.go go/libbolt_test.go
alltests:
cargo +nightly clean
cargo +nightly update
cargo +nightly build --release
cargo +nightly test --release #-- --nocapture
python py/libbolt.py
python py/libbolt_tests.py
go test go/libbolt.go go/libbolt_test.go
clean:
cargo +nightly clean

264
README.md
View File

@ -15,11 +15,10 @@ Active development of libbolt is ongoing at [Bolt Labs, Inc](https://github.com/
# Dependencies
* secp256k1
* sodiumoxide
* bn
* curve25519_dalek
* merlin
* bulletproofs
* ff
* pairing
* serde
* sha2
Note that the above rust dependencies will be compiled and installed as a result of running the `make` command.
@ -59,7 +58,7 @@ To use the libbolt library, add the `libbolt` crate to your dependency file in `
```toml
[dependencies]
bolt = "0.2.0"
bolt = "0.3.0"
```
Then add an extern declaration at the root of your crate as follows:
@ -69,194 +68,113 @@ extern crate bolt;
# API
The libbolt library provides APIs for three types of payment channels:
The libbolt library provides APIs for two types of payment channels:
* unidirectional payment channels (*work in progress*)
* bidirectional payment channels
* third-party payments
## Unidirectional Payment Channels
A unidirectional payment channel enables payments from a customer to a merchant and only supports transfer of fixed-sized values in one direction.
### Channel Setup and Key Generation
The first part of setting up unidirectional payment channels involve generating initial setup parameters, channel state and key generation for both parties.
use bolt::unidirectional;
// setup unidirectional scheme params
let pp = unidirectional::setup(true);
// generate the initial channel state
let mut channel = unidirectional::ChannelState::new(String::from("My New Channel A -> B"));
To generate keys for both parties, call the `unidirectional::keygen()` routine with the public parameters as input.
// merchant generates a long-lived key pair
let m_keypair = unidirectional::keygen(&pp);
// customer generates an ephemeral keypair for use on a single channel
let c_keypair = unidirectional::keygen(&pp);
### Initialization
To initialize the channel for both parties, do the following:
let b0_merch = 50;
let b0_cust = 50;
// initialize on the merchant side with balance, b0_merch
let mut m_data = unidirectional::init_merchant(&pp, b0_merch, &m_keypair));
// generate the public params for the commitment scheme
let cm_csp = unidirectional::generate_commit_setup(&pp, &m_keypair.pk);
// initialize on the customer side with balance, b0_cust
let mut c_data = unidirectional::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
**TODO**
### Pay protocol
**TODO**
### Channel Closure Algorithms
**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.
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 state/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 state and initialize with balance
let (mut channel_token, mut merch_state, mut channel_state) = bidirectional::init_merchant(rng, &mut channel_state, "Bob");
// generate the customer state using the channel token from the merchant
let mut cust_state = bidirectional::init_customer(rng, // rng
&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 state commitment proof
let (com, com_proof) = bidirectional::establish_customer_generate_proof(rng, &mut channel_token, &mut cust_state);
// obtain close token for closing out channel
let close_token = bidirectional::establish_merchant_issue_close_token(rng, &channel_state, &com, &com_proof, &merch_state);
// customer verifies that close-token
assert!(cust_state.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_state);
// customer
assert!(bidirectional::establish_final(&mut channel_state, &mut cust_state, &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));
// phase 1 - payment proof and new cust state
let (payment, new_cust_state) = bidirectional::generate_payment_proof(rng, &channel_state, &cust_state, 10);
// phase 1 - merchant verifies the payment proof and returns a close-token
let new_close_token = bidirectional::verify_payment_proof(rng, &channel_state, &payment, &mut merch_state);
// phase 2 - verify the close-token, update cust state and generate a revoke token for previous cust state state
let revoke_token = bidirectional::generate_revoke_token(&channel_state, &mut cust_state, new_cust_state, &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_state);
// final - customer verifies the pay token and updates internal state
assert!(cust_state.verify_pay_token(&channel_state, &new_pay_token));
### Channel Closure Algorithms
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_msg = bidirectional::customer_close(&channel_state, &cust_state);
The merchant can dispute a customer's claim by executing the `bidirectional::merchant_retute()` routine as follows:
If the customer broadcasts an outdated version of his state, then the merchant can dispute this 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);
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));
let merch_close = bidirectional::merchant_close(&channel_state, &channel_token, &cust_close_msg, &merch_state);
`new_b0_cust` and `new_b0_merch` represent the new balances for the customer and merchant (respectively).
## Third-party Payments
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_state = bidirectional::ChannelState::<Bls12>::new(String::from("Third-party Channels"), true);
Moreover, the intermediary can set a channel fee as follows:
channel_a.set_channel_fee(5);
channel_state.set_channel_fee(5);
The channel establishment still works as described before and the pay protocol includes an additional step to verify that the payments on both channels cancel out or include a channel fee (if specified).
@ -264,28 +182,46 @@ 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,
&c1_data.channel_token, // channel token
&merch_keys.pk, // merchant pub key
&c1_data.csk, // wallet
// get payment proof on first channel with party A and H
let (sender_payment, new_cust_stateA) = bidirectional::generate_payment_proof(rng, &channel_state,
&cust_stateA,
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,
&c2_data.channel_token, // channel token
&m_keys.pk, // merchant pub key
&c2_data.csk, // wallet
-payment_amount); // bal dec
// verify that the payment proof is valid and cancels out or results in a fee
let tx_fee = channel_a.get_channel_fee() + channel_b.get_channel_fee();
assert!(bidirectional::verify_third_party_payment(&pp, tx_fee, &pay_proof1.bal_proof, &pay_proof2.bal_proof));
// get payment proof on second channel with party B and H
let (receiver_payment, new_cust_stateB) = bidirectional::generate_payment_proof(rng, &channel_state,
&cust_stateB,
-payment_amount); // bal dec
// intermediary executes the following on the two payment proofs
// verifies that the payment proof is valid & cancels out and results in hub's fee
let close_token_result = bidirectional::verify_multiple_payment_proofs(rng, &channel_state,
&sender_payment,
&receiver_payment,
&mut merch_state);
// alice gets a close token and bob gets a conditional token which requires alice's revoke token to be valid
let (alice_close_token, bob_cond_close_token) = handle_bolt_result!(close_token_result).unwrap();
// both alice and bob generate a revoke token
let revoke_token_alice = bidirectional::generate_revoke_token(&channel_state,
&mut cust_stateA,
new_cust_stateA,
&alice_close_token);
let revoke_token_bob = bidirectional::generate_revoke_token(&channel_state,
&mut cust_stateB,
new_cust_stateB,
&bob_cond_close_token);
// send both revoke tokens to intermediary and receive pay-tokens (one for sender and another for receiver)
let new_pay_tokens: BoltResult<(cl::Signature<Bls12>,cl::Signature<Bls12>)> = \
bidirectional::verify_multiple_revoke_tokens(&revoke_token_sender,
&revoke_token_receiver,
&mut merch_state);
...
See the `third_party_payment_basics_work()` unit test in `src/lib.rs` for more details.
See the `intermediary_payment_basics_works()` 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.
@ -301,11 +237,7 @@ To contribute code improvements, please checkout the repository, make your chang
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.
* Add more unit tests for other dispute resolution scenarios and third-party test cases
# License

28
cpp/libbolt.cpp Normal file
View File

@ -0,0 +1,28 @@
#include <iostream>
using namespace std;
#include "libbolt.h"
//#include "ChannelToken.h"
//#include "PublicKey.h"
//#include "PublicParams.h"
//#include "Wallet.h"
//#include "ChannelClosure.h"
int main()
{
// Test independent verification of customer close message
string channel_token = "{\"pk_c\":\"0264eb63272c8d85710cbe6ef8229a658e3760ee7cebcc9e3edcfc61b35152a91b\",\"pk_m\":\"034c5b6bd4484d0d0bf326603a1a233e4355bc387e86e9a79e30c46af8a5fe63d3\",\"cl_pk_m\":{\"X\":\"99534f1842c1cbfe3deaea17706118d1174bddd93608aa9b0272daff8b437f4b3a8f494459225eef60fd876603f1ce4f15d4e0b9fb03c5a2794c811e9292891af50ad5376a2957597e5fba62dbd128b75537e2758012d2e30bebb7932bb840ed\",\"Y\":[\"b799fc236c3da7a853399574c88843778abb741909f86c292a3219370ac38f75a23a91e614e78cf1897917504baf937d046b1cd8b1174ef6af5679a88e39629c13d1d43b4ae4e3873939922b4eefd5615724840b68cc5a105c8dec95630036f6\",\"939d1534dfc80141cc0a899d3de005959042f468935964d3e9e32ffa39ca748d73a031d8bf341786bbf09935f600f2550c5824b8be916e1f932e5c5c91170c75b2428a0beec60f3ba2662a3af3c5f802d0bc2c70de7835453d176efcbf37945d\",\"a3de219da28620ed395390e548dad8fb77018c1980f6f27da01e108ab4c733dfc762a1a3eea13be8cdaa0dea3bb97a800fb93c302d5399df71795f1576537e480ce4e426c56a3005e0f941acef33ce55307f9845b5b38a7b609fb1693abd5c00\",\"95b13dd750dd0607a8ba57bfef66061fe3f36e1d44a6dc139981f3d1254b65ea13281bcb927d36ed131b21b2b0a57db90ea2c1bf0e3bd9bc91ae37b207964371f86d1be1f673ae4025e297f52ee050921ce9e84745bcdc90e6964e960e321502\",\"acf4148503d43a671f138866c3dfd0b1f3cdcbb72792c81a17cc1dc7c45f83d6ba0c6e48d0e7fcdcfd19ab580269189d1718fca84a504cdc8dc82ed050771fd241cb0760224fa32d32caf6dff099bb0401e1ba116c6345844c9daa379d65a212\"]},\"mpk\":{\"g1\":\"b63bbc7ec491fa7554b5e869a54348932e2f1a33c5609f38fa1a1a02673838aeb6e64ac9d9a9780918369fc261aac15a\",\"g2\":\"85b95bf5e2334ee60138a9cccf3a81ea9e9c6ea50363e5b4c177618f23a479b2f16508e5af724e86faeb14799c2c6d6416ef8bb1b543851ceeaa011ce4a428b393649d1233cdbdcc7396726bdb75164c9569c967ef1240ea0336168a5b45e543\"},\"comParams\":{\"pub_bases\":[\"b63bbc7ec491fa7554b5e869a54348932e2f1a33c5609f38fa1a1a02673838aeb6e64ac9d9a9780918369fc261aac15a\",\"8fedaf4d995cd200ec0a95169b1e62076d9a313e1e3112cc1638f8e73c8033132f3f26c3e456b643de1ac9d771dddb98\",\"91d41ebf58fea106c4fc8a1143592c6b3d9079b65973653bf583197bc3b0ae4819d95d77b854896c7f06cbb7596a901d\",\"8b9d2c8ab1e7e8dc14cb45c455d2a11fbc3567f85142aa060065b3b7f903cd35e9eb40715308fec4d03d6e7082f2192b\",\"82463691a20acdf8d04ab402f084357b2f76b91fb72b4084d458e945ab33047f1800193f1f332c4e99ccd6a78ab15cdb\",\"85c709721b23511f80f116ed2aa382b6605ba088485922fef22cb1ddf8461e0fdc46abc8ab850c073a22c3d51418560c\"]}}";
string wpk = "\"02db85f7008f01a1984594d853baebca47b32d41814c0cb2312667d64556b497d3\"";
string cust_close = "{\"pkc\": [\"bbe4ec11548e83e7bd99aef03fae8deb0f31febcef4bd9e742dc24f50477f656\"], \"wpk\": [\"492cc33fae8d985ad8bc8c37d01f8b5a357a601cf141d9cc2aff15e359d5c0ae\"], \"bc\": 85, \"bm\": 25, \"close\": [\"bcd99a3e99e42360850e3e5eab0e5c8261fa14d6a47a20376be126a2f3ac5c0d\"]}";
string close_token = "{\"h\": \"b328d8e57391ed3b2a6844dbb51b21e3b59ae3a7b4c8960bb1c09800fd2a32c1fd7ae9e51a2f438ad0eed1614c2e303f\", \"H\": \"b31a760561e83fe3735f9af8c063e7501052df58dd17a469ee1be486c45f904cedf6b0f49be3f54499283f4dae2ee41a\"}";
int rc0 = wtp_verify_cust_close_message(channel_token.c_str(), wpk.c_str(), cust_close.c_str(), close_token.c_str());
cout << "wpk => " << wpk << endl;
cout << "cust close token => " << close_token << endl;
cout << "Valid WTP cust close message: " << (rc0 ? "true" : "false") << endl;
//int rc1 = wtp_verify_merch_close_message();
return 0;
}

View File

@ -1,179 +1,122 @@
extern crate bn;
extern crate rand;
extern crate rand_core;
extern crate bolt;
extern crate bincode;
extern crate ff;
extern crate pairing;
extern crate time;
extern crate secp256k1;
//extern crate serde_derive;
//extern crate serde;
//use bolt::unidirectional;
use bolt::bidirectional;
use time::PreciseTime;
use std::time::Instant;
use pairing::bls12_381::{Bls12};
use bolt::handle_bolt_result;
macro_rules! measure {
macro_rules! measure_one_arg {
($x: expr) => {
{
let s = PreciseTime::now();
let s = Instant::now();
let res = $x;
let e = PreciseTime::now();
(res, s.to(e))
let e = s.elapsed();
(res, e.as_millis())
};
}
}
macro_rules! measure_ret_mut {
macro_rules! measure_two_arg {
($x: expr) => {
{
let s = PreciseTime::now();
let mut handle = $x;
let e = PreciseTime::now();
(handle, s.to(e))
let s = Instant::now();
let (res1, res2) = $x;
let e = s.elapsed();
(res1, res2, e.as_millis())
};
}
}
//macro_rules! measure_ret_mut {
// ($x: expr) => {
// {
// let s = Instant::now();
// let mut handle = $x;
// let e = s.elapsed();
// (handle, s.as_millis())
// };
// }
//}
fn main() {
println!("******************************************");
// libbolt tests below
println!("Testing the channel setup...");
let mut channel_state = bidirectional::ChannelState::<Bls12>::new(String::from("Channel A -> B"), false);
let rng = &mut rand::thread_rng();
//println!("[1a] libbolt - setup bidirectional scheme params");
let (pp, setup_time1) = measure!(bidirectional::setup(false));
let b0_customer = 150;
let b0_merchant = 10;
let pay_inc = 20;
let pay_inc2 = 10;
//println!("[1b] libbolt - generate the initial channel state");
let mut channel = bidirectional::ChannelState::new(String::from("My New Channel A"), false);
let (mut channel_token, mut merch_state, mut channel_state) = bidirectional::init_merchant(rng, &mut channel_state, "Merchant Bob");
println!("Setup time: {}", setup_time1);
let mut cust_state = bidirectional::init_customer(rng, &mut channel_token, b0_customer, b0_merchant, "Alice");
//let msg = "Open Channel ID: ";
//libbolt::debug_elem_in_hex(msg, &channel.cid);
println!("{}", cust_state);
let b0_cust = 50;
let b0_merch = 50;
// lets establish the channel
let (com, com_proof, est_time) = measure_two_arg!(bidirectional::establish_customer_generate_proof(rng, &mut channel_token, &mut cust_state));
println!(">> Time to generate proof for establish: {} ms", est_time);
// 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));
// obtain close token for closing out channel
let channel_id = channel_token.compute_channel_id();
let option = bidirectional::establish_merchant_issue_close_token(rng, &channel_state, &com, &com_proof,
&channel_id, b0_customer, b0_merchant, &merch_state);
let close_token= match option {
Ok(n) => n.unwrap(),
Err(e) => panic!("Failed - bidirectional::establish_merchant_issue_close_token(): {}", e)
};
// 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));
assert!(cust_state.verify_close_token(&channel_state, &close_token));
// 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);
// wait for funding tx to be confirmed, etc
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...");
// obtain payment token for pay protocol
let pay_token = bidirectional::establish_merchant_issue_pay_token(rng, &channel_state, &com, &merch_state);
//assert!(cust_state.verify_pay_token(&channel_state, &pay_token));
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);
assert!(bidirectional::establish_customer_final(&mut channel_state, &mut cust_state, &pay_token));
println!("Channel established!");
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);
let (payment, new_cust_state, pay_time) = measure_two_arg!(bidirectional::generate_payment_proof(rng, &channel_state, &cust_state, pay_inc));
println!(">> Time to generate payment proof: {} ms", pay_time);
println!("[6c] libbolt - complete channel establishment");
assert!(bidirectional::establish_customer_final(&pp, &merch_keypair.pk, &mut cust_data.csk, wallet_sig));
let (new_close_token, verify_time) = measure_one_arg!(bidirectional::verify_payment_proof(rng, &channel_state, &payment, &mut merch_state));
println!(">> Time to verify payment proof: {} ms", verify_time);
assert!(channel.channel_established);
let revoke_token = bidirectional::generate_revoke_token(&channel_state, &mut cust_state, new_cust_state, &new_close_token);
println!("Channel has been established!");
println!("******************************************");
// send revoke token and get pay-token in response
let new_pay_token_result = bidirectional::verify_revoke_token(&revoke_token, &mut merch_state);
let new_pay_token = handle_bolt_result!(new_pay_token_result);
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, 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, 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!");
// verify the pay token and update internal state
assert!(cust_state.verify_pay_token(&channel_state, &new_pay_token.unwrap()));
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 (payment2, new_cust_state2, pay_time2) = measure_two_arg!(bidirectional::generate_payment_proof(rng, &channel_state, &cust_state, pay_inc2));
println!(">> Time to generate payment proof 2: {} ms", pay_time2);
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_close_token2, verify_time2) = measure_one_arg!(bidirectional::verify_payment_proof(rng, &channel_state, &payment2, &mut merch_state));
println!(">> Time to verify payment proof 2: {} ms", verify_time2);
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);
}
let revoke_token2 = bidirectional::generate_revoke_token(&channel_state, &mut cust_state, new_cust_state2, &new_close_token2);
// send revoke token and get pay-token in response
let new_pay_token_result2 = bidirectional::verify_revoke_token(&revoke_token2, &mut merch_state);
let new_pay_token2 = handle_bolt_result!(new_pay_token_result2);
// verify the pay token and update internal state
assert!(cust_state.verify_pay_token(&channel_state, &new_pay_token2.unwrap()));
println!("Final Cust state: {}", cust_state);
// TODO: add tests for customer/merchant cheating scenarios
println!("******************************************");
}

708
go/libbolt.go Normal file
View File

@ -0,0 +1,708 @@
package main
// #cgo CFLAGS: -I ../include -D LD_LIBRARY_PATH=../target/release
// #cgo LDFLAGS: -L ../target/release/ -lbolt
// #include <libbolt.h>
import "C"
import (
"encoding/json"
"strings"
)
type setupResp struct {
ChannelState string `json:"channel_state"`
ChannelToken string `json:"channel_token"`
CustState string `json:"cust_state"`
MerchState string `json:"merch_state"`
Com string `json:"com"`
ComProof string `json:"com_proof"`
IsTokenValid bool `json:"is_token_valid,string"`
IsEstablished bool `json:"is_established,string"`
IsPayValid bool `json:"is_pay_valid,string"`
Payment string `json:"payment"`
CloseToken string `json:"close_token"`
SenderCloseToken string `json:"sender_close_token"`
ReceiverCondCloseToken string `json:"receiver_cond_close_token"`
RevokeToken string `json:"revoke_token"`
PayToken string `json:"pay_token"`
SenderPayToken string `json:"sender_pay_token"`
ReceiverPayToken string `json:"receiver_pay_token"`
CustClose string `json:"cust_close"`
MerchClose string `json:"merch_close"`
Wpk string `json:"wpk"`
Error string `json:"error"`
Result string `json:"result"`
}
type ChannelState struct {
R int `json:"R"`
TxFee int64 `json:"tx_fee"`
Cp *ChannelParams `json:"cp"`
Name string `json:"name"`
PayInit bool `json:"pay_init"`
ChannelEstablished bool `json:"channel_established"`
ThirdParty bool `json:"third_party"`
}
type ChannelParams struct {
ExtraVerify bool `json:"extra_verify"`
L int64 `json:"l"`
PubParams PubParams `json:"pub_params"`
}
type PubParams struct {
ComParams ComParams `json:"comParams"`
Mpk MPK `json:"mpk"`
Pk PublicKey `json:"pk"`
RpParams RpPubParams `json:"rpParams"`
}
type RpPubParams struct {
ComParams ComParams `json:"csParams"`
L int64 `json:"l"`
U int64 `json:"u"`
Mpk MPK `json:"mpk"`
Pk PublicKey `json:"pk"`
Signatures map[string]Signature `json:"signatures"`
}
type MerchState struct {
Id string `json:"id"`
KeyPair KeyPair `json:"keypair"`
NizkParams NIZKParams `json:"nizkParams"`
Pk string `json:"pk"`
Sk string `json:"sk"`
ComParams ComParams `json:"comParams"`
Keys map[string]RevokedKey `json:"keys"`
PayTokens map[string]Signature `json:"pay_tokens"`
}
type RevokedKey struct {
Wpk string `json:"wpk"`
RevokeToken *string `json:"revoke_token"`
}
type NIZKParams struct {
PubParams PubParams `json:"pubParams"`
KeyPair KeyPair `json:"keypair"`
RpParams RpParams `json:"rpParams"`
}
type RpParams struct {
PubParams RpPubParams `json:"pubParams"`
KeyPair KeyPair `json:"kp"`
}
type CustState struct {
Name string `json:"name"`
PkC string `json:"pk_c"`
SkC string `json:"sk_c"`
CustBalance int64 `json:"cust_balance"`
MerchBalance int64 `json:"merch_balance"`
Wpk string `json:"wpk"`
Wsk string `json:"wsk"`
OldKP *KP `json:"old_kp,omitempty"`
T []string `json:"t"`
Wallet Wallet `json:"wallet"`
WCom Commitment `json:"w_com"`
Index int `json:"index"`
CloseTokens map[string]Signature `json:"close_tokens"`
PayTokens map[string]Signature `json:"pay_tokens"`
}
type KP struct {
Wpk string `json:"wpk,omitempty"`
Wsk string `json:"wsk,omitempty"`
}
type Commitment struct {
C string `json:"c"`
}
type Wallet struct {
ChannelId []string `json:"channelId"`
Wpk []string `json:"wpk"`
Bc int64 `json:"bc"`
Bm int64 `json:"bm"`
Close []string `json:"close"`
}
type KeyPair struct {
Secret SecretKey `json:"secret"`
Public PublicKey `json:"public"`
}
type SecretKey struct {
X []string `json:"x"`
Y [][]string `json:"y"`
}
type Payment struct {
Proof Proof `json:"proof"`
Com Commitment `json:"com"`
Wpk string `json:"wpk"`
Amount int64 `json:"amount"`
}
type Proof struct {
Sig Signature `json:"sig"`
SigProof SigProof `json:"sigProof"`
ComProof CommitmentProof `json:"comProof"`
RangeProofBC RangeProof `json:"rpBC"`
RangeProofBM RangeProof `json:"rpBM"`
}
type SigProof struct {
Zsig [][]string `json:"zsig"`
Zv []string `json:"zv"`
A interface{} `json:"a"`
}
type RangeProof struct {
V []Signature `json:"V"`
D string `json:"D"`
Com Commitment `json:"comm"`
SigProofs []SigProof `json:"sigProofs"`
Zr []string `json:"zr"`
Zs [][]string `json:"zs"`
}
type PublicKey struct {
X1 string `json:"X1"`
X2 string `json:"X2"`
Y1 []string `json:"Y1"`
Y2 []string `json:"Y2"`
}
type PublicKeySingle struct {
X string `json:"X"`
Y []string `json:"Y"`
}
type ComParams struct {
PubBases []string `json:"pub_bases"`
}
type Signature struct {
H1 string `json:"h"`
H2 string `json:"H"`
}
type ChannelToken struct {
Pkc *string `json:"pk_c"`
Pkm string `json:"pk_m"`
ClPkM PublicKeySingle `json:"cl_pk_m"`
Mpk MPK `json:"mpk"`
ComParams ComParams `json:"comParams"`
}
type MPK struct {
G1 string `json:"g1"`
G2 string `json:"g2"`
}
type RevokeToken struct {
Message Message `json:"message"`
Signature string `json:"signature"`
}
type Message struct {
Type string `json:"msgtype"`
Wpk string `json:"wpk"`
}
type CommitmentProof struct {
T string `json:"T"`
Z [][]string `json:"z"`
Ts [][]string `json:"t"`
Index []int `json:"index"`
Reveal [][]string `json:"reveal"`
}
type CustClose struct {
Wpk string `json:"wpk"`
Message Wallet `json:"message"`
Signature Signature `json:"signature"`
}
type ZkChannelParams struct {
Commitment Commitment `json:"commitment"`
CommitmentProof CommitmentProof `json:"commproof"`
CustPkC string `json:"custstatepkc"`
}
func BidirectionalChannelSetup(name string, channelSupport bool) (ChannelState, error) {
resp := C.GoString(C.ffishim_bidirectional_channel_setup(C.CString(name), C.uint(btoi(channelSupport))))
r, err := processCResponse(resp)
if err != nil {
return ChannelState{}, err
}
channelState := ChannelState{}
err = json.Unmarshal([]byte(r.ChannelState), &channelState)
return channelState, err
}
func BidirectionalInitMerchant(channelState ChannelState, nameMerchant string) (ChannelToken, MerchState, ChannelState, error) {
serChannelState, err := json.Marshal(channelState)
if err != nil {
return ChannelToken{}, MerchState{}, ChannelState{}, err
}
resp := C.GoString(C.ffishim_bidirectional_init_merchant(C.CString(string(serChannelState)), C.CString(nameMerchant)))
r, err := processCResponse(resp)
if err != nil {
return ChannelToken{}, MerchState{}, ChannelState{}, err
}
merchState := MerchState{}
err = json.Unmarshal([]byte(r.MerchState), &merchState)
if err != nil {
return ChannelToken{}, MerchState{}, ChannelState{}, err
}
err = json.Unmarshal([]byte(r.ChannelState), &channelState)
if err != nil {
return ChannelToken{}, MerchState{}, ChannelState{}, err
}
channelToken := ChannelToken{}
err = json.Unmarshal([]byte(r.ChannelToken), &channelToken)
return channelToken, merchState, channelState, err
}
func BidirectionalInitCustomer(channelToken ChannelToken, balanceCustomer int, balanceMerchant int, nameCustomer string) (ChannelToken, CustState, error) {
serChannelToken, err := json.Marshal(channelToken)
if err != nil {
return ChannelToken{}, CustState{}, err
}
resp := C.GoString(C.ffishim_bidirectional_init_customer(C.CString(string(serChannelToken)), C.longlong(balanceCustomer), C.longlong(balanceMerchant), C.CString(nameCustomer)))
r, err := processCResponse(resp)
if err != nil {
return ChannelToken{}, CustState{}, err
}
custState := CustState{}
err = json.Unmarshal([]byte(r.CustState), &custState)
if err != nil {
return ChannelToken{}, CustState{}, err
}
err = json.Unmarshal([]byte(r.ChannelToken), &channelToken)
return channelToken, custState, err
}
func BidirectionalEstablishCustomerGenerateProof(channelToken ChannelToken, custState CustState) (ChannelToken, CustState, Commitment, CommitmentProof, error) {
serChannelToken, err := json.Marshal(channelToken)
if err != nil {
return ChannelToken{}, CustState{}, Commitment{}, CommitmentProof{}, err
}
serCustState, err := json.Marshal(custState)
if err != nil {
return ChannelToken{}, CustState{}, Commitment{}, CommitmentProof{}, err
}
resp := C.GoString(C.ffishim_bidirectional_establish_customer_generate_proof(C.CString(string(serChannelToken)), C.CString(string(serCustState))))
r, err := processCResponse(resp)
if err != nil {
return ChannelToken{}, CustState{}, Commitment{}, CommitmentProof{}, err
}
err = json.Unmarshal([]byte(r.CustState), &custState)
if err != nil {
return ChannelToken{}, CustState{}, Commitment{}, CommitmentProof{}, err
}
err = json.Unmarshal([]byte(r.ChannelToken), &channelToken)
if err != nil {
return ChannelToken{}, CustState{}, Commitment{}, CommitmentProof{}, err
}
com := Commitment{}
err = json.Unmarshal([]byte(r.Com), &com)
if err != nil {
return ChannelToken{}, CustState{}, Commitment{}, CommitmentProof{}, err
}
comProof := CommitmentProof{}
err = json.Unmarshal([]byte(r.ComProof), &comProof)
return channelToken, custState, com, comProof, err
}
func BidirectionalEstablishMerchantIssueCloseToken(channelState ChannelState, com Commitment, comProof CommitmentProof, channelId []string, initCustBal int, initMerchBal int, merchState MerchState) (Signature, error) {
serChannelState, err := json.Marshal(channelState)
if err != nil {
return Signature{}, err
}
serCom, err := json.Marshal(com)
if err != nil {
return Signature{}, err
}
serMerchState, err := json.Marshal(merchState)
if err != nil {
return Signature{}, err
}
serComProof, err := json.Marshal(comProof)
if err != nil {
return Signature{}, err
}
serChannelId, err := json.Marshal(channelId)
if err != nil {
return Signature{}, err
}
resp := C.GoString(C.ffishim_bidirectional_establish_merchant_issue_close_token(C.CString(string(serChannelState)), C.CString(string(serCom)), C.CString(string(serComProof)), C.CString(string(serChannelId)), C.longlong(initCustBal), C.longlong(initMerchBal), C.CString(string(serMerchState))))
r, err := processCResponse(resp)
if err != nil {
return Signature{}, err
}
closeToken := Signature{}
err = json.Unmarshal([]byte(r.CloseToken), &closeToken)
return closeToken, err
}
func BidirectionalEstablishMerchantIssuePayToken(channelState ChannelState, com Commitment, merchState MerchState) (Signature, error) {
serChannelState, err := json.Marshal(channelState)
if err != nil {
return Signature{}, err
}
serCom, err := json.Marshal(com)
if err != nil {
return Signature{}, err
}
serMerchState, err := json.Marshal(merchState)
if err != nil {
return Signature{}, err
}
resp := C.GoString(C.ffishim_bidirectional_establish_merchant_issue_pay_token(C.CString(string(serChannelState)), C.CString(string(serCom)), C.CString(string(serMerchState))))
r, err := processCResponse(resp)
if err != nil {
return Signature{}, err
}
payToken := Signature{}
err = json.Unmarshal([]byte(r.PayToken), &payToken)
return payToken, err
}
func BidirectionalVerifyCloseToken(channelState ChannelState, custState CustState, closeToken Signature) (bool, ChannelState, CustState, error) {
serChannelState, err := json.Marshal(channelState)
if err != nil {
return false, ChannelState{}, CustState{}, err
}
serCloseToken, err := json.Marshal(closeToken)
if err != nil {
return false, ChannelState{}, CustState{}, err
}
serCustState, err := json.Marshal(custState)
if err != nil {
return false, ChannelState{}, CustState{}, err
}
resp := C.GoString(C.ffishim_bidirectional_verify_close_token(C.CString(string(serChannelState)), C.CString(string(serCustState)), C.CString(string(serCloseToken))))
r, err := processCResponse(resp)
if err != nil {
return false, ChannelState{}, CustState{}, err
}
err = json.Unmarshal([]byte(r.ChannelState), &channelState)
if err != nil {
return false, ChannelState{}, CustState{}, err
}
err = json.Unmarshal([]byte(r.CustState), &custState)
return r.IsTokenValid, channelState, custState, err
}
func BidirectionalEstablishCustomerFinal(channelState ChannelState, custState CustState, payToken Signature) (bool, ChannelState, CustState, error) {
serChannelState, err := json.Marshal(channelState)
if err != nil {
return false, ChannelState{}, CustState{}, err
}
serPayToken, err := json.Marshal(payToken)
if err != nil {
return false, ChannelState{}, CustState{}, err
}
serCustState, err := json.Marshal(custState)
if err != nil {
return false, ChannelState{}, CustState{}, err
}
resp := C.GoString(C.ffishim_bidirectional_establish_customer_final(C.CString(string(serChannelState)), C.CString(string(serCustState)), C.CString(string(serPayToken))))
r, err := processCResponse(resp)
if err != nil {
return false, ChannelState{}, CustState{}, err
}
err = json.Unmarshal([]byte(r.ChannelState), &channelState)
if err != nil {
return false, ChannelState{}, CustState{}, err
}
err = json.Unmarshal([]byte(r.CustState), &custState)
return r.IsEstablished, channelState, custState, err
}
func BidirectionalPayGeneratePaymentProof(channelState ChannelState, custState CustState, amount int) (Payment, CustState, error) {
serChannelState, err := json.Marshal(channelState)
if err != nil {
return Payment{}, CustState{}, err
}
serCustState, err := json.Marshal(custState)
if err != nil {
return Payment{}, CustState{}, err
}
resp := C.GoString(C.ffishim_bidirectional_pay_generate_payment_proof(C.CString(string(serChannelState)), C.CString(string(serCustState)), C.longlong(amount)))
r, err := processCResponse(resp)
if err != nil {
return Payment{}, CustState{}, err
}
payProof := Payment{}
err = json.Unmarshal([]byte(r.Payment), &payProof)
if err != nil {
return Payment{}, CustState{}, err
}
err = json.Unmarshal([]byte(r.CustState), &custState)
return payProof, custState, err
}
func BidirectionalPayVerifyPaymentProof(channelState ChannelState, payProof Payment, merchState MerchState) (Signature, MerchState, error) {
serChannelState, err := json.Marshal(channelState)
if err != nil {
return Signature{}, MerchState{}, err
}
serPayProof, err := json.Marshal(payProof)
if err != nil {
return Signature{}, MerchState{}, err
}
serMerchState, err := json.Marshal(merchState)
if err != nil {
return Signature{}, MerchState{}, err
}
resp := C.GoString(C.ffishim_bidirectional_pay_verify_payment_proof(C.CString(string(serChannelState)), C.CString(string(serPayProof)), C.CString(string(serMerchState))))
r, err := processCResponse(resp)
if err != nil {
return Signature{}, MerchState{}, err
}
err = json.Unmarshal([]byte(r.MerchState), &merchState)
if err != nil {
return Signature{}, MerchState{}, err
}
closeToken := &Signature{}
err = json.Unmarshal([]byte(r.CloseToken), closeToken)
return *closeToken, merchState, err
}
func BidirectionalPayVerifyMultiplePaymentProofs(channelState ChannelState, senderPayProof Payment, receiverPayProof Payment, merchState MerchState) (Signature, Signature, MerchState, error) {
serChannelState, err := json.Marshal(channelState)
if err != nil {
return Signature{}, Signature{}, MerchState{}, err
}
serSenderPayProof, err := json.Marshal(senderPayProof)
if err != nil {
return Signature{}, Signature{}, MerchState{}, err
}
serReceiverPayProof, err := json.Marshal(receiverPayProof)
if err != nil {
return Signature{}, Signature{}, MerchState{}, err
}
serMerchState, err := json.Marshal(merchState)
if err != nil {
return Signature{}, Signature{}, MerchState{}, err
}
resp := C.GoString(C.ffishim_bidirectional_pay_verify_multiple_payment_proofs(C.CString(string(serChannelState)), C.CString(string(serSenderPayProof)), C.CString(string(serReceiverPayProof)), C.CString(string(serMerchState))))
r, err := processCResponse(resp)
if err != nil {
return Signature{}, Signature{}, MerchState{}, err
}
err = json.Unmarshal([]byte(r.MerchState), &merchState)
if err != nil {
return Signature{}, Signature{}, MerchState{}, err
}
senderCloseToken := &Signature{}
err = json.Unmarshal([]byte(r.SenderCloseToken), senderCloseToken)
if err != nil {
return Signature{}, Signature{}, MerchState{}, err
}
receiverCondCloseToken := &Signature{}
err = json.Unmarshal([]byte(r.ReceiverCondCloseToken), receiverCondCloseToken)
return *senderCloseToken, *receiverCondCloseToken, merchState, err
}
func BidirectionalPayGenerateRevokeToken(channelState ChannelState, custState CustState, newCustState CustState, closeToken Signature) (RevokeToken, CustState, error) {
serChannelState, err := json.Marshal(channelState)
if err != nil {
return RevokeToken{}, CustState{}, err
}
serCloseToken, err := json.Marshal(closeToken)
if err != nil {
return RevokeToken{}, CustState{}, err
}
serCustState, err := json.Marshal(custState)
if err != nil {
return RevokeToken{}, CustState{}, err
}
serNewCustState, err := json.Marshal(newCustState)
if err != nil {
return RevokeToken{}, CustState{}, err
}
resp := C.GoString(C.ffishim_bidirectional_pay_generate_revoke_token(C.CString(string(serChannelState)), C.CString(string(serCustState)), C.CString(string(serNewCustState)), C.CString(string(serCloseToken))))
r, err := processCResponse(resp)
if err != nil {
return RevokeToken{}, CustState{}, err
}
err = json.Unmarshal([]byte(r.CustState), &custState)
if err != nil {
return RevokeToken{}, CustState{}, err
}
revokeToken := RevokeToken{}
err = json.Unmarshal([]byte(r.RevokeToken), &revokeToken)
return revokeToken, custState, err
}
func BidirectionalPayVerifyRevokeToken(revokeToken RevokeToken, merchState MerchState) (Signature, MerchState, error) {
serMerchState, err := json.Marshal(merchState)
if err != nil {
return Signature{}, MerchState{}, err
}
serRevokeToken, err := json.Marshal(revokeToken)
if err != nil {
return Signature{}, MerchState{}, err
}
resp := C.GoString(C.ffishim_bidirectional_pay_verify_revoke_token(C.CString(string(serRevokeToken)), C.CString(string(serMerchState))))
r, err := processCResponse(resp)
if err != nil {
return Signature{}, MerchState{}, err
}
err = json.Unmarshal([]byte(r.MerchState), &merchState)
if err != nil {
return Signature{}, MerchState{}, err
}
payToken := &Signature{}
err = json.Unmarshal([]byte(r.PayToken), payToken)
return *payToken, merchState, err
}
func BidirectionalPayVerifyMultipleRevokeTokens(senderRevokeToken RevokeToken, receiverRevokeToken RevokeToken, merchState MerchState) (Signature, Signature, MerchState, error) {
serMerchState, err := json.Marshal(merchState)
if err != nil {
return Signature{}, Signature{}, MerchState{}, err
}
serSenderRevokeToken, err := json.Marshal(senderRevokeToken)
if err != nil {
return Signature{}, Signature{}, MerchState{}, err
}
serReceiverRevokeToken, err := json.Marshal(receiverRevokeToken)
if err != nil {
return Signature{}, Signature{}, MerchState{}, err
}
resp := C.GoString(C.ffishim_bidirectional_pay_verify_multiple_revoke_tokens(C.CString(string(serSenderRevokeToken)), C.CString(string(serReceiverRevokeToken)), C.CString(string(serMerchState))))
r, err := processCResponse(resp)
if err != nil {
return Signature{}, Signature{}, MerchState{}, err
}
err = json.Unmarshal([]byte(r.MerchState), &merchState)
if err != nil {
return Signature{}, Signature{}, MerchState{}, err
}
senderPayToken := &Signature{}
err = json.Unmarshal([]byte(r.SenderPayToken), senderPayToken)
if err != nil {
return Signature{}, Signature{}, MerchState{}, err
}
receiverPayToken := &Signature{}
err = json.Unmarshal([]byte(r.ReceiverPayToken), receiverPayToken)
return *senderPayToken, *receiverPayToken, merchState, err
}
func BidirectionalPayVerifyPaymentToken(channelState ChannelState, custState CustState, payToken Signature) (CustState, bool, error) {
serChannelState, err := json.Marshal(channelState)
if err != nil {
return CustState{}, false, err
}
serPayToken, err := json.Marshal(payToken)
if err != nil {
return CustState{}, false, err
}
serCustState, err := json.Marshal(custState)
if err != nil {
return CustState{}, false, err
}
resp := C.GoString(C.ffishim_bidirectional_pay_verify_payment_token(C.CString(string(serChannelState)), C.CString(string(serCustState)), C.CString(string(serPayToken))))
r, err := processCResponse(resp)
if err != nil {
return CustState{}, false, err
}
err = json.Unmarshal([]byte(r.CustState), &custState)
return custState, r.IsPayValid, err
}
func BidirectionalCustomerClose(channelState ChannelState, custState CustState) (CustClose, error) {
serChannelState, err := json.Marshal(channelState)
if err != nil {
return CustClose{}, err
}
serCustState, err := json.Marshal(custState)
if err != nil {
return CustClose{}, err
}
resp := C.GoString(C.ffishim_bidirectional_customer_close(C.CString(string(serChannelState)), C.CString(string(serCustState))))
r, err := processCResponse(resp)
if err != nil {
return CustClose{}, err
}
custClose := CustClose{}
err = json.Unmarshal([]byte(r.CustClose), &custClose)
return custClose, err
}
func BidirectionalMerchantClose(channelState ChannelState, channelToken ChannelToken, serAddress string, custClose CustClose, merchState MerchState) (string, string, string, error) {
serChannelState, err := json.Marshal(channelState)
if err != nil {
return "", "", "", err
}
serMerchState, err := json.Marshal(merchState)
if err != nil {
return "", "", "", err
}
serChannelToken, err := json.Marshal(channelToken)
if err != nil {
return "", "", "", err
}
serCustClose, err := json.Marshal(custClose)
if err != nil {
return "", "", "", err
}
resp := C.GoString(C.ffishim_bidirectional_merchant_close(C.CString(string(serChannelState)), C.CString(string(serChannelToken)), C.CString(serAddress), C.CString(string(serCustClose)), C.CString(string(serMerchState))))
r, err := processCResponse(resp)
if err != nil {
return "", "", "", err
}
return r.Wpk, r.MerchClose, r.Error, nil
}
func BidirectionalWtpVerifyCustCloseMessage(channelToken ChannelToken, serWpk string, serCloseMsg string, serCloseToken string) (string, error) {
serChannelToken, err := json.Marshal(channelToken)
if err != nil {
return "", err
}
resp := C.GoString(C.ffishim_bidirectional_wtp_verify_cust_close_message(C.CString(string(serChannelToken)), C.CString(serWpk), C.CString(serCloseMsg), C.CString(string(serCloseToken))))
r, err := processCResponse(resp)
if err != nil {
return "", err
}
return r.Result, nil
}
func BidirectionalWtpVerifyMerchCloseMessage(channelToken ChannelToken, serWpk string, serMerchClose string) (string, error) {
serChannelToken, err := json.Marshal(channelToken)
if err != nil {
return "", err
}
resp := C.GoString(C.ffishim_bidirectional_wtp_verify_merch_close_message(C.CString(string(serChannelToken)), C.CString(serWpk), C.CString(serMerchClose)))
r, err := processCResponse(resp)
if err != nil {
return "", err
}
return r.Result, nil
}
func processCResponse(resp string) (*setupResp, error) {
resp = cleanJson(resp)
r := &setupResp{}
err := json.Unmarshal([]byte(resp), r)
return r, err
}
func cleanJson(in string) string {
resp := strings.ReplaceAll(in, "\"", "\\\"")
resp = strings.ReplaceAll(resp, "'", "\"")
return resp
}
func btoi(b bool) int {
if b {
return 1
}
return 0
}

172
go/libbolt_test.go Normal file
View File

@ -0,0 +1,172 @@
package main
import (
"github.com/stretchr/testify/assert"
"testing"
)
func Test_ChannelSetup(t *testing.T) {
_, channelToken, merchState, custState, err := setup(1000, 100)
assert.Nil(t, err)
assert.NotEqual(t, MerchState{}, merchState)
assert.NotEqual(t, CustState{}, custState)
assert.NotEqual(t, ChannelToken{}, channelToken)
}
func setup(b0Cust int, b0Merch int) (ChannelState, ChannelToken, MerchState, CustState, error) {
channelState, err := BidirectionalChannelSetup("Test Channel", false)
if err != nil {
return ChannelState{}, ChannelToken{}, MerchState{}, CustState{}, err
}
channelToken, merchState, channelState, err := BidirectionalInitMerchant(channelState, "Bob")
if err != nil {
return ChannelState{}, ChannelToken{}, MerchState{}, CustState{}, err
}
channelToken, custState, err := BidirectionalInitCustomer(channelToken, b0Cust, b0Merch, "Alice")
return channelState, channelToken, merchState, custState, err
}
func Test_Establish(t *testing.T) {
b0Cust := 1000
b0Merch := 100
channelState, channelToken, merchState, custState, err := setup(b0Cust, b0Merch)
assert.Nil(t, err)
channelToken, custState, com, comProof, err := BidirectionalEstablishCustomerGenerateProof(channelToken, custState)
assert.Nil(t, err)
closeToken, err := BidirectionalEstablishMerchantIssueCloseToken(channelState, com, comProof, custState.Wallet.ChannelId, b0Cust, b0Merch, merchState)
assert.Nil(t, err)
assert.NotNil(t, closeToken)
isTokenValid, channelState, custState, err := BidirectionalVerifyCloseToken(channelState, custState, closeToken)
assert.Nil(t, err)
assert.True(t, isTokenValid)
payToken, err := BidirectionalEstablishMerchantIssuePayToken(channelState, com, merchState)
assert.Nil(t, err)
assert.NotNil(t, payToken)
isChannelEstablished, channelState, custState, err := BidirectionalEstablishCustomerFinal(channelState, custState, payToken)
assert.Nil(t, err)
assert.True(t, isChannelEstablished)
}
func Test_Pay(t *testing.T) {
b0Cust := 1000
b0Merch := 100
channelState, channelToken, merchState, custState, err := setup(b0Cust, b0Merch)
assert.Nil(t, err)
channelToken, custState, com, comProof, err := BidirectionalEstablishCustomerGenerateProof(channelToken, custState)
assert.Nil(t, err)
closeToken, err := BidirectionalEstablishMerchantIssueCloseToken(channelState, com, comProof, custState.Wallet.ChannelId, b0Cust, b0Merch, merchState)
assert.Nil(t, err)
_, channelState, custState, err = BidirectionalVerifyCloseToken(channelState, custState, closeToken)
assert.Nil(t, err)
payToken, err := BidirectionalEstablishMerchantIssuePayToken(channelState, com, merchState)
assert.Nil(t, err)
_, channelState, custState, err = BidirectionalEstablishCustomerFinal(channelState, custState, payToken)
assert.Nil(t, err)
payment, newCustState, err := BidirectionalPayGeneratePaymentProof(channelState, custState, 10)
assert.Nil(t, err)
closeToken, merchState, err = BidirectionalPayVerifyPaymentProof(channelState, payment, merchState)
assert.Nil(t, err)
revokeToken, custState, err := BidirectionalPayGenerateRevokeToken(channelState, custState, newCustState, closeToken)
assert.Nil(t, err)
payToken, merchState, err = BidirectionalPayVerifyRevokeToken(revokeToken, merchState)
assert.Nil(t, err)
custState, isTokenValid, err := BidirectionalPayVerifyPaymentToken(channelState, custState, payToken)
assert.Nil(t, err)
assert.True(t, isTokenValid)
}
func Test_IntermediaryPay(t *testing.T) {
b0Alice := 1000
b0Bob := 100
b0Intermediary := 100
channelState, err := BidirectionalChannelSetup("Test Channel", false)
assert.Nil(t, err)
channelToken, merchState, channelState, err := BidirectionalInitMerchant(channelState, "Hub")
assert.Nil(t, err)
channelToken, custStateAlice, err := BidirectionalInitCustomer(channelToken, b0Alice, b0Intermediary, "Alice")
assert.Nil(t, err)
channelToken, custStateAlice, com, comProof, err := BidirectionalEstablishCustomerGenerateProof(channelToken, custStateAlice)
assert.Nil(t, err)
closeToken, err := BidirectionalEstablishMerchantIssueCloseToken(channelState, com, comProof, custStateAlice.Wallet.ChannelId, b0Alice, b0Intermediary, merchState)
assert.Nil(t, err)
_, channelState, custStateAlice, err = BidirectionalVerifyCloseToken(channelState, custStateAlice, closeToken)
assert.Nil(t, err)
payToken, err := BidirectionalEstablishMerchantIssuePayToken(channelState, com, merchState)
assert.Nil(t, err)
_, channelState, custStateAlice, err = BidirectionalEstablishCustomerFinal(channelState, custStateAlice, payToken)
assert.Nil(t, err)
channelToken, custStateBob, err := BidirectionalInitCustomer(channelToken, b0Bob, b0Intermediary, "Bob")
assert.Nil(t, err)
channelToken, custStateBob, com, comProof, err = BidirectionalEstablishCustomerGenerateProof(channelToken, custStateBob)
assert.Nil(t, err)
closeToken, err = BidirectionalEstablishMerchantIssueCloseToken(channelState, com, comProof, custStateBob.Wallet.ChannelId, b0Bob, b0Intermediary, merchState)
assert.Nil(t, err)
_, channelState, custStateBob, err = BidirectionalVerifyCloseToken(channelState, custStateBob, closeToken)
assert.Nil(t, err)
payToken, err = BidirectionalEstablishMerchantIssuePayToken(channelState, com, merchState)
assert.Nil(t, err)
_, channelState, custStateBob, err = BidirectionalEstablishCustomerFinal(channelState, custStateBob, payToken)
assert.Nil(t, err)
paymentA, newCustStateAlice, err := BidirectionalPayGeneratePaymentProof(channelState, custStateAlice, 10)
assert.Nil(t, err)
paymentB, newCustStateBob, err := BidirectionalPayGeneratePaymentProof(channelState, custStateBob, -10)
assert.Nil(t, err)
closeTokenA, closeTokenB, merchState, err := BidirectionalPayVerifyMultiplePaymentProofs(channelState, paymentA, paymentB, merchState)
assert.Nil(t, err)
revokeTokenA, custStateAlice, err := BidirectionalPayGenerateRevokeToken(channelState, custStateAlice, newCustStateAlice, closeTokenA)
assert.Nil(t, err)
revokeTokenB, custStateBob, err := BidirectionalPayGenerateRevokeToken(channelState, custStateBob, newCustStateBob, closeTokenB)
assert.Nil(t, err)
payTokenA, payTokenB, merchState, err := BidirectionalPayVerifyMultipleRevokeTokens(revokeTokenA, revokeTokenB, merchState)
assert.Nil(t, err)
custStateAlice, isTokenValid, err := BidirectionalPayVerifyPaymentToken(channelState, custStateAlice, payTokenA)
assert.Nil(t, err)
assert.True(t, isTokenValid)
custStateBob, isTokenValid, err = BidirectionalPayVerifyPaymentToken(channelState, custStateBob, payTokenB)
assert.Nil(t, err)
assert.True(t, isTokenValid)
}
func Test_Close(t *testing.T) {
b0Cust := 1000
b0Merch := 100
channelState, channelToken, merchState, custState, err := setup(b0Cust, b0Merch)
assert.Nil(t, err)
channelToken, custState, com, comProof, err := BidirectionalEstablishCustomerGenerateProof(channelToken, custState)
assert.Nil(t, err)
closeToken, err := BidirectionalEstablishMerchantIssueCloseToken(channelState, com, comProof, custState.Wallet.ChannelId, b0Cust, b0Merch, merchState)
assert.Nil(t, err)
_, channelState, custState, err = BidirectionalVerifyCloseToken(channelState, custState, closeToken)
assert.Nil(t, err)
payToken, err := BidirectionalEstablishMerchantIssuePayToken(channelState, com, merchState)
assert.Nil(t, err)
_, channelState, custState, err = BidirectionalEstablishCustomerFinal(channelState, custState, payToken)
assert.Nil(t, err)
payment, newCustState, err := BidirectionalPayGeneratePaymentProof(channelState, custState, 10)
assert.Nil(t, err)
closeToken, merchState, err = BidirectionalPayVerifyPaymentProof(channelState, payment, merchState)
assert.Nil(t, err)
revokeToken, custState, err := BidirectionalPayGenerateRevokeToken(channelState, custState, newCustState, closeToken)
assert.Nil(t, err)
payToken, merchState, err = BidirectionalPayVerifyRevokeToken(revokeToken, merchState)
assert.Nil(t, err)
custState, _, err = BidirectionalPayVerifyPaymentToken(channelState, custState, payToken)
assert.Nil(t, err)
custClose, err := BidirectionalCustomerClose(channelState, custState)
assert.Nil(t, err)
_, _, Err, err := BidirectionalMerchantClose(channelState, channelToken, "onChainAddress", custClose, merchState)
assert.Nil(t, err)
assert.Equal(t, "merchant_close - Could not find entry for wpk & revoke token pair. Valid close!", Err)
}

327
include/ChannelClosure.h Normal file
View File

@ -0,0 +1,327 @@
#ifndef __CHANNELCLOSURE_H__
#define __CHANNELCLOSURE_H__
#include <cstring>
#include <vector>
#include <ostream>
#include <sstream>
#include <iostream>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/prettywriter.h"
#define HEX_CHARS "0123456789abcdefABCDEF"
#define CHANNEL_CLOSURE_VECTOR_LENGTH 1426
#define CHANNEL_CLOSURE_HEX_STRING_LENGTH CHANNEL_CLOSURE_VECTOR_LENGTH*2
#define CHANNEL_CLOSURE_G2_LENGTH 129
#define CHANNEL_CLOSURE_A_ARRAY_SIZE 4 //2 <--- TODO GABE I thought these are supposed to be 2 long?
#define CHANNEL_CLOSURE_B_ARRAY_SIZE 4 //2
using namespace rapidjson;
class ChannelClosure : public std::vector<uint8_t> {
public:
std::string toJson() {
Document json;
// allocator for memory management
Document::AllocatorType& allocator = json.GetAllocator();
// define the document as an object rather than an array
json.SetObject();
if( CHANNEL_CLOSURE_VECTOR_LENGTH != this->size())
return("{}");
// iterator we are going to be using the whole time
std::vector<uint8_t>::iterator it = this->begin();
// Double check the a length
if( CHANNEL_CLOSURE_G2_LENGTH != *it )
return("{}");
it++;
Value a(kArrayType);
for( int j = 0; j< CHANNEL_CLOSURE_G2_LENGTH; j++)
{
a.PushBack(*it, allocator);
it++;
}
// Double check the b length
if( CHANNEL_CLOSURE_G2_LENGTH != *it )
return("{}");
it++;
Value b(kArrayType);
for( int j = 0; j< CHANNEL_CLOSURE_G2_LENGTH; j++)
{
b.PushBack(*it, allocator);
it++;
}
// Double check the c length
if( CHANNEL_CLOSURE_G2_LENGTH != *it )
return("{}");
it++;
Value c(kArrayType);
for( int j = 0; j< CHANNEL_CLOSURE_G2_LENGTH; j++)
{
c.PushBack(*it, allocator);
it++;
}
json.AddMember("a", a, allocator);
json.AddMember("b", b, allocator);
json.AddMember("c", c, allocator);
Value A(kArrayType);
// Check how many things there are
if( CHANNEL_CLOSURE_A_ARRAY_SIZE != *it )
return("{}");
it++;
// Check the size of the things
if( CHANNEL_CLOSURE_G2_LENGTH != *it )
return("{}");
it++;
for (int i =0; i <CHANNEL_CLOSURE_A_ARRAY_SIZE; i++)
{
Value temp(kArrayType);
for( int j = 0; j< CHANNEL_CLOSURE_G2_LENGTH; j++)
{
temp.PushBack(*it, allocator);
it++;
}
A.PushBack(temp, allocator);
}
Value B(kArrayType);
// Check how many things there are
if( CHANNEL_CLOSURE_B_ARRAY_SIZE != *it )
return("{}");
it++;
// Check the size of the things
if( CHANNEL_CLOSURE_G2_LENGTH != *it )
return("{}");
it++;
for (int i =0; i <CHANNEL_CLOSURE_A_ARRAY_SIZE; i++)
{
Value temp(kArrayType);
for( int j = 0; j< CHANNEL_CLOSURE_G2_LENGTH; j++)
{
temp.PushBack(*it, allocator);
it++;
}
B.PushBack(temp, allocator);
}
json.AddMember("A", A, allocator);
json.AddMember("B", B, allocator);
StringBuffer sb;
Writer<StringBuffer> writer(sb);
json.Accept(writer);
return sb.GetString();
}
bool fromJson(std::string s) {
Document json;
json.Parse(s.c_str());
eraseAll();
// // Make sure we arent going to get an error when indexing into the JSON
if(!json.HasMember("a"))
return false;
if(!json.HasMember("b"))
return false;
if(!json.HasMember("c"))
return false;
if(!json.HasMember("A"))
return false;
if(!json.HasMember("B"))
return false;
const Value& a = json["a"];
const Value& b = json["b"];
const Value& c = json["c"];
const Value& A = json["A"];
const Value& B = json["B"];
if(!a.IsArray())
return false;
if(!b.IsArray())
return false;
if(!c.IsArray())
return false;
if(!A.IsArray())
return false;
if(!B.IsArray())
return false;
if(!(a.Size() == SizeType(CHANNEL_CLOSURE_G2_LENGTH)))
return false;
if(!(b.Size() == SizeType(CHANNEL_CLOSURE_G2_LENGTH)))
return false;
if(!(c.Size() == SizeType(CHANNEL_CLOSURE_G2_LENGTH)))
return false;
if(!(A.Size() == SizeType(CHANNEL_CLOSURE_A_ARRAY_SIZE)))
return false;
if(!(B.Size() == SizeType(CHANNEL_CLOSURE_B_ARRAY_SIZE)))
return false;
// Add the header information
// From here on out, make sure to call cleanupAndFalse() instead of false
// a
this->push_back(CHANNEL_CLOSURE_G2_LENGTH);
for(SizeType j = 0; j < a.Size(); j++)
this->push_back(a[j].GetUint64());
// b
this->push_back(CHANNEL_CLOSURE_G2_LENGTH);
for(SizeType j = 0; j < c.Size(); j++)
this->push_back(b[j].GetUint64());
// c
this->push_back(CHANNEL_CLOSURE_G2_LENGTH);
for(SizeType j = 0; j < c.Size(); j++)
this->push_back(c[j].GetUint64());
// A
this->push_back(CHANNEL_CLOSURE_A_ARRAY_SIZE);
this->push_back(CHANNEL_CLOSURE_G2_LENGTH);
for (SizeType i = 0; i < A.Size(); i++)
{
const Value& vec = A[i];
if(!vec.IsArray())
return cleanupAndFalse();
if(!(vec.Size() == SizeType(CHANNEL_CLOSURE_G2_LENGTH)))
return cleanupAndFalse();
for(SizeType j = 0; j < vec.Size(); j++)
{
this->push_back(vec[j].GetUint64());
}
}
// B
this->push_back(CHANNEL_CLOSURE_B_ARRAY_SIZE);
this->push_back(CHANNEL_CLOSURE_G2_LENGTH);
for (SizeType i = 0; i < B.Size(); i++)
{
const Value& vec = B[i];
if(!vec.IsArray())
return cleanupAndFalse();
if(!(vec.Size() == SizeType(CHANNEL_CLOSURE_G2_LENGTH)))
return cleanupAndFalse();
for(SizeType j = 0; j < vec.Size(); j++)
{
this->push_back(vec[j].GetUint64());
}
}
// Make sure the final length is good
if(!(this->size() == CHANNEL_CLOSURE_VECTOR_LENGTH))
return cleanupAndFalse();
return true;
}
// Stolen from https://github.com/zeutro/openabe/blob/master/src/include/openabe/utils/zbytestring.h
bool fromHex(std::string s) {
if((s.find_first_not_of(HEX_CHARS) != std::string::npos) ||
(s.size() % 2 != 0)) {
return false;
}
if ( s.length() != CHANNEL_CLOSURE_HEX_STRING_LENGTH)
return false;
std::string hex_str;
std::stringstream ss;
int tmp;
this->clear();
for (size_t i = 0; i < s.size(); i += 2) {
hex_str = s[i];
hex_str += s[i+1];
ss << hex_str;
ss >> std::hex >> tmp;
this->push_back(tmp & 0xFF);
ss.clear();
}
return true;
}
std::string toHex() const {
std::stringstream ss;
int hex_len = 2;
char hex[hex_len+1];
std::memset(hex, 0, hex_len+1);
for (std::vector<uint8_t>::const_iterator it = this->begin();
it != this->end(); ++it) {
sprintf(hex, "%02X", *it);
ss << hex;
}
return ss.str();
}
std::string toLowerHex() const {
std::stringstream ss;
int hex_len = 2;
char hex[hex_len+1];
std::memset(hex, 0, hex_len+1);
for (std::vector<uint8_t>::const_iterator it = this->begin() ; it != this->end(); ++it) {
sprintf(hex, "%02x", *it);
ss << hex;
}
return ss.str();
}
void eraseAll() {
this->erase(this->begin(), this->end());
}
private:
bool cleanupAndFalse() {
eraseAll();
return false;
}
};
#endif

269
include/ChannelToken.h Normal file
View File

@ -0,0 +1,269 @@
#ifndef __CHANNELTOKEN_H__
#define __CHANNELTOKEN_H__
#include <cstring>
#include <vector>
#include <ostream>
#include <sstream>
#include <iostream>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/prettywriter.h"
#define HEX_CHARS "0123456789abcdefABCDEF"
#define VECTOR_LENGTH 810
#define HEX_STRING_LENGTH VECTOR_LENGTH*2
#define CHANNEL_TOKEN_NUM_BASE_POINTS 5
#define CHANNEL_TOKEN_LENGTH_POINT 129
#define CHANNEL_TOKEN_LENGTH_R 32
using namespace rapidjson;
class ChannelToken : public std::vector<uint8_t> {
public:
std::string toJson() {
Document json;
// allocator for memory management
Document::AllocatorType& allocator = json.GetAllocator();
// define the document as an object rather than an array
json.SetObject();
// 810 bytes => [g-count] [g-len] [g-bytes_i] [g-bytes_count] [c-len] [c-bytes] [r-len] [r-bytes]
if( VECTOR_LENGTH != this->size())
return("{}");
// iterator we are going to be using the whole time
std::vector<uint8_t>::iterator it = this->begin();
// Double check the g-count
if( CHANNEL_TOKEN_NUM_BASE_POINTS != *it )
return("{}");
it++;
// Double check the g-len
if( CHANNEL_TOKEN_LENGTH_POINT != *it )
return("{}");
it++;
Value params(kObjectType);
Value pub_bases(kArrayType);
for( int i = 0; i< CHANNEL_TOKEN_NUM_BASE_POINTS; i++)
{
Value base(kArrayType);
for( int j = 0; j< CHANNEL_TOKEN_LENGTH_POINT; j++)
{
base.PushBack(*it, allocator);
it++;
}
pub_bases.PushBack(base, allocator);
}
params.AddMember("pub_bases", pub_bases, allocator);
Value com(kObjectType);
// double check c-len
if( CHANNEL_TOKEN_LENGTH_POINT != *it )
return("{}");
it++;
Value c(kArrayType);
for( int j = 0; j< CHANNEL_TOKEN_LENGTH_POINT; j++)
{
c.PushBack(*it, allocator);
it++;
}
com.AddMember("c",c,allocator);
// double check r-len
if( CHANNEL_TOKEN_LENGTH_R != *it )
return("{}");
it++;
Value r(kArrayType);
for( int j = 0; j< CHANNEL_TOKEN_LENGTH_R; j++)
{
r.PushBack(*it, allocator);
it++;
}
com.AddMember("r",r,allocator);
// build final json string
json.AddMember("com", com, allocator);
json.AddMember("params", params, allocator);
StringBuffer sb;
Writer<StringBuffer> writer(sb);
json.Accept(writer);
return sb.GetString();
}
bool fromJson(std::string s) {
Document json;
json.Parse(s.c_str());
eraseAll();
// Make sure we arent going to get an error when indexing into the JSON
if(!json.HasMember("params"))
return false;
const Value& params = json["params"];
if(!params.IsObject())
return false;
if(!params.HasMember("pub_bases"))
return false;
const Value& pub_bases = params["pub_bases"];
if(!pub_bases.IsArray())
return false;
if(!(pub_bases.Size() == SizeType(CHANNEL_TOKEN_NUM_BASE_POINTS)))
return false;
//Checking the formatting in com ahead of time before we edit the internal vector
if(!json.HasMember("com"))
return false;
const Value& com = json["com"];
if(!com.IsObject())
return false;
if(!com.HasMember("c"))
return false;
if(!com.HasMember("r"))
return false;
const Value& c = com["c"];
const Value& r = com["r"];
if(!c.IsArray())
return false;
if(!r.IsArray())
return false;
if(!(c.Size() == SizeType(CHANNEL_TOKEN_LENGTH_POINT)))
return false;
if(!(r.Size() == SizeType(CHANNEL_TOKEN_LENGTH_R)))
return false;
// Add the header information
// From here on out, make sure to call cleanupAndFalse() instead of false
this->push_back(CHANNEL_TOKEN_NUM_BASE_POINTS);
this->push_back(CHANNEL_TOKEN_LENGTH_POINT);
for (SizeType i = 0; i < pub_bases.Size(); i++)
{
const Value& basepoint = pub_bases[i];
if(!basepoint.IsArray())
return cleanupAndFalse();
if(!(basepoint.Size() == SizeType(CHANNEL_TOKEN_LENGTH_POINT)))
return cleanupAndFalse();
for(SizeType j = 0; j < basepoint.Size(); j++)
{
this->push_back(basepoint[j].GetUint64());
}
}
this->push_back(CHANNEL_TOKEN_LENGTH_POINT);
for(SizeType j = 0; j < c.Size(); j++)
this->push_back(c[j].GetUint64());
this->push_back(CHANNEL_TOKEN_LENGTH_R);
for(SizeType j = 0; j < r.Size(); j++)
this->push_back(r[j].GetUint64());
// Make sure the final length is good
if(!(this->size() == VECTOR_LENGTH))
return cleanupAndFalse();
return true;
}
// Stolen from https://github.com/zeutro/openabe/blob/master/src/include/openabe/utils/zbytestring.h
bool fromHex(std::string s) {
if((s.find_first_not_of(HEX_CHARS) != std::string::npos) ||
(s.size() % 2 != 0)) {
return false;
}
if ( s.length() != HEX_STRING_LENGTH)
return false;
std::string hex_str;
std::stringstream ss;
int tmp;
this->clear();
for (size_t i = 0; i < s.size(); i += 2) {
hex_str = s[i];
hex_str += s[i+1];
ss << hex_str;
ss >> std::hex >> tmp;
this->push_back(tmp & 0xFF);
ss.clear();
}
return true;
}
std::string toHex() const {
std::stringstream ss;
int hex_len = 2;
char hex[hex_len+1];
std::memset(hex, 0, hex_len+1);
for (std::vector<uint8_t>::const_iterator it = this->begin();
it != this->end(); ++it) {
sprintf(hex, "%02X", *it);
ss << hex;
}
return ss.str();
}
std::string toLowerHex() const {
std::stringstream ss;
int hex_len = 2;
char hex[hex_len+1];
std::memset(hex, 0, hex_len+1);
for (std::vector<uint8_t>::const_iterator it = this->begin() ; it != this->end(); ++it) {
sprintf(hex, "%02x", *it);
ss << hex;
}
return ss.str();
}
void eraseAll() {
this->erase(this->begin(), this->end());
}
private:
bool cleanupAndFalse() {
eraseAll();
return false;
}
};
#endif

349
include/PublicKey.h Normal file
View File

@ -0,0 +1,349 @@
#ifndef __PUBLICKEY_H__
#define __PUBLICKEY_H__
#include <cstring>
#include <vector>
#include <ostream>
#include <sstream>
#include <iostream>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/prettywriter.h"
#define HEX_CHARS "0123456789abcdefABCDEF"
#define PUBLIC_KEY_VECTOR_LENGTH 1174
#define PUBLIC_KEY_HEX_STRING_LENGTH PUBLIC_KEY_VECTOR_LENGTH*2
#define PUBLIC_KEY_X_LENGTH 65
#define PUBLIC_KEY_Y_LENGTH 65
#define PUBLIC_KEY_Z_LENGTH 4
#define PUBLIC_KEY_Z_POINT_LENGTH 65
#define PUBLIC_KEY_Z2_LENGTH 4
#define PUBLIC_KEY_Z2_POINT_LENGTH 129
#define PUBLIC_KEY_W_LENGTH 4 //2
#define PUBLIC_KEY_W_POINT_LENGTH 65//130
using namespace rapidjson;
class PublicKey : public std::vector<uint8_t> {
public:
std::string toJson() {
Document json;
// allocator for memory management
Document::AllocatorType& allocator = json.GetAllocator();
// define the document as an object rather than an array
json.SetObject();
if( PUBLIC_KEY_VECTOR_LENGTH != this->size())
return("{}");
// iterator we are going to be using the whole time
std::vector<uint8_t>::iterator it = this->begin();
// Double check the X length
if( PUBLIC_KEY_X_LENGTH != *it )
return("{}");
it++;
Value X(kArrayType);
for( int j = 0; j< PUBLIC_KEY_X_LENGTH; j++)
{
X.PushBack(*it, allocator);
it++;
}
// Double check the Y length
if( PUBLIC_KEY_Y_LENGTH != *it )
return("{}");
it++;
Value Y(kArrayType);
for( int j = 0; j< PUBLIC_KEY_Y_LENGTH; j++)
{
Y.PushBack(*it, allocator);
it++;
}
// Double check the Z number
if( PUBLIC_KEY_Z_LENGTH != *it )
return("{}");
it++;
// ... and that they are the correct length
if( PUBLIC_KEY_Z_POINT_LENGTH != *it )
return("{}");
it++;
Value Z(kArrayType);
for( int i = 0; i< PUBLIC_KEY_Z_LENGTH; i++)
{
Value vec(kArrayType);
for( int j = 0; j< PUBLIC_KEY_Z_POINT_LENGTH; j++)
{
vec.PushBack(*it, allocator);
it++;
}
Z.PushBack(vec, allocator);
}
// Double check the Z2 number
if( PUBLIC_KEY_Z2_LENGTH != *it )
return("{}");
it++;
// ... and that they are the correct length
if( PUBLIC_KEY_Z2_POINT_LENGTH != *it )
return("{}");
it++;
Value Z2(kArrayType);
for( int i = 0; i< PUBLIC_KEY_Z2_LENGTH; i++)
{
Value vec(kArrayType);
for( int j = 0; j< PUBLIC_KEY_Z2_POINT_LENGTH; j++)
{
vec.PushBack(*it, allocator);
it++;
}
Z2.PushBack(vec, allocator);
}
// Double check the W number
if( PUBLIC_KEY_W_LENGTH != *it )
return("{}");
it++;
// ... and that they are the correct length
if( PUBLIC_KEY_W_POINT_LENGTH != *it )
return("{}");
it++;
Value W(kArrayType);
for( int j = 0; j< PUBLIC_KEY_W_LENGTH; j++)
{
Value vec(kArrayType);
for( int j = 0; j< PUBLIC_KEY_W_POINT_LENGTH; j++)
{
vec.PushBack(*it, allocator);
it++;
}
W.PushBack(vec, allocator);
}
// build final json string
json.AddMember("X", X, allocator);
json.AddMember("Y", Y, allocator);
json.AddMember("Z", Z, allocator);
json.AddMember("Z2", Z2, allocator);
json.AddMember("W", W, allocator);
StringBuffer sb;
Writer<StringBuffer> writer(sb);
json.Accept(writer);
return sb.GetString();
}
bool fromJson(std::string s) {
Document json;
json.Parse(s.c_str());
eraseAll();
// Make sure we arent going to get an error when indexing into the JSON
if(!json.HasMember("X"))
return false;
if(!json.HasMember("Y"))
return false;
if(!json.HasMember("Z"))
return false;
if(!json.HasMember("Z2"))
return false;
if(!json.HasMember("W"))
return false;
const Value& X = json["X"];
const Value& Y = json["Y"];
const Value& Z = json["Z"];
const Value& Z2 = json["Z2"];
const Value& W = json["W"];
if(!X.IsArray())
return false;
if(!Y.IsArray())
return false;
if(!Z.IsArray())
return false;
if(!Z2.IsArray())
return false;
if(!W.IsArray())
return false;
if(!(Z.Size() == SizeType(PUBLIC_KEY_Z_LENGTH)))
return false;
if(!(Z2.Size() == SizeType(PUBLIC_KEY_Z2_LENGTH)))
return false;
if(!(W.Size() == SizeType(PUBLIC_KEY_W_LENGTH)))
return false;
// Add the header information
// From here on out, make sure to call cleanupAndFalse() instead of false
// X
this->push_back(PUBLIC_KEY_X_LENGTH);
for(SizeType j = 0; j < X.Size(); j++)
this->push_back(X[j].GetUint64());
// Y
this->push_back(PUBLIC_KEY_Y_LENGTH);
for(SizeType j = 0; j < Y.Size(); j++)
this->push_back(Y[j].GetUint64());
// Z
this->push_back(PUBLIC_KEY_Z_LENGTH);
this->push_back(PUBLIC_KEY_Z_POINT_LENGTH);
for (SizeType i = 0; i < Z.Size(); i++)
{
const Value& vec = Z[i];
if(!vec.IsArray())
return cleanupAndFalse();
if(!(vec.Size() == SizeType(PUBLIC_KEY_Z_POINT_LENGTH)))
return cleanupAndFalse();
for(SizeType j = 0; j < vec.Size(); j++)
{
this->push_back(vec[j].GetUint64());
}
}
//Z2
this->push_back(PUBLIC_KEY_Z2_LENGTH);
this->push_back(PUBLIC_KEY_Z2_POINT_LENGTH);
for (SizeType i = 0; i < Z2.Size(); i++)
{
const Value& vec = Z2[i];
if(!vec.IsArray())
return cleanupAndFalse();
if(!(vec.Size() == SizeType(PUBLIC_KEY_Z2_POINT_LENGTH)))
return cleanupAndFalse();
for(SizeType j = 0; j < vec.Size(); j++)
{
this->push_back(vec[j].GetUint64());
}
}
// W
this->push_back(PUBLIC_KEY_W_LENGTH);
this->push_back(PUBLIC_KEY_W_POINT_LENGTH);
for (SizeType i = 0; i < W.Size(); i++)
{
const Value& vec = W[i];
if(!vec.IsArray())
return cleanupAndFalse();
if(!(vec.Size() == SizeType(PUBLIC_KEY_W_POINT_LENGTH)))
return cleanupAndFalse();
for(SizeType j = 0; j < vec.Size(); j++)
{
this->push_back(vec[j].GetUint64());
}
}
// Make sure the final length is good
if(!(this->size() == PUBLIC_KEY_VECTOR_LENGTH))
return cleanupAndFalse();
return true;
}
// Stolen from https://github.com/zeutro/openabe/blob/master/src/include/openabe/utils/zbytestring.h
bool fromHex(std::string s) {
if((s.find_first_not_of(HEX_CHARS) != std::string::npos) ||
(s.size() % 2 != 0)) {
return false;
}
if ( s.length() != PUBLIC_KEY_HEX_STRING_LENGTH)
return false;
std::string hex_str;
std::stringstream ss;
int tmp;
this->clear();
for (size_t i = 0; i < s.size(); i += 2) {
hex_str = s[i];
hex_str += s[i+1];
ss << hex_str;
ss >> std::hex >> tmp;
this->push_back(tmp & 0xFF);
ss.clear();
}
return true;
}
std::string toHex() const {
std::stringstream ss;
int hex_len = 2;
char hex[hex_len+1];
std::memset(hex, 0, hex_len+1);
for (std::vector<uint8_t>::const_iterator it = this->begin();
it != this->end(); ++it) {
sprintf(hex, "%02X", *it);
ss << hex;
}
return ss.str();
}
std::string toLowerHex() const {
std::stringstream ss;
int hex_len = 2;
char hex[hex_len+1];
std::memset(hex, 0, hex_len+1);
for (std::vector<uint8_t>::const_iterator it = this->begin() ; it != this->end(); ++it) {
sprintf(hex, "%02x", *it);
ss << hex;
}
return ss.str();
}
void eraseAll() {
this->erase(this->begin(), this->end());
}
private:
bool cleanupAndFalse() {
eraseAll();
return false;
}
};
#endif

232
include/PublicParams.h Normal file
View File

@ -0,0 +1,232 @@
#ifndef __PUBLICPARAMS_H__
#define __PUBLICPARAMS_H__
#include <cstring>
#include <vector>
#include <ostream>
#include <sstream>
#include <iostream>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/prettywriter.h"
#define HEX_CHARS "0123456789abcdefABCDEF"
#define PUBLIC_PARAMS_VECTOR_LENGTH 196
#define PUBLIC_PARAMS_HEX_STRING_LENGTH PUBLIC_PARAMS_VECTOR_LENGTH*2
#define PUBLIC_PARAMS_G1_LENGTH 65
#define PUBLIC_PARAMS_G2_LENGTH 129
#define PUBLIC_PARAMS_BPGENS_LENGTH 2
using namespace rapidjson;
class PublicParams : public std::vector<uint8_t> {
public:
std::string toJson() {
Document json;
// allocator for memory management
Document::AllocatorType& allocator = json.GetAllocator();
// define the document as an object rather than an array
json.SetObject();
if( PUBLIC_PARAMS_VECTOR_LENGTH != this->size())
return("{}");
// iterator we are going to be using the whole time
std::vector<uint8_t>::iterator it = this->begin();
Value cl_mpk(kObjectType);
// Double check the G1 length
if( PUBLIC_PARAMS_G1_LENGTH != *it )
return("{}");
it++;
Value g1(kArrayType);
for( int j = 0; j< PUBLIC_PARAMS_G1_LENGTH; j++)
{
g1.PushBack(*it, allocator);
it++;
}
// Double check the Y length
if( PUBLIC_PARAMS_G2_LENGTH != *it )
return("{}");
it++;
Value g2(kArrayType);
for( int j = 0; j< PUBLIC_PARAMS_G2_LENGTH; j++)
{
g2.PushBack(*it, allocator);
it++;
}
cl_mpk.AddMember("g1", g1, allocator);
cl_mpk.AddMember("g2", g2, allocator);
json.AddMember("cl_mpk", cl_mpk, allocator);
//Adding in the defaults TODO FIGURE OUT IS THESE ARE CONSTANT
json.AddMember("l", 4, allocator);
Value bp_gens(kArrayType);
bp_gens.PushBack(64, allocator).PushBack(1, allocator);
json.AddMember("bp_gens", bp_gens, allocator);
json.AddMember("range_proof_bits",32, allocator);
json.AddMember("extra_verify", false, allocator);
StringBuffer sb;
Writer<StringBuffer> writer(sb);
json.Accept(writer);
return sb.GetString();
}
bool fromJson(std::string s) {
Document json;
json.Parse(s.c_str());
eraseAll();
// // Make sure we arent going to get an error when indexing into the JSON
if(!json.HasMember("cl_mpk"))
return false;
// Defaults. We might be able to ignore?
if(!json.HasMember("l"))
return false;
if(!json.HasMember("bp_gens"))
return false;
if(!json.HasMember("range_proof_bits"))
return false;
if(!json.HasMember("extra_verify"))
return false;
const Value& cl_mpk = json["cl_mpk"];
if(!cl_mpk.IsObject())
return false;
if(!cl_mpk.HasMember("g1"))
return false;
if(!cl_mpk.HasMember("g2"))
return false;
const Value& g1 = cl_mpk["g1"];
const Value& g2 = cl_mpk["g2"];
if(!g1.IsArray())
return false;
if(!g2.IsArray())
return false;
if(!(g1.Size() == SizeType(PUBLIC_PARAMS_G1_LENGTH)))
return false;
if(!(g2.Size() == SizeType(PUBLIC_PARAMS_G2_LENGTH)))
return false;
// Add the header information
// From here on out, make sure to call cleanupAndFalse() instead of false
// g1
this->push_back(PUBLIC_PARAMS_G1_LENGTH);
for(SizeType j = 0; j < g1.Size(); j++)
this->push_back(g1[j].GetUint64());
// Y
this->push_back(PUBLIC_PARAMS_G2_LENGTH);
for(SizeType j = 0; j < g2.Size(); j++)
this->push_back(g2[j].GetUint64());
// Make sure the final length is good
if(!(this->size() == PUBLIC_PARAMS_VECTOR_LENGTH))
return cleanupAndFalse();
return true;
}
// Stolen from https://github.com/zeutro/openabe/blob/master/src/include/openabe/utils/zbytestring.h
bool fromHex(std::string s) {
if((s.find_first_not_of(HEX_CHARS) != std::string::npos) ||
(s.size() % 2 != 0)) {
return false;
}
if ( s.length() != PUBLIC_PARAMS_HEX_STRING_LENGTH)
return false;
std::string hex_str;
std::stringstream ss;
int tmp;
this->clear();
for (size_t i = 0; i < s.size(); i += 2) {
hex_str = s[i];
hex_str += s[i+1];
ss << hex_str;
ss >> std::hex >> tmp;
this->push_back(tmp & 0xFF);
ss.clear();
}
return true;
}
std::string toHex() const {
std::stringstream ss;
int hex_len = 2;
char hex[hex_len+1];
std::memset(hex, 0, hex_len+1);
for (std::vector<uint8_t>::const_iterator it = this->begin();
it != this->end(); ++it) {
sprintf(hex, "%02X", *it);
ss << hex;
}
return ss.str();
}
std::string toLowerHex() const {
std::stringstream ss;
int hex_len = 2;
char hex[hex_len+1];
std::memset(hex, 0, hex_len+1);
for (std::vector<uint8_t>::const_iterator it = this->begin() ; it != this->end(); ++it) {
sprintf(hex, "%02x", *it);
ss << hex;
}
return ss.str();
}
void eraseAll() {
this->erase(this->begin(), this->end());
}
private:
bool cleanupAndFalse() {
eraseAll();
return false;
}
};
#endif

183
include/Wallet.h Normal file
View File

@ -0,0 +1,183 @@
#ifndef __WALLET_H__
#define __WALLET_H__
#include <cstring>
#include <vector>
#include <ostream>
#include <sstream>
#include <iostream>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/prettywriter.h"
#define HEX_CHARS "0123456789abcdefABCDEF"
#define WALLET_VECTOR_LENGTH 130
#define WALLET_HEX_STRING_LENGTH WALLET_VECTOR_LENGTH*2
#define WALLET_LENGTH 4
#define WALLET_POINT_LENGTH 32
using namespace rapidjson;
class Wallet : public std::vector<uint8_t> {
public:
std::string toJson() {
Document json;
// allocator for memory management
Document::AllocatorType& allocator = json.GetAllocator();
// define the document as an object rather than an array
json.SetArray();
if( WALLET_VECTOR_LENGTH != this->size())
return("{}");
// iterator we are going to be using the whole time
std::vector<uint8_t>::iterator it = this->begin();
// Double check the Wallet length
if( WALLET_LENGTH != *it )
return("{}");
it++;
// Double check the Wallet element length
if( WALLET_POINT_LENGTH != *it )
return("{}");
it++;
for( int i = 0; i< WALLET_LENGTH; i++)
{
Value point(kArrayType);
for( int j = 0; j< WALLET_POINT_LENGTH; j++)
{
point.PushBack(*it, allocator);
it++;
}
json.PushBack(point, allocator);
}
StringBuffer sb;
Writer<StringBuffer> writer(sb);
json.Accept(writer);
return sb.GetString();
}
bool fromJson(std::string s) {
Document json;
json.Parse(s.c_str());
eraseAll();
// // Make sure we arent going to get an error when indexing into the JSON
if(!json.IsArray())
return false;
if(!(json.Size() == SizeType(WALLET_LENGTH)))
return false;
this->push_back(WALLET_LENGTH);
this->push_back(WALLET_POINT_LENGTH);
for( int i =0; i< WALLET_LENGTH; i++)
{
const Value& point = json[i];
if(!point.IsArray())
return false;
if(!(point.Size() == SizeType(WALLET_POINT_LENGTH)))
cleanupAndFalse();
for(SizeType j = 0; j < point.Size(); j++)
this->push_back(point[j].GetUint64());
}
// Make sure the final length is good
if(!(this->size() == WALLET_VECTOR_LENGTH))
return cleanupAndFalse();
return true;
}
// Stolen from https://github.com/zeutro/openabe/blob/master/src/include/openabe/utils/zbytestring.h
bool fromHex(std::string s) {
if((s.find_first_not_of(HEX_CHARS) != std::string::npos) ||
(s.size() % 2 != 0)) {
return false;
}
if ( s.length() != WALLET_HEX_STRING_LENGTH)
return false;
std::string hex_str;
std::stringstream ss;
int tmp;
this->clear();
for (size_t i = 0; i < s.size(); i += 2) {
hex_str = s[i];
hex_str += s[i+1];
ss << hex_str;
ss >> std::hex >> tmp;
this->push_back(tmp & 0xFF);
ss.clear();
}
return true;
}
std::string toHex() const {
std::stringstream ss;
int hex_len = 2;
char hex[hex_len+1];
std::memset(hex, 0, hex_len+1);
for (std::vector<uint8_t>::const_iterator it = this->begin();
it != this->end(); ++it) {
sprintf(hex, "%02X", *it);
ss << hex;
}
return ss.str();
}
std::string toLowerHex() const {
std::stringstream ss;
int hex_len = 2;
char hex[hex_len+1];
std::memset(hex, 0, hex_len+1);
for (std::vector<uint8_t>::const_iterator it = this->begin() ; it != this->end(); ++it) {
sprintf(hex, "%02x", *it);
ss << hex;
}
return ss.str();
}
void eraseAll() {
this->erase(this->begin(), this->end());
}
private:
bool cleanupAndFalse() {
eraseAll();
return false;
}
};
#endif

123
include/libbolt.h Normal file
View File

@ -0,0 +1,123 @@
#ifndef LIBBOLT_INCLUDE_H_
#define LIBBOLT_INCLUDE_H_
#include <stdint.h>
#include <errno.h>
#ifdef __cplusplus
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
using namespace rapidjson;
extern "C" {
#endif
// channel init
char* ffishim_bidirectional_channel_setup(const char *channel_name, unsigned int third_party_support);
char* ffishim_bidirectional_init_merchant(const char *ser_channel_state, const char *name_ptr);
char* ffishim_bidirectional_init_customer(const char *ser_channel_token, long long int balance_customer, long long int balance_merchant, const char *name_ptr);
// channel establish protocol routines
char* ffishim_bidirectional_establish_customer_generate_proof(const char *ser_channel_token, const char *ser_customer_wallet);
char* ffishim_bidirectional_establish_merchant_issue_close_token(const char *ser_channel_state, const char *ser_com, const char *ser_com_proof, const char *ser_pk_c, long long int init_cust_bal, long long int init_merch_bal, const char *ser_merch_state);
char* ffishim_bidirectional_establish_merchant_issue_pay_token(const char *ser_channel_state, const char *ser_com, const char *ser_merch_state);
char* ffishim_bidirectional_verify_close_token(const char *ser_channel_state, const char *ser_customer_wallet, const char *ser_close_token);
char* ffishim_bidirectional_establish_customer_final(const char *ser_channel_state, const char *ser_customer_wallet, const char *ser_pay_token);
// channel pay protocol routines
char* ffishim_bidirectional_pay_generate_payment_proof(const char *ser_channel_state, const char *ser_customer_wallet, long long int amount);
char* ffishim_bidirectional_pay_verify_payment_proof(const char *ser_channel_state, const char *ser_pay_proof, const char *ser_merch_state);
char* ffishim_bidirectional_pay_verify_multiple_payment_proofs(const char *ser_channel_state, const char *ser_sender_pay_proof, const char *ser_receiver_pay_proof, const char *ser_merch_state);
char* ffishim_bidirectional_pay_generate_revoke_token(const char *ser_channel_state, const char *ser_cust_state, const char *ser_new_cust_state, const char *ser_close_token);
char* ffishim_bidirectional_pay_verify_revoke_token(const char *ser_revoke_token, const char *ser_merch_state);
char* ffishim_bidirectional_pay_verify_multiple_revoke_tokens(const char *ser_sender_revoke_token, const char *ser_receiver_revoke_token, const char *ser_merch_state);
char* ffishim_bidirectional_pay_verify_payment_token(const char *ser_channel_state, const char *ser_cust_state, const char *ser_pay_token);
// closing routines for both sides
char* ffishim_bidirectional_customer_close(const char *ser_channel_state, const char *ser_cust_state);
char* ffishim_bidirectional_merchant_close(const char *ser_channel_state, const char *ser_channel_token, const char *ser_address, const char *ser_cust_close, const char *ser_merch_state);
// WTP logic for on-chain validation of closing messages
char* ffishim_bidirectional_wtp_verify_cust_close_message(const char *ser_channel_token, const char *ser_wpk, const char *ser_close_msg, const char *ser_close_token);
char* ffishim_bidirectional_wtp_verify_merch_close_message(const char *ser_channel_token, const char *ser_wpk, const char *ser_merch_close);
char* ffishim_bidirectional_wtp_check_wpk(const char *wpk);
#ifdef __cplusplus
const char* string_replace_all(const char* previous_string, char old_char, char new_char)
{
std::string s(previous_string);
std::string old_c(1,old_char);
std::string new_c(1,new_char);
size_t index;
while ((index = s.find(old_c)) != std::string::npos) {
s.replace(index, 1, new_c);
}
printf("STRING: %s\n", s.c_str());
return s.c_str();
}
int wtp_check_wpk(const char *wpk)
{
const char *ret = ffishim_bidirectional_wtp_check_wpk(wpk);
printf("RESULT: %s\n", ret);
return 0;
}
/* Purpose: verify cust close message
* Arguments: take as input the channel token and wpk
*
* Returns: 0 (false) or 1 (true)
*/
int wtp_verify_cust_close_message(const char *channel_token, const char *wpk, const char *cust_close, const char *close_token)
{
// Call rust
const char *return_json = ffishim_bidirectional_wtp_verify_cust_close_message(channel_token, wpk, cust_close, close_token);
Document d;
d.Parse(return_json);
// Make sure we arent going to get an error when indexing into the JSON
assert(d.HasMember("result"));
Value& s = d["result"];
// If the return_value is true, then return 1. Otherwise, just assume 0
if( std::string(s.GetString()).compare(std::string("true")) == 0)
{
return 1;
}
return 0;
}
/* Purpose: verify merch close message
* Arguments: take as input the master pub params for CL (pp), serialized channel closure message (rc_c),
*
* Returns: 0 (false) or 1 (true)
*/
int wtp_verify_merch_close_message(const char *channel_token, const char *wpk, const char *merch_close)
{
// Call into Rust
const char* return_json = string_replace_all(ffishim_bidirectional_wtp_verify_merch_close_message(channel_token, wpk, merch_close), '\'', '\"');
Document d;
d.Parse(return_json);
// Make sure we arent going to get an error when indexing into the JSON
assert(d.HasMember("result"));
Value& s = d["result"];
// If the return_value is true, then return 1. Otherwise, just assume 0
if( std::string(s.GetString()).compare(std::string("true")) == 0)
{
return 1;
}
return 0;
}
}
#endif // end c++ check
#endif // LIBBOLT_INCLUDE_H_

View File

@ -0,0 +1,284 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ALLOCATORS_H_
#define RAPIDJSON_ALLOCATORS_H_
#include "rapidjson.h"
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Allocator
/*! \class rapidjson::Allocator
\brief Concept for allocating, resizing and freeing memory block.
Note that Malloc() and Realloc() are non-static but Free() is static.
So if an allocator need to support Free(), it needs to put its pointer in
the header of memory block.
\code
concept Allocator {
static const bool kNeedFree; //!< Whether this allocator needs to call Free().
// Allocate a memory block.
// \param size of the memory block in bytes.
// \returns pointer to the memory block.
void* Malloc(size_t size);
// Resize a memory block.
// \param originalPtr The pointer to current memory block. Null pointer is permitted.
// \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
// \param newSize the new size in bytes.
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
// Free a memory block.
// \param pointer to the memory block. Null pointer is permitted.
static void Free(void *ptr);
};
\endcode
*/
/*! \def RAPIDJSON_ALLOCATOR_DEFUALT_CHUNK_CAPACITY
\ingroup RAPIDJSON_CONFIG
\brief User-defined kDefaultChunkCapacity definition.
User can define this as any \c size that is a power of 2.
*/
#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY
#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024)
#endif
///////////////////////////////////////////////////////////////////////////////
// CrtAllocator
//! C-runtime library allocator.
/*! This class is just wrapper for standard C library memory routines.
\note implements Allocator concept
*/
class CrtAllocator {
public:
static const bool kNeedFree = true;
void* Malloc(size_t size) {
if (size) // behavior of malloc(0) is implementation defined.
return std::malloc(size);
else
return NULL; // standardize to returning NULL.
}
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
(void)originalSize;
if (newSize == 0) {
std::free(originalPtr);
return NULL;
}
return std::realloc(originalPtr, newSize);
}
static void Free(void *ptr) { std::free(ptr); }
};
///////////////////////////////////////////////////////////////////////////////
// MemoryPoolAllocator
//! Default memory allocator used by the parser and DOM.
/*! This allocator allocate memory blocks from pre-allocated memory chunks.
It does not free memory blocks. And Realloc() only allocate new memory.
The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
User may also supply a buffer as the first chunk.
If the user-buffer is full then additional chunks are allocated by BaseAllocator.
The user-buffer is not deallocated by this allocator.
\tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
\note implements Allocator concept
*/
template <typename BaseAllocator = CrtAllocator>
class MemoryPoolAllocator {
public:
static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
//! Constructor with chunkSize.
/*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
\param baseAllocator The allocator for allocating memory chunks.
*/
MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
{
}
//! Constructor with user-supplied buffer.
/*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
The user buffer will not be deallocated when this allocator is destructed.
\param buffer User supplied buffer.
\param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
\param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
\param baseAllocator The allocator for allocating memory chunks.
*/
MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
{
RAPIDJSON_ASSERT(buffer != 0);
RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
chunkHead_->capacity = size - sizeof(ChunkHeader);
chunkHead_->size = 0;
chunkHead_->next = 0;
}
//! Destructor.
/*! This deallocates all memory chunks, excluding the user-supplied buffer.
*/
~MemoryPoolAllocator() {
Clear();
RAPIDJSON_DELETE(ownBaseAllocator_);
}
//! Deallocates all memory chunks, excluding the user-supplied buffer.
void Clear() {
while (chunkHead_ && chunkHead_ != userBuffer_) {
ChunkHeader* next = chunkHead_->next;
baseAllocator_->Free(chunkHead_);
chunkHead_ = next;
}
if (chunkHead_ && chunkHead_ == userBuffer_)
chunkHead_->size = 0; // Clear user buffer
}
//! Computes the total capacity of allocated memory chunks.
/*! \return total capacity in bytes.
*/
size_t Capacity() const {
size_t capacity = 0;
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
capacity += c->capacity;
return capacity;
}
//! Computes the memory blocks allocated.
/*! \return total used bytes.
*/
size_t Size() const {
size_t size = 0;
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
size += c->size;
return size;
}
//! Allocates a memory block. (concept Allocator)
void* Malloc(size_t size) {
if (!size)
return NULL;
size = RAPIDJSON_ALIGN(size);
if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size))
return NULL;
void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size;
chunkHead_->size += size;
return buffer;
}
//! Resizes a memory block (concept Allocator)
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
if (originalPtr == 0)
return Malloc(newSize);
if (newSize == 0)
return NULL;
originalSize = RAPIDJSON_ALIGN(originalSize);
newSize = RAPIDJSON_ALIGN(newSize);
// Do not shrink if new size is smaller than original
if (originalSize >= newSize)
return originalPtr;
// Simply expand it if it is the last allocation and there is sufficient space
if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) {
size_t increment = static_cast<size_t>(newSize - originalSize);
if (chunkHead_->size + increment <= chunkHead_->capacity) {
chunkHead_->size += increment;
return originalPtr;
}
}
// Realloc process: allocate and copy memory, do not free original buffer.
if (void* newBuffer = Malloc(newSize)) {
if (originalSize)
std::memcpy(newBuffer, originalPtr, originalSize);
return newBuffer;
}
else
return NULL;
}
//! Frees a memory block (concept Allocator)
static void Free(void *ptr) { (void)ptr; } // Do nothing
private:
//! Copy constructor is not permitted.
MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
//! Copy assignment operator is not permitted.
MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
//! Creates a new chunk.
/*! \param capacity Capacity of the chunk in bytes.
\return true if success.
*/
bool AddChunk(size_t capacity) {
if (!baseAllocator_)
ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)();
if (ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) {
chunk->capacity = capacity;
chunk->size = 0;
chunk->next = chunkHead_;
chunkHead_ = chunk;
return true;
}
else
return false;
}
static const int kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity.
//! Chunk header for perpending to each chunk.
/*! Chunks are stored as a singly linked list.
*/
struct ChunkHeader {
size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
size_t size; //!< Current size of allocated memory in bytes.
ChunkHeader *next; //!< Next chunk in the linked list.
};
ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
void *userBuffer_; //!< User supplied buffer.
BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object.
};
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_ENCODINGS_H_

View File

@ -0,0 +1,78 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_
#define RAPIDJSON_CURSORSTREAMWRAPPER_H_
#include "stream.h"
#if defined(__GNUC__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#if defined(_MSC_VER) && _MSC_VER <= 1800
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4702) // unreachable code
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Cursor stream wrapper for counting line and column number if error exists.
/*!
\tparam InputStream Any stream that implements Stream Concept
*/
template <typename InputStream, typename Encoding = UTF8<> >
class CursorStreamWrapper : public GenericStreamWrapper<InputStream, Encoding> {
public:
typedef typename Encoding::Ch Ch;
CursorStreamWrapper(InputStream& is):
GenericStreamWrapper<InputStream, Encoding>(is), line_(1), col_(0) {}
// counting line and column number
Ch Take() {
Ch ch = this->is_.Take();
if(ch == '\n') {
line_ ++;
col_ = 0;
} else {
col_ ++;
}
return ch;
}
//! Get the error line number, if error exists.
size_t GetLine() const { return line_; }
//! Get the error column number, if error exists.
size_t GetColumn() const { return col_; }
private:
size_t line_; //!< Current Line
size_t col_; //!< Current Column
};
#if defined(_MSC_VER) && _MSC_VER <= 1800
RAPIDJSON_DIAG_POP
#endif
#if defined(__GNUC__)
RAPIDJSON_DIAG_POP
#endif
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_

2652
include/rapidjson/document.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,299 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ENCODEDSTREAM_H_
#define RAPIDJSON_ENCODEDSTREAM_H_
#include "stream.h"
#include "memorystream.h"
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Input byte stream wrapper with a statically bound encoding.
/*!
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
\tparam InputByteStream Type of input byte stream. For example, FileReadStream.
*/
template <typename Encoding, typename InputByteStream>
class EncodedInputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
public:
typedef typename Encoding::Ch Ch;
EncodedInputStream(InputByteStream& is) : is_(is) {
current_ = Encoding::TakeBOM(is_);
}
Ch Peek() const { return current_; }
Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
size_t Tell() const { return is_.Tell(); }
// Not implemented
void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
EncodedInputStream(const EncodedInputStream&);
EncodedInputStream& operator=(const EncodedInputStream&);
InputByteStream& is_;
Ch current_;
};
//! Specialized for UTF8 MemoryStream.
template <>
class EncodedInputStream<UTF8<>, MemoryStream> {
public:
typedef UTF8<>::Ch Ch;
EncodedInputStream(MemoryStream& is) : is_(is) {
if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take();
if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take();
if (static_cast<unsigned char>(is_.Peek()) == 0xBFu) is_.Take();
}
Ch Peek() const { return is_.Peek(); }
Ch Take() { return is_.Take(); }
size_t Tell() const { return is_.Tell(); }
// Not implemented
void Put(Ch) {}
void Flush() {}
Ch* PutBegin() { return 0; }
size_t PutEnd(Ch*) { return 0; }
MemoryStream& is_;
private:
EncodedInputStream(const EncodedInputStream&);
EncodedInputStream& operator=(const EncodedInputStream&);
};
//! Output byte stream wrapper with statically bound encoding.
/*!
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
\tparam OutputByteStream Type of input byte stream. For example, FileWriteStream.
*/
template <typename Encoding, typename OutputByteStream>
class EncodedOutputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
public:
typedef typename Encoding::Ch Ch;
EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) {
if (putBOM)
Encoding::PutBOM(os_);
}
void Put(Ch c) { Encoding::Put(os_, c); }
void Flush() { os_.Flush(); }
// Not implemented
Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
EncodedOutputStream(const EncodedOutputStream&);
EncodedOutputStream& operator=(const EncodedOutputStream&);
OutputByteStream& os_;
};
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
//! Input stream wrapper with dynamically bound encoding and automatic encoding detection.
/*!
\tparam CharType Type of character for reading.
\tparam InputByteStream type of input byte stream to be wrapped.
*/
template <typename CharType, typename InputByteStream>
class AutoUTFInputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
public:
typedef CharType Ch;
//! Constructor.
/*!
\param is input stream to be wrapped.
\param type UTF encoding type if it is not detected from the stream.
*/
AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
DetectType();
static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
takeFunc_ = f[type_];
current_ = takeFunc_(*is_);
}
UTFType GetType() const { return type_; }
bool HasBOM() const { return hasBOM_; }
Ch Peek() const { return current_; }
Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
size_t Tell() const { return is_->Tell(); }
// Not implemented
void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
AutoUTFInputStream(const AutoUTFInputStream&);
AutoUTFInputStream& operator=(const AutoUTFInputStream&);
// Detect encoding type with BOM or RFC 4627
void DetectType() {
// BOM (Byte Order Mark):
// 00 00 FE FF UTF-32BE
// FF FE 00 00 UTF-32LE
// FE FF UTF-16BE
// FF FE UTF-16LE
// EF BB BF UTF-8
const unsigned char* c = reinterpret_cast<const unsigned char *>(is_->Peek4());
if (!c)
return;
unsigned bom = static_cast<unsigned>(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24));
hasBOM_ = false;
if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); }
else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); }
else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); }
// RFC 4627: Section 3
// "Since the first two characters of a JSON text will always be ASCII
// characters [RFC0020], it is possible to determine whether an octet
// stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
// at the pattern of nulls in the first four octets."
// 00 00 00 xx UTF-32BE
// 00 xx 00 xx UTF-16BE
// xx 00 00 00 UTF-32LE
// xx 00 xx 00 UTF-16LE
// xx xx xx xx UTF-8
if (!hasBOM_) {
int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0);
switch (pattern) {
case 0x08: type_ = kUTF32BE; break;
case 0x0A: type_ = kUTF16BE; break;
case 0x01: type_ = kUTF32LE; break;
case 0x05: type_ = kUTF16LE; break;
case 0x0F: type_ = kUTF8; break;
default: break; // Use type defined by user.
}
}
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
}
typedef Ch (*TakeFunc)(InputByteStream& is);
InputByteStream* is_;
UTFType type_;
Ch current_;
TakeFunc takeFunc_;
bool hasBOM_;
};
//! Output stream wrapper with dynamically bound encoding and automatic encoding detection.
/*!
\tparam CharType Type of character for writing.
\tparam OutputByteStream type of output byte stream to be wrapped.
*/
template <typename CharType, typename OutputByteStream>
class AutoUTFOutputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
public:
typedef CharType Ch;
//! Constructor.
/*!
\param os output stream to be wrapped.
\param type UTF encoding type.
\param putBOM Whether to write BOM at the beginning of the stream.
*/
AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
putFunc_ = f[type_];
if (putBOM)
PutBOM();
}
UTFType GetType() const { return type_; }
void Put(Ch c) { putFunc_(*os_, c); }
void Flush() { os_->Flush(); }
// Not implemented
Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
AutoUTFOutputStream(const AutoUTFOutputStream&);
AutoUTFOutputStream& operator=(const AutoUTFOutputStream&);
void PutBOM() {
typedef void (*PutBOMFunc)(OutputByteStream&);
static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) };
f[type_](*os_);
}
typedef void (*PutFunc)(OutputByteStream&, Ch);
OutputByteStream* os_;
UTFType type_;
PutFunc putFunc_;
};
#undef RAPIDJSON_ENCODINGS_FUNC
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_FILESTREAM_H_

View File

@ -0,0 +1,716 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ENCODINGS_H_
#define RAPIDJSON_ENCODINGS_H_
#include "rapidjson.h"
#if defined(_MSC_VER) && !defined(__clang__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data
RAPIDJSON_DIAG_OFF(4702) // unreachable code
#elif defined(__GNUC__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
RAPIDJSON_DIAG_OFF(overflow)
#endif
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Encoding
/*! \class rapidjson::Encoding
\brief Concept for encoding of Unicode characters.
\code
concept Encoding {
typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition.
enum { supportUnicode = 1 }; // or 0 if not supporting unicode
//! \brief Encode a Unicode codepoint to an output stream.
//! \param os Output stream.
//! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively.
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint);
//! \brief Decode a Unicode codepoint from an input stream.
//! \param is Input stream.
//! \param codepoint Output of the unicode codepoint.
//! \return true if a valid codepoint can be decoded from the stream.
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint);
//! \brief Validate one Unicode codepoint from an encoded stream.
//! \param is Input stream to obtain codepoint.
//! \param os Output for copying one codepoint.
//! \return true if it is valid.
//! \note This function just validating and copying the codepoint without actually decode it.
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os);
// The following functions are deal with byte streams.
//! Take a character from input byte stream, skip BOM if exist.
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is);
//! Take a character from input byte stream.
template <typename InputByteStream>
static Ch Take(InputByteStream& is);
//! Put BOM to output byte stream.
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os);
//! Put a character to output byte stream.
template <typename OutputByteStream>
static void Put(OutputByteStream& os, Ch c);
};
\endcode
*/
///////////////////////////////////////////////////////////////////////////////
// UTF8
//! UTF-8 encoding.
/*! http://en.wikipedia.org/wiki/UTF-8
http://tools.ietf.org/html/rfc3629
\tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char.
\note implements Encoding concept
*/
template<typename CharType = char>
struct UTF8 {
typedef CharType Ch;
enum { supportUnicode = 1 };
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint) {
if (codepoint <= 0x7F)
os.Put(static_cast<Ch>(codepoint & 0xFF));
else if (codepoint <= 0x7FF) {
os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
}
else if (codepoint <= 0xFFFF) {
os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
else {
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
}
template<typename OutputStream>
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
if (codepoint <= 0x7F)
PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
else if (codepoint <= 0x7FF) {
PutUnsafe(os, static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
}
else if (codepoint <= 0xFFFF) {
PutUnsafe(os, static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
else {
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
PutUnsafe(os, static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
}
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu)
#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70)
typename InputStream::Ch c = is.Take();
if (!(c & 0x80)) {
*codepoint = static_cast<unsigned char>(c);
return true;
}
unsigned char type = GetRange(static_cast<unsigned char>(c));
if (type >= 32) {
*codepoint = 0;
} else {
*codepoint = (0xFFu >> type) & static_cast<unsigned char>(c);
}
bool result = true;
switch (type) {
case 2: RAPIDJSON_TAIL(); return result;
case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result;
case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result;
case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
default: return false;
}
#undef RAPIDJSON_COPY
#undef RAPIDJSON_TRANS
#undef RAPIDJSON_TAIL
}
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os) {
#define RAPIDJSON_COPY() os.Put(c = is.Take())
#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70)
Ch c;
RAPIDJSON_COPY();
if (!(c & 0x80))
return true;
bool result = true;
switch (GetRange(static_cast<unsigned char>(c))) {
case 2: RAPIDJSON_TAIL(); return result;
case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result;
case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result;
case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
default: return false;
}
#undef RAPIDJSON_COPY
#undef RAPIDJSON_TRANS
#undef RAPIDJSON_TAIL
}
static unsigned char GetRange(unsigned char c) {
// Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
// With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types.
static const unsigned char type[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
};
return type[c];
}
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
typename InputByteStream::Ch c = Take(is);
if (static_cast<unsigned char>(c) != 0xEFu) return c;
c = is.Take();
if (static_cast<unsigned char>(c) != 0xBBu) return c;
c = is.Take();
if (static_cast<unsigned char>(c) != 0xBFu) return c;
c = is.Take();
return c;
}
template <typename InputByteStream>
static Ch Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
return static_cast<Ch>(is.Take());
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0xEFu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xBBu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xBFu));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, Ch c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(c));
}
};
///////////////////////////////////////////////////////////////////////////////
// UTF16
//! UTF-16 encoding.
/*! http://en.wikipedia.org/wiki/UTF-16
http://tools.ietf.org/html/rfc2781
\tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead.
\note implements Encoding concept
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
For streaming, use UTF16LE and UTF16BE, which handle endianness.
*/
template<typename CharType = wchar_t>
struct UTF16 {
typedef CharType Ch;
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2);
enum { supportUnicode = 1 };
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
if (codepoint <= 0xFFFF) {
RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
os.Put(static_cast<typename OutputStream::Ch>(codepoint));
}
else {
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
unsigned v = codepoint - 0x10000;
os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
os.Put(static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00));
}
}
template<typename OutputStream>
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
if (codepoint <= 0xFFFF) {
RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
PutUnsafe(os, static_cast<typename OutputStream::Ch>(codepoint));
}
else {
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
unsigned v = codepoint - 0x10000;
PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
PutUnsafe(os, static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00));
}
}
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
typename InputStream::Ch c = is.Take();
if (c < 0xD800 || c > 0xDFFF) {
*codepoint = static_cast<unsigned>(c);
return true;
}
else if (c <= 0xDBFF) {
*codepoint = (static_cast<unsigned>(c) & 0x3FF) << 10;
c = is.Take();
*codepoint |= (static_cast<unsigned>(c) & 0x3FF);
*codepoint += 0x10000;
return c >= 0xDC00 && c <= 0xDFFF;
}
return false;
}
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
typename InputStream::Ch c;
os.Put(static_cast<typename OutputStream::Ch>(c = is.Take()));
if (c < 0xD800 || c > 0xDFFF)
return true;
else if (c <= 0xDBFF) {
os.Put(c = is.Take());
return c >= 0xDC00 && c <= 0xDFFF;
}
return false;
}
};
//! UTF-16 little endian encoding.
template<typename CharType = wchar_t>
struct UTF16LE : UTF16<CharType> {
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
CharType c = Take(is);
return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
}
template <typename InputByteStream>
static CharType Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
unsigned c = static_cast<uint8_t>(is.Take());
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
return static_cast<CharType>(c);
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, CharType c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
}
};
//! UTF-16 big endian encoding.
template<typename CharType = wchar_t>
struct UTF16BE : UTF16<CharType> {
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
CharType c = Take(is);
return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
}
template <typename InputByteStream>
static CharType Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
return static_cast<CharType>(c);
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, CharType c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
}
};
///////////////////////////////////////////////////////////////////////////////
// UTF32
//! UTF-32 encoding.
/*! http://en.wikipedia.org/wiki/UTF-32
\tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead.
\note implements Encoding concept
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
For streaming, use UTF32LE and UTF32BE, which handle endianness.
*/
template<typename CharType = unsigned>
struct UTF32 {
typedef CharType Ch;
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4);
enum { supportUnicode = 1 };
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
os.Put(codepoint);
}
template<typename OutputStream>
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
PutUnsafe(os, codepoint);
}
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
Ch c = is.Take();
*codepoint = c;
return c <= 0x10FFFF;
}
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
Ch c;
os.Put(c = is.Take());
return c <= 0x10FFFF;
}
};
//! UTF-32 little endian enocoding.
template<typename CharType = unsigned>
struct UTF32LE : UTF32<CharType> {
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
CharType c = Take(is);
return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
}
template <typename InputByteStream>
static CharType Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
unsigned c = static_cast<uint8_t>(is.Take());
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
return static_cast<CharType>(c);
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, CharType c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
}
};
//! UTF-32 big endian encoding.
template<typename CharType = unsigned>
struct UTF32BE : UTF32<CharType> {
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
CharType c = Take(is);
return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
}
template <typename InputByteStream>
static CharType Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
return static_cast<CharType>(c);
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, CharType c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
}
};
///////////////////////////////////////////////////////////////////////////////
// ASCII
//! ASCII encoding.
/*! http://en.wikipedia.org/wiki/ASCII
\tparam CharType Code unit for storing 7-bit ASCII data. Default is char.
\note implements Encoding concept
*/
template<typename CharType = char>
struct ASCII {
typedef CharType Ch;
enum { supportUnicode = 0 };
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint) {
RAPIDJSON_ASSERT(codepoint <= 0x7F);
os.Put(static_cast<Ch>(codepoint & 0xFF));
}
template<typename OutputStream>
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
RAPIDJSON_ASSERT(codepoint <= 0x7F);
PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
}
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
uint8_t c = static_cast<uint8_t>(is.Take());
*codepoint = c;
return c <= 0X7F;
}
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os) {
uint8_t c = static_cast<uint8_t>(is.Take());
os.Put(static_cast<typename OutputStream::Ch>(c));
return c <= 0x7F;
}
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
uint8_t c = static_cast<uint8_t>(Take(is));
return static_cast<Ch>(c);
}
template <typename InputByteStream>
static Ch Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
return static_cast<Ch>(is.Take());
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
(void)os;
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, Ch c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(c));
}
};
///////////////////////////////////////////////////////////////////////////////
// AutoUTF
//! Runtime-specified UTF encoding type of a stream.
enum UTFType {
kUTF8 = 0, //!< UTF-8.
kUTF16LE = 1, //!< UTF-16 little endian.
kUTF16BE = 2, //!< UTF-16 big endian.
kUTF32LE = 3, //!< UTF-32 little endian.
kUTF32BE = 4 //!< UTF-32 big endian.
};
//! Dynamically select encoding according to stream's runtime-specified UTF encoding type.
/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType().
*/
template<typename CharType>
struct AutoUTF {
typedef CharType Ch;
enum { supportUnicode = 1 };
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
template<typename OutputStream>
static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) {
typedef void (*EncodeFunc)(OutputStream&, unsigned);
static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) };
(*f[os.GetType()])(os, codepoint);
}
template<typename OutputStream>
static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
typedef void (*EncodeFunc)(OutputStream&, unsigned);
static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) };
(*f[os.GetType()])(os, codepoint);
}
template <typename InputStream>
static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) {
typedef bool (*DecodeFunc)(InputStream&, unsigned*);
static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) };
return (*f[is.GetType()])(is, codepoint);
}
template <typename InputStream, typename OutputStream>
static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
typedef bool (*ValidateFunc)(InputStream&, OutputStream&);
static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) };
return (*f[is.GetType()])(is, os);
}
#undef RAPIDJSON_ENCODINGS_FUNC
};
///////////////////////////////////////////////////////////////////////////////
// Transcoder
//! Encoding conversion.
template<typename SourceEncoding, typename TargetEncoding>
struct Transcoder {
//! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
template<typename InputStream, typename OutputStream>
static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) {
unsigned codepoint;
if (!SourceEncoding::Decode(is, &codepoint))
return false;
TargetEncoding::Encode(os, codepoint);
return true;
}
template<typename InputStream, typename OutputStream>
static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
unsigned codepoint;
if (!SourceEncoding::Decode(is, &codepoint))
return false;
TargetEncoding::EncodeUnsafe(os, codepoint);
return true;
}
//! Validate one Unicode codepoint from an encoded stream.
template<typename InputStream, typename OutputStream>
static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
return Transcode(is, os); // Since source/target encoding is different, must transcode.
}
};
// Forward declaration.
template<typename Stream>
inline void PutUnsafe(Stream& stream, typename Stream::Ch c);
//! Specialization of Transcoder with same source and target encoding.
template<typename Encoding>
struct Transcoder<Encoding, Encoding> {
template<typename InputStream, typename OutputStream>
static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) {
os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class.
return true;
}
template<typename InputStream, typename OutputStream>
static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class.
return true;
}
template<typename InputStream, typename OutputStream>
static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
return Encoding::Validate(is, os); // source/target encoding are the same
}
};
RAPIDJSON_NAMESPACE_END
#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__))
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_ENCODINGS_H_

View File

@ -0,0 +1,74 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ERROR_EN_H_
#define RAPIDJSON_ERROR_EN_H_
#include "error.h"
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(switch-enum)
RAPIDJSON_DIAG_OFF(covered-switch-default)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Maps error code of parsing into error message.
/*!
\ingroup RAPIDJSON_ERRORS
\param parseErrorCode Error code obtained in parsing.
\return the error message.
\note User can make a copy of this function for localization.
Using switch-case is safer for future modification of error codes.
*/
inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) {
switch (parseErrorCode) {
case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty.");
case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values.");
case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value.");
case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member.");
case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member.");
case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member.");
case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element.");
case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string.");
case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid.");
case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string.");
case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string.");
case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string.");
case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double.");
case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number.");
case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number.");
case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error.");
case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
default: return RAPIDJSON_ERROR_STRING("Unknown error.");
}
}
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_ERROR_EN_H_

View File

@ -0,0 +1,161 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ERROR_ERROR_H_
#define RAPIDJSON_ERROR_ERROR_H_
#include "../rapidjson.h"
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
#endif
/*! \file error.h */
/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_ERROR_CHARTYPE
//! Character type of error messages.
/*! \ingroup RAPIDJSON_ERRORS
The default character type is \c char.
On Windows, user can define this macro as \c TCHAR for supporting both
unicode/non-unicode settings.
*/
#ifndef RAPIDJSON_ERROR_CHARTYPE
#define RAPIDJSON_ERROR_CHARTYPE char
#endif
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_ERROR_STRING
//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[].
/*! \ingroup RAPIDJSON_ERRORS
By default this conversion macro does nothing.
On Windows, user can define this macro as \c _T(x) for supporting both
unicode/non-unicode settings.
*/
#ifndef RAPIDJSON_ERROR_STRING
#define RAPIDJSON_ERROR_STRING(x) x
#endif
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// ParseErrorCode
//! Error code of parsing.
/*! \ingroup RAPIDJSON_ERRORS
\see GenericReader::Parse, GenericReader::GetParseErrorCode
*/
enum ParseErrorCode {
kParseErrorNone = 0, //!< No error.
kParseErrorDocumentEmpty, //!< The document is empty.
kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values.
kParseErrorValueInvalid, //!< Invalid value.
kParseErrorObjectMissName, //!< Missing a name for object member.
kParseErrorObjectMissColon, //!< Missing a colon after a name of object member.
kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member.
kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element.
kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string.
kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid.
kParseErrorStringEscapeInvalid, //!< Invalid escape character in string.
kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string.
kParseErrorStringInvalidEncoding, //!< Invalid encoding in string.
kParseErrorNumberTooBig, //!< Number too big to be stored in double.
kParseErrorNumberMissFraction, //!< Miss fraction part in number.
kParseErrorNumberMissExponent, //!< Miss exponent in number.
kParseErrorTermination, //!< Parsing was terminated.
kParseErrorUnspecificSyntaxError //!< Unspecific syntax error.
};
//! Result of parsing (wraps ParseErrorCode)
/*!
\ingroup RAPIDJSON_ERRORS
\code
Document doc;
ParseResult ok = doc.Parse("[42]");
if (!ok) {
fprintf(stderr, "JSON parse error: %s (%u)",
GetParseError_En(ok.Code()), ok.Offset());
exit(EXIT_FAILURE);
}
\endcode
\see GenericReader::Parse, GenericDocument::Parse
*/
struct ParseResult {
//!! Unspecified boolean type
typedef bool (ParseResult::*BooleanType)() const;
public:
//! Default constructor, no error.
ParseResult() : code_(kParseErrorNone), offset_(0) {}
//! Constructor to set an error.
ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {}
//! Get the error code.
ParseErrorCode Code() const { return code_; }
//! Get the error offset, if \ref IsError(), 0 otherwise.
size_t Offset() const { return offset_; }
//! Explicit conversion to \c bool, returns \c true, iff !\ref IsError().
operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; }
//! Whether the result is an error.
bool IsError() const { return code_ != kParseErrorNone; }
bool operator==(const ParseResult& that) const { return code_ == that.code_; }
bool operator==(ParseErrorCode code) const { return code_ == code; }
friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; }
bool operator!=(const ParseResult& that) const { return !(*this == that); }
bool operator!=(ParseErrorCode code) const { return !(*this == code); }
friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; }
//! Reset error code.
void Clear() { Set(kParseErrorNone); }
//! Update error code and offset.
void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; }
private:
ParseErrorCode code_;
size_t offset_;
};
//! Function pointer type of GetParseError().
/*! \ingroup RAPIDJSON_ERRORS
This is the prototype for \c GetParseError_X(), where \c X is a locale.
User can dynamically change locale in runtime, e.g.:
\code
GetParseErrorFunc GetParseError = GetParseError_En; // or whatever
const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode());
\endcode
*/
typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode);
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_ERROR_ERROR_H_

View File

@ -0,0 +1,99 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_FILEREADSTREAM_H_
#define RAPIDJSON_FILEREADSTREAM_H_
#include "stream.h"
#include <cstdio>
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
RAPIDJSON_DIAG_OFF(unreachable-code)
RAPIDJSON_DIAG_OFF(missing-noreturn)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! File byte stream for input using fread().
/*!
\note implements Stream concept
*/
class FileReadStream {
public:
typedef char Ch; //!< Character type (byte).
//! Constructor.
/*!
\param fp File pointer opened for read.
\param buffer user-supplied buffer.
\param bufferSize size of buffer in bytes. Must >=4 bytes.
*/
FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
RAPIDJSON_ASSERT(fp_ != 0);
RAPIDJSON_ASSERT(bufferSize >= 4);
Read();
}
Ch Peek() const { return *current_; }
Ch Take() { Ch c = *current_; Read(); return c; }
size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
// Not implemented
void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
// For encoding detection only.
const Ch* Peek4() const {
return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0;
}
private:
void Read() {
if (current_ < bufferLast_)
++current_;
else if (!eof_) {
count_ += readCount_;
readCount_ = std::fread(buffer_, 1, bufferSize_, fp_);
bufferLast_ = buffer_ + readCount_ - 1;
current_ = buffer_;
if (readCount_ < bufferSize_) {
buffer_[readCount_] = '\0';
++bufferLast_;
eof_ = true;
}
}
}
std::FILE* fp_;
Ch *buffer_;
size_t bufferSize_;
Ch *bufferLast_;
Ch *current_;
size_t readCount_;
size_t count_; //!< Number of characters read
bool eof_;
};
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_FILESTREAM_H_

View File

@ -0,0 +1,104 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_FILEWRITESTREAM_H_
#define RAPIDJSON_FILEWRITESTREAM_H_
#include "stream.h"
#include <cstdio>
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(unreachable-code)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Wrapper of C file stream for output using fwrite().
/*!
\note implements Stream concept
*/
class FileWriteStream {
public:
typedef char Ch; //!< Character type. Only support char.
FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) {
RAPIDJSON_ASSERT(fp_ != 0);
}
void Put(char c) {
if (current_ >= bufferEnd_)
Flush();
*current_++ = c;
}
void PutN(char c, size_t n) {
size_t avail = static_cast<size_t>(bufferEnd_ - current_);
while (n > avail) {
std::memset(current_, c, avail);
current_ += avail;
Flush();
n -= avail;
avail = static_cast<size_t>(bufferEnd_ - current_);
}
if (n > 0) {
std::memset(current_, c, n);
current_ += n;
}
}
void Flush() {
if (current_ != buffer_) {
size_t result = std::fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
if (result < static_cast<size_t>(current_ - buffer_)) {
// failure deliberately ignored at this time
// added to avoid warn_unused_result build errors
}
current_ = buffer_;
}
}
// Not implemented
char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
char Take() { RAPIDJSON_ASSERT(false); return 0; }
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
private:
// Prohibit copy constructor & assignment operator.
FileWriteStream(const FileWriteStream&);
FileWriteStream& operator=(const FileWriteStream&);
std::FILE* fp_;
char *buffer_;
char *bufferEnd_;
char *current_;
};
//! Implement specialized version of PutN() with memset() for better performance.
template<>
inline void PutN(FileWriteStream& stream, char c, size_t n) {
stream.PutN(c, n);
}
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_FILESTREAM_H_

151
include/rapidjson/fwd.h Normal file
View File

@ -0,0 +1,151 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_FWD_H_
#define RAPIDJSON_FWD_H_
#include "rapidjson.h"
RAPIDJSON_NAMESPACE_BEGIN
// encodings.h
template<typename CharType> struct UTF8;
template<typename CharType> struct UTF16;
template<typename CharType> struct UTF16BE;
template<typename CharType> struct UTF16LE;
template<typename CharType> struct UTF32;
template<typename CharType> struct UTF32BE;
template<typename CharType> struct UTF32LE;
template<typename CharType> struct ASCII;
template<typename CharType> struct AutoUTF;
template<typename SourceEncoding, typename TargetEncoding>
struct Transcoder;
// allocators.h
class CrtAllocator;
template <typename BaseAllocator>
class MemoryPoolAllocator;
// stream.h
template <typename Encoding>
struct GenericStringStream;
typedef GenericStringStream<UTF8<char> > StringStream;
template <typename Encoding>
struct GenericInsituStringStream;
typedef GenericInsituStringStream<UTF8<char> > InsituStringStream;
// stringbuffer.h
template <typename Encoding, typename Allocator>
class GenericStringBuffer;
typedef GenericStringBuffer<UTF8<char>, CrtAllocator> StringBuffer;
// filereadstream.h
class FileReadStream;
// filewritestream.h
class FileWriteStream;
// memorybuffer.h
template <typename Allocator>
struct GenericMemoryBuffer;
typedef GenericMemoryBuffer<CrtAllocator> MemoryBuffer;
// memorystream.h
struct MemoryStream;
// reader.h
template<typename Encoding, typename Derived>
struct BaseReaderHandler;
template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator>
class GenericReader;
typedef GenericReader<UTF8<char>, UTF8<char>, CrtAllocator> Reader;
// writer.h
template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
class Writer;
// prettywriter.h
template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
class PrettyWriter;
// document.h
template <typename Encoding, typename Allocator>
struct GenericMember;
template <bool Const, typename Encoding, typename Allocator>
class GenericMemberIterator;
template<typename CharType>
struct GenericStringRef;
template <typename Encoding, typename Allocator>
class GenericValue;
typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
template <typename Encoding, typename Allocator, typename StackAllocator>
class GenericDocument;
typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator>, CrtAllocator> Document;
// pointer.h
template <typename ValueType, typename Allocator>
class GenericPointer;
typedef GenericPointer<Value, CrtAllocator> Pointer;
// schema.h
template <typename SchemaDocumentType>
class IGenericRemoteSchemaDocumentProvider;
template <typename ValueT, typename Allocator>
class GenericSchemaDocument;
typedef GenericSchemaDocument<Value, CrtAllocator> SchemaDocument;
typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
template <
typename SchemaDocumentType,
typename OutputHandler,
typename StateAllocator>
class GenericSchemaValidator;
typedef GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<char>, void>, CrtAllocator> SchemaValidator;
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_RAPIDJSONFWD_H_

View File

@ -0,0 +1,290 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_BIGINTEGER_H_
#define RAPIDJSON_BIGINTEGER_H_
#include "../rapidjson.h"
#if defined(_MSC_VER) && !__INTEL_COMPILER && defined(_M_AMD64)
#include <intrin.h> // for _umul128
#pragma intrinsic(_umul128)
#endif
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
class BigInteger {
public:
typedef uint64_t Type;
BigInteger(const BigInteger& rhs) : count_(rhs.count_) {
std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
}
explicit BigInteger(uint64_t u) : count_(1) {
digits_[0] = u;
}
BigInteger(const char* decimals, size_t length) : count_(1) {
RAPIDJSON_ASSERT(length > 0);
digits_[0] = 0;
size_t i = 0;
const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19
while (length >= kMaxDigitPerIteration) {
AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration);
length -= kMaxDigitPerIteration;
i += kMaxDigitPerIteration;
}
if (length > 0)
AppendDecimal64(decimals + i, decimals + i + length);
}
BigInteger& operator=(const BigInteger &rhs)
{
if (this != &rhs) {
count_ = rhs.count_;
std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
}
return *this;
}
BigInteger& operator=(uint64_t u) {
digits_[0] = u;
count_ = 1;
return *this;
}
BigInteger& operator+=(uint64_t u) {
Type backup = digits_[0];
digits_[0] += u;
for (size_t i = 0; i < count_ - 1; i++) {
if (digits_[i] >= backup)
return *this; // no carry
backup = digits_[i + 1];
digits_[i + 1] += 1;
}
// Last carry
if (digits_[count_ - 1] < backup)
PushBack(1);
return *this;
}
BigInteger& operator*=(uint64_t u) {
if (u == 0) return *this = 0;
if (u == 1) return *this;
if (*this == 1) return *this = u;
uint64_t k = 0;
for (size_t i = 0; i < count_; i++) {
uint64_t hi;
digits_[i] = MulAdd64(digits_[i], u, k, &hi);
k = hi;
}
if (k > 0)
PushBack(k);
return *this;
}
BigInteger& operator*=(uint32_t u) {
if (u == 0) return *this = 0;
if (u == 1) return *this;
if (*this == 1) return *this = u;
uint64_t k = 0;
for (size_t i = 0; i < count_; i++) {
const uint64_t c = digits_[i] >> 32;
const uint64_t d = digits_[i] & 0xFFFFFFFF;
const uint64_t uc = u * c;
const uint64_t ud = u * d;
const uint64_t p0 = ud + k;
const uint64_t p1 = uc + (p0 >> 32);
digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32);
k = p1 >> 32;
}
if (k > 0)
PushBack(k);
return *this;
}
BigInteger& operator<<=(size_t shift) {
if (IsZero() || shift == 0) return *this;
size_t offset = shift / kTypeBit;
size_t interShift = shift % kTypeBit;
RAPIDJSON_ASSERT(count_ + offset <= kCapacity);
if (interShift == 0) {
std::memmove(digits_ + offset, digits_, count_ * sizeof(Type));
count_ += offset;
}
else {
digits_[count_] = 0;
for (size_t i = count_; i > 0; i--)
digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift));
digits_[offset] = digits_[0] << interShift;
count_ += offset;
if (digits_[count_])
count_++;
}
std::memset(digits_, 0, offset * sizeof(Type));
return *this;
}
bool operator==(const BigInteger& rhs) const {
return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0;
}
bool operator==(const Type rhs) const {
return count_ == 1 && digits_[0] == rhs;
}
BigInteger& MultiplyPow5(unsigned exp) {
static const uint32_t kPow5[12] = {
5,
5 * 5,
5 * 5 * 5,
5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
};
if (exp == 0) return *this;
for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27
for (; exp >= 13; exp -= 13) *this *= static_cast<uint32_t>(1220703125u); // 5^13
if (exp > 0) *this *= kPow5[exp - 1];
return *this;
}
// Compute absolute difference of this and rhs.
// Assume this != rhs
bool Difference(const BigInteger& rhs, BigInteger* out) const {
int cmp = Compare(rhs);
RAPIDJSON_ASSERT(cmp != 0);
const BigInteger *a, *b; // Makes a > b
bool ret;
if (cmp < 0) { a = &rhs; b = this; ret = true; }
else { a = this; b = &rhs; ret = false; }
Type borrow = 0;
for (size_t i = 0; i < a->count_; i++) {
Type d = a->digits_[i] - borrow;
if (i < b->count_)
d -= b->digits_[i];
borrow = (d > a->digits_[i]) ? 1 : 0;
out->digits_[i] = d;
if (d != 0)
out->count_ = i + 1;
}
return ret;
}
int Compare(const BigInteger& rhs) const {
if (count_ != rhs.count_)
return count_ < rhs.count_ ? -1 : 1;
for (size_t i = count_; i-- > 0;)
if (digits_[i] != rhs.digits_[i])
return digits_[i] < rhs.digits_[i] ? -1 : 1;
return 0;
}
size_t GetCount() const { return count_; }
Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; }
bool IsZero() const { return count_ == 1 && digits_[0] == 0; }
private:
void AppendDecimal64(const char* begin, const char* end) {
uint64_t u = ParseUint64(begin, end);
if (IsZero())
*this = u;
else {
unsigned exp = static_cast<unsigned>(end - begin);
(MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u
}
}
void PushBack(Type digit) {
RAPIDJSON_ASSERT(count_ < kCapacity);
digits_[count_++] = digit;
}
static uint64_t ParseUint64(const char* begin, const char* end) {
uint64_t r = 0;
for (const char* p = begin; p != end; ++p) {
RAPIDJSON_ASSERT(*p >= '0' && *p <= '9');
r = r * 10u + static_cast<unsigned>(*p - '0');
}
return r;
}
// Assume a * b + k < 2^128
static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) {
#if defined(_MSC_VER) && defined(_M_AMD64)
uint64_t low = _umul128(a, b, outHigh) + k;
if (low < k)
(*outHigh)++;
return low;
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
__extension__ typedef unsigned __int128 uint128;
uint128 p = static_cast<uint128>(a) * static_cast<uint128>(b);
p += k;
*outHigh = static_cast<uint64_t>(p >> 64);
return static_cast<uint64_t>(p);
#else
const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32;
uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1;
x1 += (x0 >> 32); // can't give carry
x1 += x2;
if (x1 < x2)
x3 += (static_cast<uint64_t>(1) << 32);
uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF);
uint64_t hi = x3 + (x1 >> 32);
lo += k;
if (lo < k)
hi++;
*outHigh = hi;
return lo;
#endif
}
static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000
static const size_t kCapacity = kBitCount / sizeof(Type);
static const size_t kTypeBit = sizeof(Type) * 8;
Type digits_[kCapacity];
size_t count_;
};
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_BIGINTEGER_H_

View File

@ -0,0 +1,271 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
#ifndef RAPIDJSON_DIYFP_H_
#define RAPIDJSON_DIYFP_H_
#include "../rapidjson.h"
#include <limits>
#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER)
#include <intrin.h>
#pragma intrinsic(_BitScanReverse64)
#pragma intrinsic(_umul128)
#endif
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
#endif
struct DiyFp {
DiyFp() : f(), e() {}
DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {}
explicit DiyFp(double d) {
union {
double d;
uint64_t u64;
} u = { d };
int biased_e = static_cast<int>((u.u64 & kDpExponentMask) >> kDpSignificandSize);
uint64_t significand = (u.u64 & kDpSignificandMask);
if (biased_e != 0) {
f = significand + kDpHiddenBit;
e = biased_e - kDpExponentBias;
}
else {
f = significand;
e = kDpMinExponent + 1;
}
}
DiyFp operator-(const DiyFp& rhs) const {
return DiyFp(f - rhs.f, e);
}
DiyFp operator*(const DiyFp& rhs) const {
#if defined(_MSC_VER) && defined(_M_AMD64)
uint64_t h;
uint64_t l = _umul128(f, rhs.f, &h);
if (l & (uint64_t(1) << 63)) // rounding
h++;
return DiyFp(h, e + rhs.e + 64);
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
__extension__ typedef unsigned __int128 uint128;
uint128 p = static_cast<uint128>(f) * static_cast<uint128>(rhs.f);
uint64_t h = static_cast<uint64_t>(p >> 64);
uint64_t l = static_cast<uint64_t>(p);
if (l & (uint64_t(1) << 63)) // rounding
h++;
return DiyFp(h, e + rhs.e + 64);
#else
const uint64_t M32 = 0xFFFFFFFF;
const uint64_t a = f >> 32;
const uint64_t b = f & M32;
const uint64_t c = rhs.f >> 32;
const uint64_t d = rhs.f & M32;
const uint64_t ac = a * c;
const uint64_t bc = b * c;
const uint64_t ad = a * d;
const uint64_t bd = b * d;
uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32);
tmp += 1U << 31; /// mult_round
return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
#endif
}
DiyFp Normalize() const {
RAPIDJSON_ASSERT(f != 0); // https://stackoverflow.com/a/26809183/291737
#if defined(_MSC_VER) && defined(_M_AMD64)
unsigned long index;
_BitScanReverse64(&index, f);
return DiyFp(f << (63 - index), e - (63 - index));
#elif defined(__GNUC__) && __GNUC__ >= 4
int s = __builtin_clzll(f);
return DiyFp(f << s, e - s);
#else
DiyFp res = *this;
while (!(res.f & (static_cast<uint64_t>(1) << 63))) {
res.f <<= 1;
res.e--;
}
return res;
#endif
}
DiyFp NormalizeBoundary() const {
DiyFp res = *this;
while (!(res.f & (kDpHiddenBit << 1))) {
res.f <<= 1;
res.e--;
}
res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
return res;
}
void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
mi.f <<= mi.e - pl.e;
mi.e = pl.e;
*plus = pl;
*minus = mi;
}
double ToDouble() const {
union {
double d;
uint64_t u64;
}u;
RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask);
if (e < kDpDenormalExponent) {
// Underflow.
return 0.0;
}
if (e >= kDpMaxExponent) {
// Overflow.
return std::numeric_limits<double>::infinity();
}
const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
static_cast<uint64_t>(e + kDpExponentBias);
u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize);
return u.d;
}
static const int kDiySignificandSize = 64;
static const int kDpSignificandSize = 52;
static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
static const int kDpMaxExponent = 0x7FF - kDpExponentBias;
static const int kDpMinExponent = -kDpExponentBias;
static const int kDpDenormalExponent = -kDpExponentBias + 1;
static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
uint64_t f;
int e;
};
inline DiyFp GetCachedPowerByIndex(size_t index) {
// 10^-348, 10^-340, ..., 10^340
static const uint64_t kCachedPowers_F[] = {
RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76),
RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea),
RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df),
RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f),
RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c),
RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5),
RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d),
RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637),
RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7),
RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5),
RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b),
RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996),
RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8),
RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053),
RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd),
RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94),
RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b),
RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac),
RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3),
RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb),
RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c),
RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000),
RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984),
RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70),
RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245),
RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8),
RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a),
RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea),
RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85),
RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2),
RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3),
RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25),
RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece),
RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5),
RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a),
RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a),
RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129),
RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429),
RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d),
RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841),
RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9),
RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b)
};
static const int16_t kCachedPowers_E[] = {
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980,
-954, -927, -901, -874, -847, -821, -794, -768, -741, -715,
-688, -661, -635, -608, -582, -555, -529, -502, -475, -449,
-422, -396, -369, -343, -316, -289, -263, -236, -210, -183,
-157, -130, -103, -77, -50, -24, 3, 30, 56, 83,
109, 136, 162, 189, 216, 242, 269, 295, 322, 348,
375, 402, 428, 455, 481, 508, 534, 561, 588, 614,
641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
907, 933, 960, 986, 1013, 1039, 1066
};
RAPIDJSON_ASSERT(index < 87);
return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
}
inline DiyFp GetCachedPower(int e, int* K) {
//int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive
int k = static_cast<int>(dk);
if (dk - k > 0.0)
k++;
unsigned index = static_cast<unsigned>((k >> 3) + 1);
*K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table
return GetCachedPowerByIndex(index);
}
inline DiyFp GetCachedPower10(int exp, int *outExp) {
RAPIDJSON_ASSERT(exp >= -348);
unsigned index = static_cast<unsigned>(exp + 348) / 8u;
*outExp = -348 + static_cast<int>(index) * 8;
return GetCachedPowerByIndex(index);
}
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif
#ifdef __clang__
RAPIDJSON_DIAG_POP
RAPIDJSON_DIAG_OFF(padded)
#endif
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_DIYFP_H_

View File

@ -0,0 +1,245 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
#ifndef RAPIDJSON_DTOA_
#define RAPIDJSON_DTOA_
#include "itoa.h" // GetDigitsLut()
#include "diyfp.h"
#include "ieee754.h"
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124
#endif
inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
while (rest < wp_w && delta - rest >= ten_kappa &&
(rest + ten_kappa < wp_w || /// closer
wp_w - rest > rest + ten_kappa - wp_w)) {
buffer[len - 1]--;
rest += ten_kappa;
}
}
inline int CountDecimalDigit32(uint32_t n) {
// Simple pure C++ implementation was faster than __builtin_clz version in this situation.
if (n < 10) return 1;
if (n < 100) return 2;
if (n < 1000) return 3;
if (n < 10000) return 4;
if (n < 100000) return 5;
if (n < 1000000) return 6;
if (n < 10000000) return 7;
if (n < 100000000) return 8;
// Will not reach 10 digits in DigitGen()
//if (n < 1000000000) return 9;
//return 10;
return 9;
}
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
const DiyFp wp_w = Mp - W;
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
uint64_t p2 = Mp.f & (one.f - 1);
int kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
*len = 0;
while (kappa > 0) {
uint32_t d = 0;
switch (kappa) {
case 9: d = p1 / 100000000; p1 %= 100000000; break;
case 8: d = p1 / 10000000; p1 %= 10000000; break;
case 7: d = p1 / 1000000; p1 %= 1000000; break;
case 6: d = p1 / 100000; p1 %= 100000; break;
case 5: d = p1 / 10000; p1 %= 10000; break;
case 4: d = p1 / 1000; p1 %= 1000; break;
case 3: d = p1 / 100; p1 %= 100; break;
case 2: d = p1 / 10; p1 %= 10; break;
case 1: d = p1; p1 = 0; break;
default:;
}
if (d || *len)
buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
kappa--;
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
if (tmp <= delta) {
*K += kappa;
GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
return;
}
}
// kappa = 0
for (;;) {
p2 *= 10;
delta *= 10;
char d = static_cast<char>(p2 >> -one.e);
if (d || *len)
buffer[(*len)++] = static_cast<char>('0' + d);
p2 &= one.f - 1;
kappa--;
if (p2 < delta) {
*K += kappa;
int index = -kappa;
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0));
return;
}
}
}
inline void Grisu2(double value, char* buffer, int* length, int* K) {
const DiyFp v(value);
DiyFp w_m, w_p;
v.NormalizedBoundaries(&w_m, &w_p);
const DiyFp c_mk = GetCachedPower(w_p.e, K);
const DiyFp W = v.Normalize() * c_mk;
DiyFp Wp = w_p * c_mk;
DiyFp Wm = w_m * c_mk;
Wm.f++;
Wp.f--;
DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
}
inline char* WriteExponent(int K, char* buffer) {
if (K < 0) {
*buffer++ = '-';
K = -K;
}
if (K >= 100) {
*buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
K %= 100;
const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0];
*buffer++ = d[1];
}
else if (K >= 10) {
const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0];
*buffer++ = d[1];
}
else
*buffer++ = static_cast<char>('0' + static_cast<char>(K));
return buffer;
}
inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
if (0 <= k && kk <= 21) {
// 1234e7 -> 12340000000
for (int i = length; i < kk; i++)
buffer[i] = '0';
buffer[kk] = '.';
buffer[kk + 1] = '0';
return &buffer[kk + 2];
}
else if (0 < kk && kk <= 21) {
// 1234e-2 -> 12.34
std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
buffer[kk] = '.';
if (0 > k + maxDecimalPlaces) {
// When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
// Remove extra trailing zeros (at least one) after truncation.
for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
if (buffer[i] != '0')
return &buffer[i + 1];
return &buffer[kk + 2]; // Reserve one zero
}
else
return &buffer[length + 1];
}
else if (-6 < kk && kk <= 0) {
// 1234e-6 -> 0.001234
const int offset = 2 - kk;
std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length));
buffer[0] = '0';
buffer[1] = '.';
for (int i = 2; i < offset; i++)
buffer[i] = '0';
if (length - kk > maxDecimalPlaces) {
// When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
// Remove extra trailing zeros (at least one) after truncation.
for (int i = maxDecimalPlaces + 1; i > 2; i--)
if (buffer[i] != '0')
return &buffer[i + 1];
return &buffer[3]; // Reserve one zero
}
else
return &buffer[length + offset];
}
else if (kk < -maxDecimalPlaces) {
// Truncate to zero
buffer[0] = '0';
buffer[1] = '.';
buffer[2] = '0';
return &buffer[3];
}
else if (length == 1) {
// 1e30
buffer[1] = 'e';
return WriteExponent(kk - 1, &buffer[2]);
}
else {
// 1234e30 -> 1.234e33
std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1));
buffer[1] = '.';
buffer[length + 1] = 'e';
return WriteExponent(kk - 1, &buffer[0 + length + 2]);
}
}
inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) {
RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
Double d(value);
if (d.IsZero()) {
if (d.Sign())
*buffer++ = '-'; // -0.0, Issue #289
buffer[0] = '0';
buffer[1] = '.';
buffer[2] = '0';
return &buffer[3];
}
else {
if (value < 0) {
*buffer++ = '-';
value = -value;
}
int length, K;
Grisu2(value, buffer, &length, &K);
return Prettify(buffer, length, K, maxDecimalPlaces);
}
}
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_DTOA_

View File

@ -0,0 +1,78 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_IEEE754_
#define RAPIDJSON_IEEE754_
#include "../rapidjson.h"
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
class Double {
public:
Double() {}
Double(double d) : d_(d) {}
Double(uint64_t u) : u_(u) {}
double Value() const { return d_; }
uint64_t Uint64Value() const { return u_; }
double NextPositiveDouble() const {
RAPIDJSON_ASSERT(!Sign());
return Double(u_ + 1).Value();
}
bool Sign() const { return (u_ & kSignMask) != 0; }
uint64_t Significand() const { return u_ & kSignificandMask; }
int Exponent() const { return static_cast<int>(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); }
bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; }
bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; }
bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; }
bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; }
bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; }
uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); }
int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; }
uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; }
static int EffectiveSignificandSize(int order) {
if (order >= -1021)
return 53;
else if (order <= -1074)
return 0;
else
return order + 1074;
}
private:
static const int kSignificandSize = 52;
static const int kExponentBias = 0x3FF;
static const int kDenormalExponent = 1 - kExponentBias;
static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000);
static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
union {
double d_;
uint64_t u_;
};
};
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_IEEE754_

View File

@ -0,0 +1,308 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ITOA_
#define RAPIDJSON_ITOA_
#include "../rapidjson.h"
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
inline const char* GetDigitsLut() {
static const char cDigitsLut[200] = {
'0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9',
'1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9',
'2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9',
'3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9',
'4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9',
'5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9',
'6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9',
'7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9',
'8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9',
'9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9'
};
return cDigitsLut;
}
inline char* u32toa(uint32_t value, char* buffer) {
RAPIDJSON_ASSERT(buffer != 0);
const char* cDigitsLut = GetDigitsLut();
if (value < 10000) {
const uint32_t d1 = (value / 100) << 1;
const uint32_t d2 = (value % 100) << 1;
if (value >= 1000)
*buffer++ = cDigitsLut[d1];
if (value >= 100)
*buffer++ = cDigitsLut[d1 + 1];
if (value >= 10)
*buffer++ = cDigitsLut[d2];
*buffer++ = cDigitsLut[d2 + 1];
}
else if (value < 100000000) {
// value = bbbbcccc
const uint32_t b = value / 10000;
const uint32_t c = value % 10000;
const uint32_t d1 = (b / 100) << 1;
const uint32_t d2 = (b % 100) << 1;
const uint32_t d3 = (c / 100) << 1;
const uint32_t d4 = (c % 100) << 1;
if (value >= 10000000)
*buffer++ = cDigitsLut[d1];
if (value >= 1000000)
*buffer++ = cDigitsLut[d1 + 1];
if (value >= 100000)
*buffer++ = cDigitsLut[d2];
*buffer++ = cDigitsLut[d2 + 1];
*buffer++ = cDigitsLut[d3];
*buffer++ = cDigitsLut[d3 + 1];
*buffer++ = cDigitsLut[d4];
*buffer++ = cDigitsLut[d4 + 1];
}
else {
// value = aabbbbcccc in decimal
const uint32_t a = value / 100000000; // 1 to 42
value %= 100000000;
if (a >= 10) {
const unsigned i = a << 1;
*buffer++ = cDigitsLut[i];
*buffer++ = cDigitsLut[i + 1];
}
else
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
const uint32_t b = value / 10000; // 0 to 9999
const uint32_t c = value % 10000; // 0 to 9999
const uint32_t d1 = (b / 100) << 1;
const uint32_t d2 = (b % 100) << 1;
const uint32_t d3 = (c / 100) << 1;
const uint32_t d4 = (c % 100) << 1;
*buffer++ = cDigitsLut[d1];
*buffer++ = cDigitsLut[d1 + 1];
*buffer++ = cDigitsLut[d2];
*buffer++ = cDigitsLut[d2 + 1];
*buffer++ = cDigitsLut[d3];
*buffer++ = cDigitsLut[d3 + 1];
*buffer++ = cDigitsLut[d4];
*buffer++ = cDigitsLut[d4 + 1];
}
return buffer;
}
inline char* i32toa(int32_t value, char* buffer) {
RAPIDJSON_ASSERT(buffer != 0);
uint32_t u = static_cast<uint32_t>(value);
if (value < 0) {
*buffer++ = '-';
u = ~u + 1;
}
return u32toa(u, buffer);
}
inline char* u64toa(uint64_t value, char* buffer) {
RAPIDJSON_ASSERT(buffer != 0);
const char* cDigitsLut = GetDigitsLut();
const uint64_t kTen8 = 100000000;
const uint64_t kTen9 = kTen8 * 10;
const uint64_t kTen10 = kTen8 * 100;
const uint64_t kTen11 = kTen8 * 1000;
const uint64_t kTen12 = kTen8 * 10000;
const uint64_t kTen13 = kTen8 * 100000;
const uint64_t kTen14 = kTen8 * 1000000;
const uint64_t kTen15 = kTen8 * 10000000;
const uint64_t kTen16 = kTen8 * kTen8;
if (value < kTen8) {
uint32_t v = static_cast<uint32_t>(value);
if (v < 10000) {
const uint32_t d1 = (v / 100) << 1;
const uint32_t d2 = (v % 100) << 1;
if (v >= 1000)
*buffer++ = cDigitsLut[d1];
if (v >= 100)
*buffer++ = cDigitsLut[d1 + 1];
if (v >= 10)
*buffer++ = cDigitsLut[d2];
*buffer++ = cDigitsLut[d2 + 1];
}
else {
// value = bbbbcccc
const uint32_t b = v / 10000;
const uint32_t c = v % 10000;
const uint32_t d1 = (b / 100) << 1;
const uint32_t d2 = (b % 100) << 1;
const uint32_t d3 = (c / 100) << 1;
const uint32_t d4 = (c % 100) << 1;
if (value >= 10000000)
*buffer++ = cDigitsLut[d1];
if (value >= 1000000)
*buffer++ = cDigitsLut[d1 + 1];
if (value >= 100000)
*buffer++ = cDigitsLut[d2];
*buffer++ = cDigitsLut[d2 + 1];
*buffer++ = cDigitsLut[d3];
*buffer++ = cDigitsLut[d3 + 1];
*buffer++ = cDigitsLut[d4];
*buffer++ = cDigitsLut[d4 + 1];
}
}
else if (value < kTen16) {
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
const uint32_t b0 = v0 / 10000;
const uint32_t c0 = v0 % 10000;
const uint32_t d1 = (b0 / 100) << 1;
const uint32_t d2 = (b0 % 100) << 1;
const uint32_t d3 = (c0 / 100) << 1;
const uint32_t d4 = (c0 % 100) << 1;
const uint32_t b1 = v1 / 10000;
const uint32_t c1 = v1 % 10000;
const uint32_t d5 = (b1 / 100) << 1;
const uint32_t d6 = (b1 % 100) << 1;
const uint32_t d7 = (c1 / 100) << 1;
const uint32_t d8 = (c1 % 100) << 1;
if (value >= kTen15)
*buffer++ = cDigitsLut[d1];
if (value >= kTen14)
*buffer++ = cDigitsLut[d1 + 1];
if (value >= kTen13)
*buffer++ = cDigitsLut[d2];
if (value >= kTen12)
*buffer++ = cDigitsLut[d2 + 1];
if (value >= kTen11)
*buffer++ = cDigitsLut[d3];
if (value >= kTen10)
*buffer++ = cDigitsLut[d3 + 1];
if (value >= kTen9)
*buffer++ = cDigitsLut[d4];
*buffer++ = cDigitsLut[d4 + 1];
*buffer++ = cDigitsLut[d5];
*buffer++ = cDigitsLut[d5 + 1];
*buffer++ = cDigitsLut[d6];
*buffer++ = cDigitsLut[d6 + 1];
*buffer++ = cDigitsLut[d7];
*buffer++ = cDigitsLut[d7 + 1];
*buffer++ = cDigitsLut[d8];
*buffer++ = cDigitsLut[d8 + 1];
}
else {
const uint32_t a = static_cast<uint32_t>(value / kTen16); // 1 to 1844
value %= kTen16;
if (a < 10)
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
else if (a < 100) {
const uint32_t i = a << 1;
*buffer++ = cDigitsLut[i];
*buffer++ = cDigitsLut[i + 1];
}
else if (a < 1000) {
*buffer++ = static_cast<char>('0' + static_cast<char>(a / 100));
const uint32_t i = (a % 100) << 1;
*buffer++ = cDigitsLut[i];
*buffer++ = cDigitsLut[i + 1];
}
else {
const uint32_t i = (a / 100) << 1;
const uint32_t j = (a % 100) << 1;
*buffer++ = cDigitsLut[i];
*buffer++ = cDigitsLut[i + 1];
*buffer++ = cDigitsLut[j];
*buffer++ = cDigitsLut[j + 1];
}
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
const uint32_t b0 = v0 / 10000;
const uint32_t c0 = v0 % 10000;
const uint32_t d1 = (b0 / 100) << 1;
const uint32_t d2 = (b0 % 100) << 1;
const uint32_t d3 = (c0 / 100) << 1;
const uint32_t d4 = (c0 % 100) << 1;
const uint32_t b1 = v1 / 10000;
const uint32_t c1 = v1 % 10000;
const uint32_t d5 = (b1 / 100) << 1;
const uint32_t d6 = (b1 % 100) << 1;
const uint32_t d7 = (c1 / 100) << 1;
const uint32_t d8 = (c1 % 100) << 1;
*buffer++ = cDigitsLut[d1];
*buffer++ = cDigitsLut[d1 + 1];
*buffer++ = cDigitsLut[d2];
*buffer++ = cDigitsLut[d2 + 1];
*buffer++ = cDigitsLut[d3];
*buffer++ = cDigitsLut[d3 + 1];
*buffer++ = cDigitsLut[d4];
*buffer++ = cDigitsLut[d4 + 1];
*buffer++ = cDigitsLut[d5];
*buffer++ = cDigitsLut[d5 + 1];
*buffer++ = cDigitsLut[d6];
*buffer++ = cDigitsLut[d6 + 1];
*buffer++ = cDigitsLut[d7];
*buffer++ = cDigitsLut[d7 + 1];
*buffer++ = cDigitsLut[d8];
*buffer++ = cDigitsLut[d8 + 1];
}
return buffer;
}
inline char* i64toa(int64_t value, char* buffer) {
RAPIDJSON_ASSERT(buffer != 0);
uint64_t u = static_cast<uint64_t>(value);
if (value < 0) {
*buffer++ = '-';
u = ~u + 1;
}
return u64toa(u, buffer);
}
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_ITOA_

View File

@ -0,0 +1,186 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_INTERNAL_META_H_
#define RAPIDJSON_INTERNAL_META_H_
#include "../rapidjson.h"
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#if defined(_MSC_VER) && !defined(__clang__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(6334)
#endif
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
#include <type_traits>
#endif
//@cond RAPIDJSON_INTERNAL
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching
template <typename T> struct Void { typedef void Type; };
///////////////////////////////////////////////////////////////////////////////
// BoolType, TrueType, FalseType
//
template <bool Cond> struct BoolType {
static const bool Value = Cond;
typedef BoolType Type;
};
typedef BoolType<true> TrueType;
typedef BoolType<false> FalseType;
///////////////////////////////////////////////////////////////////////////////
// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr
//
template <bool C> struct SelectIfImpl { template <typename T1, typename T2> struct Apply { typedef T1 Type; }; };
template <> struct SelectIfImpl<false> { template <typename T1, typename T2> struct Apply { typedef T2 Type; }; };
template <bool C, typename T1, typename T2> struct SelectIfCond : SelectIfImpl<C>::template Apply<T1,T2> {};
template <typename C, typename T1, typename T2> struct SelectIf : SelectIfCond<C::Value, T1, T2> {};
template <bool Cond1, bool Cond2> struct AndExprCond : FalseType {};
template <> struct AndExprCond<true, true> : TrueType {};
template <bool Cond1, bool Cond2> struct OrExprCond : TrueType {};
template <> struct OrExprCond<false, false> : FalseType {};
template <typename C> struct BoolExpr : SelectIf<C,TrueType,FalseType>::Type {};
template <typename C> struct NotExpr : SelectIf<C,FalseType,TrueType>::Type {};
template <typename C1, typename C2> struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {};
template <typename C1, typename C2> struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {};
///////////////////////////////////////////////////////////////////////////////
// AddConst, MaybeAddConst, RemoveConst
template <typename T> struct AddConst { typedef const T Type; };
template <bool Constify, typename T> struct MaybeAddConst : SelectIfCond<Constify, const T, T> {};
template <typename T> struct RemoveConst { typedef T Type; };
template <typename T> struct RemoveConst<const T> { typedef T Type; };
///////////////////////////////////////////////////////////////////////////////
// IsSame, IsConst, IsMoreConst, IsPointer
//
template <typename T, typename U> struct IsSame : FalseType {};
template <typename T> struct IsSame<T, T> : TrueType {};
template <typename T> struct IsConst : FalseType {};
template <typename T> struct IsConst<const T> : TrueType {};
template <typename CT, typename T>
struct IsMoreConst
: AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>,
BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {};
template <typename T> struct IsPointer : FalseType {};
template <typename T> struct IsPointer<T*> : TrueType {};
///////////////////////////////////////////////////////////////////////////////
// IsBaseOf
//
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
template <typename B, typename D> struct IsBaseOf
: BoolType< ::std::is_base_of<B,D>::value> {};
#else // simplified version adopted from Boost
template<typename B, typename D> struct IsBaseOfImpl {
RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0);
RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0);
typedef char (&Yes)[1];
typedef char (&No) [2];
template <typename T>
static Yes Check(const D*, T);
static No Check(const B*, int);
struct Host {
operator const B*() const;
operator const D*();
};
enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) };
};
template <typename B, typename D> struct IsBaseOf
: OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {};
#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS
//////////////////////////////////////////////////////////////////////////
// EnableIf / DisableIf
//
template <bool Condition, typename T = void> struct EnableIfCond { typedef T Type; };
template <typename T> struct EnableIfCond<false, T> { /* empty */ };
template <bool Condition, typename T = void> struct DisableIfCond { typedef T Type; };
template <typename T> struct DisableIfCond<true, T> { /* empty */ };
template <typename Condition, typename T = void>
struct EnableIf : EnableIfCond<Condition::Value, T> {};
template <typename Condition, typename T = void>
struct DisableIf : DisableIfCond<Condition::Value, T> {};
// SFINAE helpers
struct SfinaeTag {};
template <typename T> struct RemoveSfinaeTag;
template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type; };
#define RAPIDJSON_REMOVEFPTR_(type) \
typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \
< ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type
#define RAPIDJSON_ENABLEIF(cond) \
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
#define RAPIDJSON_DISABLEIF(cond) \
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
<RAPIDJSON_REMOVEFPTR_(cond), \
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
<RAPIDJSON_REMOVEFPTR_(cond), \
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
} // namespace internal
RAPIDJSON_NAMESPACE_END
//@endcond
#if defined(_MSC_VER) && !defined(__clang__)
RAPIDJSON_DIAG_POP
#endif
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_INTERNAL_META_H_

View File

@ -0,0 +1,55 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_POW10_
#define RAPIDJSON_POW10_
#include "../rapidjson.h"
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
//! Computes integer powers of 10 in double (10.0^n).
/*! This function uses lookup table for fast and accurate results.
\param n non-negative exponent. Must <= 308.
\return 10.0^n
*/
inline double Pow10(int n) {
static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes
1e+0,
1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20,
1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40,
1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60,
1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80,
1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100,
1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120,
1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140,
1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160,
1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180,
1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200,
1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220,
1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240,
1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260,
1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280,
1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300,
1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
};
RAPIDJSON_ASSERT(n >= 0 && n <= 308);
return e[n];
}
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_POW10_

View File

@ -0,0 +1,740 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_INTERNAL_REGEX_H_
#define RAPIDJSON_INTERNAL_REGEX_H_
#include "../allocators.h"
#include "../stream.h"
#include "stack.h"
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
RAPIDJSON_DIAG_OFF(switch-enum)
RAPIDJSON_DIAG_OFF(implicit-fallthrough)
#elif defined(_MSC_VER)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
#endif
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#if __GNUC__ >= 7
RAPIDJSON_DIAG_OFF(implicit-fallthrough)
#endif
#endif
#ifndef RAPIDJSON_REGEX_VERBOSE
#define RAPIDJSON_REGEX_VERBOSE 0
#endif
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
///////////////////////////////////////////////////////////////////////////////
// DecodedStream
template <typename SourceStream, typename Encoding>
class DecodedStream {
public:
DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); }
unsigned Peek() { return codepoint_; }
unsigned Take() {
unsigned c = codepoint_;
if (c) // No further decoding when '\0'
Decode();
return c;
}
private:
void Decode() {
if (!Encoding::Decode(ss_, &codepoint_))
codepoint_ = 0;
}
SourceStream& ss_;
unsigned codepoint_;
};
///////////////////////////////////////////////////////////////////////////////
// GenericRegex
static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1
static const SizeType kRegexInvalidRange = ~SizeType(0);
template <typename Encoding, typename Allocator>
class GenericRegexSearch;
//! Regular expression engine with subset of ECMAscript grammar.
/*!
Supported regular expression syntax:
- \c ab Concatenation
- \c a|b Alternation
- \c a? Zero or one
- \c a* Zero or more
- \c a+ One or more
- \c a{3} Exactly 3 times
- \c a{3,} At least 3 times
- \c a{3,5} 3 to 5 times
- \c (ab) Grouping
- \c ^a At the beginning
- \c a$ At the end
- \c . Any character
- \c [abc] Character classes
- \c [a-c] Character class range
- \c [a-z0-9_] Character class combination
- \c [^abc] Negated character classes
- \c [^a-c] Negated character class range
- \c [\b] Backspace (U+0008)
- \c \\| \\\\ ... Escape characters
- \c \\f Form feed (U+000C)
- \c \\n Line feed (U+000A)
- \c \\r Carriage return (U+000D)
- \c \\t Tab (U+0009)
- \c \\v Vertical tab (U+000B)
\note This is a Thompson NFA engine, implemented with reference to
Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).",
https://swtch.com/~rsc/regexp/regexp1.html
*/
template <typename Encoding, typename Allocator = CrtAllocator>
class GenericRegex {
public:
typedef Encoding EncodingType;
typedef typename Encoding::Ch Ch;
template <typename, typename> friend class GenericRegexSearch;
GenericRegex(const Ch* source, Allocator* allocator = 0) :
ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_),
states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(),
anchorBegin_(), anchorEnd_()
{
GenericStringStream<Encoding> ss(source);
DecodedStream<GenericStringStream<Encoding>, Encoding> ds(ss);
Parse(ds);
}
~GenericRegex()
{
RAPIDJSON_DELETE(ownAllocator_);
}
bool IsValid() const {
return root_ != kRegexInvalidState;
}
private:
enum Operator {
kZeroOrOne,
kZeroOrMore,
kOneOrMore,
kConcatenation,
kAlternation,
kLeftParenthesis
};
static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.'
static const unsigned kRangeCharacterClass = 0xFFFFFFFE;
static const unsigned kRangeNegationFlag = 0x80000000;
struct Range {
unsigned start; //
unsigned end;
SizeType next;
};
struct State {
SizeType out; //!< Equals to kInvalid for matching state
SizeType out1; //!< Equals to non-kInvalid for split
SizeType rangeStart;
unsigned codepoint;
};
struct Frag {
Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {}
SizeType start;
SizeType out; //!< link-list of all output states
SizeType minIndex;
};
State& GetState(SizeType index) {
RAPIDJSON_ASSERT(index < stateCount_);
return states_.template Bottom<State>()[index];
}
const State& GetState(SizeType index) const {
RAPIDJSON_ASSERT(index < stateCount_);
return states_.template Bottom<State>()[index];
}
Range& GetRange(SizeType index) {
RAPIDJSON_ASSERT(index < rangeCount_);
return ranges_.template Bottom<Range>()[index];
}
const Range& GetRange(SizeType index) const {
RAPIDJSON_ASSERT(index < rangeCount_);
return ranges_.template Bottom<Range>()[index];
}
template <typename InputStream>
void Parse(DecodedStream<InputStream, Encoding>& ds) {
Stack<Allocator> operandStack(allocator_, 256); // Frag
Stack<Allocator> operatorStack(allocator_, 256); // Operator
Stack<Allocator> atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis)
*atomCountStack.template Push<unsigned>() = 0;
unsigned codepoint;
while (ds.Peek() != 0) {
switch (codepoint = ds.Take()) {
case '^':
anchorBegin_ = true;
break;
case '$':
anchorEnd_ = true;
break;
case '|':
while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() < kAlternation)
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
return;
*operatorStack.template Push<Operator>() = kAlternation;
*atomCountStack.template Top<unsigned>() = 0;
break;
case '(':
*operatorStack.template Push<Operator>() = kLeftParenthesis;
*atomCountStack.template Push<unsigned>() = 0;
break;
case ')':
while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() != kLeftParenthesis)
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
return;
if (operatorStack.Empty())
return;
operatorStack.template Pop<Operator>(1);
atomCountStack.template Pop<unsigned>(1);
ImplicitConcatenation(atomCountStack, operatorStack);
break;
case '?':
if (!Eval(operandStack, kZeroOrOne))
return;
break;
case '*':
if (!Eval(operandStack, kZeroOrMore))
return;
break;
case '+':
if (!Eval(operandStack, kOneOrMore))
return;
break;
case '{':
{
unsigned n, m;
if (!ParseUnsigned(ds, &n))
return;
if (ds.Peek() == ',') {
ds.Take();
if (ds.Peek() == '}')
m = kInfinityQuantifier;
else if (!ParseUnsigned(ds, &m) || m < n)
return;
}
else
m = n;
if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}')
return;
ds.Take();
}
break;
case '.':
PushOperand(operandStack, kAnyCharacterClass);
ImplicitConcatenation(atomCountStack, operatorStack);
break;
case '[':
{
SizeType range;
if (!ParseRange(ds, &range))
return;
SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass);
GetState(s).rangeStart = range;
*operandStack.template Push<Frag>() = Frag(s, s, s);
}
ImplicitConcatenation(atomCountStack, operatorStack);
break;
case '\\': // Escape character
if (!CharacterEscape(ds, &codepoint))
return; // Unsupported escape character
// fall through to default
default: // Pattern character
PushOperand(operandStack, codepoint);
ImplicitConcatenation(atomCountStack, operatorStack);
}
}
while (!operatorStack.Empty())
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
return;
// Link the operand to matching state.
if (operandStack.GetSize() == sizeof(Frag)) {
Frag* e = operandStack.template Pop<Frag>(1);
Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0));
root_ = e->start;
#if RAPIDJSON_REGEX_VERBOSE
printf("root: %d\n", root_);
for (SizeType i = 0; i < stateCount_ ; i++) {
State& s = GetState(i);
printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint);
}
printf("\n");
#endif
}
}
SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) {
State* s = states_.template Push<State>();
s->out = out;
s->out1 = out1;
s->codepoint = codepoint;
s->rangeStart = kRegexInvalidRange;
return stateCount_++;
}
void PushOperand(Stack<Allocator>& operandStack, unsigned codepoint) {
SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint);
*operandStack.template Push<Frag>() = Frag(s, s, s);
}
void ImplicitConcatenation(Stack<Allocator>& atomCountStack, Stack<Allocator>& operatorStack) {
if (*atomCountStack.template Top<unsigned>())
*operatorStack.template Push<Operator>() = kConcatenation;
(*atomCountStack.template Top<unsigned>())++;
}
SizeType Append(SizeType l1, SizeType l2) {
SizeType old = l1;
while (GetState(l1).out != kRegexInvalidState)
l1 = GetState(l1).out;
GetState(l1).out = l2;
return old;
}
void Patch(SizeType l, SizeType s) {
for (SizeType next; l != kRegexInvalidState; l = next) {
next = GetState(l).out;
GetState(l).out = s;
}
}
bool Eval(Stack<Allocator>& operandStack, Operator op) {
switch (op) {
case kConcatenation:
RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2);
{
Frag e2 = *operandStack.template Pop<Frag>(1);
Frag e1 = *operandStack.template Pop<Frag>(1);
Patch(e1.out, e2.start);
*operandStack.template Push<Frag>() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex));
}
return true;
case kAlternation:
if (operandStack.GetSize() >= sizeof(Frag) * 2) {
Frag e2 = *operandStack.template Pop<Frag>(1);
Frag e1 = *operandStack.template Pop<Frag>(1);
SizeType s = NewState(e1.start, e2.start, 0);
*operandStack.template Push<Frag>() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex));
return true;
}
return false;
case kZeroOrOne:
if (operandStack.GetSize() >= sizeof(Frag)) {
Frag e = *operandStack.template Pop<Frag>(1);
SizeType s = NewState(kRegexInvalidState, e.start, 0);
*operandStack.template Push<Frag>() = Frag(s, Append(e.out, s), e.minIndex);
return true;
}
return false;
case kZeroOrMore:
if (operandStack.GetSize() >= sizeof(Frag)) {
Frag e = *operandStack.template Pop<Frag>(1);
SizeType s = NewState(kRegexInvalidState, e.start, 0);
Patch(e.out, s);
*operandStack.template Push<Frag>() = Frag(s, s, e.minIndex);
return true;
}
return false;
case kOneOrMore:
if (operandStack.GetSize() >= sizeof(Frag)) {
Frag e = *operandStack.template Pop<Frag>(1);
SizeType s = NewState(kRegexInvalidState, e.start, 0);
Patch(e.out, s);
*operandStack.template Push<Frag>() = Frag(e.start, s, e.minIndex);
return true;
}
return false;
default:
// syntax error (e.g. unclosed kLeftParenthesis)
return false;
}
}
bool EvalQuantifier(Stack<Allocator>& operandStack, unsigned n, unsigned m) {
RAPIDJSON_ASSERT(n <= m);
RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag));
if (n == 0) {
if (m == 0) // a{0} not support
return false;
else if (m == kInfinityQuantifier)
Eval(operandStack, kZeroOrMore); // a{0,} -> a*
else {
Eval(operandStack, kZeroOrOne); // a{0,5} -> a?
for (unsigned i = 0; i < m - 1; i++)
CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a?
for (unsigned i = 0; i < m - 1; i++)
Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a?
}
return true;
}
for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a
CloneTopOperand(operandStack);
if (m == kInfinityQuantifier)
Eval(operandStack, kOneOrMore); // a{3,} -> a a a+
else if (m > n) {
CloneTopOperand(operandStack); // a{3,5} -> a a a a
Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a?
for (unsigned i = n; i < m - 1; i++)
CloneTopOperand(operandStack); // a{3,5} -> a a a a? a?
for (unsigned i = n; i < m; i++)
Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a?
}
for (unsigned i = 0; i < n - 1; i++)
Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a?
return true;
}
static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; }
void CloneTopOperand(Stack<Allocator>& operandStack) {
const Frag src = *operandStack.template Top<Frag>(); // Copy constructor to prevent invalidation
SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_)
State* s = states_.template Push<State>(count);
memcpy(s, &GetState(src.minIndex), count * sizeof(State));
for (SizeType j = 0; j < count; j++) {
if (s[j].out != kRegexInvalidState)
s[j].out += count;
if (s[j].out1 != kRegexInvalidState)
s[j].out1 += count;
}
*operandStack.template Push<Frag>() = Frag(src.start + count, src.out + count, src.minIndex + count);
stateCount_ += count;
}
template <typename InputStream>
bool ParseUnsigned(DecodedStream<InputStream, Encoding>& ds, unsigned* u) {
unsigned r = 0;
if (ds.Peek() < '0' || ds.Peek() > '9')
return false;
while (ds.Peek() >= '0' && ds.Peek() <= '9') {
if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295
return false; // overflow
r = r * 10 + (ds.Take() - '0');
}
*u = r;
return true;
}
template <typename InputStream>
bool ParseRange(DecodedStream<InputStream, Encoding>& ds, SizeType* range) {
bool isBegin = true;
bool negate = false;
int step = 0;
SizeType start = kRegexInvalidRange;
SizeType current = kRegexInvalidRange;
unsigned codepoint;
while ((codepoint = ds.Take()) != 0) {
if (isBegin) {
isBegin = false;
if (codepoint == '^') {
negate = true;
continue;
}
}
switch (codepoint) {
case ']':
if (start == kRegexInvalidRange)
return false; // Error: nothing inside []
if (step == 2) { // Add trailing '-'
SizeType r = NewRange('-');
RAPIDJSON_ASSERT(current != kRegexInvalidRange);
GetRange(current).next = r;
}
if (negate)
GetRange(start).start |= kRangeNegationFlag;
*range = start;
return true;
case '\\':
if (ds.Peek() == 'b') {
ds.Take();
codepoint = 0x0008; // Escape backspace character
}
else if (!CharacterEscape(ds, &codepoint))
return false;
// fall through to default
default:
switch (step) {
case 1:
if (codepoint == '-') {
step++;
break;
}
// fall through to step 0 for other characters
case 0:
{
SizeType r = NewRange(codepoint);
if (current != kRegexInvalidRange)
GetRange(current).next = r;
if (start == kRegexInvalidRange)
start = r;
current = r;
}
step = 1;
break;
default:
RAPIDJSON_ASSERT(step == 2);
GetRange(current).end = codepoint;
step = 0;
}
}
}
return false;
}
SizeType NewRange(unsigned codepoint) {
Range* r = ranges_.template Push<Range>();
r->start = r->end = codepoint;
r->next = kRegexInvalidRange;
return rangeCount_++;
}
template <typename InputStream>
bool CharacterEscape(DecodedStream<InputStream, Encoding>& ds, unsigned* escapedCodepoint) {
unsigned codepoint;
switch (codepoint = ds.Take()) {
case '^':
case '$':
case '|':
case '(':
case ')':
case '?':
case '*':
case '+':
case '.':
case '[':
case ']':
case '{':
case '}':
case '\\':
*escapedCodepoint = codepoint; return true;
case 'f': *escapedCodepoint = 0x000C; return true;
case 'n': *escapedCodepoint = 0x000A; return true;
case 'r': *escapedCodepoint = 0x000D; return true;
case 't': *escapedCodepoint = 0x0009; return true;
case 'v': *escapedCodepoint = 0x000B; return true;
default:
return false; // Unsupported escape character
}
}
Allocator* ownAllocator_;
Allocator* allocator_;
Stack<Allocator> states_;
Stack<Allocator> ranges_;
SizeType root_;
SizeType stateCount_;
SizeType rangeCount_;
static const unsigned kInfinityQuantifier = ~0u;
// For SearchWithAnchoring()
bool anchorBegin_;
bool anchorEnd_;
};
template <typename RegexType, typename Allocator = CrtAllocator>
class GenericRegexSearch {
public:
typedef typename RegexType::EncodingType Encoding;
typedef typename Encoding::Ch Ch;
GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) :
regex_(regex), allocator_(allocator), ownAllocator_(0),
state0_(allocator, 0), state1_(allocator, 0), stateSet_()
{
RAPIDJSON_ASSERT(regex_.IsValid());
if (!allocator_)
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
stateSet_ = static_cast<unsigned*>(allocator_->Malloc(GetStateSetSize()));
state0_.template Reserve<SizeType>(regex_.stateCount_);
state1_.template Reserve<SizeType>(regex_.stateCount_);
}
~GenericRegexSearch() {
Allocator::Free(stateSet_);
RAPIDJSON_DELETE(ownAllocator_);
}
template <typename InputStream>
bool Match(InputStream& is) {
return SearchWithAnchoring(is, true, true);
}
bool Match(const Ch* s) {
GenericStringStream<Encoding> is(s);
return Match(is);
}
template <typename InputStream>
bool Search(InputStream& is) {
return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_);
}
bool Search(const Ch* s) {
GenericStringStream<Encoding> is(s);
return Search(is);
}
private:
typedef typename RegexType::State State;
typedef typename RegexType::Range Range;
template <typename InputStream>
bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) {
DecodedStream<InputStream, Encoding> ds(is);
state0_.Clear();
Stack<Allocator> *current = &state0_, *next = &state1_;
const size_t stateSetSize = GetStateSetSize();
std::memset(stateSet_, 0, stateSetSize);
bool matched = AddState(*current, regex_.root_);
unsigned codepoint;
while (!current->Empty() && (codepoint = ds.Take()) != 0) {
std::memset(stateSet_, 0, stateSetSize);
next->Clear();
matched = false;
for (const SizeType* s = current->template Bottom<SizeType>(); s != current->template End<SizeType>(); ++s) {
const State& sr = regex_.GetState(*s);
if (sr.codepoint == codepoint ||
sr.codepoint == RegexType::kAnyCharacterClass ||
(sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint)))
{
matched = AddState(*next, sr.out) || matched;
if (!anchorEnd && matched)
return true;
}
if (!anchorBegin)
AddState(*next, regex_.root_);
}
internal::Swap(current, next);
}
return matched;
}
size_t GetStateSetSize() const {
return (regex_.stateCount_ + 31) / 32 * 4;
}
// Return whether the added states is a match state
bool AddState(Stack<Allocator>& l, SizeType index) {
RAPIDJSON_ASSERT(index != kRegexInvalidState);
const State& s = regex_.GetState(index);
if (s.out1 != kRegexInvalidState) { // Split
bool matched = AddState(l, s.out);
return AddState(l, s.out1) || matched;
}
else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) {
stateSet_[index >> 5] |= (1u << (index & 31));
*l.template PushUnsafe<SizeType>() = index;
}
return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation.
}
bool MatchRange(SizeType rangeIndex, unsigned codepoint) const {
bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0;
while (rangeIndex != kRegexInvalidRange) {
const Range& r = regex_.GetRange(rangeIndex);
if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end)
return yes;
rangeIndex = r.next;
}
return !yes;
}
const RegexType& regex_;
Allocator* allocator_;
Allocator* ownAllocator_;
Stack<Allocator> state0_;
Stack<Allocator> state1_;
uint32_t* stateSet_;
};
typedef GenericRegex<UTF8<> > Regex;
typedef GenericRegexSearch<Regex> RegexSearch;
} // namespace internal
RAPIDJSON_NAMESPACE_END
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif
#if defined(__clang__) || defined(_MSC_VER)
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_INTERNAL_REGEX_H_

View File

@ -0,0 +1,232 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_INTERNAL_STACK_H_
#define RAPIDJSON_INTERNAL_STACK_H_
#include "../allocators.h"
#include "swap.h"
#include <cstddef>
#if defined(__clang__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(c++98-compat)
#endif
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
///////////////////////////////////////////////////////////////////////////////
// Stack
//! A type-unsafe stack for storing different types of data.
/*! \tparam Allocator Allocator for allocating stack memory.
*/
template <typename Allocator>
class Stack {
public:
// Optimization note: Do not allocate memory for stack_ in constructor.
// Do it lazily when first Push() -> Expand() -> Resize().
Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) {
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
Stack(Stack&& rhs)
: allocator_(rhs.allocator_),
ownAllocator_(rhs.ownAllocator_),
stack_(rhs.stack_),
stackTop_(rhs.stackTop_),
stackEnd_(rhs.stackEnd_),
initialCapacity_(rhs.initialCapacity_)
{
rhs.allocator_ = 0;
rhs.ownAllocator_ = 0;
rhs.stack_ = 0;
rhs.stackTop_ = 0;
rhs.stackEnd_ = 0;
rhs.initialCapacity_ = 0;
}
#endif
~Stack() {
Destroy();
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
Stack& operator=(Stack&& rhs) {
if (&rhs != this)
{
Destroy();
allocator_ = rhs.allocator_;
ownAllocator_ = rhs.ownAllocator_;
stack_ = rhs.stack_;
stackTop_ = rhs.stackTop_;
stackEnd_ = rhs.stackEnd_;
initialCapacity_ = rhs.initialCapacity_;
rhs.allocator_ = 0;
rhs.ownAllocator_ = 0;
rhs.stack_ = 0;
rhs.stackTop_ = 0;
rhs.stackEnd_ = 0;
rhs.initialCapacity_ = 0;
}
return *this;
}
#endif
void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT {
internal::Swap(allocator_, rhs.allocator_);
internal::Swap(ownAllocator_, rhs.ownAllocator_);
internal::Swap(stack_, rhs.stack_);
internal::Swap(stackTop_, rhs.stackTop_);
internal::Swap(stackEnd_, rhs.stackEnd_);
internal::Swap(initialCapacity_, rhs.initialCapacity_);
}
void Clear() { stackTop_ = stack_; }
void ShrinkToFit() {
if (Empty()) {
// If the stack is empty, completely deallocate the memory.
Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc)
stack_ = 0;
stackTop_ = 0;
stackEnd_ = 0;
}
else
Resize(GetSize());
}
// Optimization note: try to minimize the size of this function for force inline.
// Expansion is run very infrequently, so it is moved to another (probably non-inline) function.
template<typename T>
RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) {
// Expand the stack if needed
if (RAPIDJSON_UNLIKELY(static_cast<std::ptrdiff_t>(sizeof(T) * count) > (stackEnd_ - stackTop_)))
Expand<T>(count);
}
template<typename T>
RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
Reserve<T>(count);
return PushUnsafe<T>(count);
}
template<typename T>
RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) {
RAPIDJSON_ASSERT(stackTop_);
RAPIDJSON_ASSERT(static_cast<std::ptrdiff_t>(sizeof(T) * count) <= (stackEnd_ - stackTop_));
T* ret = reinterpret_cast<T*>(stackTop_);
stackTop_ += sizeof(T) * count;
return ret;
}
template<typename T>
T* Pop(size_t count) {
RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T));
stackTop_ -= count * sizeof(T);
return reinterpret_cast<T*>(stackTop_);
}
template<typename T>
T* Top() {
RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
return reinterpret_cast<T*>(stackTop_ - sizeof(T));
}
template<typename T>
const T* Top() const {
RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
return reinterpret_cast<T*>(stackTop_ - sizeof(T));
}
template<typename T>
T* End() { return reinterpret_cast<T*>(stackTop_); }
template<typename T>
const T* End() const { return reinterpret_cast<T*>(stackTop_); }
template<typename T>
T* Bottom() { return reinterpret_cast<T*>(stack_); }
template<typename T>
const T* Bottom() const { return reinterpret_cast<T*>(stack_); }
bool HasAllocator() const {
return allocator_ != 0;
}
Allocator& GetAllocator() {
RAPIDJSON_ASSERT(allocator_);
return *allocator_;
}
bool Empty() const { return stackTop_ == stack_; }
size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); }
size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); }
private:
template<typename T>
void Expand(size_t count) {
// Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity.
size_t newCapacity;
if (stack_ == 0) {
if (!allocator_)
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
newCapacity = initialCapacity_;
} else {
newCapacity = GetCapacity();
newCapacity += (newCapacity + 1) / 2;
}
size_t newSize = GetSize() + sizeof(T) * count;
if (newCapacity < newSize)
newCapacity = newSize;
Resize(newCapacity);
}
void Resize(size_t newCapacity) {
const size_t size = GetSize(); // Backup the current size
stack_ = static_cast<char*>(allocator_->Realloc(stack_, GetCapacity(), newCapacity));
stackTop_ = stack_ + size;
stackEnd_ = stack_ + newCapacity;
}
void Destroy() {
Allocator::Free(stack_);
RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack
}
// Prohibit copy constructor & assignment operator.
Stack(const Stack&);
Stack& operator=(const Stack&);
Allocator* allocator_;
Allocator* ownAllocator_;
char *stack_;
char *stackTop_;
char *stackEnd_;
size_t initialCapacity_;
};
} // namespace internal
RAPIDJSON_NAMESPACE_END
#if defined(__clang__)
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_STACK_H_

View File

@ -0,0 +1,69 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_
#define RAPIDJSON_INTERNAL_STRFUNC_H_
#include "../stream.h"
#include <cwchar>
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
//! Custom strlen() which works on different character types.
/*! \tparam Ch Character type (e.g. char, wchar_t, short)
\param s Null-terminated input string.
\return Number of characters in the string.
\note This has the same semantics as strlen(), the return value is not number of Unicode codepoints.
*/
template <typename Ch>
inline SizeType StrLen(const Ch* s) {
RAPIDJSON_ASSERT(s != 0);
const Ch* p = s;
while (*p) ++p;
return SizeType(p - s);
}
template <>
inline SizeType StrLen(const char* s) {
return SizeType(std::strlen(s));
}
template <>
inline SizeType StrLen(const wchar_t* s) {
return SizeType(std::wcslen(s));
}
//! Returns number of code points in a encoded string.
template<typename Encoding>
bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) {
RAPIDJSON_ASSERT(s != 0);
RAPIDJSON_ASSERT(outCount != 0);
GenericStringStream<Encoding> is(s);
const typename Encoding::Ch* end = s + length;
SizeType count = 0;
while (is.src_ < end) {
unsigned codepoint;
if (!Encoding::Decode(is, &codepoint))
return false;
count++;
}
*outCount = count;
return true;
}
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_INTERNAL_STRFUNC_H_

View File

@ -0,0 +1,290 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_STRTOD_
#define RAPIDJSON_STRTOD_
#include "ieee754.h"
#include "biginteger.h"
#include "diyfp.h"
#include "pow10.h"
#include <climits>
#include <limits>
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
inline double FastPath(double significand, int exp) {
if (exp < -308)
return 0.0;
else if (exp >= 0)
return significand * internal::Pow10(exp);
else
return significand / internal::Pow10(-exp);
}
inline double StrtodNormalPrecision(double d, int p) {
if (p < -308) {
// Prevent expSum < -308, making Pow10(p) = 0
d = FastPath(d, -308);
d = FastPath(d, p + 308);
}
else
d = FastPath(d, p);
return d;
}
template <typename T>
inline T Min3(T a, T b, T c) {
T m = a;
if (m > b) m = b;
if (m > c) m = c;
return m;
}
inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) {
const Double db(b);
const uint64_t bInt = db.IntegerSignificand();
const int bExp = db.IntegerExponent();
const int hExp = bExp - 1;
int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0;
// Adjust for decimal exponent
if (dExp >= 0) {
dS_Exp2 += dExp;
dS_Exp5 += dExp;
}
else {
bS_Exp2 -= dExp;
bS_Exp5 -= dExp;
hS_Exp2 -= dExp;
hS_Exp5 -= dExp;
}
// Adjust for binary exponent
if (bExp >= 0)
bS_Exp2 += bExp;
else {
dS_Exp2 -= bExp;
hS_Exp2 -= bExp;
}
// Adjust for half ulp exponent
if (hExp >= 0)
hS_Exp2 += hExp;
else {
dS_Exp2 -= hExp;
bS_Exp2 -= hExp;
}
// Remove common power of two factor from all three scaled values
int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2);
dS_Exp2 -= common_Exp2;
bS_Exp2 -= common_Exp2;
hS_Exp2 -= common_Exp2;
BigInteger dS = d;
dS.MultiplyPow5(static_cast<unsigned>(dS_Exp5)) <<= static_cast<unsigned>(dS_Exp2);
BigInteger bS(bInt);
bS.MultiplyPow5(static_cast<unsigned>(bS_Exp5)) <<= static_cast<unsigned>(bS_Exp2);
BigInteger hS(1);
hS.MultiplyPow5(static_cast<unsigned>(hS_Exp5)) <<= static_cast<unsigned>(hS_Exp2);
BigInteger delta(0);
dS.Difference(bS, &delta);
return delta.Compare(hS);
}
inline bool StrtodFast(double d, int p, double* result) {
// Use fast path for string-to-double conversion if possible
// see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
if (p > 22 && p < 22 + 16) {
// Fast Path Cases In Disguise
d *= internal::Pow10(p - 22);
p = 22;
}
if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1
*result = FastPath(d, p);
return true;
}
else
return false;
}
// Compute an approximation and see if it is within 1/2 ULP
inline bool StrtodDiyFp(const char* decimals, int dLen, int dExp, double* result) {
uint64_t significand = 0;
int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999
for (; i < dLen; i++) {
if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) ||
(significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5'))
break;
significand = significand * 10u + static_cast<unsigned>(decimals[i] - '0');
}
if (i < dLen && decimals[i] >= '5') // Rounding
significand++;
int remaining = dLen - i;
const int kUlpShift = 3;
const int kUlp = 1 << kUlpShift;
int64_t error = (remaining == 0) ? 0 : kUlp / 2;
DiyFp v(significand, 0);
v = v.Normalize();
error <<= -v.e;
dExp += remaining;
int actualExp;
DiyFp cachedPower = GetCachedPower10(dExp, &actualExp);
if (actualExp != dExp) {
static const DiyFp kPow10[] = {
DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1
DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2
DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3
DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4
DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5
DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6
DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7
};
int adjustment = dExp - actualExp;
RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8);
v = v * kPow10[adjustment - 1];
if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit
error += kUlp / 2;
}
v = v * cachedPower;
error += kUlp + (error == 0 ? 0 : 1);
const int oldExp = v.e;
v = v.Normalize();
error <<= oldExp - v.e;
const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e);
int precisionSize = 64 - effectiveSignificandSize;
if (precisionSize + kUlpShift >= 64) {
int scaleExp = (precisionSize + kUlpShift) - 63;
v.f >>= scaleExp;
v.e += scaleExp;
error = (error >> scaleExp) + 1 + kUlp;
precisionSize -= scaleExp;
}
DiyFp rounded(v.f >> precisionSize, v.e + precisionSize);
const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp;
const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp;
if (precisionBits >= halfWay + static_cast<unsigned>(error)) {
rounded.f++;
if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340)
rounded.f >>= 1;
rounded.e++;
}
}
*result = rounded.ToDouble();
return halfWay - static_cast<unsigned>(error) >= precisionBits || precisionBits >= halfWay + static_cast<unsigned>(error);
}
inline double StrtodBigInteger(double approx, const char* decimals, int dLen, int dExp) {
RAPIDJSON_ASSERT(dLen >= 0);
const BigInteger dInt(decimals, static_cast<unsigned>(dLen));
Double a(approx);
int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp);
if (cmp < 0)
return a.Value(); // within half ULP
else if (cmp == 0) {
// Round towards even
if (a.Significand() & 1)
return a.NextPositiveDouble();
else
return a.Value();
}
else // adjustment
return a.NextPositiveDouble();
}
inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) {
RAPIDJSON_ASSERT(d >= 0.0);
RAPIDJSON_ASSERT(length >= 1);
double result = 0.0;
if (StrtodFast(d, p, &result))
return result;
RAPIDJSON_ASSERT(length <= INT_MAX);
int dLen = static_cast<int>(length);
RAPIDJSON_ASSERT(length >= decimalPosition);
RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX);
int dExpAdjust = static_cast<int>(length - decimalPosition);
RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust);
int dExp = exp - dExpAdjust;
// Make sure length+dExp does not overflow
RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen);
// Trim leading zeros
while (dLen > 0 && *decimals == '0') {
dLen--;
decimals++;
}
// Trim trailing zeros
while (dLen > 0 && decimals[dLen - 1] == '0') {
dLen--;
dExp++;
}
if (dLen == 0) { // Buffer only contains zeros.
return 0.0;
}
// Trim right-most digits
const int kMaxDecimalDigit = 767 + 1;
if (dLen > kMaxDecimalDigit) {
dExp += dLen - kMaxDecimalDigit;
dLen = kMaxDecimalDigit;
}
// If too small, underflow to zero.
// Any x <= 10^-324 is interpreted as zero.
if (dLen + dExp <= -324)
return 0.0;
// If too large, overflow to infinity.
// Any x >= 10^309 is interpreted as +infinity.
if (dLen + dExp > 309)
return std::numeric_limits<double>::infinity();
if (StrtodDiyFp(decimals, dLen, dExp, &result))
return result;
// Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison
return StrtodBigInteger(result, decimals, dLen, dExp);
}
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_STRTOD_

View File

@ -0,0 +1,46 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_INTERNAL_SWAP_H_
#define RAPIDJSON_INTERNAL_SWAP_H_
#include "../rapidjson.h"
#if defined(__clang__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(c++98-compat)
#endif
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
//! Custom swap() to avoid dependency on C++ <algorithm> header
/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only.
\note This has the same semantics as std::swap().
*/
template <typename T>
inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT {
T tmp = a;
a = b;
b = tmp;
}
} // namespace internal
RAPIDJSON_NAMESPACE_END
#if defined(__clang__)
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_INTERNAL_SWAP_H_

View File

@ -0,0 +1,127 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ISTREAMWRAPPER_H_
#define RAPIDJSON_ISTREAMWRAPPER_H_
#include "stream.h"
#include <iosfwd>
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
#elif defined(_MSC_VER)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept.
/*!
The classes can be wrapped including but not limited to:
- \c std::istringstream
- \c std::stringstream
- \c std::wistringstream
- \c std::wstringstream
- \c std::ifstream
- \c std::fstream
- \c std::wifstream
- \c std::wfstream
\tparam StreamType Class derived from \c std::basic_istream.
*/
template <typename StreamType>
class BasicIStreamWrapper {
public:
typedef typename StreamType::char_type Ch;
//! Constructor.
/*!
\param stream stream opened for read.
*/
BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
Read();
}
//! Constructor.
/*!
\param stream stream opened for read.
\param buffer user-supplied buffer.
\param bufferSize size of buffer in bytes. Must >=4 bytes.
*/
BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
RAPIDJSON_ASSERT(bufferSize >= 4);
Read();
}
Ch Peek() const { return *current_; }
Ch Take() { Ch c = *current_; Read(); return c; }
size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
// Not implemented
void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
// For encoding detection only.
const Ch* Peek4() const {
return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0;
}
private:
BasicIStreamWrapper();
BasicIStreamWrapper(const BasicIStreamWrapper&);
BasicIStreamWrapper& operator=(const BasicIStreamWrapper&);
void Read() {
if (current_ < bufferLast_)
++current_;
else if (!eof_) {
count_ += readCount_;
readCount_ = bufferSize_;
bufferLast_ = buffer_ + readCount_ - 1;
current_ = buffer_;
if (!stream_.read(buffer_, static_cast<std::streamsize>(bufferSize_))) {
readCount_ = static_cast<size_t>(stream_.gcount());
*(bufferLast_ = buffer_ + readCount_) = '\0';
eof_ = true;
}
}
}
StreamType &stream_;
Ch peekBuffer_[4], *buffer_;
size_t bufferSize_;
Ch *bufferLast_;
Ch *current_;
size_t readCount_;
size_t count_; //!< Number of characters read
bool eof_;
};
typedef BasicIStreamWrapper<std::istream> IStreamWrapper;
typedef BasicIStreamWrapper<std::wistream> WIStreamWrapper;
#if defined(__clang__) || defined(_MSC_VER)
RAPIDJSON_DIAG_POP
#endif
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_ISTREAMWRAPPER_H_

View File

@ -0,0 +1,70 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_MEMORYBUFFER_H_
#define RAPIDJSON_MEMORYBUFFER_H_
#include "stream.h"
#include "internal/stack.h"
RAPIDJSON_NAMESPACE_BEGIN
//! Represents an in-memory output byte stream.
/*!
This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream.
It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file.
Differences between MemoryBuffer and StringBuffer:
1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer.
2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator.
\tparam Allocator type for allocating memory buffer.
\note implements Stream concept
*/
template <typename Allocator = CrtAllocator>
struct GenericMemoryBuffer {
typedef char Ch; // byte
GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
void Put(Ch c) { *stack_.template Push<Ch>() = c; }
void Flush() {}
void Clear() { stack_.Clear(); }
void ShrinkToFit() { stack_.ShrinkToFit(); }
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
void Pop(size_t count) { stack_.template Pop<Ch>(count); }
const Ch* GetBuffer() const {
return stack_.template Bottom<Ch>();
}
size_t GetSize() const { return stack_.GetSize(); }
static const size_t kDefaultCapacity = 256;
mutable internal::Stack<Allocator> stack_;
};
typedef GenericMemoryBuffer<> MemoryBuffer;
//! Implement specialized version of PutN() with memset() for better performance.
template<>
inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) {
std::memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c));
}
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_MEMORYBUFFER_H_

View File

@ -0,0 +1,71 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_MEMORYSTREAM_H_
#define RAPIDJSON_MEMORYSTREAM_H_
#include "stream.h"
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(unreachable-code)
RAPIDJSON_DIAG_OFF(missing-noreturn)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Represents an in-memory input byte stream.
/*!
This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream.
It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file.
Differences between MemoryStream and StringStream:
1. StringStream has encoding but MemoryStream is a byte stream.
2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source.
3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4().
\note implements Stream concept
*/
struct MemoryStream {
typedef char Ch; // byte
MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {}
Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; }
Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; }
size_t Tell() const { return static_cast<size_t>(src_ - begin_); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
// For encoding detection only.
const Ch* Peek4() const {
return Tell() + 4 <= size_ ? src_ : 0;
}
const Ch* src_; //!< Current read position.
const Ch* begin_; //!< Original head of the string.
const Ch* end_; //!< End of stream.
size_t size_; //!< Size of the stream.
};
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_MEMORYBUFFER_H_

View File

@ -0,0 +1,316 @@
// ISO C9x compliant inttypes.h for Microsoft Visual Studio
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
//
// Copyright (c) 2006-2013 Alexander Chemeris
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the product nor the names of its contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////////
// The above software in this distribution may have been modified by
// THL A29 Limited ("Tencent Modifications").
// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
#ifndef _MSC_VER // [
#error "Use this header only with Microsoft Visual C++ compilers!"
#endif // _MSC_VER ]
#ifndef _MSC_INTTYPES_H_ // [
#define _MSC_INTTYPES_H_
#if _MSC_VER > 1000
#pragma once
#endif
#include "stdint.h"
// miloyip: VC supports inttypes.h since VC2013
#if _MSC_VER >= 1800
#include <inttypes.h>
#else
// 7.8 Format conversion of integer types
typedef struct {
intmax_t quot;
intmax_t rem;
} imaxdiv_t;
// 7.8.1 Macros for format specifiers
#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198
// The fprintf macros for signed integers are:
#define PRId8 "d"
#define PRIi8 "i"
#define PRIdLEAST8 "d"
#define PRIiLEAST8 "i"
#define PRIdFAST8 "d"
#define PRIiFAST8 "i"
#define PRId16 "hd"
#define PRIi16 "hi"
#define PRIdLEAST16 "hd"
#define PRIiLEAST16 "hi"
#define PRIdFAST16 "hd"
#define PRIiFAST16 "hi"
#define PRId32 "I32d"
#define PRIi32 "I32i"
#define PRIdLEAST32 "I32d"
#define PRIiLEAST32 "I32i"
#define PRIdFAST32 "I32d"
#define PRIiFAST32 "I32i"
#define PRId64 "I64d"
#define PRIi64 "I64i"
#define PRIdLEAST64 "I64d"
#define PRIiLEAST64 "I64i"
#define PRIdFAST64 "I64d"
#define PRIiFAST64 "I64i"
#define PRIdMAX "I64d"
#define PRIiMAX "I64i"
#define PRIdPTR "Id"
#define PRIiPTR "Ii"
// The fprintf macros for unsigned integers are:
#define PRIo8 "o"
#define PRIu8 "u"
#define PRIx8 "x"
#define PRIX8 "X"
#define PRIoLEAST8 "o"
#define PRIuLEAST8 "u"
#define PRIxLEAST8 "x"
#define PRIXLEAST8 "X"
#define PRIoFAST8 "o"
#define PRIuFAST8 "u"
#define PRIxFAST8 "x"
#define PRIXFAST8 "X"
#define PRIo16 "ho"
#define PRIu16 "hu"
#define PRIx16 "hx"
#define PRIX16 "hX"
#define PRIoLEAST16 "ho"
#define PRIuLEAST16 "hu"
#define PRIxLEAST16 "hx"
#define PRIXLEAST16 "hX"
#define PRIoFAST16 "ho"
#define PRIuFAST16 "hu"
#define PRIxFAST16 "hx"
#define PRIXFAST16 "hX"
#define PRIo32 "I32o"
#define PRIu32 "I32u"
#define PRIx32 "I32x"
#define PRIX32 "I32X"
#define PRIoLEAST32 "I32o"
#define PRIuLEAST32 "I32u"
#define PRIxLEAST32 "I32x"
#define PRIXLEAST32 "I32X"
#define PRIoFAST32 "I32o"
#define PRIuFAST32 "I32u"
#define PRIxFAST32 "I32x"
#define PRIXFAST32 "I32X"
#define PRIo64 "I64o"
#define PRIu64 "I64u"
#define PRIx64 "I64x"
#define PRIX64 "I64X"
#define PRIoLEAST64 "I64o"
#define PRIuLEAST64 "I64u"
#define PRIxLEAST64 "I64x"
#define PRIXLEAST64 "I64X"
#define PRIoFAST64 "I64o"
#define PRIuFAST64 "I64u"
#define PRIxFAST64 "I64x"
#define PRIXFAST64 "I64X"
#define PRIoMAX "I64o"
#define PRIuMAX "I64u"
#define PRIxMAX "I64x"
#define PRIXMAX "I64X"
#define PRIoPTR "Io"
#define PRIuPTR "Iu"
#define PRIxPTR "Ix"
#define PRIXPTR "IX"
// The fscanf macros for signed integers are:
#define SCNd8 "d"
#define SCNi8 "i"
#define SCNdLEAST8 "d"
#define SCNiLEAST8 "i"
#define SCNdFAST8 "d"
#define SCNiFAST8 "i"
#define SCNd16 "hd"
#define SCNi16 "hi"
#define SCNdLEAST16 "hd"
#define SCNiLEAST16 "hi"
#define SCNdFAST16 "hd"
#define SCNiFAST16 "hi"
#define SCNd32 "ld"
#define SCNi32 "li"
#define SCNdLEAST32 "ld"
#define SCNiLEAST32 "li"
#define SCNdFAST32 "ld"
#define SCNiFAST32 "li"
#define SCNd64 "I64d"
#define SCNi64 "I64i"
#define SCNdLEAST64 "I64d"
#define SCNiLEAST64 "I64i"
#define SCNdFAST64 "I64d"
#define SCNiFAST64 "I64i"
#define SCNdMAX "I64d"
#define SCNiMAX "I64i"
#ifdef _WIN64 // [
# define SCNdPTR "I64d"
# define SCNiPTR "I64i"
#else // _WIN64 ][
# define SCNdPTR "ld"
# define SCNiPTR "li"
#endif // _WIN64 ]
// The fscanf macros for unsigned integers are:
#define SCNo8 "o"
#define SCNu8 "u"
#define SCNx8 "x"
#define SCNX8 "X"
#define SCNoLEAST8 "o"
#define SCNuLEAST8 "u"
#define SCNxLEAST8 "x"
#define SCNXLEAST8 "X"
#define SCNoFAST8 "o"
#define SCNuFAST8 "u"
#define SCNxFAST8 "x"
#define SCNXFAST8 "X"
#define SCNo16 "ho"
#define SCNu16 "hu"
#define SCNx16 "hx"
#define SCNX16 "hX"
#define SCNoLEAST16 "ho"
#define SCNuLEAST16 "hu"
#define SCNxLEAST16 "hx"
#define SCNXLEAST16 "hX"
#define SCNoFAST16 "ho"
#define SCNuFAST16 "hu"
#define SCNxFAST16 "hx"
#define SCNXFAST16 "hX"
#define SCNo32 "lo"
#define SCNu32 "lu"
#define SCNx32 "lx"
#define SCNX32 "lX"
#define SCNoLEAST32 "lo"
#define SCNuLEAST32 "lu"
#define SCNxLEAST32 "lx"
#define SCNXLEAST32 "lX"
#define SCNoFAST32 "lo"
#define SCNuFAST32 "lu"
#define SCNxFAST32 "lx"
#define SCNXFAST32 "lX"
#define SCNo64 "I64o"
#define SCNu64 "I64u"
#define SCNx64 "I64x"
#define SCNX64 "I64X"
#define SCNoLEAST64 "I64o"
#define SCNuLEAST64 "I64u"
#define SCNxLEAST64 "I64x"
#define SCNXLEAST64 "I64X"
#define SCNoFAST64 "I64o"
#define SCNuFAST64 "I64u"
#define SCNxFAST64 "I64x"
#define SCNXFAST64 "I64X"
#define SCNoMAX "I64o"
#define SCNuMAX "I64u"
#define SCNxMAX "I64x"
#define SCNXMAX "I64X"
#ifdef _WIN64 // [
# define SCNoPTR "I64o"
# define SCNuPTR "I64u"
# define SCNxPTR "I64x"
# define SCNXPTR "I64X"
#else // _WIN64 ][
# define SCNoPTR "lo"
# define SCNuPTR "lu"
# define SCNxPTR "lx"
# define SCNXPTR "lX"
#endif // _WIN64 ]
#endif // __STDC_FORMAT_MACROS ]
// 7.8.2 Functions for greatest-width integer types
// 7.8.2.1 The imaxabs function
#define imaxabs _abs64
// 7.8.2.2 The imaxdiv function
// This is modified version of div() function from Microsoft's div.c found
// in %MSVC.NET%\crt\src\div.c
#ifdef STATIC_IMAXDIV // [
static
#else // STATIC_IMAXDIV ][
_inline
#endif // STATIC_IMAXDIV ]
imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
{
imaxdiv_t result;
result.quot = numer / denom;
result.rem = numer % denom;
if (numer < 0 && result.rem > 0) {
// did division wrong; must fix up
++result.quot;
result.rem -= denom;
}
return result;
}
// 7.8.2.3 The strtoimax and strtoumax functions
#define strtoimax _strtoi64
#define strtoumax _strtoui64
// 7.8.2.4 The wcstoimax and wcstoumax functions
#define wcstoimax _wcstoi64
#define wcstoumax _wcstoui64
#endif // _MSC_VER >= 1800
#endif // _MSC_INTTYPES_H_ ]

View File

@ -0,0 +1,300 @@
// ISO C9x compliant stdint.h for Microsoft Visual Studio
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
//
// Copyright (c) 2006-2013 Alexander Chemeris
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the product nor the names of its contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////////
// The above software in this distribution may have been modified by
// THL A29 Limited ("Tencent Modifications").
// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
#ifndef _MSC_VER // [
#error "Use this header only with Microsoft Visual C++ compilers!"
#endif // _MSC_VER ]
#ifndef _MSC_STDINT_H_ // [
#define _MSC_STDINT_H_
#if _MSC_VER > 1000
#pragma once
#endif
// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010.
#if _MSC_VER >= 1600 // [
#include <stdint.h>
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
#undef INT8_C
#undef INT16_C
#undef INT32_C
#undef INT64_C
#undef UINT8_C
#undef UINT16_C
#undef UINT32_C
#undef UINT64_C
// 7.18.4.1 Macros for minimum-width integer constants
#define INT8_C(val) val##i8
#define INT16_C(val) val##i16
#define INT32_C(val) val##i32
#define INT64_C(val) val##i64
#define UINT8_C(val) val##ui8
#define UINT16_C(val) val##ui16
#define UINT32_C(val) val##ui32
#define UINT64_C(val) val##ui64
// 7.18.4.2 Macros for greatest-width integer constants
// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
// Check out Issue 9 for the details.
#ifndef INTMAX_C // [
# define INTMAX_C INT64_C
#endif // INTMAX_C ]
#ifndef UINTMAX_C // [
# define UINTMAX_C UINT64_C
#endif // UINTMAX_C ]
#endif // __STDC_CONSTANT_MACROS ]
#else // ] _MSC_VER >= 1700 [
#include <limits.h>
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
// compiling for ARM we have to wrap <wchar.h> include with 'extern "C++" {}'
// or compiler would give many errors like this:
// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
#if defined(__cplusplus) && !defined(_M_ARM)
extern "C" {
#endif
# include <wchar.h>
#if defined(__cplusplus) && !defined(_M_ARM)
}
#endif
// Define _W64 macros to mark types changing their size, like intptr_t.
#ifndef _W64
# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
# define _W64 __w64
# else
# define _W64
# endif
#endif
// 7.18.1 Integer types
// 7.18.1.1 Exact-width integer types
// Visual Studio 6 and Embedded Visual C++ 4 doesn't
// realize that, e.g. char has the same size as __int8
// so we give up on __intX for them.
#if (_MSC_VER < 1300)
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
#else
typedef signed __int8 int8_t;
typedef signed __int16 int16_t;
typedef signed __int32 int32_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
#endif
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
// 7.18.1.2 Minimum-width integer types
typedef int8_t int_least8_t;
typedef int16_t int_least16_t;
typedef int32_t int_least32_t;
typedef int64_t int_least64_t;
typedef uint8_t uint_least8_t;
typedef uint16_t uint_least16_t;
typedef uint32_t uint_least32_t;
typedef uint64_t uint_least64_t;
// 7.18.1.3 Fastest minimum-width integer types
typedef int8_t int_fast8_t;
typedef int16_t int_fast16_t;
typedef int32_t int_fast32_t;
typedef int64_t int_fast64_t;
typedef uint8_t uint_fast8_t;
typedef uint16_t uint_fast16_t;
typedef uint32_t uint_fast32_t;
typedef uint64_t uint_fast64_t;
// 7.18.1.4 Integer types capable of holding object pointers
#ifdef _WIN64 // [
typedef signed __int64 intptr_t;
typedef unsigned __int64 uintptr_t;
#else // _WIN64 ][
typedef _W64 signed int intptr_t;
typedef _W64 unsigned int uintptr_t;
#endif // _WIN64 ]
// 7.18.1.5 Greatest-width integer types
typedef int64_t intmax_t;
typedef uint64_t uintmax_t;
// 7.18.2 Limits of specified-width integer types
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
// 7.18.2.1 Limits of exact-width integer types
#define INT8_MIN ((int8_t)_I8_MIN)
#define INT8_MAX _I8_MAX
#define INT16_MIN ((int16_t)_I16_MIN)
#define INT16_MAX _I16_MAX
#define INT32_MIN ((int32_t)_I32_MIN)
#define INT32_MAX _I32_MAX
#define INT64_MIN ((int64_t)_I64_MIN)
#define INT64_MAX _I64_MAX
#define UINT8_MAX _UI8_MAX
#define UINT16_MAX _UI16_MAX
#define UINT32_MAX _UI32_MAX
#define UINT64_MAX _UI64_MAX
// 7.18.2.2 Limits of minimum-width integer types
#define INT_LEAST8_MIN INT8_MIN
#define INT_LEAST8_MAX INT8_MAX
#define INT_LEAST16_MIN INT16_MIN
#define INT_LEAST16_MAX INT16_MAX
#define INT_LEAST32_MIN INT32_MIN
#define INT_LEAST32_MAX INT32_MAX
#define INT_LEAST64_MIN INT64_MIN
#define INT_LEAST64_MAX INT64_MAX
#define UINT_LEAST8_MAX UINT8_MAX
#define UINT_LEAST16_MAX UINT16_MAX
#define UINT_LEAST32_MAX UINT32_MAX
#define UINT_LEAST64_MAX UINT64_MAX
// 7.18.2.3 Limits of fastest minimum-width integer types
#define INT_FAST8_MIN INT8_MIN
#define INT_FAST8_MAX INT8_MAX
#define INT_FAST16_MIN INT16_MIN
#define INT_FAST16_MAX INT16_MAX
#define INT_FAST32_MIN INT32_MIN
#define INT_FAST32_MAX INT32_MAX
#define INT_FAST64_MIN INT64_MIN
#define INT_FAST64_MAX INT64_MAX
#define UINT_FAST8_MAX UINT8_MAX
#define UINT_FAST16_MAX UINT16_MAX
#define UINT_FAST32_MAX UINT32_MAX
#define UINT_FAST64_MAX UINT64_MAX
// 7.18.2.4 Limits of integer types capable of holding object pointers
#ifdef _WIN64 // [
# define INTPTR_MIN INT64_MIN
# define INTPTR_MAX INT64_MAX
# define UINTPTR_MAX UINT64_MAX
#else // _WIN64 ][
# define INTPTR_MIN INT32_MIN
# define INTPTR_MAX INT32_MAX
# define UINTPTR_MAX UINT32_MAX
#endif // _WIN64 ]
// 7.18.2.5 Limits of greatest-width integer types
#define INTMAX_MIN INT64_MIN
#define INTMAX_MAX INT64_MAX
#define UINTMAX_MAX UINT64_MAX
// 7.18.3 Limits of other integer types
#ifdef _WIN64 // [
# define PTRDIFF_MIN _I64_MIN
# define PTRDIFF_MAX _I64_MAX
#else // _WIN64 ][
# define PTRDIFF_MIN _I32_MIN
# define PTRDIFF_MAX _I32_MAX
#endif // _WIN64 ]
#define SIG_ATOMIC_MIN INT_MIN
#define SIG_ATOMIC_MAX INT_MAX
#ifndef SIZE_MAX // [
# ifdef _WIN64 // [
# define SIZE_MAX _UI64_MAX
# else // _WIN64 ][
# define SIZE_MAX _UI32_MAX
# endif // _WIN64 ]
#endif // SIZE_MAX ]
// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
#ifndef WCHAR_MIN // [
# define WCHAR_MIN 0
#endif // WCHAR_MIN ]
#ifndef WCHAR_MAX // [
# define WCHAR_MAX _UI16_MAX
#endif // WCHAR_MAX ]
#define WINT_MIN 0
#define WINT_MAX _UI16_MAX
#endif // __STDC_LIMIT_MACROS ]
// 7.18.4 Limits of other integer types
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
// 7.18.4.1 Macros for minimum-width integer constants
#define INT8_C(val) val##i8
#define INT16_C(val) val##i16
#define INT32_C(val) val##i32
#define INT64_C(val) val##i64
#define UINT8_C(val) val##ui8
#define UINT16_C(val) val##ui16
#define UINT32_C(val) val##ui32
#define UINT64_C(val) val##ui64
// 7.18.4.2 Macros for greatest-width integer constants
// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
// Check out Issue 9 for the details.
#ifndef INTMAX_C // [
# define INTMAX_C INT64_C
#endif // INTMAX_C ]
#ifndef UINTMAX_C // [
# define UINTMAX_C UINT64_C
#endif // UINTMAX_C ]
#endif // __STDC_CONSTANT_MACROS ]
#endif // _MSC_VER >= 1600 ]
#endif // _MSC_STDINT_H_ ]

View File

@ -0,0 +1,81 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_OSTREAMWRAPPER_H_
#define RAPIDJSON_OSTREAMWRAPPER_H_
#include "stream.h"
#include <iosfwd>
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept.
/*!
The classes can be wrapped including but not limited to:
- \c std::ostringstream
- \c std::stringstream
- \c std::wpstringstream
- \c std::wstringstream
- \c std::ifstream
- \c std::fstream
- \c std::wofstream
- \c std::wfstream
\tparam StreamType Class derived from \c std::basic_ostream.
*/
template <typename StreamType>
class BasicOStreamWrapper {
public:
typedef typename StreamType::char_type Ch;
BasicOStreamWrapper(StreamType& stream) : stream_(stream) {}
void Put(Ch c) {
stream_.put(c);
}
void Flush() {
stream_.flush();
}
// Not implemented
char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
char Take() { RAPIDJSON_ASSERT(false); return 0; }
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
private:
BasicOStreamWrapper(const BasicOStreamWrapper&);
BasicOStreamWrapper& operator=(const BasicOStreamWrapper&);
StreamType& stream_;
};
typedef BasicOStreamWrapper<std::ostream> OStreamWrapper;
typedef BasicOStreamWrapper<std::wostream> WOStreamWrapper;
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_OSTREAMWRAPPER_H_

1414
include/rapidjson/pointer.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,277 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_PRETTYWRITER_H_
#define RAPIDJSON_PRETTYWRITER_H_
#include "writer.h"
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#if defined(__clang__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(c++98-compat)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Combination of PrettyWriter format flags.
/*! \see PrettyWriter::SetFormatOptions
*/
enum PrettyFormatOptions {
kFormatDefault = 0, //!< Default pretty formatting.
kFormatSingleLineArray = 1 //!< Format arrays on a single line.
};
//! Writer with indentation and spacing.
/*!
\tparam OutputStream Type of output os.
\tparam SourceEncoding Encoding of source string.
\tparam TargetEncoding Encoding of output stream.
\tparam StackAllocator Type of allocator for allocating memory of stack.
*/
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> {
public:
typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> Base;
typedef typename Base::Ch Ch;
//! Constructor
/*! \param os Output stream.
\param allocator User supplied allocator. If it is null, it will create a private one.
\param levelDepth Initial capacity of stack.
*/
explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {}
explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
PrettyWriter(PrettyWriter&& rhs) :
Base(std::forward<PrettyWriter>(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {}
#endif
//! Set custom indentation.
/*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
\param indentCharCount Number of indent characters for each indentation level.
\note The default indentation is 4 spaces.
*/
PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) {
RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r');
indentChar_ = indentChar;
indentCharCount_ = indentCharCount;
return *this;
}
//! Set pretty writer formatting options.
/*! \param options Formatting options.
*/
PrettyWriter& SetFormatOptions(PrettyFormatOptions options) {
formatOptions_ = options;
return *this;
}
/*! @name Implementation of Handler
\see Handler
*/
//@{
bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); }
bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); }
bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); }
bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); }
bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); }
bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); }
bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); }
bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
RAPIDJSON_ASSERT(str != 0);
(void)copy;
PrettyPrefix(kNumberType);
return Base::EndValue(Base::WriteString(str, length));
}
bool String(const Ch* str, SizeType length, bool copy = false) {
RAPIDJSON_ASSERT(str != 0);
(void)copy;
PrettyPrefix(kStringType);
return Base::EndValue(Base::WriteString(str, length));
}
#if RAPIDJSON_HAS_STDSTRING
bool String(const std::basic_string<Ch>& str) {
return String(str.data(), SizeType(str.size()));
}
#endif
bool StartObject() {
PrettyPrefix(kObjectType);
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
return Base::WriteStartObject();
}
bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
#if RAPIDJSON_HAS_STDSTRING
bool Key(const std::basic_string<Ch>& str) {
return Key(str.data(), SizeType(str.size()));
}
#endif
bool EndObject(SizeType memberCount = 0) {
(void)memberCount;
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object
RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray); // currently inside an Array, not Object
RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top<typename Base::Level>()->valueCount % 2); // Object has a Key without a Value
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
if (!empty) {
Base::os_->Put('\n');
WriteIndent();
}
bool ret = Base::EndValue(Base::WriteEndObject());
(void)ret;
RAPIDJSON_ASSERT(ret == true);
if (Base::level_stack_.Empty()) // end of json text
Base::Flush();
return true;
}
bool StartArray() {
PrettyPrefix(kArrayType);
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
return Base::WriteStartArray();
}
bool EndArray(SizeType memberCount = 0) {
(void)memberCount;
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
if (!empty && !(formatOptions_ & kFormatSingleLineArray)) {
Base::os_->Put('\n');
WriteIndent();
}
bool ret = Base::EndValue(Base::WriteEndArray());
(void)ret;
RAPIDJSON_ASSERT(ret == true);
if (Base::level_stack_.Empty()) // end of json text
Base::Flush();
return true;
}
//@}
/*! @name Convenience extensions */
//@{
//! Simpler but slower overload.
bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
//@}
//! Write a raw JSON value.
/*!
For user to write a stringified JSON as a value.
\param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
\param length Length of the json.
\param type Type of the root of json.
\note When using PrettyWriter::RawValue(), the result json may not be indented correctly.
*/
bool RawValue(const Ch* json, size_t length, Type type) {
RAPIDJSON_ASSERT(json != 0);
PrettyPrefix(type);
return Base::EndValue(Base::WriteRawValue(json, length));
}
protected:
void PrettyPrefix(Type type) {
(void)type;
if (Base::level_stack_.GetSize() != 0) { // this value is not at root
typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>();
if (level->inArray) {
if (level->valueCount > 0) {
Base::os_->Put(','); // add comma if it is not the first element in array
if (formatOptions_ & kFormatSingleLineArray)
Base::os_->Put(' ');
}
if (!(formatOptions_ & kFormatSingleLineArray)) {
Base::os_->Put('\n');
WriteIndent();
}
}
else { // in object
if (level->valueCount > 0) {
if (level->valueCount % 2 == 0) {
Base::os_->Put(',');
Base::os_->Put('\n');
}
else {
Base::os_->Put(':');
Base::os_->Put(' ');
}
}
else
Base::os_->Put('\n');
if (level->valueCount % 2 == 0)
WriteIndent();
}
if (!level->inArray && level->valueCount % 2 == 0)
RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
level->valueCount++;
}
else {
RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root.
Base::hasRoot_ = true;
}
}
void WriteIndent() {
size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_;
PutN(*Base::os_, static_cast<typename OutputStream::Ch>(indentChar_), count);
}
Ch indentChar_;
unsigned indentCharCount_;
PrettyFormatOptions formatOptions_;
private:
// Prohibit copy constructor & assignment operator.
PrettyWriter(const PrettyWriter&);
PrettyWriter& operator=(const PrettyWriter&);
};
RAPIDJSON_NAMESPACE_END
#if defined(__clang__)
RAPIDJSON_DIAG_POP
#endif
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_RAPIDJSON_H_

View File

@ -0,0 +1,656 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_RAPIDJSON_H_
#define RAPIDJSON_RAPIDJSON_H_
/*!\file rapidjson.h
\brief common definitions and configuration
\see RAPIDJSON_CONFIG
*/
/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration
\brief Configuration macros for library features
Some RapidJSON features are configurable to adapt the library to a wide
variety of platforms, environments and usage scenarios. Most of the
features can be configured in terms of overridden or predefined
preprocessor macros at compile-time.
Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs.
\note These macros should be given on the compiler command-line
(where applicable) to avoid inconsistent values when compiling
different translation units of a single application.
*/
#include <cstdlib> // malloc(), realloc(), free(), size_t
#include <cstring> // memset(), memcpy(), memmove(), memcmp()
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_VERSION_STRING
//
// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt.
//
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
// token stringification
#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x)
#define RAPIDJSON_DO_STRINGIFY(x) #x
// token concatenation
#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y)
#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y)
#define RAPIDJSON_DO_JOIN2(X, Y) X##Y
//!@endcond
/*! \def RAPIDJSON_MAJOR_VERSION
\ingroup RAPIDJSON_CONFIG
\brief Major version of RapidJSON in integer.
*/
/*! \def RAPIDJSON_MINOR_VERSION
\ingroup RAPIDJSON_CONFIG
\brief Minor version of RapidJSON in integer.
*/
/*! \def RAPIDJSON_PATCH_VERSION
\ingroup RAPIDJSON_CONFIG
\brief Patch version of RapidJSON in integer.
*/
/*! \def RAPIDJSON_VERSION_STRING
\ingroup RAPIDJSON_CONFIG
\brief Version of RapidJSON in "<major>.<minor>.<patch>" string format.
*/
#define RAPIDJSON_MAJOR_VERSION 1
#define RAPIDJSON_MINOR_VERSION 1
#define RAPIDJSON_PATCH_VERSION 0
#define RAPIDJSON_VERSION_STRING \
RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION)
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_NAMESPACE_(BEGIN|END)
/*! \def RAPIDJSON_NAMESPACE
\ingroup RAPIDJSON_CONFIG
\brief provide custom rapidjson namespace
In order to avoid symbol clashes and/or "One Definition Rule" errors
between multiple inclusions of (different versions of) RapidJSON in
a single binary, users can customize the name of the main RapidJSON
namespace.
In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE
to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple
levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref
RAPIDJSON_NAMESPACE_END need to be defined as well:
\code
// in some .cpp file
#define RAPIDJSON_NAMESPACE my::rapidjson
#define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson {
#define RAPIDJSON_NAMESPACE_END } }
#include "rapidjson/..."
\endcode
\see rapidjson
*/
/*! \def RAPIDJSON_NAMESPACE_BEGIN
\ingroup RAPIDJSON_CONFIG
\brief provide custom rapidjson namespace (opening expression)
\see RAPIDJSON_NAMESPACE
*/
/*! \def RAPIDJSON_NAMESPACE_END
\ingroup RAPIDJSON_CONFIG
\brief provide custom rapidjson namespace (closing expression)
\see RAPIDJSON_NAMESPACE
*/
#ifndef RAPIDJSON_NAMESPACE
#define RAPIDJSON_NAMESPACE rapidjson
#endif
#ifndef RAPIDJSON_NAMESPACE_BEGIN
#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE {
#endif
#ifndef RAPIDJSON_NAMESPACE_END
#define RAPIDJSON_NAMESPACE_END }
#endif
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_HAS_STDSTRING
#ifndef RAPIDJSON_HAS_STDSTRING
#ifdef RAPIDJSON_DOXYGEN_RUNNING
#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation
#else
#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default
#endif
/*! \def RAPIDJSON_HAS_STDSTRING
\ingroup RAPIDJSON_CONFIG
\brief Enable RapidJSON support for \c std::string
By defining this preprocessor symbol to \c 1, several convenience functions for using
\ref rapidjson::GenericValue with \c std::string are enabled, especially
for construction and comparison.
\hideinitializer
*/
#endif // !defined(RAPIDJSON_HAS_STDSTRING)
#if RAPIDJSON_HAS_STDSTRING
#include <string>
#endif // RAPIDJSON_HAS_STDSTRING
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_NO_INT64DEFINE
/*! \def RAPIDJSON_NO_INT64DEFINE
\ingroup RAPIDJSON_CONFIG
\brief Use external 64-bit integer types.
RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types
to be available at global scope.
If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to
prevent RapidJSON from defining its own types.
*/
#ifndef RAPIDJSON_NO_INT64DEFINE
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013
#include "msinttypes/stdint.h"
#include "msinttypes/inttypes.h"
#else
// Other compilers should have this.
#include <stdint.h>
#include <inttypes.h>
#endif
//!@endcond
#ifdef RAPIDJSON_DOXYGEN_RUNNING
#define RAPIDJSON_NO_INT64DEFINE
#endif
#endif // RAPIDJSON_NO_INT64TYPEDEF
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_FORCEINLINE
#ifndef RAPIDJSON_FORCEINLINE
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
#if defined(_MSC_VER) && defined(NDEBUG)
#define RAPIDJSON_FORCEINLINE __forceinline
#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG)
#define RAPIDJSON_FORCEINLINE __attribute__((always_inline))
#else
#define RAPIDJSON_FORCEINLINE
#endif
//!@endcond
#endif // RAPIDJSON_FORCEINLINE
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_ENDIAN
#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine
#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine
//! Endianness of the machine.
/*!
\def RAPIDJSON_ENDIAN
\ingroup RAPIDJSON_CONFIG
GCC 4.6 provided macro for detecting endianness of the target machine. But other
compilers may not have this. User can define RAPIDJSON_ENDIAN to either
\ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN.
Default detection implemented with reference to
\li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html
\li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp
*/
#ifndef RAPIDJSON_ENDIAN
// Detect with GCC 4.6's macro
# ifdef __BYTE_ORDER__
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
# else
# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN.
# endif // __BYTE_ORDER__
// Detect with GLIBC's endian.h
# elif defined(__GLIBC__)
# include <endian.h>
# if (__BYTE_ORDER == __LITTLE_ENDIAN)
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
# elif (__BYTE_ORDER == __BIG_ENDIAN)
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
# else
# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN.
# endif // __GLIBC__
// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro
# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
// Detect with architecture macros
# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
# elif defined(RAPIDJSON_DOXYGEN_RUNNING)
# define RAPIDJSON_ENDIAN
# else
# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN.
# endif
#endif // RAPIDJSON_ENDIAN
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_64BIT
//! Whether using 64-bit architecture
#ifndef RAPIDJSON_64BIT
#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__)
#define RAPIDJSON_64BIT 1
#else
#define RAPIDJSON_64BIT 0
#endif
#endif // RAPIDJSON_64BIT
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_ALIGN
//! Data alignment of the machine.
/*! \ingroup RAPIDJSON_CONFIG
\param x pointer to align
Some machines require strict data alignment. The default is 8 bytes.
User can customize by defining the RAPIDJSON_ALIGN function macro.
*/
#ifndef RAPIDJSON_ALIGN
#define RAPIDJSON_ALIGN(x) (((x) + static_cast<size_t>(7u)) & ~static_cast<size_t>(7u))
#endif
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_UINT64_C2
//! Construct a 64-bit literal by a pair of 32-bit integer.
/*!
64-bit literal with or without ULL suffix is prone to compiler warnings.
UINT64_C() is C macro which cause compilation problems.
Use this macro to define 64-bit constants by a pair of 32-bit integer.
*/
#ifndef RAPIDJSON_UINT64_C2
#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32))
#endif
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_48BITPOINTER_OPTIMIZATION
//! Use only lower 48-bit address for some pointers.
/*!
\ingroup RAPIDJSON_CONFIG
This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address.
The higher 16-bit can be used for storing other data.
\c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture.
*/
#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1
#else
#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0
#endif
#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION
#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1
#if RAPIDJSON_64BIT != 1
#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1
#endif
#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast<type *>((reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast<uintptr_t>(reinterpret_cast<const void*>(x))))
#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast<type *>(reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF))))
#else
#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x))
#define RAPIDJSON_GETPOINTER(type, p) (p)
#endif
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD
/*! \def RAPIDJSON_SIMD
\ingroup RAPIDJSON_CONFIG
\brief Enable SSE2/SSE4.2/Neon optimization.
RapidJSON supports optimized implementations for some parsing operations
based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel
or ARM compatible processors.
To enable these optimizations, three different symbols can be defined;
\code
// Enable SSE2 optimization.
#define RAPIDJSON_SSE2
// Enable SSE4.2 optimization.
#define RAPIDJSON_SSE42
\endcode
// Enable ARM Neon optimization.
#define RAPIDJSON_NEON
\endcode
\c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined.
If any of these symbols is defined, RapidJSON defines the macro
\c RAPIDJSON_SIMD to indicate the availability of the optimized code.
*/
#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \
|| defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING)
#define RAPIDJSON_SIMD
#endif
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_NO_SIZETYPEDEFINE
#ifndef RAPIDJSON_NO_SIZETYPEDEFINE
/*! \def RAPIDJSON_NO_SIZETYPEDEFINE
\ingroup RAPIDJSON_CONFIG
\brief User-provided \c SizeType definition.
In order to avoid using 32-bit size types for indexing strings and arrays,
define this preprocessor symbol and provide the type rapidjson::SizeType
before including RapidJSON:
\code
#define RAPIDJSON_NO_SIZETYPEDEFINE
namespace rapidjson { typedef ::std::size_t SizeType; }
#include "rapidjson/..."
\endcode
\see rapidjson::SizeType
*/
#ifdef RAPIDJSON_DOXYGEN_RUNNING
#define RAPIDJSON_NO_SIZETYPEDEFINE
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Size type (for string lengths, array sizes, etc.)
/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms,
instead of using \c size_t. Users may override the SizeType by defining
\ref RAPIDJSON_NO_SIZETYPEDEFINE.
*/
typedef unsigned SizeType;
RAPIDJSON_NAMESPACE_END
#endif
// always import std::size_t to rapidjson namespace
RAPIDJSON_NAMESPACE_BEGIN
using std::size_t;
RAPIDJSON_NAMESPACE_END
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_ASSERT
//! Assertion.
/*! \ingroup RAPIDJSON_CONFIG
By default, rapidjson uses C \c assert() for internal assertions.
User can override it by defining RAPIDJSON_ASSERT(x) macro.
\note Parsing errors are handled and can be customized by the
\ref RAPIDJSON_ERRORS APIs.
*/
#ifndef RAPIDJSON_ASSERT
#include <cassert>
#define RAPIDJSON_ASSERT(x) assert(x)
#endif // RAPIDJSON_ASSERT
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_STATIC_ASSERT
// Prefer C++11 static_assert, if available
#ifndef RAPIDJSON_STATIC_ASSERT
#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 )
#define RAPIDJSON_STATIC_ASSERT(x) \
static_assert(x, RAPIDJSON_STRINGIFY(x))
#endif // C++11
#endif // RAPIDJSON_STATIC_ASSERT
// Adopt C++03 implementation from boost
#ifndef RAPIDJSON_STATIC_ASSERT
#ifndef __clang__
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
#endif
RAPIDJSON_NAMESPACE_BEGIN
template <bool x> struct STATIC_ASSERTION_FAILURE;
template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
template <size_t x> struct StaticAssertTest {};
RAPIDJSON_NAMESPACE_END
#if defined(__GNUC__) || defined(__clang__)
#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused))
#else
#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
#endif
#ifndef __clang__
//!@endcond
#endif
/*! \def RAPIDJSON_STATIC_ASSERT
\brief (Internal) macro to check for conditions at compile-time
\param x compile-time condition
\hideinitializer
*/
#define RAPIDJSON_STATIC_ASSERT(x) \
typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \
sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE<bool(x) >)> \
RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
#endif // RAPIDJSON_STATIC_ASSERT
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY
//! Compiler branching hint for expression with high probability to be true.
/*!
\ingroup RAPIDJSON_CONFIG
\param x Boolean expression likely to be true.
*/
#ifndef RAPIDJSON_LIKELY
#if defined(__GNUC__) || defined(__clang__)
#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1)
#else
#define RAPIDJSON_LIKELY(x) (x)
#endif
#endif
//! Compiler branching hint for expression with low probability to be true.
/*!
\ingroup RAPIDJSON_CONFIG
\param x Boolean expression unlikely to be true.
*/
#ifndef RAPIDJSON_UNLIKELY
#if defined(__GNUC__) || defined(__clang__)
#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0)
#else
#define RAPIDJSON_UNLIKELY(x) (x)
#endif
#endif
///////////////////////////////////////////////////////////////////////////////
// Helpers
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
#define RAPIDJSON_MULTILINEMACRO_BEGIN do {
#define RAPIDJSON_MULTILINEMACRO_END \
} while((void)0, 0)
// adopted from Boost
#define RAPIDJSON_VERSION_CODE(x,y,z) \
(((x)*100000) + ((y)*100) + (z))
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF
#if defined(__GNUC__)
#define RAPIDJSON_GNUC \
RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__)
#endif
#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0))
#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x))
#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x)
#define RAPIDJSON_DIAG_OFF(x) \
RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x)))
// push/pop support in Clang and GCC>=4.6
#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0))
#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
#else // GCC >= 4.2, < 4.6
#define RAPIDJSON_DIAG_PUSH /* ignored */
#define RAPIDJSON_DIAG_POP /* ignored */
#endif
#elif defined(_MSC_VER)
// pragma (MSVC specific)
#define RAPIDJSON_PRAGMA(x) __pragma(x)
#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x))
#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x)
#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
#else
#define RAPIDJSON_DIAG_OFF(x) /* ignored */
#define RAPIDJSON_DIAG_PUSH /* ignored */
#define RAPIDJSON_DIAG_POP /* ignored */
#endif // RAPIDJSON_DIAG_*
///////////////////////////////////////////////////////////////////////////////
// C++11 features
#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS
#if defined(__clang__)
#if __has_feature(cxx_rvalue_references) && \
(defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306)
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
#else
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
#endif
#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
(defined(_MSC_VER) && _MSC_VER >= 1600) || \
(defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__))
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
#else
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
#endif
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT
#if defined(__clang__)
#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept)
#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
(defined(_MSC_VER) && _MSC_VER >= 1900) || \
(defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__))
#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1
#else
#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0
#endif
#endif
#if RAPIDJSON_HAS_CXX11_NOEXCEPT
#define RAPIDJSON_NOEXCEPT noexcept
#else
#define RAPIDJSON_NOEXCEPT /* noexcept */
#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT
// no automatic detection, yet
#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS
#if (defined(_MSC_VER) && _MSC_VER >= 1700)
#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1
#else
#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0
#endif
#endif
#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR
#if defined(__clang__)
#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for)
#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
(defined(_MSC_VER) && _MSC_VER >= 1700) || \
(defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__))
#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1
#else
#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0
#endif
#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR
//!@endcond
//! Assertion (in non-throwing contexts).
/*! \ingroup RAPIDJSON_CONFIG
Some functions provide a \c noexcept guarantee, if the compiler supports it.
In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to
throw an exception. This macro adds a separate customization point for
such cases.
Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is
supported, and to \ref RAPIDJSON_ASSERT otherwise.
*/
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_NOEXCEPT_ASSERT
#ifndef RAPIDJSON_NOEXCEPT_ASSERT
#ifdef RAPIDJSON_ASSERT_THROWS
#if RAPIDJSON_HAS_CXX11_NOEXCEPT
#define RAPIDJSON_NOEXCEPT_ASSERT(x)
#else
#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x)
#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT
#else
#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x)
#endif // RAPIDJSON_ASSERT_THROWS
#endif // RAPIDJSON_NOEXCEPT_ASSERT
///////////////////////////////////////////////////////////////////////////////
// new/delete
#ifndef RAPIDJSON_NEW
///! customization point for global \c new
#define RAPIDJSON_NEW(TypeName) new TypeName
#endif
#ifndef RAPIDJSON_DELETE
///! customization point for global \c delete
#define RAPIDJSON_DELETE(x) delete x
#endif
///////////////////////////////////////////////////////////////////////////////
// Type
/*! \namespace rapidjson
\brief main RapidJSON namespace
\see RAPIDJSON_NAMESPACE
*/
RAPIDJSON_NAMESPACE_BEGIN
//! Type of JSON value
enum Type {
kNullType = 0, //!< null
kFalseType = 1, //!< false
kTrueType = 2, //!< true
kObjectType = 3, //!< object
kArrayType = 4, //!< array
kStringType = 5, //!< string
kNumberType = 6 //!< number
};
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_RAPIDJSON_H_

2230
include/rapidjson/reader.h Normal file

File diff suppressed because it is too large Load Diff

2496
include/rapidjson/schema.h Normal file

File diff suppressed because it is too large Load Diff

223
include/rapidjson/stream.h Normal file
View File

@ -0,0 +1,223 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#include "rapidjson.h"
#ifndef RAPIDJSON_STREAM_H_
#define RAPIDJSON_STREAM_H_
#include "encodings.h"
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Stream
/*! \class rapidjson::Stream
\brief Concept for reading and writing characters.
For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd().
For write-only stream, only need to implement Put() and Flush().
\code
concept Stream {
typename Ch; //!< Character type of the stream.
//! Read the current character from stream without moving the read cursor.
Ch Peek() const;
//! Read the current character from stream and moving the read cursor to next character.
Ch Take();
//! Get the current read cursor.
//! \return Number of characters read from start.
size_t Tell();
//! Begin writing operation at the current read pointer.
//! \return The begin writer pointer.
Ch* PutBegin();
//! Write a character.
void Put(Ch c);
//! Flush the buffer.
void Flush();
//! End the writing operation.
//! \param begin The begin write pointer returned by PutBegin().
//! \return Number of characters written.
size_t PutEnd(Ch* begin);
}
\endcode
*/
//! Provides additional information for stream.
/*!
By using traits pattern, this type provides a default configuration for stream.
For custom stream, this type can be specialized for other configuration.
See TEST(Reader, CustomStringStream) in readertest.cpp for example.
*/
template<typename Stream>
struct StreamTraits {
//! Whether to make local copy of stream for optimization during parsing.
/*!
By default, for safety, streams do not use local copy optimization.
Stream that can be copied fast should specialize this, like StreamTraits<StringStream>.
*/
enum { copyOptimization = 0 };
};
//! Reserve n characters for writing to a stream.
template<typename Stream>
inline void PutReserve(Stream& stream, size_t count) {
(void)stream;
(void)count;
}
//! Write character to a stream, presuming buffer is reserved.
template<typename Stream>
inline void PutUnsafe(Stream& stream, typename Stream::Ch c) {
stream.Put(c);
}
//! Put N copies of a character to a stream.
template<typename Stream, typename Ch>
inline void PutN(Stream& stream, Ch c, size_t n) {
PutReserve(stream, n);
for (size_t i = 0; i < n; i++)
PutUnsafe(stream, c);
}
///////////////////////////////////////////////////////////////////////////////
// GenericStreamWrapper
//! A Stream Wrapper
/*! \tThis string stream is a wrapper for any stream by just forwarding any
\treceived message to the origin stream.
\note implements Stream concept
*/
#if defined(_MSC_VER) && _MSC_VER <= 1800
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4702) // unreachable code
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
#endif
template <typename InputStream, typename Encoding = UTF8<> >
class GenericStreamWrapper {
public:
typedef typename Encoding::Ch Ch;
GenericStreamWrapper(InputStream& is): is_(is) {}
Ch Peek() const { return is_.Peek(); }
Ch Take() { return is_.Take(); }
size_t Tell() { return is_.Tell(); }
Ch* PutBegin() { return is_.PutBegin(); }
void Put(Ch ch) { is_.Put(ch); }
void Flush() { is_.Flush(); }
size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); }
// wrapper for MemoryStream
const Ch* Peek4() const { return is_.Peek4(); }
// wrapper for AutoUTFInputStream
UTFType GetType() const { return is_.GetType(); }
bool HasBOM() const { return is_.HasBOM(); }
protected:
InputStream& is_;
};
#if defined(_MSC_VER) && _MSC_VER <= 1800
RAPIDJSON_DIAG_POP
#endif
///////////////////////////////////////////////////////////////////////////////
// StringStream
//! Read-only string stream.
/*! \note implements Stream concept
*/
template <typename Encoding>
struct GenericStringStream {
typedef typename Encoding::Ch Ch;
GenericStringStream(const Ch *src) : src_(src), head_(src) {}
Ch Peek() const { return *src_; }
Ch Take() { return *src_++; }
size_t Tell() const { return static_cast<size_t>(src_ - head_); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
const Ch* src_; //!< Current read position.
const Ch* head_; //!< Original head of the string.
};
template <typename Encoding>
struct StreamTraits<GenericStringStream<Encoding> > {
enum { copyOptimization = 1 };
};
//! String stream with UTF8 encoding.
typedef GenericStringStream<UTF8<> > StringStream;
///////////////////////////////////////////////////////////////////////////////
// InsituStringStream
//! A read-write string stream.
/*! This string stream is particularly designed for in-situ parsing.
\note implements Stream concept
*/
template <typename Encoding>
struct GenericInsituStringStream {
typedef typename Encoding::Ch Ch;
GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {}
// Read
Ch Peek() { return *src_; }
Ch Take() { return *src_++; }
size_t Tell() { return static_cast<size_t>(src_ - head_); }
// Write
void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; }
Ch* PutBegin() { return dst_ = src_; }
size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); }
void Flush() {}
Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; }
void Pop(size_t count) { dst_ -= count; }
Ch* src_;
Ch* dst_;
Ch* head_;
};
template <typename Encoding>
struct StreamTraits<GenericInsituStringStream<Encoding> > {
enum { copyOptimization = 1 };
};
//! Insitu string stream with UTF8 encoding.
typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_STREAM_H_

View File

@ -0,0 +1,121 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_STRINGBUFFER_H_
#define RAPIDJSON_STRINGBUFFER_H_
#include "stream.h"
#include "internal/stack.h"
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
#include <utility> // std::move
#endif
#include "internal/stack.h"
#if defined(__clang__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(c++98-compat)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Represents an in-memory output stream.
/*!
\tparam Encoding Encoding of the stream.
\tparam Allocator type for allocating memory buffer.
\note implements Stream concept
*/
template <typename Encoding, typename Allocator = CrtAllocator>
class GenericStringBuffer {
public:
typedef typename Encoding::Ch Ch;
GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {}
GenericStringBuffer& operator=(GenericStringBuffer&& rhs) {
if (&rhs != this)
stack_ = std::move(rhs.stack_);
return *this;
}
#endif
void Put(Ch c) { *stack_.template Push<Ch>() = c; }
void PutUnsafe(Ch c) { *stack_.template PushUnsafe<Ch>() = c; }
void Flush() {}
void Clear() { stack_.Clear(); }
void ShrinkToFit() {
// Push and pop a null terminator. This is safe.
*stack_.template Push<Ch>() = '\0';
stack_.ShrinkToFit();
stack_.template Pop<Ch>(1);
}
void Reserve(size_t count) { stack_.template Reserve<Ch>(count); }
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe<Ch>(count); }
void Pop(size_t count) { stack_.template Pop<Ch>(count); }
const Ch* GetString() const {
// Push and pop a null terminator. This is safe.
*stack_.template Push<Ch>() = '\0';
stack_.template Pop<Ch>(1);
return stack_.template Bottom<Ch>();
}
//! Get the size of string in bytes in the string buffer.
size_t GetSize() const { return stack_.GetSize(); }
//! Get the length of string in Ch in the string buffer.
size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); }
static const size_t kDefaultCapacity = 256;
mutable internal::Stack<Allocator> stack_;
private:
// Prohibit copy constructor & assignment operator.
GenericStringBuffer(const GenericStringBuffer&);
GenericStringBuffer& operator=(const GenericStringBuffer&);
};
//! String buffer with UTF8 encoding
typedef GenericStringBuffer<UTF8<> > StringBuffer;
template<typename Encoding, typename Allocator>
inline void PutReserve(GenericStringBuffer<Encoding, Allocator>& stream, size_t count) {
stream.Reserve(count);
}
template<typename Encoding, typename Allocator>
inline void PutUnsafe(GenericStringBuffer<Encoding, Allocator>& stream, typename Encoding::Ch c) {
stream.PutUnsafe(c);
}
//! Implement specialized version of PutN() with memset() for better performance.
template<>
inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) {
std::memset(stream.stack_.Push<char>(n), c, n * sizeof(c));
}
RAPIDJSON_NAMESPACE_END
#if defined(__clang__)
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_STRINGBUFFER_H_

709
include/rapidjson/writer.h Normal file
View File

@ -0,0 +1,709 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_WRITER_H_
#define RAPIDJSON_WRITER_H_
#include "stream.h"
#include "internal/meta.h"
#include "internal/stack.h"
#include "internal/strfunc.h"
#include "internal/dtoa.h"
#include "internal/itoa.h"
#include "stringbuffer.h"
#include <new> // placement new
#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
#include <intrin.h>
#pragma intrinsic(_BitScanForward)
#endif
#ifdef RAPIDJSON_SSE42
#include <nmmintrin.h>
#elif defined(RAPIDJSON_SSE2)
#include <emmintrin.h>
#elif defined(RAPIDJSON_NEON)
#include <arm_neon.h>
#endif
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
RAPIDJSON_DIAG_OFF(unreachable-code)
RAPIDJSON_DIAG_OFF(c++98-compat)
#elif defined(_MSC_VER)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
#endif
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// WriteFlag
/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS
\ingroup RAPIDJSON_CONFIG
\brief User-defined kWriteDefaultFlags definition.
User can define this as any \c WriteFlag combinations.
*/
#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS
#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags
#endif
//! Combination of writeFlags
enum WriteFlag {
kWriteNoFlags = 0, //!< No flags are set.
kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings.
kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN.
kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS
};
//! JSON writer
/*! Writer implements the concept Handler.
It generates JSON text by events to an output os.
User may programmatically calls the functions of a writer to generate JSON text.
On the other side, a writer can also be passed to objects that generates events,
for example Reader::Parse() and Document::Accept().
\tparam OutputStream Type of output stream.
\tparam SourceEncoding Encoding of source string.
\tparam TargetEncoding Encoding of output stream.
\tparam StackAllocator Type of allocator for allocating memory of stack.
\note implements Handler concept
*/
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
class Writer {
public:
typedef typename SourceEncoding::Ch Ch;
static const int kDefaultMaxDecimalPlaces = 324;
//! Constructor
/*! \param os Output stream.
\param stackAllocator User supplied allocator. If it is null, it will create a private one.
\param levelDepth Initial capacity of stack.
*/
explicit
Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) :
os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
explicit
Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
Writer(Writer&& rhs) :
os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) {
rhs.os_ = 0;
}
#endif
//! Reset the writer with a new stream.
/*!
This function reset the writer with a new stream and default settings,
in order to make a Writer object reusable for output multiple JSONs.
\param os New output stream.
\code
Writer<OutputStream> writer(os1);
writer.StartObject();
// ...
writer.EndObject();
writer.Reset(os2);
writer.StartObject();
// ...
writer.EndObject();
\endcode
*/
void Reset(OutputStream& os) {
os_ = &os;
hasRoot_ = false;
level_stack_.Clear();
}
//! Checks whether the output is a complete JSON.
/*!
A complete JSON has a complete root object or array.
*/
bool IsComplete() const {
return hasRoot_ && level_stack_.Empty();
}
int GetMaxDecimalPlaces() const {
return maxDecimalPlaces_;
}
//! Sets the maximum number of decimal places for double output.
/*!
This setting truncates the output with specified number of decimal places.
For example,
\code
writer.SetMaxDecimalPlaces(3);
writer.StartArray();
writer.Double(0.12345); // "0.123"
writer.Double(0.0001); // "0.0"
writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent)
writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent)
writer.EndArray();
\endcode
The default setting does not truncate any decimal places. You can restore to this setting by calling
\code
writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces);
\endcode
*/
void SetMaxDecimalPlaces(int maxDecimalPlaces) {
maxDecimalPlaces_ = maxDecimalPlaces;
}
/*!@name Implementation of Handler
\see Handler
*/
//@{
bool Null() { Prefix(kNullType); return EndValue(WriteNull()); }
bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); }
bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); }
bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); }
bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); }
bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); }
//! Writes the given \c double value to the stream
/*!
\param d The value to be written.
\return Whether it is succeed.
*/
bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); }
bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
RAPIDJSON_ASSERT(str != 0);
(void)copy;
Prefix(kNumberType);
return EndValue(WriteString(str, length));
}
bool String(const Ch* str, SizeType length, bool copy = false) {
RAPIDJSON_ASSERT(str != 0);
(void)copy;
Prefix(kStringType);
return EndValue(WriteString(str, length));
}
#if RAPIDJSON_HAS_STDSTRING
bool String(const std::basic_string<Ch>& str) {
return String(str.data(), SizeType(str.size()));
}
#endif
bool StartObject() {
Prefix(kObjectType);
new (level_stack_.template Push<Level>()) Level(false);
return WriteStartObject();
}
bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
#if RAPIDJSON_HAS_STDSTRING
bool Key(const std::basic_string<Ch>& str)
{
return Key(str.data(), SizeType(str.size()));
}
#endif
bool EndObject(SizeType memberCount = 0) {
(void)memberCount;
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object
RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray); // currently inside an Array, not Object
RAPIDJSON_ASSERT(0 == level_stack_.template Top<Level>()->valueCount % 2); // Object has a Key without a Value
level_stack_.template Pop<Level>(1);
return EndValue(WriteEndObject());
}
bool StartArray() {
Prefix(kArrayType);
new (level_stack_.template Push<Level>()) Level(true);
return WriteStartArray();
}
bool EndArray(SizeType elementCount = 0) {
(void)elementCount;
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
level_stack_.template Pop<Level>(1);
return EndValue(WriteEndArray());
}
//@}
/*! @name Convenience extensions */
//@{
//! Simpler but slower overload.
bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); }
bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); }
//@}
//! Write a raw JSON value.
/*!
For user to write a stringified JSON as a value.
\param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
\param length Length of the json.
\param type Type of the root of json.
*/
bool RawValue(const Ch* json, size_t length, Type type) {
RAPIDJSON_ASSERT(json != 0);
Prefix(type);
return EndValue(WriteRawValue(json, length));
}
//! Flush the output stream.
/*!
Allows the user to flush the output stream immediately.
*/
void Flush() {
os_->Flush();
}
protected:
//! Information for each nested level
struct Level {
Level(bool inArray_) : valueCount(0), inArray(inArray_) {}
size_t valueCount; //!< number of values in this level
bool inArray; //!< true if in array, otherwise in object
};
static const size_t kDefaultLevelDepth = 32;
bool WriteNull() {
PutReserve(*os_, 4);
PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true;
}
bool WriteBool(bool b) {
if (b) {
PutReserve(*os_, 4);
PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e');
}
else {
PutReserve(*os_, 5);
PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e');
}
return true;
}
bool WriteInt(int i) {
char buffer[11];
const char* end = internal::i32toa(i, buffer);
PutReserve(*os_, static_cast<size_t>(end - buffer));
for (const char* p = buffer; p != end; ++p)
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
return true;
}
bool WriteUint(unsigned u) {
char buffer[10];
const char* end = internal::u32toa(u, buffer);
PutReserve(*os_, static_cast<size_t>(end - buffer));
for (const char* p = buffer; p != end; ++p)
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
return true;
}
bool WriteInt64(int64_t i64) {
char buffer[21];
const char* end = internal::i64toa(i64, buffer);
PutReserve(*os_, static_cast<size_t>(end - buffer));
for (const char* p = buffer; p != end; ++p)
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
return true;
}
bool WriteUint64(uint64_t u64) {
char buffer[20];
char* end = internal::u64toa(u64, buffer);
PutReserve(*os_, static_cast<size_t>(end - buffer));
for (char* p = buffer; p != end; ++p)
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
return true;
}
bool WriteDouble(double d) {
if (internal::Double(d).IsNanOrInf()) {
if (!(writeFlags & kWriteNanAndInfFlag))
return false;
if (internal::Double(d).IsNan()) {
PutReserve(*os_, 3);
PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
return true;
}
if (internal::Double(d).Sign()) {
PutReserve(*os_, 9);
PutUnsafe(*os_, '-');
}
else
PutReserve(*os_, 8);
PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
return true;
}
char buffer[25];
char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
PutReserve(*os_, static_cast<size_t>(end - buffer));
for (char* p = buffer; p != end; ++p)
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
return true;
}
bool WriteString(const Ch* str, SizeType length) {
static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
static const char escape[256] = {
#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
//0 1 2 3 4 5 6 7 8 9 A B C D E F
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10
0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20
Z16, Z16, // 30~4F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50
Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF
#undef Z16
};
if (TargetEncoding::supportUnicode)
PutReserve(*os_, 2 + length * 6); // "\uxxxx..."
else
PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..."
PutUnsafe(*os_, '\"');
GenericStringStream<SourceEncoding> is(str);
while (ScanWriteUnescapedString(is, length)) {
const Ch c = is.Peek();
if (!TargetEncoding::supportUnicode && static_cast<unsigned>(c) >= 0x80) {
// Unicode escaping
unsigned codepoint;
if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint)))
return false;
PutUnsafe(*os_, '\\');
PutUnsafe(*os_, 'u');
if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) {
PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]);
PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]);
PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]);
PutUnsafe(*os_, hexDigits[(codepoint ) & 15]);
}
else {
RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF);
// Surrogate pair
unsigned s = codepoint - 0x010000;
unsigned lead = (s >> 10) + 0xD800;
unsigned trail = (s & 0x3FF) + 0xDC00;
PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]);
PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]);
PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]);
PutUnsafe(*os_, hexDigits[(lead ) & 15]);
PutUnsafe(*os_, '\\');
PutUnsafe(*os_, 'u');
PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]);
PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]);
PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]);
PutUnsafe(*os_, hexDigits[(trail ) & 15]);
}
}
else if ((sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast<unsigned char>(c)])) {
is.Take();
PutUnsafe(*os_, '\\');
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(escape[static_cast<unsigned char>(c)]));
if (escape[static_cast<unsigned char>(c)] == 'u') {
PutUnsafe(*os_, '0');
PutUnsafe(*os_, '0');
PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) >> 4]);
PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) & 0xF]);
}
}
else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ?
Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) :
Transcoder<SourceEncoding, TargetEncoding>::TranscodeUnsafe(is, *os_))))
return false;
}
PutUnsafe(*os_, '\"');
return true;
}
bool ScanWriteUnescapedString(GenericStringStream<SourceEncoding>& is, size_t length) {
return RAPIDJSON_LIKELY(is.Tell() < length);
}
bool WriteStartObject() { os_->Put('{'); return true; }
bool WriteEndObject() { os_->Put('}'); return true; }
bool WriteStartArray() { os_->Put('['); return true; }
bool WriteEndArray() { os_->Put(']'); return true; }
bool WriteRawValue(const Ch* json, size_t length) {
PutReserve(*os_, length);
GenericStringStream<SourceEncoding> is(json);
while (RAPIDJSON_LIKELY(is.Tell() < length)) {
RAPIDJSON_ASSERT(is.Peek() != '\0');
if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ?
Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) :
Transcoder<SourceEncoding, TargetEncoding>::TranscodeUnsafe(is, *os_))))
return false;
}
return true;
}
void Prefix(Type type) {
(void)type;
if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root
Level* level = level_stack_.template Top<Level>();
if (level->valueCount > 0) {
if (level->inArray)
os_->Put(','); // add comma if it is not the first element in array
else // in object
os_->Put((level->valueCount % 2 == 0) ? ',' : ':');
}
if (!level->inArray && level->valueCount % 2 == 0)
RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
level->valueCount++;
}
else {
RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root.
hasRoot_ = true;
}
}
// Flush the value if it is the top level one.
bool EndValue(bool ret) {
if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text
Flush();
return ret;
}
OutputStream* os_;
internal::Stack<StackAllocator> level_stack_;
int maxDecimalPlaces_;
bool hasRoot_;
private:
// Prohibit copy constructor & assignment operator.
Writer(const Writer&);
Writer& operator=(const Writer&);
};
// Full specialization for StringStream to prevent memory copying
template<>
inline bool Writer<StringBuffer>::WriteInt(int i) {
char *buffer = os_->Push(11);
const char* end = internal::i32toa(i, buffer);
os_->Pop(static_cast<size_t>(11 - (end - buffer)));
return true;
}
template<>
inline bool Writer<StringBuffer>::WriteUint(unsigned u) {
char *buffer = os_->Push(10);
const char* end = internal::u32toa(u, buffer);
os_->Pop(static_cast<size_t>(10 - (end - buffer)));
return true;
}
template<>
inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) {
char *buffer = os_->Push(21);
const char* end = internal::i64toa(i64, buffer);
os_->Pop(static_cast<size_t>(21 - (end - buffer)));
return true;
}
template<>
inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
char *buffer = os_->Push(20);
const char* end = internal::u64toa(u, buffer);
os_->Pop(static_cast<size_t>(20 - (end - buffer)));
return true;
}
template<>
inline bool Writer<StringBuffer>::WriteDouble(double d) {
if (internal::Double(d).IsNanOrInf()) {
// Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag).
if (!(kWriteDefaultFlags & kWriteNanAndInfFlag))
return false;
if (internal::Double(d).IsNan()) {
PutReserve(*os_, 3);
PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
return true;
}
if (internal::Double(d).Sign()) {
PutReserve(*os_, 9);
PutUnsafe(*os_, '-');
}
else
PutReserve(*os_, 8);
PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
return true;
}
char *buffer = os_->Push(25);
char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
os_->Pop(static_cast<size_t>(25 - (end - buffer)));
return true;
}
#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
template<>
inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) {
if (length < 16)
return RAPIDJSON_LIKELY(is.Tell() < length);
if (!RAPIDJSON_LIKELY(is.Tell() < length))
return false;
const char* p = is.src_;
const char* end = is.head_ + length;
const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~15));
if (nextAligned > end)
return true;
while (p != nextAligned)
if (*p < 0x20 || *p == '\"' || *p == '\\') {
is.src_ = p;
return RAPIDJSON_LIKELY(is.Tell() < length);
}
else
os_->PutUnsafe(*p++);
// The rest of string using SIMD
static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F };
const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
for (; p != endAligned; p += 16) {
const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
const __m128i t1 = _mm_cmpeq_epi8(s, dq);
const __m128i t2 = _mm_cmpeq_epi8(s, bs);
const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F
const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
SizeType len;
#ifdef _MSC_VER // Find the index of first escaped
unsigned long offset;
_BitScanForward(&offset, r);
len = offset;
#else
len = static_cast<SizeType>(__builtin_ffs(r) - 1);
#endif
char* q = reinterpret_cast<char*>(os_->PushUnsafe(len));
for (size_t i = 0; i < len; i++)
q[i] = p[i];
p += len;
break;
}
_mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s);
}
is.src_ = p;
return RAPIDJSON_LIKELY(is.Tell() < length);
}
#elif defined(RAPIDJSON_NEON)
template<>
inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) {
if (length < 16)
return RAPIDJSON_LIKELY(is.Tell() < length);
if (!RAPIDJSON_LIKELY(is.Tell() < length))
return false;
const char* p = is.src_;
const char* end = is.head_ + length;
const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~15));
if (nextAligned > end)
return true;
while (p != nextAligned)
if (*p < 0x20 || *p == '\"' || *p == '\\') {
is.src_ = p;
return RAPIDJSON_LIKELY(is.Tell() < length);
}
else
os_->PutUnsafe(*p++);
// The rest of string using SIMD
const uint8x16_t s0 = vmovq_n_u8('"');
const uint8x16_t s1 = vmovq_n_u8('\\');
const uint8x16_t s2 = vmovq_n_u8('\b');
const uint8x16_t s3 = vmovq_n_u8(32);
for (; p != endAligned; p += 16) {
const uint8x16_t s = vld1q_u8(reinterpret_cast<const uint8_t *>(p));
uint8x16_t x = vceqq_u8(s, s0);
x = vorrq_u8(x, vceqq_u8(s, s1));
x = vorrq_u8(x, vceqq_u8(s, s2));
x = vorrq_u8(x, vcltq_u8(s, s3));
x = vrev64q_u8(x); // Rev in 64
uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0); // extract
uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1); // extract
SizeType len = 0;
bool escaped = false;
if (low == 0) {
if (high != 0) {
unsigned lz = (unsigned)__builtin_clzll(high);
len = 8 + (lz >> 3);
escaped = true;
}
} else {
unsigned lz = (unsigned)__builtin_clzll(low);
len = lz >> 3;
escaped = true;
}
if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped
char* q = reinterpret_cast<char*>(os_->PushUnsafe(len));
for (size_t i = 0; i < len; i++)
q[i] = p[i];
p += len;
break;
}
vst1q_u8(reinterpret_cast<uint8_t *>(os_->PushUnsafe(16)), s);
}
is.src_ = p;
return RAPIDJSON_LIKELY(is.Tell() < length);
}
#endif // RAPIDJSON_NEON
RAPIDJSON_NAMESPACE_END
#if defined(_MSC_VER) || defined(__clang__)
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_RAPIDJSON_H_

356
py/libbolt.py Normal file
View File

@ -0,0 +1,356 @@
from ctypes import cdll
from sys import platform
import sys, ctypes
from ctypes import c_void_p, c_uint8
import ast
import json
class Libbolt(object):
"""Libbolt Py/C low-level API"""
def __init__(self, path):
self.lib = cdll.LoadLibrary(path)
self.load_library_params()
def load_library_params(self):
self.lib.ffishim_bidirectional_channel_setup.argtypes = (c_void_p, c_uint8)
self.lib.ffishim_bidirectional_channel_setup.restype = c_void_p
# ESTABLISH PROTOCOL
self.lib.ffishim_bidirectional_init_merchant.argtypes = (c_void_p, c_void_p)
self.lib.ffishim_bidirectional_init_merchant.restype = c_void_p
self.lib.ffishim_bidirectional_init_customer.argtypes = (c_void_p, ctypes.c_int32, ctypes.c_int32, c_void_p)
self.lib.ffishim_bidirectional_init_customer.restype = c_void_p
self.lib.ffishim_bidirectional_establish_customer_generate_proof.argtypes = (c_void_p, c_void_p)
self.lib.ffishim_bidirectional_establish_customer_generate_proof.restype = c_void_p
self.lib.ffishim_bidirectional_establish_merchant_issue_close_token.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_establish_merchant_issue_close_token.restype = c_void_p
self.lib.ffishim_bidirectional_establish_merchant_issue_pay_token.argtypes = (c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_establish_merchant_issue_pay_token.restype = c_void_p
self.lib.ffishim_bidirectional_verify_close_token.argtypes = (c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_verify_close_token.restype = c_void_p
self.lib.ffishim_bidirectional_establish_customer_final.argtypes = (c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_establish_customer_final.restype = c_void_p
# PAY PROTOCOL
self.lib.ffishim_bidirectional_pay_generate_payment_proof.argtypes = (c_void_p, c_void_p, ctypes.c_int32)
self.lib.ffishim_bidirectional_pay_generate_payment_proof.restype = c_void_p
self.lib.ffishim_bidirectional_pay_verify_payment_proof.argtypes = (c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_pay_verify_payment_proof.restype = c_void_p
self.lib.ffishim_bidirectional_pay_verify_multiple_payment_proofs.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_pay_verify_multiple_payment_proofs.restype = c_void_p
self.lib.ffishim_bidirectional_pay_generate_revoke_token.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_pay_generate_revoke_token.restype = c_void_p
self.lib.ffishim_bidirectional_pay_verify_revoke_token.argtypes = (c_void_p, c_void_p)
self.lib.ffishim_bidirectional_pay_verify_revoke_token.restype = c_void_p
self.lib.ffishim_bidirectional_pay_verify_multiple_revoke_tokens.argtypes = (c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_pay_verify_multiple_revoke_tokens.restype = c_void_p
self.lib.ffishim_bidirectional_pay_verify_payment_token.argtypes = (c_void_p, c_void_p)
self.lib.ffishim_bidirectional_pay_verify_payment_token.restype = c_void_p
# CLOSE
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)
self.lib.ffishim_bidirectional_merchant_close.restype = c_void_p
# ON-CHAIN BOLT LOGIC / WTPs
self.lib.ffishim_bidirectional_wtp_verify_cust_close_message.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_wtp_verify_cust_close_message.restype = c_void_p
self.lib.ffishim_bidirectional_wtp_verify_merch_close_message.argtypes = (c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_wtp_verify_merch_close_message.restype = c_void_p
self.lib.ffishim_free_string.argtypes = (c_void_p, )
def channel_setup(self, name, third_party_support=0):
output_string = self.lib.ffishim_bidirectional_channel_setup(name.encode(), third_party_support)
output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
return output_dictionary.get('channel_state')
# INIT PROTOCOL
def bidirectional_init_merchant(self, channel_state, name):
output_string = self.lib.ffishim_bidirectional_init_merchant(channel_state.encode(), name.encode())
output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
return output_dictionary.get('channel_token'), output_dictionary.get('merch_state'), output_dictionary.get('channel_state')
def bidirectional_init_customer(self, channel_token, b0_cust, b0_merch, name):
output_string = self.lib.ffishim_bidirectional_init_customer(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.get('channel_token'), output_dictionary.get('cust_state'))
# ESTABLISH PROTOCOL
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.get('channel_token'), output_dictionary.get('cust_state'), output_dictionary.get('com'), output_dictionary.get('com_proof')
def bidirectional_establish_merchant_issue_close_token(self, channel_state, com, com_proof, channel_id, init_cust, init_merch, merch_state):
output_string = self.lib.ffishim_bidirectional_establish_merchant_issue_close_token(channel_state.encode(), com.encode(), com_proof.encode(), json.dumps(channel_id).encode(), init_cust, init_merch, merch_state.encode())
output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
return output_dictionary.get('close_token')
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.get('pay_token')
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'))
is_token_valid = self._convert_boolean(output_dictionary.get('is_token_valid'))
return is_token_valid, output_dictionary.get('channel_state'), output_dictionary.get('cust_state')
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'))
is_established = self._convert_boolean(output_dictionary.get('is_established'))
return is_established, output_dictionary.get('channel_state'), output_dictionary.get('cust_state')
# PAY PROTOCOL
# generate payment proof and new cust state
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.get('payment'), output_dictionary.get('cust_state')
# verify payment proof
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.get('close_token'), output_dictionary.get('merch_state'))
# verify multiple payment proof
def bidirectional_pay_verify_multiple_payment_proofs(self, channel_state, sender_pay_proof, receiver_pay_proof, merch_state):
output_string = self.lib.ffishim_bidirectional_pay_verify_multiple_payment_proofs(channel_state.encode(), sender_pay_proof.encode(), receiver_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.get('sender_close_token'), output_dictionary.get('receiver_cond_close_token'), output_dictionary.get('merch_state'))
# generate revoke token
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.get('revoke_token'), output_dictionary.get('cust_state')
# verify revoke token
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.get('pay_token'), output_dictionary.get('merch_state'))
# verify multiple revoke tokens
def bidirectional_pay_verify_multiple_revoke_tokens(self, sender_revoke_token, receiver_revoke_token, merch_state):
output_string = self.lib.ffishim_bidirectional_pay_verify_multiple_revoke_tokens(sender_revoke_token.encode(), receiver_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.get('sender_pay_token'), output_dictionary.get('receiver_pay_token'), output_dictionary.get('merch_state'))
# verify payment token
def bidirectional_pay_verify_payment_token(self, channel_state, cust_state, pay_token):
output_string = self.lib.ffishim_bidirectional_pay_verify_payment_token(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'))
is_pay_valid = self._convert_boolean(output_dictionary.get('is_pay_valid'))
return (output_dictionary.get('cust_state'), is_pay_valid)
# CLOSE
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.get('cust_close')
def bidirectional_merchant_close(self, channel_state, channel_token, address, cust_close, merch_state):
output_string = self.lib.ffishim_bidirectional_merchant_close(channel_state.encode(), channel_token.encode(),
address.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('merch_close'), output_dictionary.get('error'))
# WTP logic
def wtp_get_wallet(self, cust_state):
cust_state_dict = self._interperate_json_string_as_dictionary(cust_state)
return json.dumps(cust_state_dict.get("wpk")), json.dumps(cust_state_dict.get("wallet"))
def wtp_get_close_token(self, cust_close):
cust_close_dict = self._interperate_json_string_as_dictionary(cust_close)
return json.dumps(cust_close_dict.get("signature"))
def wtp_verify_cust_close_message(self, channel_token, wpk, cust_close_wallet, close_token):
output_string = self.lib.ffishim_bidirectional_wtp_verify_cust_close_message(channel_token.encode(),
wpk.encode(),
cust_close_wallet.encode(),
close_token.encode())
output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
return output_dictionary.get('result')
def wtp_verify_merch_close_message(self, channel_token, wpk, merch_close):
output_string = self.lib.ffishim_bidirectional_wtp_verify_merch_close_message(channel_token.encode(),
wpk.encode(),
merch_close.encode())
output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
return output_dictionary.get('result')
def _interperate_json_string_as_dictionary(self, json_string):
return ast.literal_eval(json_string)
def _convert_boolean(self, bool_str):
if bool_str == "true":
return True
if bool_str == "false":
return False
return bool_str
if platform == 'darwin':
prefix = 'lib'
ext = 'dylib'
elif platform == 'win32':
prefix = ''
ext = 'dll'
else:
prefix = 'lib'
ext = 'so'
DEBUG = 'debug'
RELEASE = 'release'
mode = RELEASE # debug or release
def run_unit_test():
libbolt = Libbolt('target/{}/{}bolt.{}'.format(mode, prefix, ext))
b0_cust = 100
b0_merch = 10
channel_state = libbolt.channel_setup("My New Channel A")
print("channel state new: ", len(channel_state))
(channel_token, merch_state, channel_state) = libbolt.bidirectional_init_merchant(channel_state, "Bob")
print("merch_state: ", len(merch_state))
#print("channel_token: ", type(_channel_token))
(channel_token, cust_state) = libbolt.bidirectional_init_customer(channel_token, b0_cust, b0_merch, "Alice")
print("cust_state: ", len(cust_state))
(channel_token, cust_state, com, com_proof) = libbolt.bidirectional_establish_customer_generate_proof(channel_token, cust_state)
print("channel token: => ", channel_token)
print("com: ", com)
cust_state_dict = json.loads(cust_state)
close_token = libbolt.bidirectional_establish_merchant_issue_close_token(channel_state, com, com_proof, cust_state_dict["wallet"]["channelId"], b0_cust, b0_merch, merch_state)
print("close token: ", 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_state)
print("pay token: ", 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_state: ", cust_state)
else:
print("channel still not established. did you verify close token?")
# Pay protocol
print("Pay protocol...")
# make a payment
amount = 5
(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_state)
print("<========================================>")
revoked_wpk, _ = libbolt.wtp_get_wallet(new_cust_state)
(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_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_state) = libbolt.bidirectional_pay_verify_revoke_token(revoke_token, merch_state)
print("Pay token: ", pay_token)
(cust_state, is_pay_valid) = libbolt.bidirectional_pay_verify_payment_token(channel_state, cust_state, pay_token)
print("Pay token is valid: ", is_pay_valid)
old_cust_close = libbolt.bidirectional_customer_close(channel_state, cust_state)
# make a payment
amount = 10
(payment_proof2, new_cust_state2) = libbolt.bidirectional_pay_generate_payment_proof(channel_state, cust_state, amount)
print("Pay proof 2: ", len(payment_proof2))
print("new cust wallet 2: ", new_cust_state2)
print("<========================================>")
(new_close_token2, merch_state) = libbolt.bidirectional_pay_verify_payment_proof(channel_state, payment_proof2, merch_state)
print("Close token 2: ", new_close_token2)
print("<========================================>")
(revoke_token2, cust_state) = libbolt.bidirectional_pay_generate_revoke_token(channel_state, cust_state, new_cust_state2, new_close_token2)
print("Revoke token 2: ", revoke_token)
(pay_token2, merch_state) = libbolt.bidirectional_pay_verify_revoke_token(revoke_token2, merch_state)
print("Pay token 2: ", pay_token2)
(cust_state, is_pay_valid) = libbolt.bidirectional_pay_verify_payment_token(channel_state, cust_state, pay_token2)
print("Pay token is valid: ", is_pay_valid)
print("<========================================>")
print("<========================================>")
cust_close = libbolt.bidirectional_customer_close(channel_state, cust_state)
print("Cust close msg: ", cust_close)
print("<========================================>")
# normal case: no action b/c cust close is valid
address = "11" * 32
merch_close = libbolt.bidirectional_merchant_close(channel_state, channel_token, address, cust_close, merch_state)
print("Customer initiated - Merch close msg: ", merch_close)
print("<========================================>")
# common case: merchant catches customer double spending
address = "11" * 32
merch_wpk, merch_close_msg, _ = libbolt.bidirectional_merchant_close(channel_state, channel_token, address, old_cust_close, merch_state)
print("Double spend - Merch close msg: ", merch_close_msg)
merch_close_valid = libbolt.wtp_verify_merch_close_message(channel_token, merch_wpk, merch_close_msg)
print("Merchant close msg valid: ", merch_close_valid)
print("<========================================>")
print("<========================================>")
wpk, cust_close_wallet = libbolt.wtp_get_wallet(cust_state)
print("wpk = ", wpk)
print("close-msg wallet = ", cust_close_wallet)
cust_close_token = libbolt.wtp_get_close_token(cust_close)
print("close token: ", cust_close_token)
print("Valid channel opening: ", libbolt.wtp_verify_cust_close_message(channel_token, wpk, cust_close_wallet, cust_close_token))
# TODO: merch close when cust_close represents correct channel state
print("Invalid channel opening: ", libbolt.wtp_verify_cust_close_message(channel_token, revoked_wpk, cust_close_wallet, cust_close_token))
print("<========================================>")
if __name__ == "__main__":
run_unit_test()

397
py/libbolt_tests.py Normal file
View File

@ -0,0 +1,397 @@
import unittest
import libbolt
import ast, random, json
def rand_hex(stringLength=10):
"""Generate a random hex string of fixed length """
hex_letters = '0123456789abcdef'
return ''.join(random.choice(hex_letters) for i in range(stringLength))
def malformed_token(token):
token_dict = ast.literal_eval(token)
updated_token = {}
for k,v in token_dict.items():
if type(v) == str:
updated_token[k] = v[:-4] + rand_hex(4)
else:
updated_token[k] = v
return json.dumps(updated_token)
def malformed_proof(proof):
bad_proof = proof.replace("0", "1")
bad_proof = bad_proof.replace("1", "2")
return bad_proof
class BoltEstablishTests(unittest.TestCase):
def setUp(self):
self.bolt = libbolt.Libbolt('target/{}/{}bolt.{}'.format(libbolt.mode, libbolt.prefix, libbolt.ext))
self.channel_state = self.bolt.channel_setup("Test Channel")
self.b0_cust = 1000
self.b0_merch = 100
(self.channel_token, self.merch_state, self.channel_state) = self.bolt.bidirectional_init_merchant(self.channel_state, "Bob")
(channel_token, self.cust_state) = self.bolt.bidirectional_init_customer(self.channel_token, self.b0_cust, self.b0_merch, "Alice")
# generate some bad stuff here
larger_b0_cust = 2000
(channel_token_bad, self.cust_state_bad) = self.bolt.bidirectional_init_customer(self.channel_token, larger_b0_cust, self.b0_merch, "Alice")
# set them
self.channel_token = channel_token
self.channel_token_bad = channel_token_bad
def test_establish_works_okay(self):
"""
Establish protocol common case works
"""
(channel_token, cust_state, com, com_proof) = self.bolt.bidirectional_establish_customer_generate_proof(self.channel_token, self.cust_state)
cust_state_dict = json.loads(cust_state)
close_token = self.bolt.bidirectional_establish_merchant_issue_close_token(self.channel_state, com, com_proof, cust_state_dict["wallet"]["channelId"], self.b0_cust, self.b0_merch, self.merch_state)
self.assertTrue(close_token is not None)
(is_token_valid, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_verify_close_token(self.channel_state, cust_state, close_token)
self.assertTrue(is_token_valid)
pay_token = self.bolt.bidirectional_establish_merchant_issue_pay_token(channel_state, com, self.merch_state)
self.assertTrue(pay_token is not None)
(is_channel_established, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_final(channel_state, cust_state, pay_token)
self.assertTrue(is_channel_established)
#print("Establish protocol works as expected.")
def test_establish_merchant_issue_close_token_fail_as_expected(self):
"""
Initial com proof fails as expected when commitment opening doesn't match expected initial customer and merchant balances
"""
(channel_token, cust_state, com, com_proof) = self.bolt.bidirectional_establish_customer_generate_proof(self.channel_token_bad, self.cust_state_bad)
cust_state_dict = json.loads(cust_state)
close_token = self.bolt.bidirectional_establish_merchant_issue_close_token(self.channel_state, com, com_proof, cust_state_dict["wallet"]["channelId"], self.b0_cust, self.b0_merch, self.merch_state)
self.assertTrue(close_token is None)
#print("Establish protocol fail works as expected.")
def test_establish_customer_verify_close_token_fail_as_expected(self):
"""
Not-signed close token fails to verify
"""
close_token = json.dumps({"h":"b896166d76a7bd02565b6431dca27da4c290e234edfbca8d9189f78311e18f66a138684c91efdf7fd1c4b192bf27f68e",
"H":"add6c20994749185fb7d44f8f5f1f3dbbcd250e4922a9c6c9017c25dda670d94c4b279b7f0fccd56916bf737a29a1938"})
(channel_token, cust_state, com, com_proof) = self.bolt.bidirectional_establish_customer_generate_proof(self.channel_token, self.cust_state)
(is_token_valid, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_verify_close_token(self.channel_state, cust_state, close_token)
self.assertTrue(is_token_valid is False)
def test_establish_merchant_issue_pay_token_fail_as_expected(self):
"""
Specifying a different commitment leads to an invalid pay token as expected
"""
bad_com = json.dumps({"c":"852a57e24a2192e1cea19157e44f92d58369751f2012bc1f4a4312a89a63c74a92a4cb1d362b37ae0eda3b3bd1333502"})
(channel_token, cust_state, com, com_proof) = self.bolt.bidirectional_establish_customer_generate_proof(self.channel_token, self.cust_state)
cust_state_dict = json.loads(cust_state)
close_token = self.bolt.bidirectional_establish_merchant_issue_close_token(self.channel_state, com, com_proof, cust_state_dict["wallet"]["channelId"], self.b0_cust, self.b0_merch, self.merch_state)
self.assertTrue(close_token is not None)
(is_token_valid, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_verify_close_token(self.channel_state, cust_state, close_token)
self.assertTrue(is_token_valid)
pay_token = self.bolt.bidirectional_establish_merchant_issue_pay_token(channel_state, bad_com, self.merch_state)
self.assertTrue(pay_token is not None)
(is_channel_established, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_final(channel_state, cust_state, pay_token)
self.assertFalse(is_channel_established)
def test_establish_not_complete_without_close_token(self):
"""
Test that missing close token prevents the customer from establishing
"""
(channel_token, cust_state, com, com_proof) = self.bolt.bidirectional_establish_customer_generate_proof(self.channel_token, self.cust_state)
pay_token = self.bolt.bidirectional_establish_merchant_issue_pay_token(self.channel_state, com, self.merch_state)
self.assertTrue(pay_token is not None)
(is_channel_established, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_final(self.channel_state, cust_state, pay_token)
self.assertFalse(is_channel_established)
def test_error_handling_with_serialization(self):
"""
Test that malformed close and/or pay token results in failure
:return:
"""
(channel_token, cust_state, com, com_proof) = self.bolt.bidirectional_establish_customer_generate_proof(self.channel_token, self.cust_state)
cust_state_dict = json.loads(cust_state)
close_token = self.bolt.bidirectional_establish_merchant_issue_close_token(self.channel_state, com, com_proof, cust_state_dict["wallet"]["channelId"], self.b0_cust, self.b0_merch, self.merch_state)
self.assertTrue(close_token is not None)
malformed_close_token = malformed_token(close_token)
(is_token_valid, bad_channel_state, bad_cust_state) = self.bolt.bidirectional_establish_customer_verify_close_token(self.channel_state, cust_state, malformed_close_token)
self.assertTrue(is_token_valid is None)
(is_token_valid, self.channel_state, cust_state) = self.bolt.bidirectional_establish_customer_verify_close_token(self.channel_state, cust_state, close_token)
pay_token = self.bolt.bidirectional_establish_merchant_issue_pay_token(self.channel_state, com, self.merch_state)
malformed_pay_token = malformed_token(pay_token)
(is_channel_established, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_final(self.channel_state, cust_state, malformed_pay_token)
self.assertFalse(is_channel_established)
class BoltPayTests(unittest.TestCase):
def setUp(self):
"""
Setup init customer/merchant state and establish phase of Bolt protocol
:return:
"""
self.bolt = libbolt.Libbolt('target/{}/{}bolt.{}'.format(libbolt.mode, libbolt.prefix, libbolt.ext))
self.channel_state = self.bolt.channel_setup("Test Channel")
self.b0_cust = 500
self.b0_merch = 10
(self.channel_token, self.merch_state, self.channel_state) = self.bolt.bidirectional_init_merchant(self.channel_state, "Bob")
(self.channel_token, self.cust_state) = self.bolt.bidirectional_init_customer(self.channel_token, self.b0_cust, self.b0_merch, "Alice")
(self.channel_token, self.cust_state, com, com_proof) = self.bolt.bidirectional_establish_customer_generate_proof(self.channel_token, self.cust_state)
cust_state_dict = json.loads(self.cust_state)
close_token = self.bolt.bidirectional_establish_merchant_issue_close_token(self.channel_state, com, com_proof, cust_state_dict["wallet"]["channelId"], self.b0_cust, self.b0_merch, self.merch_state)
self.assertTrue(close_token is not None)
(is_token_valid, self.channel_state, self.cust_state) = self.bolt.bidirectional_establish_customer_verify_close_token(self.channel_state, self.cust_state, close_token)
self.assertTrue(is_token_valid)
pay_token = self.bolt.bidirectional_establish_merchant_issue_pay_token(self.channel_state, com, self.merch_state)
self.assertTrue(pay_token is not None)
(is_channel_established, self.channel_state, self.cust_state) = self.bolt.bidirectional_establish_customer_final(self.channel_state, self.cust_state, pay_token)
self.assertTrue(is_channel_established)
def test_pay_protocol_works(self):
"""
Payment protocol works
:return:
"""
amount = 10
(payment_proof, new_cust_state) = self.bolt.bidirectional_pay_generate_payment_proof(self.channel_state, self.cust_state, amount)
(new_close_token, self.merch_state) = self.bolt.bidirectional_pay_verify_payment_proof(self.channel_state, payment_proof, self.merch_state)
(revoke_token, self.cust_state) = self.bolt.bidirectional_pay_generate_revoke_token(self.channel_state, self.cust_state, new_cust_state, new_close_token)
(pay_token, self.merch_state) = self.bolt.bidirectional_pay_verify_revoke_token(revoke_token, self.merch_state)
(self.cust_state, is_pay_valid) = self.bolt.bidirectional_pay_verify_payment_token(self.channel_state, self.cust_state, pay_token)
self.assertTrue(is_pay_valid)
def test_pay_protocol_bad_payment_proof_fail_handled(self):
"""
Payment protocol fails as expected when customer sends a bad payment proof
:return:
"""
amount = 15
(payment_proof, new_cust_state) = self.bolt.bidirectional_pay_generate_payment_proof(self.channel_state, self.cust_state, amount)
bad_payment_proof = malformed_proof(payment_proof)
(new_close_token, self.merch_state) = self.bolt.bidirectional_pay_verify_payment_proof(self.channel_state, bad_payment_proof, self.merch_state)
self.assertTrue(new_close_token is None)
def test_pay_protocol_bad_close_token_fail_handled(self):
"""
Payment protocol fails as expected when merchant returns a malformed/bad close token
:return:
"""
amount = 10
(payment_proof, new_cust_state) = self.bolt.bidirectional_pay_generate_payment_proof(self.channel_state, self.cust_state, amount)
(new_close_token, self.merch_state) = self.bolt.bidirectional_pay_verify_payment_proof(self.channel_state, payment_proof, self.merch_state)
bad_close_token = malformed_token(new_close_token)
(revoke_token, self.cust_state) = self.bolt.bidirectional_pay_generate_revoke_token(self.channel_state, self.cust_state, new_cust_state, bad_close_token)
self.assertTrue(revoke_token is None)
def test_pay_protocol_bad_revoke_token_fail_handled(self):
"""
Payment protocol fails as expected when customer sends a bad revoke token
:return:
"""
amount = 20
(payment_proof, new_cust_state) = self.bolt.bidirectional_pay_generate_payment_proof(self.channel_state, self.cust_state, amount)
(new_close_token, self.merch_state) = self.bolt.bidirectional_pay_verify_payment_proof(self.channel_state, payment_proof, self.merch_state)
(revoke_token, self.cust_state) = self.bolt.bidirectional_pay_generate_revoke_token(self.channel_state, self.cust_state, new_cust_state, new_close_token)
bad_revoke_token = malformed_token(revoke_token)
(pay_token, merch_state) = self.bolt.bidirectional_pay_verify_revoke_token(bad_revoke_token, self.merch_state)
self.assertTrue(pay_token is None)
def test_pay_protocol_bad_payment_token_fail_handled(self):
"""
Payment protocol fails as expected when merchant returns a malformed pay token
:return:
"""
amount = 25
(payment_proof, new_cust_state) = self.bolt.bidirectional_pay_generate_payment_proof(self.channel_state, self.cust_state, amount)
(new_close_token, self.merch_state) = self.bolt.bidirectional_pay_verify_payment_proof(self.channel_state, payment_proof, self.merch_state)
(revoke_token, self.cust_state) = self.bolt.bidirectional_pay_generate_revoke_token(self.channel_state, self.cust_state, new_cust_state, new_close_token)
(pay_token, self.merch_state) = self.bolt.bidirectional_pay_verify_revoke_token(revoke_token, self.merch_state)
bad_pay_token = malformed_token(pay_token)
(cust_state, is_pay_valid) = self.bolt.bidirectional_pay_verify_payment_token(self.channel_state, self.cust_state, bad_pay_token)
self.assertTrue(is_pay_valid is None)
class BoltMultiChannelTests(unittest.TestCase):
def setUp(self):
"""
Setup init customer/merchant state and establish phase of Bolt protocol
:return:
"""
self.bolt = libbolt.Libbolt('target/{}/{}bolt.{}'.format(libbolt.mode, libbolt.prefix, libbolt.ext))
self.channel_state = self.bolt.channel_setup("Test Channel")
self.b0_alice = self.b0_charlie = 150
self.b0_merch = 5
(self.channel_token, self.merch_state, self.channel_state) = self.bolt.bidirectional_init_merchant(self.channel_state, "Bob")
(self.channel_token_a, self.alice_state) = self.bolt.bidirectional_init_customer(self.channel_token, self.b0_alice, self.b0_merch, "Alice")
(self.channel_token_c, self.charlie_state) = self.bolt.bidirectional_init_customer(self.channel_token, self.b0_charlie, self.b0_merch, "Charlie")
def _establish_channel(self, channel_token, channel_state, cust_state, pkc, b0_cust, b0_merch):
(channel_token, cust_state, com, com_proof) = self.bolt.bidirectional_establish_customer_generate_proof(channel_token, cust_state)
close_token = self.bolt.bidirectional_establish_merchant_issue_close_token(channel_state, com, com_proof, pkc, b0_cust, b0_merch, self.merch_state)
self.assertTrue(close_token is not None)
(is_token_valid, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_verify_close_token(channel_state, cust_state, close_token)
self.assertTrue(is_token_valid)
pay_token = self.bolt.bidirectional_establish_merchant_issue_pay_token(channel_state, com, self.merch_state)
self.assertTrue(pay_token is not None)
(is_channel_established, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_final(channel_state, cust_state, pay_token)
self.assertTrue(is_channel_established)
return channel_token, channel_state, cust_state
def _pay_on_channel(self, channel_state, cust_state, amount):
(payment_proof, new_cust_state) = self.bolt.bidirectional_pay_generate_payment_proof(channel_state, cust_state, amount)
(new_close_token, self.merch_state) = self.bolt.bidirectional_pay_verify_payment_proof(channel_state, payment_proof, self.merch_state)
(revoke_token, cust_state) = self.bolt.bidirectional_pay_generate_revoke_token(channel_state, cust_state, new_cust_state, new_close_token)
(pay_token, self.merch_state) = self.bolt.bidirectional_pay_verify_revoke_token(revoke_token, self.merch_state)
(cust_state, is_pay_valid) = self.bolt.bidirectional_pay_verify_payment_token(channel_state, cust_state, pay_token)
self.assertTrue(is_pay_valid)
return channel_state, cust_state
def test_multiple_channels_work(self):
"""Establishing concurrent channels with a merchant works as expected
"""
alice_cust_state_dict = json.loads(self.alice_state)
self.channel_token_a, self.channel_state_a, alice_cust_state = self._establish_channel(self.channel_token_a, self.channel_state,
self.alice_state, alice_cust_state_dict["wallet"]["channelId"],
self.b0_alice, self.b0_merch)
charlie_cust_state_dict = json.loads(self.charlie_state)
self.channel_token_c, self.channel_state_c, charlie_cust_state = self._establish_channel(self.channel_token_c, self.channel_state,
self.charlie_state, charlie_cust_state_dict["wallet"]["channelId"],
self.b0_charlie, self.b0_merch)
self.channel_state_a, alice_cust_state = self._pay_on_channel(self.channel_state_a, alice_cust_state, 15)
#print("Alice cust state => ", alice_cust_state)
self.channel_state_c, charlie_cust_state = self._pay_on_channel(self.channel_state_c, charlie_cust_state, 10)
self.channel_state_c, charlie_cust_state = self._pay_on_channel(self.channel_state_c, charlie_cust_state, 20)
#print("Charlie cust state => ", charlie_cust_state)
alice_bal = json.loads(alice_cust_state)["cust_balance"]
charlie_bal = json.loads(charlie_cust_state)["cust_balance"]
self.assertTrue(alice_bal != charlie_bal)
class BoltIntermediaryTests(unittest.TestCase):
def setUp(self):
"""
Setup init alice/bob/intermediary state and establish phase of Bolt protocol
:return:
"""
self.bolt = libbolt.Libbolt('target/{}/{}bolt.{}'.format(libbolt.mode, libbolt.prefix, libbolt.ext))
self.channel_state = self.bolt.channel_setup("Test Channel")
self.b0_alice = self.b0_bob = 100
self.b0_intermediary = 100
(self.channel_token, self.merch_state, self.channel_state) = self.bolt.bidirectional_init_merchant(self.channel_state, "Hub")
(self.channel_token_a, self.alice_state) = self.bolt.bidirectional_init_customer(self.channel_token, self.b0_alice, self.b0_intermediary, "Alice")
(self.channel_token_c, self.bob_state) = self.bolt.bidirectional_init_customer(self.channel_token, self.b0_bob, self.b0_intermediary, "Bob")
def _establish_channel(self, channel_token, channel_state, cust_state, pkc, b0_cust, b0_merch):
(channel_token, cust_state, com, com_proof) = self.bolt.bidirectional_establish_customer_generate_proof(channel_token, cust_state)
close_token = self.bolt.bidirectional_establish_merchant_issue_close_token(channel_state, com, com_proof, pkc, b0_cust, b0_merch, self.merch_state)
self.assertTrue(close_token is not None)
(is_token_valid, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_verify_close_token(channel_state, cust_state, close_token)
self.assertTrue(is_token_valid)
pay_token = self.bolt.bidirectional_establish_merchant_issue_pay_token(channel_state, com, self.merch_state)
self.assertTrue(pay_token is not None)
(is_channel_established, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_final(channel_state, cust_state, pay_token)
self.assertTrue(is_channel_established)
return channel_token, channel_state, cust_state
def test_payment_with_intermediary_works(self):
"""Making a payment using an intermediary works
"""
alice_cust_state_dict = json.loads(self.alice_state)
self.channel_token_a, self.channel_state_a, alice_cust_state = self._establish_channel(self.channel_token_a, self.channel_state,
self.alice_state, alice_cust_state_dict["wallet"]["channelId"],
self.b0_alice, self.b0_intermediary)
bob_cust_state_dict = json.loads(self.bob_state)
self.channel_token_b, self.channel_state_c, bob_cust_state = self._establish_channel(self.channel_token_c, self.channel_state,
self.bob_state, bob_cust_state_dict["wallet"]["channelId"],
self.b0_bob, self.b0_intermediary)
#A prepares payment A -> I
(payment_proof_a, new_alice_cust_state) = self.bolt.bidirectional_pay_generate_payment_proof(self.channel_state, alice_cust_state, 10)
#B prepares payment I -> B
(payment_proof_b, new_bob_cust_state) = self.bolt.bidirectional_pay_generate_payment_proof(self.channel_state, bob_cust_state, -10)
#I verifies payment proofs
(new_close_token_a, cond_close_token_b, self.merch_state) = self.bolt.bidirectional_pay_verify_multiple_payment_proofs(self.channel_state, payment_proof_a, payment_proof_b, self.merch_state)
#A generates revoke token
(revoke_token_a, alice_cust_state) = self.bolt.bidirectional_pay_generate_revoke_token(self.channel_state, alice_cust_state, new_alice_cust_state, new_close_token_a)
#B generates revoke token
(revoke_token_b, bob_cust_state) = self.bolt.bidirectional_pay_generate_revoke_token(self.channel_state, bob_cust_state, new_bob_cust_state, cond_close_token_b)
#I verifies both revoke tokens
(pay_token_a, pay_token_b, self.merch_state) = self.bolt.bidirectional_pay_verify_multiple_revoke_tokens(revoke_token_a, revoke_token_b, self.merch_state)
#A verifies payment token
(alice_cust_state, is_pay_valid_a) = self.bolt.bidirectional_pay_verify_payment_token(self.channel_state, alice_cust_state, pay_token_a)
self.assertTrue(is_pay_valid_a)
#B verifies payment token
(bob_cust_state, is_pay_valid_b) = self.bolt.bidirectional_pay_verify_payment_token(self.channel_state, bob_cust_state, pay_token_b)
self.assertTrue(is_pay_valid_b)
alice_bal = json.loads(alice_cust_state)["cust_balance"]
bob_bal = json.loads(bob_cust_state)["cust_balance"]
self.assertTrue(alice_bal == 90)
self.assertTrue(bob_bal == 110)
if __name__ == '__main__':
unittest.main()

715
src/ccs08.rs Normal file
View File

@ -0,0 +1,715 @@
/*
Implementation of the ZK Range Proof scheme, based on:
Efficient Protocols for Set Membership and Range Proofs
Jan Camenisch, Rafik Chaabouni, and abhi shelat
Asiacrypt 2008
*/
extern crate pairing;
extern crate rand;
use rand::Rng;
use super::*;
use cl::{Signature, PublicParams, setup, BlindKeyPair, ProofState, SignatureProof, BlindPublicKey};
use ped92::{Commitment, CSMultiParams};
use pairing::{Engine, CurveProjective};
use ff::PrimeField;
use std::collections::HashMap;
/**
paramsUL contains elements generated by the verifier, which are necessary for the prover.
This must be computed in a trusted setup.
*/
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, <E as pairing::Engine>::G1: serde::Serialize, <E as pairing::Engine>::G2: serde::Serialize"))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, <E as pairing::Engine>::G1: serde::Deserialize<'de>, <E as pairing::Engine>::G2: serde::Deserialize<'de>"))]
pub struct ParamsUL<E: Engine> {
pub mpk: PublicParams<E>,
pub signatures: HashMap<String, Signature<E>>,
pub csParams: CSMultiParams<E>,
pk: BlindPublicKey<E>,
// u determines the amount of signatures we need in the public params.
// Each signature can be compressed to just 1 field element of 256 bits.
// Then the parameters have minimum size equal to 256*u bits.
u: i64,
// l determines how many pairings we need to compute, then in order to improve
// verifier`s performance we want to minize it.
// Namely, we have 2*l pairings for the prover and 3*l for the verifier.
l: i64,
}
/**
paramsUL contains elements generated by the verifier, which are necessary for the prover.
This must be computed in a trusted setup.
*/
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, <E as pairing::Engine>::G1: serde::Serialize, <E as pairing::Engine>::G2: serde::Serialize"))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, <E as pairing::Engine>::G1: serde::Deserialize<'de>, <E as pairing::Engine>::G2: serde::Deserialize<'de>"))]
pub struct SecretParamsUL<E: Engine> {
pub pubParams: ParamsUL<E>,
pub kp: BlindKeyPair<E>,
}
#[derive(Clone)]
pub struct ProofULState<E: Engine> {
pub decx: Vec<i64>,
pub proofStates: Vec<ProofState<E>>,
pub V: Vec<Signature<E>>,
pub D: E::G1,
pub m: E::Fr,
pub s: Vec<E::Fr>,
}
/**
proofUL contains the necessary elements for the ZK range proof with range [0,u^l).
*/
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize, \
<E as pairing::Engine>::Fqk: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>, \
<E as pairing::Engine>::Fqk: serde::Deserialize<'de>"
))]
pub struct ProofUL<E: Engine> {
pub V: Vec<Signature<E>>,
pub D: E::G1,
pub comm: Commitment<E>,
pub sigProofs: Vec<SignatureProof<E>>,
pub zr: E::Fr,
pub zs: Vec<E::Fr>,
}
#[derive(Clone)]
pub struct RangeProofState<E: Engine> {
pub com1: Commitment<E>,
pub ps1: ProofULState<E>,
pub com2: Commitment<E>,
pub ps2: ProofULState<E>,
}
/**
RangeProof contains the necessary elements for the ZK range proof.
*/
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize, \
<E as pairing::Engine>::Fqk: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>, \
<E as pairing::Engine>::Fqk: serde::Deserialize<'de>"
))]
pub struct RangeProof<E: Engine> {
pub p1: ProofUL<E>,
pub p2: ProofUL<E>,
}
/**
params contains elements generated by the verifier, which are necessary for the prover.
This must be computed in a trusted setup.
*/
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, <E as pairing::Engine>::G1: serde::Serialize, <E as pairing::Engine>::G2: serde::Serialize"))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, <E as pairing::Engine>::G1: serde::Deserialize<'de>, <E as pairing::Engine>::G2: serde::Deserialize<'de>"))]
pub struct RPPublicParams<E: Engine> {
pub p: ParamsUL<E>,
pub a: i64,
pub b: i64,
}
/**
params contains elements generated by the verifier, which are necessary for the prover.
This must be computed in a trusted setup.
*/
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, <E as pairing::Engine>::G1: serde::Serialize, <E as pairing::Engine>::G2: serde::Serialize"))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, <E as pairing::Engine>::G1: serde::Deserialize<'de>, <E as pairing::Engine>::G2: serde::Deserialize<'de>"))]
pub struct RPSecretParams<E: Engine> {
pub pubParams: RPPublicParams<E>,
pub p: SecretParamsUL<E>,
}
impl<E: Engine> SecretParamsUL<E> {
/**
setup_ul generates the signature for the interval [0,u^l).
The value of u should be roughly b/log(b), but we can choose smaller values in
order to get smaller parameters, at the cost of having worse performance.
*/
pub fn setup_ul<R: Rng>(rng: &mut R, u: i64, l: i64, csParams: CSMultiParams<E>) -> Self {
let mpk = setup(rng);
let kp = BlindKeyPair::<E>::generate(rng, &mpk, 1);
let mut signatures: HashMap<String, Signature<E>> = HashMap::new();
for i in 0..u {
let sig_i = kp.sign(rng, &vec! {E::Fr::from_str(i.to_string().as_str()).unwrap()});
signatures.insert(i.to_string(), sig_i);
}
let pubParams = ParamsUL { mpk, signatures, csParams, pk: kp.public.clone(), u, l };
SecretParamsUL{pubParams, kp}
}
/**
verify_ul is used to validate the ZKRP proof. It returns true iff the proof is valid.
*/
pub fn verify_ul(&self, proof: &ProofUL<E>, ch: E::Fr, k: usize) -> bool {
let r1 = self.verify_part1(&proof, ch.clone(), k);
let r2 = self.verify_part2(&proof, ch.clone());
r1 && r2
}
fn verify_part2(&self, proof: &ProofUL<E>, challenge: E::Fr) -> bool {
let mut r2 = true;
for i in 0..self.pubParams.l as usize {
let subResult = self.kp.public.verify_proof(&self.pubParams.mpk, proof.V[i].clone(), proof.sigProofs[i].clone(), challenge);
r2 = r2 && subResult;
}
r2
}
fn verify_part1(&self, proof: &ProofUL<E>, challenge: E::Fr, k: usize) -> bool {
let mut D = proof.comm.c.clone();
D.mul_assign(challenge);
D.negate();
let mut hzr = self.pubParams.csParams.pub_bases[0].clone();
hzr.mul_assign(proof.zr);
D.add_assign(&hzr);
for i in 0..self.pubParams.l as usize {
let ui = self.pubParams.u.pow(i as u32);
let mut aux = self.pubParams.csParams.pub_bases[k].clone();
for j in 0..self.kp.public.Y1.len() {
let mut muizsigi = proof.sigProofs[i].zsig[j];
muizsigi.mul_assign(&E::Fr::from_str(&ui.to_string()).unwrap());
aux.mul_assign(muizsigi);
}
D.add_assign(&aux);
}
for i in 1..self.pubParams.csParams.pub_bases.len() {
let j: usize;
if i < k {
j = i - 1;
} else if i > k {
j = i - 2;
} else {
continue;
}
let mut g = self.pubParams.csParams.pub_bases[i].clone();
g.mul_assign(proof.zs[j].into_repr());
D.add_assign(&g);
}
D == proof.D
}
}
impl<E: Engine> ParamsUL<E> {
/**
prove_ul method is used to produce the ZKRP proof that secret x belongs to the interval [0,U^L).
*/
pub fn prove_ul<R: Rng>(&self, rng: &mut R, x: i64, r: E::Fr, C: Commitment<E>, k: usize, otherM: Vec<E::Fr>) -> ProofUL<E> {
let proofUlState = self.prove_ul_commitment(rng, x, k, None, None);
// Fiat-Shamir heuristic
let mut a = Vec::<E::Fqk>::with_capacity(self.l as usize);
for state in proofUlState.proofStates.clone() {
a.push(state.a);
}
let c = hash::<E>(a, vec!(proofUlState.D.clone()));
self.prove_ul_response(r, C, &proofUlState, c, k, otherM)
}
pub fn prove_ul_commitment<R: Rng>(&self, rng: &mut R, x: i64, k: usize, sOptional: Option<Vec<E::Fr>>, mOptional: Option<E::Fr>) -> ProofULState<E> {
if x > ((self.u as i128).pow(self.l as u32) - 1) as i64 || x < 0 {
panic!("x is not within the range.");
}
let decx = decompose(x, self.u, self.l);
// Initialize variables
let mut proofStates = Vec::<ProofState<E>>::with_capacity(self.l as usize);
let mut V = Vec::<Signature<E>>::with_capacity(self.l as usize);
let mut s = Vec::<E::Fr>::with_capacity(self.csParams.pub_bases.len() - 2);
let mut D = E::G1::zero();
let m = mOptional.unwrap_or(E::Fr::rand(rng));
// D = H^m
let mut hm = self.csParams.pub_bases[0].clone();
hm.mul_assign(m);
for i in 0..self.l as usize {
let signature = self.signatures.get(&decx[i].to_string()).unwrap();
let proofState = self.pk.prove_commitment(rng, &self.mpk, &signature, None, None);
V.push(proofState.blindSig.clone());
proofStates.push(proofState);
let ui = self.u.pow(i as u32);
let mut aux = self.csParams.pub_bases[k].clone();
for j in 0..self.pk.Y1.len() {
let mut muiti = proofStates[i].t[j].clone();
muiti.mul_assign(&E::Fr::from_str(&ui.to_string()).unwrap());
aux.mul_assign(muiti);
}
D.add_assign(&aux);
}
let sVec = sOptional.unwrap_or(Vec::<E::Fr>::with_capacity(0));
for i in 1..self.csParams.pub_bases.len() {
if i != k {
let mut g = self.csParams.pub_bases[i].clone();
let s1: E::Fr;
if sVec.len() >= i {
s1 = sVec[i-1];
} else {
s1 = E::Fr::rand(rng);
}
s.push(s1);
g.mul_assign(s1);
D.add_assign(&g);
}
}
D.add_assign(&hm);
ProofULState { decx, proofStates, V, D, m, s }
}
pub fn prove_ul_response(&self, r: E::Fr, C: Commitment<E>, proofUlState: &ProofULState<E>, c: E::Fr, k: usize, otherM: Vec<E::Fr>) -> ProofUL<E> {
let mut sigProofs = Vec::<SignatureProof<E>>::with_capacity(self.l as usize);
let mut zr = proofUlState.m.clone();
let mut rc = r.clone();
rc.mul_assign(&c);
zr.add_assign(&rc);
for i in 0..self.l as usize {
let dx = E::Fr::from_str(&proofUlState.decx[i].to_string()).unwrap();
let proof = self.pk.prove_response(&proofUlState.proofStates[i].clone(), c, &mut vec! {dx});
sigProofs.push(proof);
}
let mut zs = Vec::<E::Fr>::with_capacity(self.csParams.pub_bases.len() - 2);
for i in 1..self.csParams.pub_bases.len() {
let j: usize;
if i < k {
j = i - 1;
} else if i > k {
j = i - 2;
} else {
continue;
}
let mut mc = otherM[j].clone();
mc.mul_assign(&c);
let mut s = proofUlState.s[j].clone();
s.add_assign(&mc);
zs.push(s);
}
ProofUL { V: proofUlState.V.clone(), D: proofUlState.D.clone(), comm: C, sigProofs, zr, zs }
}
}
fn hash<E: Engine>(a: Vec<E::Fqk>, D: Vec<E::G1>) -> E::Fr {
// create a Sha256 object
let mut a_vec: Vec<u8> = Vec::new();
for a_el in a {
a_vec.extend(format!("{}", a_el).bytes());
}
let mut x_vec: Vec<u8> = Vec::new();
for d_el in D {
x_vec.extend(format!("{}", d_el).bytes());
}
a_vec.extend(x_vec);
util::hash_to_fr::<E>(a_vec)
}
/*
Decompose receives as input an integer x and outputs an array of integers such that
x = sum(xi.u^i), i.e. it returns the decomposition of x into base u.
*/
fn decompose(x: i64, u: i64, l: i64) -> Vec<i64> {
let mut result = Vec::with_capacity(l as usize);
let mut decomposer = x.clone();
for _i in 0..l {
result.push(decomposer % u);
decomposer = decomposer / u;
}
return result;
}
impl<E: Engine> RPSecretParams<E> {
/**
Setup receives integers a and b, and configures the parameters for the rangeproof scheme.
*/
pub fn setup<R: Rng>(rng: &mut R, a: i64, b: i64, csParams: CSMultiParams<E>) -> Self {
// Compute optimal values for u and l
if a > b {
panic!("a must be less than or equal to b");
}
let logb = (b as f32).log2();
let loglogb = logb.log2();
if loglogb > 0.0 {
// let mut u = (logb / loglogb) as i64;
let u = 57; //TODO: optimize u?
let l = (b as f64).log(u as f64).ceil() as i64;
let secParamsOut = SecretParamsUL::<E>::setup_ul(rng, u, l, csParams.clone());
let pubParams = RPPublicParams { p: secParamsOut.pubParams.clone(), a, b };
RPSecretParams{pubParams, p: secParamsOut}
} else {
panic!("log(log(b)) is zero");
}
}
/**
Verify is responsible for validating the range proof.
*/
pub fn verify(&self, proof: RangeProof<E>, ch: E::Fr, k: usize) -> bool {
let first = self.p.verify_ul(&proof.p1, ch.clone(), k);
let second = self.p.verify_ul(&proof.p2, ch.clone(), k);
first & &second
}
pub fn compute_challenge(&self, proof: &RangeProof<E>) -> E::Fr {
let mut a = Vec::<E::Fqk>::with_capacity(self.p.pubParams.l as usize);
for i in 0..proof.p1.sigProofs.len() {
a.push(proof.p1.sigProofs[i].a);
a.push(proof.p2.sigProofs[i].a);
}
hash::<E>(a, vec!(proof.p1.D.clone(), proof.p2.D.clone()))
}
}
impl<E: Engine> RPPublicParams<E> {
/**
Prove method is responsible for generating the zero knowledge range proof.
*/
pub fn prove<R: Rng>(&self, rng: &mut R, x: i64, C: Commitment<E>, r: E::Fr, k: usize, otherM: Vec<E::Fr>) -> RangeProof<E> {
let rpState = self.prove_commitment(rng, x, C, k, None, None);
let mut a = Vec::<E::Fqk>::with_capacity(self.p.l as usize);
for i in 0..rpState.ps1.proofStates.len() {
a.push(rpState.ps1.proofStates[i].a);
a.push(rpState.ps2.proofStates[i].a);
}
let ch = hash::<E>(a, vec!(rpState.ps1.D.clone(), rpState.ps2.D.clone()));
self.prove_response(r, &rpState, ch, k, otherM)
}
pub fn prove_commitment<R: Rng>(&self, rng: &mut R, x: i64, C: Commitment<E>, k: usize, sOptional: Option<Vec<E::Fr>>, mOptional: Option<E::Fr>) -> RangeProofState<E> {
if x > self.b || x < self.a {
panic!("x is not within the range.");
}
let ul = self.p.u.pow(self.p.l as u32);
// x - b + ul
let xb = x - self.b + ul;
let mut gb = self.p.csParams.pub_bases[k].clone();
let mut b = E::Fr::from_str(&(self.b.to_string())).unwrap();
b.negate();
gb.mul_assign(b.into_repr());
let mut gul = self.p.csParams.pub_bases[k].clone();
gul.mul_assign(E::Fr::from_str(&(ul.to_string())).unwrap().into_repr());
let mut comXB = C.clone();
comXB.c.add_assign(&gb);
comXB.c.add_assign(&gul);
let firstState = self.p.prove_ul_commitment(rng, xb, k, sOptional.clone(), mOptional.clone());
// x - a
let xa = x - self.a;
let mut ga = self.p.csParams.pub_bases[k].clone();
let mut a = E::Fr::from_str(&(self.a.to_string())).unwrap();
a.negate();
ga.mul_assign(a.into_repr());
let mut comXA = C.clone();
comXA.c.add_assign(&ga);
let secondState = self.p.prove_ul_commitment(rng, xa, k, sOptional.clone(), mOptional.clone());
RangeProofState { com1: comXB, ps1: firstState, com2: comXA, ps2: secondState }
}
pub fn prove_response(&self, r: E::Fr, rpState: &RangeProofState<E>, ch: E::Fr, k: usize, otherM: Vec<E::Fr>) -> RangeProof<E> {
let first = self.p.prove_ul_response(r.clone(), rpState.com1.clone(), &rpState.ps1, ch.clone(), k, otherM.clone());
let second = self.p.prove_ul_response(r.clone(), rpState.com2.clone(), &rpState.ps2, ch.clone(), k, otherM.clone());
RangeProof { p1: first, p2: second }
}
}
#[cfg(test)]
mod tests {
use super::*;
use pairing::bls12_381::{Bls12, G1, Fr};
use time::PreciseTime;
use std::ops::Add;
use core::mem;
use rand::rngs::ThreadRng;
#[test]
fn setup_ul_works() {
let rng = &mut rand::thread_rng();
let csParams = CSMultiParams::setup_gen_params(rng, 1);
let secParams = SecretParamsUL::<Bls12>::setup_ul(rng, 2, 3, csParams.clone());
assert_eq!(secParams.pubParams.signatures.len(), 2);
for (m, s) in secParams.pubParams.signatures {
assert_eq!(secParams.kp.public.verify_blind(&secParams.pubParams.mpk, &vec! {Fr::from_str(m.to_string().as_str()).unwrap()}, &Fr::zero(), &s), true);
}
}
#[test]
fn prove_ul_works() {
let rng = &mut rand::thread_rng();
let csParams = CSMultiParams::setup_gen_params(rng, 1);
let secParams = SecretParamsUL::<Bls12>::setup_ul(rng, 2, 4, csParams.clone());
let fr = Fr::rand(rng);
let modx = Fr::from_str(&(10.to_string())).unwrap();
let C = csParams.commit(&vec!(modx), &fr.clone());
let proof = secParams.pubParams.prove_ul(rng, 10, fr, C, 1, vec!{});
assert_eq!(proof.V.len(), 4);
assert_eq!(proof.sigProofs.len(), 4);
}
#[test]
#[should_panic(expected = "x is not within the range")]
fn prove_ul_not_in_range() {
let rng = &mut rand::thread_rng();
let csParams = CSMultiParams::setup_gen_params(rng, 1);
let secParams = SecretParamsUL::<Bls12>::setup_ul(rng, 2, 3, csParams.clone());
let fr = Fr::rand(rng);
let modx = Fr::from_str(&(100.to_string())).unwrap();
let C = csParams.commit(&vec!(modx), &fr.clone());
secParams.pubParams.prove_ul(rng, 100, fr, C, 1, vec!{});
}
#[test]
fn prove_and_verify_part1_ul_works() {
let rng = &mut rand::thread_rng();
let csParams = CSMultiParams::setup_gen_params(rng, 1);
let secParams = SecretParamsUL::<Bls12>::setup_ul(rng, 2, 4, csParams.clone());
let fr = Fr::rand(rng);
let modx = Fr::from_str(&(10.to_string())).unwrap();
let C = csParams.commit(&vec!(modx), &fr.clone());
let proof = secParams.pubParams.prove_ul(rng, 10, fr, C, 1, vec!{});
let ch = compute_challenge(secParams.pubParams.clone(), &proof);
assert_eq!(secParams.verify_part1(&proof, ch, 1), true);
}
#[test]
fn prove_and_verify_part2_ul_works() {
let rng = &mut rand::thread_rng();
let csParams = CSMultiParams::setup_gen_params(rng, 1);
let secParams = SecretParamsUL::<Bls12>::setup_ul(rng, 2, 4, csParams.clone());
let fr = Fr::rand(rng);
let modx = Fr::from_str(&(10.to_string())).unwrap();
let C = csParams.commit(&vec!(modx), &fr.clone());
let proof = secParams.pubParams.prove_ul(rng, 10, fr, C, 1, vec!{});
let ch = compute_challenge(secParams.pubParams.clone(), &proof);
assert_eq!(secParams.verify_part2(&proof, ch), true);
}
#[test]
fn prove_and_verify_ul_works() {
let rng = &mut rand::thread_rng();
let csParams = CSMultiParams::setup_gen_params(rng, 1);
let secParams = SecretParamsUL::<Bls12>::setup_ul(rng, 2, 4, csParams.clone());
let fr = Fr::rand(rng);
let modx = Fr::from_str(&(10.to_string())).unwrap();
let C = csParams.commit(&vec!(modx), &fr.clone());
let proof = secParams.pubParams.prove_ul(rng, 10, fr, C, 1, vec!{});
let ch = compute_challenge(secParams.pubParams.clone(), &proof);
assert_eq!(secParams.verify_ul(&proof, ch, 1), true);
}
#[test]
fn prove_and_verify_ul_bigger_commit_works() {
let rng = &mut rand::thread_rng();
let csParams = CSMultiParams::setup_gen_params(rng, 3);
let secParams = SecretParamsUL::<Bls12>::setup_ul(rng, 2, 4, csParams.clone());
let fr = Fr::rand(rng);
let modx = Fr::from_str(&(10.to_string())).unwrap();
let fr1 = Fr::rand(rng);
let fr2 = Fr::rand(rng);
let C = csParams.commit(&vec!(fr1, modx, fr2), &fr.clone());
let proof = secParams.pubParams.prove_ul(rng, 10, fr, C, 2, vec!{fr1, fr2});
let ch = compute_challenge(secParams.pubParams.clone(), &proof);
assert_eq!(secParams.verify_ul(&proof, ch, 2), true);
}
#[test]
fn prove_and_verify_works() {
let rng = &mut rand::thread_rng();
let csParams = CSMultiParams::setup_gen_params(rng, 1);
let secParams = RPSecretParams::<Bls12>::setup(rng, 2, 25, csParams.clone());
let fr = Fr::rand(rng);
let modx = Fr::from_str(&(10.to_string())).unwrap();
let C = csParams.commit(&vec!(modx), &fr.clone());
let proof = secParams.pubParams.prove(rng, 10, C, fr, 1, vec!{});
let ch = secParams.compute_challenge(&proof);
assert_eq!(secParams.verify(proof, ch, 1), true);
}
#[test]
fn prove_and_verify_bigger_commit_works() {
let rng = &mut rand::thread_rng();
let csParams = CSMultiParams::setup_gen_params(rng, 3);
let secParams = RPSecretParams::<Bls12>::setup(rng, 2, 25, csParams.clone());
let fr = Fr::rand(rng);
let modx = Fr::from_str(&(10.to_string())).unwrap();
let fr1 = Fr::rand(rng);
let fr2 = Fr::rand(rng);
let C = csParams.commit(&vec!(fr1, modx, fr2), &fr.clone());
let proof = secParams.pubParams.prove(rng, 10, C, fr, 2, vec!{fr1, fr2});
let ch = secParams.compute_challenge(&proof);
assert_eq!(secParams.verify(proof, ch, 2), true);
}
#[test]
#[should_panic(expected = "x is not within the range")]
fn prove_not_in_range() {
let rng = &mut rand::thread_rng();
let csParams = CSMultiParams::setup_gen_params(rng, 1);
let secParams = RPSecretParams::<Bls12>::setup(rng, 2, 25, csParams.clone());
let fr = Fr::rand(rng);
let modx = Fr::from_str(&(26.to_string())).unwrap();
let C = csParams.commit(&vec!(modx), &fr.clone());
secParams.pubParams.prove(rng, 26, C, fr, 1, vec!{});
}
#[test]
#[ignore]
fn prove_and_verify_performance() {
let rng = &mut rand::thread_rng();
let mut averageSetup = time::Duration::nanoseconds(0);
let mut averageSetupSize = 0;
let mut averageProve = time::Duration::nanoseconds(0);
let mut averageProofSize = 0;
let mut averageVerify = time::Duration::nanoseconds(0);
let iter = 5;
for _i in 0..iter {
let a = rng.gen_range(0, 1000000);
let b = rng.gen_range(a, 1000000);
let x = rng.gen_range(a, b);
let sSetup = PreciseTime::now();
let csParams = CSMultiParams::setup_gen_params(rng, 1);
let secParams = RPSecretParams::<Bls12>::setup(rng, a, b, csParams.clone());
averageSetup = averageSetup.add(sSetup.to(PreciseTime::now()));
averageSetupSize += mem::size_of_val(&secParams.pubParams);
let sProve = PreciseTime::now();
let fr = Fr::rand(rng);
let modx = Fr::from_str(&(x.to_string())).unwrap();
let C = csParams.commit(&vec!(modx), &fr.clone());
let proof = secParams.pubParams.prove(rng, x, C, fr, 1, vec!{});
averageProve = averageProve.add(sProve.to(PreciseTime::now()));
averageProofSize += mem::size_of_val(&proof);
let sVerify = PreciseTime::now();
let ch = secParams.compute_challenge(&proof);
secParams.verify(proof, ch, 1);
averageVerify = averageVerify.add(sVerify.to(PreciseTime::now()));
}
print!("Setup: {}\n", averageSetup.num_milliseconds() / iter);
print!("Setup size: {}\n", averageSetupSize / iter as usize);
print!("Prove: {}\n", averageProve.num_milliseconds() / iter);
print!("Proof size: {}\n", averageProofSize / iter as usize);
print!("Verify: {}\n", averageVerify.num_milliseconds() / iter);
}
#[test]
fn decompose_works() {
assert_eq!(decompose(25, 3, 3), vec! {1, 2, 2});
assert_eq!(decompose(336, 7, 3), vec! {0, 6, 6});
assert_eq!(decompose(285, 8, 3), vec! {5, 3, 4});
assert_eq!(decompose(125, 13, 2), vec! {8, 9});
assert_eq!(decompose(143225, 6, 7), vec! {5, 2, 0, 3, 2, 0, 3});
}
#[test]
fn decompose_recompose_works() {
let vec1 = decompose(25, 3, 5);
let mut result = 0;
for i in 0..5 {
result += vec1[i] * 3i64.pow(i as u32);
}
assert_eq!(result, 25);
let vec1 = decompose(143225, 6, 7);
let mut result = 0;
for i in 0..7 {
result += vec1[i] * 6i64.pow(i as u32);
}
assert_eq!(result, 143225);
}
#[test]
fn setup_works() {
let rng = &mut rand::thread_rng();
let csParams = CSMultiParams::setup_gen_params(rng, 1);
let secParams = RPSecretParams::<Bls12>::setup(rng, 2, 10, csParams);
let public_params = secParams.pubParams.clone();
assert_eq!(public_params.a, 2);
assert_eq!(public_params.b, 10);
assert_eq!(public_params.p.signatures.len(), 57);
assert_eq!(public_params.p.u, 57);
assert_eq!(public_params.p.l, 1);
for (m, s) in public_params.p.signatures {
assert_eq!(secParams.p.kp.public.verify_blind(&public_params.p.mpk, &vec! {Fr::from_str(m.to_string().as_str()).unwrap()}, &Fr::zero(), &s), true);
}
}
#[test]
#[should_panic(expected = "a must be less than or equal to b")]
fn setup_wrong_a_and_b() {
let rng = &mut rand::thread_rng();
let csParams = CSMultiParams::setup_gen_params(rng, 1);
RPSecretParams::<Bls12>::setup(rng, 10, 2, csParams);
}
#[test]
#[should_panic(expected = "log(log(b)) is zero")]
fn setup_wrong_logb() {
let rng = &mut rand::thread_rng();
let csParams = CSMultiParams::setup_gen_params(rng, 1);
RPSecretParams::<Bls12>::setup(rng, -2, -1, csParams);
}
#[test]
fn hash_works() {
let rng = &mut rand::thread_rng();
let D = G1::rand(rng);
let D2 = G1::rand(rng);
let params = setup::<ThreadRng, Bls12>(rng);
let kp = BlindKeyPair::generate(rng, &params, 2);
let m1 = Fr::rand(rng);
let m2 = Fr::rand(rng);
let sig = kp.sign(rng, &vec! {m1, m2});
let state = kp.public.prove_commitment(rng, &params, &sig, None, None);
let state1 = kp.public.prove_commitment(rng, &params, &sig, None, None);
let state2 = kp.public.prove_commitment(rng, &params, &sig, None, None);
let state3 = kp.public.prove_commitment(rng, &params, &sig, None, None);
let state4 = kp.public.prove_commitment(rng, &params, &sig, None, None);
let a = vec! {state.a, state1.a, state2.a};
let a2 = vec! {state3.a, state4.a};
assert_eq!(hash::<Bls12>(a.clone(), vec!(D.clone())).is_zero(), false);
assert_ne!(hash::<Bls12>(a2.clone(), vec!(D.clone())), hash::<Bls12>(a.clone(), vec!(D.clone())));
assert_ne!(hash::<Bls12>(a.clone(), vec!(D2.clone())), hash::<Bls12>(a.clone(), vec!(D.clone())));
assert_ne!(hash::<Bls12>(a2.clone(), vec!(D2.clone())), hash::<Bls12>(a.clone(), vec!(D.clone())))
}
fn compute_challenge<E: Engine>(pubParams: ParamsUL<E>, proof: &ProofUL<E>) -> E::Fr {
let mut a = Vec::<E::Fqk>::with_capacity(pubParams.l as usize);
for sigProof in proof.sigProofs.clone() {
a.push(sigProof.a);
}
hash::<E>(a, vec!(proof.D.clone()))
}
}

692
src/channels.rs Normal file
View File

@ -0,0 +1,692 @@
/*
* Implement for Bolt protocols:
* - initializing channel state and generating cust/merch wallets
* - establish protocol
* - pay protocol
* - channel close algorithms (WIP)
*/
extern crate pairing;
extern crate rand;
use super::*;
use pairing::Engine;
use cl::{BlindKeyPair, Signature};
use ped92::{Commitment, CSMultiParams, CommitmentProof};
use util::{hash_pubkey_to_fr, hash_to_fr, RevokedMessage, hash_to_slice};
use rand::Rng;
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use nizk::{NIZKPublicParams, NIZKSecretParams, NIZKProof};
use wallet::Wallet;
use std::error::Error;
use std::fmt;
#[derive(Debug)]
pub struct BoltError {
details: String
}
pub type ResultBoltType<E> = Result<E, BoltError>;
impl BoltError {
fn new(msg: &str) -> BoltError {
BoltError { details: msg.to_string() }
}
}
impl fmt::Display for BoltError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.details)
}
}
impl Error for BoltError {
fn description(&self) -> &str {
&self.details
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct PubKeyMap {
pub wpk: secp256k1::PublicKey,
pub revoke_token: Option<secp256k1::Signature>,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>"
))]
pub struct ChannelParams<E: Engine> {
pub pub_params: NIZKPublicParams<E>,
l: usize,
// messages for commitment
extra_verify: bool, // extra verification for certain points in the establish/pay protocol
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>"
))]
pub struct ChannelState<E: Engine> {
R: i32,
tx_fee: i64,
pub cp: Option<ChannelParams<E>>,
pub name: String,
pub pay_init: bool,
pub channel_established: bool,
pub third_party: bool,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>"
))]
pub struct ChannelToken<E: Engine> {
pub pk_c: Option<secp256k1::PublicKey>,
// pk_c
pub pk_m: secp256k1::PublicKey,
// pk_m
pub cl_pk_m: cl::PublicKey<E>,
// PK_m (used for verifying blind signatures)
pub mpk: cl::PublicParams<E>,
// mpk for PK_m
pub comParams: CSMultiParams<E>,
}
impl<E: Engine> ChannelToken<E> {
pub fn set_customer_pk(&mut self, pk_c: &secp256k1::PublicKey) {
self.pk_c = Some(pk_c.clone());
}
pub fn is_init(&self) -> bool {
return !self.pk_c.is_none();
}
pub fn compute_channel_id(&self) -> E::Fr
where <E as pairing::Engine>::G1: serde::Serialize,
<E as pairing::Engine>::G2: serde::Serialize,
<E as ff::ScalarEngine>::Fr: serde::Serialize
{
if self.pk_c.is_none() {
panic!("pk_c is not initialized yet");
}
let input = serde_json::to_vec(&self).unwrap();
return hash_to_fr::<E>(input);
}
// add a method to compute hash on chain: SHA256 + RIPEMD160?
}
// add methods to check if channel token is initialized
// (only if
///
/// Channel state for generating/loading channel parameters and generating keypairs
///
impl<E: Engine> ChannelState<E> {
pub fn new(name: String, third_party_support: bool) -> ChannelState<E> {
ChannelState {
R: 0,
tx_fee: 0,
cp: None,
name: name.to_string(),
pay_init: false,
channel_established: false,
third_party: third_party_support,
}
}
///
/// keygen - takes as input public parameters and generates a digital signature keypair
///
pub fn keygen<R: Rng>(&mut self, csprng: &mut R, _id: String) -> cl::BlindKeyPair<E> {
let cp = self.cp.as_ref();
let keypair = BlindKeyPair::<E>::generate(csprng, &cp.unwrap().pub_params.mpk, cp.unwrap().l);
// print the keypair as well
return keypair;
}
pub fn load_params(&mut self, _cp: &ChannelParams<E>) {
// load external params
}
pub fn set_channel_fee(&mut self, fee: i64) {
self.tx_fee = fee;
}
pub fn get_channel_fee(&self) -> i64 {
return self.tx_fee as i64;
}
}
#[derive(Copy, Clone, Serialize, Deserialize)]
struct WalletKeyPair {
pub wpk: secp256k1::PublicKey,
pub wsk: secp256k1::SecretKey,
}
///
/// Customer state
///
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>"
))]
pub struct CustomerState<E: Engine> {
pub name: String,
pub pk_c: secp256k1::PublicKey,
sk_c: secp256k1::SecretKey,
pub cust_balance: i64,
//
pub merch_balance: i64,
pub wpk: secp256k1::PublicKey,
// keypair bound to the wallet
wsk: secp256k1::SecretKey,
old_kp: Option<WalletKeyPair>,
// old wallet key pair
t: E::Fr,
// randomness used to form the commitment
wallet: Wallet<E>,
// vector of field elements that represent wallet
pub w_com: Commitment<E>,
// commitment to the current state of the wallet
index: i32,
close_tokens: HashMap<i32, Signature<E>>,
pay_tokens: HashMap<i32, Signature<E>>,
}
impl<E: Engine> CustomerState<E> {
pub fn new<R: Rng>(csprng: &mut R, channel_token: &mut ChannelToken<E>, cust_bal: i64, merch_bal: i64, name: String) -> Self
where <E as pairing::Engine>::G1: serde::Serialize,
<E as pairing::Engine>::G2: serde::Serialize,
<E as ff::ScalarEngine>::Fr: serde::Serialize
{
let mut kp = secp256k1::Secp256k1::new();
kp.randomize(csprng);
// generate the keypair for the channel
let (sk_c, pk_c) = kp.generate_keypair(csprng);
// generate the keypair for the initial wallet
let (wsk, wpk) = kp.generate_keypair(csprng);
// hash the wallet pub key
let wpk_h = hash_pubkey_to_fr::<E>(&wpk);
channel_token.set_customer_pk(&pk_c);
// compute the channel ID
let channelId = channel_token.compute_channel_id();
// randomness for commitment
let t = E::Fr::rand(csprng);
// initialize wallet vector
let wallet = Wallet { channelId: channelId, wpk: wpk_h, bc: cust_bal, bm: merch_bal, close: None };
let w_com = channel_token.comParams.commit(&wallet.as_fr_vec(), &t);
assert!(channel_token.is_init());
let ct_db = HashMap::new();
let pt_db = HashMap::new();
return CustomerState {
name: name,
pk_c: pk_c,
sk_c: sk_c,
cust_balance: cust_bal,
merch_balance: merch_bal,
wpk: wpk,
wsk: wsk,
old_kp: None,
t: t,
w_com: w_com,
wallet: wallet,
index: 0,
close_tokens: ct_db,
pay_tokens: pt_db,
};
}
pub fn get_wallet(&self) -> Wallet<E> {
return self.wallet.clone();
}
pub fn get_public_key(&self) -> E::Fr {
// hash the channel pub key
let pk_h = hash_pubkey_to_fr::<E>(&self.pk_c);
return pk_h;
}
pub fn get_close_token(&self) -> cl::Signature<E> {
let index = self.index;
let close_token = self.close_tokens.get(&index).unwrap();
// rerandomize first
return close_token.clone();
}
// generate nizk proof of knowledge of commitment opening
pub fn generate_proof<R: Rng>(&self, csprng: &mut R, channel_token: &ChannelToken<E>) -> CommitmentProof<E> {
// generate proof and do a partial reveal of channelId and bc/bm (init balances)
return CommitmentProof::<E>::new(csprng, &channel_token.comParams, &self.w_com.c, &self.wallet.as_fr_vec(), &self.t, &vec![1, 3, 4]);
}
pub fn verify_close_token(&mut self, channel: &ChannelState<E>, close_token: &Signature<E>) -> bool {
// add a prefix to the wallet for close-message
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.pk.verify_blind(&mpk, &close_wallet, &self.t, &close_token);
if is_close_valid {
//println!("verify_close_token - Blinded close token is valid!!");
let unblind_close_token = cp.pub_params.pk.unblind(&self.t, &close_token);
let pk = cp.pub_params.pk.get_pub_key();
let is_valid = pk.verify(&mpk, &close_wallet, &unblind_close_token);
if is_valid {
// record the unblinded close token
self.close_tokens.insert(self.index, unblind_close_token);
}
return is_valid;
}
//println!("Customer - Verification failed for close token!");
return is_close_valid;
}
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();
// 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.pk.verify_blind(&mpk, &wallet, &self.t, &pay_token);
if is_pay_valid {
//println!("verify_pay_token - Blinded pay token is valid!!");
let unblind_pay_token = cp.pub_params.pk.unblind(&self.t, &pay_token);
let pk = cp.pub_params.pk.get_pub_key();
let is_valid = pk.verify(&mpk, &wallet, &unblind_pay_token);
if is_valid {
self.pay_tokens.insert(self.index, unblind_pay_token);
}
return is_valid;
}
//println!("Customer - Verification failed for pay token!");
return is_pay_valid;
}
pub fn has_tokens(&self) -> bool {
let index = self.index;
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>(&self, csprng: &mut R, channel: &ChannelState<E>, amount: i64) -> (NIZKProof<E>, Commitment<E>, secp256k1::PublicKey, CustomerState<E>) {
// 1 - chooose new wpk/wsk pair
let mut kp = secp256k1::Secp256k1::new();
kp.randomize(csprng);
let (new_wsk, new_wpk) = kp.generate_keypair(csprng);
let wpk_h = hash_pubkey_to_fr::<E>(&new_wpk);
// 2 - form new wallet and commitment
let new_cust_bal = self.cust_balance - amount;
let new_merch_bal = self.merch_balance + amount;
let new_t = E::Fr::rand(csprng);
let cp = channel.cp.as_ref().unwrap();
let old_wallet = Wallet { channelId: self.wallet.channelId.clone(), wpk: self.wallet.wpk.clone(), bc: self.cust_balance, bm: self.merch_balance, close: None };
let new_wallet = Wallet { channelId: self.wallet.channelId.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_t);
// 3 - generate new blinded and randomized pay token
let i = self.index;
let prev_pay_token = self.pay_tokens.get(&i).unwrap();
//println!("Found prev pay token: {}", prev_pay_token);
let pay_proof = cp.pub_params.prove(csprng, old_wallet, new_wallet.clone(),
new_wcom.clone(), new_t, &prev_pay_token);
// update internal state after proof has been verified by remote
let new_cw = CustomerState {
name: self.name.clone(),
pk_c: self.pk_c.clone(),
sk_c: self.sk_c.clone(),
cust_balance: new_cust_bal,
merch_balance: new_merch_bal,
wpk: new_wpk,
wsk: new_wsk,
old_kp: Some(WalletKeyPair { wpk: self.wpk.clone(), wsk: self.wsk.clone() }),
t: new_t,
w_com: new_wcom.clone(),
wallet: new_wallet.clone(),
index: self.index, // increment index here
close_tokens: self.close_tokens.clone(),
pay_tokens: self.pay_tokens.clone(),
};
return (pay_proof, new_wcom, self.wpk, new_cw);
}
// update the internal state of the customer wallet
pub fn update(&mut self, new_wallet: CustomerState<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.t = new_wallet.t;
self.old_kp = new_wallet.old_kp;
self.wpk = new_wallet.wpk;
self.wsk = new_wallet.wsk;
self.w_com = new_wallet.w_com;
self.wallet = new_wallet.wallet;
self.index = new_wallet.index;
self.close_tokens = new_wallet.close_tokens;
self.pay_tokens = new_wallet.pay_tokens;
return true;
}
pub fn generate_revoke_token(&mut self, channel: &ChannelState<E>, close_token: &Signature<E>) -> ResultBoltType<(RevokedMessage, secp256k1::Signature)> {
if self.verify_close_token(channel, close_token) {
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);
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);
return Ok((rm, revoke_token));
}
Err(BoltError::new("generate_revoke_token - could not verify the close token."))
}
}
impl<E: Engine> fmt::Display for CustomerState<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut content = format!("id = {}\n", &self.name);
content = format!("{}pk = {}\n", content, &self.pk_c);
content = format!("{}sk = {}\n", content, &self.sk_c);
content = format!("{}cust-bal = {}\n", content, &self.cust_balance);
content = format!("{}merch-bal = {}\n", content, &self.merch_balance);
content = format!("{}wpk = {}\nwsk = {}\n", content, &self.wpk, &self.wsk);
if (!self.old_kp.is_none()) {
let old_kp = self.old_kp.unwrap();
content = format!("{}revoked: wpk = {}\nrevoked: wsk = {}\n", content, &old_kp.wpk, &old_kp.wsk);
}
content = format!("{}t = {}\n", content, &self.t);
content = format!("{}wallet = {}\n", content, &self.wallet);
content = format!("{}w_com = {}\n", content, &self.w_com);
let close_token = self.close_tokens.get(&self.index);
let pay_token = self.pay_tokens.get(&self.index);
if (!close_token.is_none()) {
content = format!("{}close_token = {}\n", content, &self.close_tokens.get(&self.index).unwrap());
}
if (!pay_token.is_none()) {
content = format!("{}pay_token = {}\n", content, &self.pay_tokens.get(&self.index).unwrap());
}
write!(f, "CustomerState : (\n{}\n)", &content)
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct ChannelcloseM {
pub address: String,
pub revoke: Option<secp256k1::Signature>,
pub signature: secp256k1::Signature,
}
///
/// Merchant State
///
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>"
))]
pub struct MerchantState<E: Engine> {
id: String,
keypair: cl::BlindKeyPair<E>,
nizkParams: NIZKSecretParams<E>,
pk: secp256k1::PublicKey,
// pk_m
sk: secp256k1::SecretKey,
// sk_m
comParams: CSMultiParams<E>,
pub keys: HashMap<String, PubKeyMap>,
pub pay_tokens: HashMap<String, cl::Signature<E>>,
}
impl<E: Engine> MerchantState<E> {
pub fn new<R: Rng>(csprng: &mut R, channel: &mut ChannelState<E>, id: String) -> (Self, ChannelState<E>) {
let l = 5;
// generate keys here
let mut tx_kp = secp256k1::Secp256k1::new();
tx_kp.randomize(csprng);
let (wsk, wpk) = tx_kp.generate_keypair(csprng);
let mut ch = channel.clone();
let nizkParams = NIZKSecretParams::<E>::setup(csprng, l);
ch.cp = Some(ChannelParams::<E> { pub_params: nizkParams.pubParams.clone(), l, extra_verify: true });
(MerchantState {
id: id.clone(),
keypair: nizkParams.keypair.clone(),
nizkParams: nizkParams.clone(),
pk: wpk,
sk: wsk,
comParams: nizkParams.pubParams.comParams.clone(),
keys: HashMap::new(), // store wpks/revoke_tokens
pay_tokens: HashMap::new(),
}, ch)
}
pub fn init(&mut self, channel: &mut ChannelState<E>) -> ChannelToken<E> {
let cp = channel.cp.as_ref().unwrap(); // if not set, then panic!
let mpk = cp.pub_params.mpk.clone();
let cl_pk = self.keypair.get_public_key(&mpk);
return ChannelToken {
pk_c: None,
cl_pk_m: cl_pk.clone(), // extract the regular public key
pk_m: self.pk.clone(),
mpk: mpk,
comParams: self.comParams.clone(),
};
}
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_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>, remove_close: bool) -> Signature<E> {
//println!("issue_pay_token => generating token");
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>, channelId: &E::Fr, cust_balance: i64, merch_balance: i64) -> ResultBoltType<(Signature<E>, Signature<E>)> {
let is_valid = nizk::verify_opening(&self.comParams, &com.c, &com_proof, &channelId, cust_balance, merch_balance);
let cp = channel.cp.as_ref().unwrap();
if is_valid {
let close_token = self.issue_close_token(csprng, cp, com, true);
let pay_token = self.issue_pay_token(csprng, cp, com, false);
return Ok((close_token, pay_token));
}
Err(BoltError::new("verify_proof - Failed to verify PoK of commitment opening"))
}
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: &NIZKProof<E>, com: &Commitment<E>, wpk: &secp256k1::PublicKey, amount: i64) -> ResultBoltType<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 = util::convert_int_to_fr::<E>(amount);
if self.nizkParams.verify(pay_proof, epsilon, com, prev_wpk) {
// 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, true);
// let's store the pay token with the wpk for now
self.store_wpk_with_token(wpk, pay_token);
return Ok(close_token);
}
Err(BoltError::new("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) -> ResultBoltType<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
if secp.verify(&msg, revoke_token, wpk).is_ok() {
return Ok(self.get_pay_token(wpk));
}
Err(BoltError::new("verify_revoke_token - Failed to verify the revoke token for wpk!"))
}
pub fn sign_revoke_message(&self, address: String, revoke_token: &Option<secp256k1::Signature>) -> ChannelcloseM {
let secp = secp256k1::Secp256k1::signing_only();
let mut msg = Vec::new();
msg.extend(address.as_bytes());
if !revoke_token.is_none() {
let r = revoke_token.unwrap().serialize_der().to_vec();
msg.extend(r);
}
let msg2 = secp256k1::Message::from_slice(&hash_to_slice(&msg)).unwrap();
let merch_sig = secp.sign(&msg2, &self.sk);
return ChannelcloseM { address: address.clone(), revoke: revoke_token.clone(), signature: merch_sig };
}
}
#[cfg(test)]
mod tests {
use super::*;
use pairing::bls12_381::Bls12;
#[test]
fn channel_util_works() {
let mut channel = ChannelState::<Bls12>::new(String::from("Channel A <-> B"), false);
let rng = &mut rand::thread_rng();
let b0_cust = 100;
let b0_merch = 20;
// each party executes the init algorithm on the agreed initial challenge balance
// in order to derive the channel tokens
// initialize on the merchant side with balance: b0_merch
let (mut merch_state, mut channel) = MerchantState::<Bls12>::new(rng, &mut channel, String::from("Merchant B"));
// initialize the merchant wallet with the balance
let mut channel_token = merch_state.init(&mut channel);
// retrieve commitment setup params (using merchant long lived pk params)
// initialize on the customer side with balance: b0_cust
let mut cust_state = CustomerState::<Bls12>::new(rng, &mut channel_token, b0_cust, b0_merch, String::from("Alice"));
// lets establish the channel
let cust_com_proof = cust_state.generate_proof(rng, &mut channel_token);
// first return the close token, then wait for escrow-tx confirmation
// then send the pay-token after confirmation
let channelId = channel_token.compute_channel_id();
assert_eq!(channelId, cust_state.get_wallet().channelId);
let (close_token, pay_token) = merch_state.verify_proof(rng, &channel, &cust_state.w_com, &cust_com_proof, &channelId, b0_cust, b0_merch).unwrap();
// unblind tokens and verify signatures
assert!(cust_state.verify_close_token(&channel, &close_token));
assert!(cust_state.verify_pay_token(&channel, &pay_token));
println!("Done!");
// pay protocol tests
let amount = 10;
let (pay_proof, new_com, old_wpk, new_cw) = cust_state.generate_payment(rng, &channel, amount);
// new pay_token is not sent until revoke_token is obtained from the customer
let new_close_token = merch_state.verify_payment(rng, &channel, &pay_proof, &new_com, &old_wpk, amount).unwrap();
//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_state
assert!(cust_state.update(new_cw));
//println!("2 - updated customer wallet!");
assert!(cust_state.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_sig) = cust_state.generate_revoke_token(&channel, &new_close_token).unwrap();
//println!("4 - Generated revoke token successfully.");
//println!("5 - Revoke token => {}", revoke_token);
let new_pay_token = merch_state.verify_revoke_token(&revoke_sig, &revoke_msg, &old_wpk).unwrap();
assert!(cust_state.verify_pay_token(&channel, &new_pay_token));
//println!("Validated revoke token!");
}
#[test]
#[should_panic(expected = "pk_c is not initialized yet")]
fn compute_channel_id_panics() {
let mut channel = ChannelState::<Bls12>::new(String::from("Channel A <-> B"), false);
let rng = &mut rand::thread_rng();
// initialize on the merchant side with balance: b0_merch
let (mut merch_state, mut channel) = MerchantState::<Bls12>::new(rng, &mut channel, String::from("Merchant B"));
// initialize the merchant wallet with the balance
let channel_token = merch_state.init(&mut channel);
let _channelId = channel_token.compute_channel_id();
}
}

723
src/cl.rs Normal file
View File

@ -0,0 +1,723 @@
// cl.rs
// CL Sigs - Pointcheval Sanders ('06)
extern crate pairing;
extern crate rand;
use super::*;
use pairing::{CurveProjective, Engine};
use ff::PrimeField;
use rand::Rng;
use ped92::{Commitment, CSMultiParams};
use serde::{Serialize, Deserialize};
use util;
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct PublicParams<E: Engine> {
pub g1: E::G1,
pub g2: E::G2,
}
impl<E: Engine> PartialEq for PublicParams<E> {
fn eq(&self, other: &PublicParams<E>) -> bool {
self.g1 == other.g1 && self.g2 == other.g2
}
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct SecretKey<E: Engine> {
pub x: E::Fr,
pub y: Vec<E::Fr>,
}
impl<E: Engine> fmt::Display for SecretKey<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut y_str = String::new();
let mut i = 0;
for y in self.y.iter() {
y_str = format!("{}\n{} => {}", y_str, i, y);
i += 1;
}
write!(f, "SK : \nx={},\ny=[{}\n]", self.x, y_str)
}
}
impl<E: Engine> PartialEq for SecretKey<E> {
fn eq(&self, other: &SecretKey<E>) -> bool {
self.x == other.x && util::is_vec_fr_equal::<E>(&self.y, &other.y)
}
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct PublicKey<E: Engine> {
pub X: E::G2,
pub Y: Vec<E::G2>,
}
impl<E: Engine> fmt::Display for PublicKey<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut y_s = String::new();
let mut i = 0;
for y in self.Y.iter() {
y_s = format!("{}\n{} => {}", y_s, i, y);
i += 1;
}
write!(f, "PK : \nX={},\nY=[{}\n]", self.X, y_s)
}
}
impl<E: Engine> PartialEq for PublicKey<E> {
fn eq(&self, other: &PublicKey<E>) -> bool {
self.X == other.X && util::is_vec_g2_equal::<E>(&self.Y, &other.Y)
}
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct BlindPublicKey<E: Engine> {
pub X1: E::G1,
pub X2: E::G2,
pub Y1: Vec<E::G1>,
pub Y2: Vec<E::G2>,
}
impl<E: Engine> fmt::Display for BlindPublicKey<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut y1_str = String::new();
for y in self.Y1.iter() {
y1_str = format!("{}\n{}", y1_str, y);
}
let mut y2_str = String::new();
for y in self.Y2.iter() {
y2_str = format!("{}\n{}", y2_str, y);
}
write!(f, "Blind PK : \nX1={},\nX2{},\nY1=[{}\n],\nY2=[{}\n]", self.X1, self.X2, y1_str, y2_str)
}
}
impl<E: Engine> PartialEq for BlindPublicKey<E> {
fn eq(&self, other: &BlindPublicKey<E>) -> bool {
self.X1 == other.X1 && self.X2 == other.X2 &&
util::is_vec_g1_equal::<E>(&self.Y1, &other.Y1) &&
util::is_vec_g2_equal::<E>(&self.Y2, &other.Y2)
}
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Signature<E: Engine> {
pub h: E::G1,
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>,
pub public: PublicKey<E>,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>"
))]
pub struct BlindKeyPair<E: Engine> {
pub secret: SecretKey<E>,
pub public: BlindPublicKey<E>,
}
#[derive(Clone)]
pub struct ProofState<E: Engine> {
pub v: E::Fr,
pub t: Vec<E::Fr>,
pub tt: E::Fr,
pub a: E::Fqk,
pub blindSig: Signature<E>,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize, \
<E as pairing::Engine>::Fqk: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>, \
<E as pairing::Engine>::Fqk: serde::Deserialize<'de>"
))]
pub struct SignatureProof<E: Engine> {
pub zsig: Vec<E::Fr>,
pub zv: E::Fr,
pub a: E::Fqk,
}
impl<E: Engine> SecretKey<E> {
pub fn generate<R: Rng>(csprng: &mut R, l: usize) -> Self {
let mut y: Vec<E::Fr> = Vec::new();
for _i in 0..l {
let _y = E::Fr::rand(csprng);
y.push(_y);
}
SecretKey { x: E::Fr::rand(csprng), y: y }
}
pub fn sign<R: Rng>(&self, csprng: &mut R, message: &Vec<E::Fr>) -> Signature<E> {
let h = E::G1::rand(csprng);
let mut s = E::Fr::zero();
// check vector length first
assert_eq!(self.y.len(), message.len());
for i in 0..message.len() {
// s = s + (self.y[i] * message[i]);
let mut res_yi = self.y[i];
res_yi.mul_assign(&message[i]);
s.add_assign(&res_yi);
}
// h ^ (x + s)
let mut res_x = self.x;
res_x.add_assign(&s);
let mut H = h;
H.mul_assign(res_x);
Signature { h: h, H: H }
}
}
///
/// Interface for CL PS signature variant
///
impl<E: Engine> PublicKey<E> {
pub fn from_secret(mpk: &PublicParams<E>, secret: &SecretKey<E>) -> Self {
let mut Y: Vec<E::G2> = Vec::new();
for i in 0..secret.y.len() {
// Y[i] = g2 ^ y[i]
let mut g2 = mpk.g2;
g2.mul_assign(secret.y[i]);
Y.push(g2);
}
// X = g2 ^ x
let mut X = mpk.g2;
X.mul_assign(secret.x);
PublicKey {
X: X,
Y: Y,
}
}
pub fn verify(&self, mpk: &PublicParams<E>, message: &Vec<E::Fr>, signature: &Signature<E>) -> bool {
let mut L = E::G2::zero();
let mut l = self.Y.len();
let diff = (l - message.len());
l = match diff > 0 {
true => message.len(),
false => l
};
for i in 0..l {
if (i < message.len()) { // bounds check on message vector
// L = L + self.Y[i].mul(message[i]);
let mut Y = self.Y[i];
Y.mul_assign(message[i]); // Y_i ^ m_i
L.add_assign(&Y); // L += Y_i ^m_i
}
}
let mut X2 = self.X;
X2.add_assign(&L); // X2 = X + L
let lhs = E::pairing(signature.h, X2);
let rhs = E::pairing(signature.H, mpk.g2);
signature.h != E::G1::one() && lhs == rhs
}
}
///
/// Interface for blind sigs based on CL PS variant
///
impl<E: Engine> BlindPublicKey<E> {
pub fn from_secret(mpk: &PublicParams<E>, secret: &SecretKey<E>) -> Self {
let mut Y1: Vec<E::G1> = Vec::new();
let mut Y2: Vec<E::G2> = Vec::new();
for i in 0..secret.y.len() {
// Y[i] = g2 ^ y[i]
let mut g1y = mpk.g1;
let mut g2y = mpk.g2;
g1y.mul_assign(secret.y[i]);
g2y.mul_assign(secret.y[i]);
Y1.push(g1y);
Y2.push(g2y);
}
// X1 = g1 ^ x
let mut X1 = mpk.g1;
X1.mul_assign(secret.x);
// X2 = g2 ^ x
let mut X2 = mpk.g2;
X2.mul_assign(secret.x);
BlindPublicKey {
X1: X1,
X2: X2,
Y1: Y1,
Y2: Y2,
}
}
pub fn get_pub_key(&self) -> PublicKey<E> {
PublicKey { X: self.X2.clone(), Y: self.Y2.clone() }
}
pub fn verify(&self, mpk: &PublicParams<E>, message: &Vec<E::Fr>, signature: &Signature<E>) -> bool {
let mut L = E::G2::zero();
let l = self.Y2.len();
//println!("verify - m.len = {}, l = {}", message.len(), l);
assert!(message.len() <= l + 1);
let last_elem = match l == message.len() {
true => message.len() - 1,
false => l
};
let l = match l == message.len() {
true => message.len() - 1,
false => l
};
for i in 0..l {
// L = L + self.Y[i].mul(message[i]);
let mut Y = self.Y2[i];
Y.mul_assign(message[i]); // Y_i ^ m_i
L.add_assign(&Y); // L += Y_i ^m_i
}
// Y_(l+1) ^ t
let mut Yt = mpk.g2.clone();
Yt.mul_assign(message[last_elem]);
L.add_assign(&Yt);
let mut X2 = self.X2.clone();
X2.add_assign(&L); // X2 = X + L
let lhs = E::pairing(signature.h, X2);
let rhs = E::pairing(signature.H, mpk.g2);
signature.h != E::G1::one() && lhs == rhs
}
/// verify a blinded signature without unblinding it first
pub fn verify_blind(&self, mpk: &PublicParams<E>, message: &Vec<E::Fr>, bf: &E::Fr, signature: &Signature<E>) -> bool {
let mut m = message.clone();
let t = bf.clone();
m.push(t);
self.verify(mpk, &m, signature)
}
/// Verify a proof of knowledge of a signature
/// Takes in a proof generated by prove_response(), a blind signature, and a challenge
/// outputs: boolean
pub fn verify_proof(&self, mpk: &PublicParams<E>, blindSig: Signature<E>, p: SignatureProof<E>, challenge: E::Fr) -> bool {
let mut gx = E::pairing(blindSig.h, self.X2);
gx = gx.pow(challenge.into_repr());
for j in 0..self.Y2.len() {
let mut gy = E::pairing(blindSig.h, self.Y2[j]);
gy = gy.pow(p.zsig[j].into_repr());
gx.mul_assign(&gy);
}
let mut h = E::pairing(blindSig.h, mpk.g2);
h = h.pow(p.zv.into_repr());
gx.mul_assign(&h);
let mut g = E::pairing(blindSig.H, mpk.g2);
g = g.pow(challenge.into_repr());
g.mul_assign(&p.a);
gx == g
}
pub fn blind<R: Rng>(&self, csprng: &mut R, bf: &E::Fr, signature: &Signature<E>) -> Signature<E> {
let r = E::Fr::rand(csprng);
let t = bf.clone();
let mut h1 = signature.h;
h1.mul_assign(r); // sigma1 ^ r
let mut h = signature.h;
let mut H1 = signature.H;
h.mul_assign(t); // sigma1 ^ t (blinding factor)
H1.add_assign(&h); // (sigma2 * sigma1 ^ t)
// (sigma2 * sigma1 ^ t) ^ r
H1.mul_assign(r);
Signature { h: h1, H: H1 }
}
pub fn unblind(&self, bf: &E::Fr, signature: &Signature<E>) -> Signature<E> {
let mut H = signature.h;
let mut inv_bf = bf.clone();
inv_bf.negate();
// sigma2 / sigma1 ^ t
H.mul_assign(inv_bf);
H.add_assign(&signature.H);
Signature { h: signature.h, H: H }
}
/// prove knowledge of a signature: commitment phase
/// returns the proof state, including commitment a and a blind signature blindSig
pub fn prove_commitment<R: Rng>(&self, rng: &mut R, mpk: &PublicParams<E>, signature: &Signature<E>,
tOptional: Option<Vec<E::Fr>>, ttOptional: Option<E::Fr>) -> ProofState<E> {
let v = E::Fr::rand(rng);
let blindSig = self.blind(rng, &v, signature);
let mut t = tOptional.unwrap_or(Vec::<E::Fr>::with_capacity(self.Y2.len()));
let tt = ttOptional.unwrap_or(E::Fr::rand(rng));
let mut a = E::Fqk::one();
// TODO: consider optimizations to pairing in loop
for j in 0..self.Y2.len() {
if t.len() == j {
t.push(E::Fr::rand(rng));
}
let mut gy = E::pairing(blindSig.h, self.Y2[j]);
gy = gy.pow(t[j].into_repr());
a.mul_assign(&gy);
}
let mut h = E::pairing(blindSig.h, mpk.g2);
h = h.pow(tt.into_repr());
a.mul_assign(&h);
ProofState { v, t, tt, a, blindSig }
}
/// prove knowledge of a signature: response phase
/// returns a proof that can be send to the verifier together with the challenge and the blind signature
pub fn prove_response(&self, ps: &ProofState<E>, challenge: E::Fr, message: &mut Vec<E::Fr>) -> SignatureProof<E> {
let mut zsig = ps.t.clone();
let z_len = zsig.len();
for i in 0..message.len() {
if i < z_len {
let mut message1 = message[i];
message1.mul_assign(&challenge);
zsig[i].add_assign(&message1);
}
}
let mut zv = ps.tt.clone();
let mut vic = ps.v.clone();
vic.mul_assign(&challenge);
zv.add_assign(&vic);
SignatureProof { zsig, zv, a: ps.a }
}
}
pub fn setup<R: Rng, E: Engine>(csprng: &mut R) -> PublicParams<E> {
let g1 = E::G1::rand(csprng);
let g2 = E::G2::rand(csprng);
let mpk = PublicParams { g1: g1, g2: g2 };
return mpk;
}
///
/// KeyPair - implements the standard CL signature variant by PS - Section 3.1
///
impl<E: Engine> KeyPair<E> {
pub fn generate<R: Rng>(csprng: &mut R, mpk: &PublicParams<E>, l: usize) -> Self {
let secret = SecretKey::generate(csprng, l);
let public = PublicKey::from_secret(mpk, &secret);
KeyPair { secret, public }
}
/// sign a vector of messages (of size l)
pub fn sign<R: Rng>(&self, csprng: &mut R, message: &Vec<E::Fr>) -> Signature<E> {
self.secret.sign(csprng, message)
}
pub fn verify(&self, mpk: &PublicParams<E>, message: &Vec<E::Fr>, signature: &Signature<E>) -> bool {
self.public.verify(mpk, message, signature)
}
}
///
/// BlindingKeyPair - implements the blinding signature scheme in PS - Section 3.1.1
///
impl<E: Engine> BlindKeyPair<E> {
/// generate public/private keypair given public params and size of vectors
pub fn generate<R: Rng>(csprng: &mut R, mpk: &PublicParams<E>, l: usize) -> Self {
let secret = SecretKey::generate(csprng, l);
let public = BlindPublicKey::from_secret(mpk, &secret);
BlindKeyPair { secret, public }
}
pub fn generate_cs_multi_params(&self, mpk: &PublicParams<E>) -> CSMultiParams<E> {
let mut com_bases = vec! {mpk.g1};
com_bases.append(&mut self.public.Y1.clone());
CSMultiParams { pub_bases: com_bases }
}
/// extract unblinded public key
pub fn get_public_key(&self, mpk: &PublicParams<E>) -> PublicKey<E> {
PublicKey::from_secret(mpk, &self.secret)
}
/// sign a vector of messages
pub fn sign<R: Rng>(&self, csprng: &mut R, message: &Vec<E::Fr>) -> Signature<E> {
self.secret.sign(csprng, message)
}
/// randomize signature
pub fn rerandomize_signature<R: Rng>(&self, csprng: &mut R, signature: &Signature<E>) -> Signature<E> {
let r = E::Fr::rand(csprng);
let mut h = signature.h.clone();
let mut H = signature.H.clone();
h.mul_assign(r.clone());
H.mul_assign(r);
Signature { h, H }
}
/// sign a commitment of a vector of messages
pub fn sign_blind<R: Rng>(&self, csprng: &mut R, mpk: &PublicParams<E>, com: Commitment<E>) -> Signature<E> {
let u = E::Fr::rand(csprng);
let mut h1 = mpk.g1;
h1.mul_assign(u); // g1 ^ u
let com1 = com.c.clone();
let mut H1 = self.public.X1.clone();
H1.add_assign(&com1); // (X * com)
H1.mul_assign(u); // (X * com) ^ u (blinding factor)
Signature { h: h1, H: H1 }
}
/// computes a blind signature from an existing one
pub fn blind<R: Rng>(&self, csprng: &mut R, bf: &E::Fr, signature: &Signature<E>) -> Signature<E> {
self.public.blind(csprng, bf, signature)
}
/// unblinds a signature given knowledge of blinding factor, t. Output should be
/// verifiable with standard signature scheme.
pub fn unblind(&self, bf: &E::Fr, signature: &Signature<E>) -> Signature<E> {
self.public.unblind(bf, signature)
}
}
// display CL signature (PS)
impl<E: Engine> fmt::Display for Signature<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Signature : \n(h = {},\nH = {})", self.h, self.H)
}
}
#[cfg(test)]
mod tests {
use super::*;
use ff::Rand;
use pairing::bls12_381::{Bls12, Fr};
#[test]
fn sign_and_verify() {
// let mut rng = XorShiftRng::seed_from_u64(0xbc4f6d44d62f276c);
// let mut rng = XorShiftRng::seed_from_u64(0xb963afd05455863d);
let mut rng = &mut rand::thread_rng();
let l = 5;
let mpk = setup(&mut rng);
let keypair = KeyPair::<Bls12>::generate(&mut rng, &mpk, l);
println!("SECRET KEY => {}", keypair.secret);
println!("PUBLIC KEY => {}", keypair.public);
let mut message1: Vec<Fr> = Vec::new();
let mut message2: Vec<Fr> = Vec::new();
for _i in 0..l {
message1.push(Fr::rand(&mut rng));
message2.push(Fr::rand(&mut rng));
}
let sig = keypair.sign(&mut rng, &message1);
println!("{}", sig);
assert_eq!(keypair.verify(&mpk, &message1, &sig), true);
assert_eq!(keypair.verify(&mpk, &message2, &sig), false);
}
#[test]
fn blind_sign_and_verify() {
let mut rng = &mut rand::thread_rng();
let l = 5;
let mpk = setup(&mut rng);
let keypair = BlindKeyPair::<Bls12>::generate(&mut rng, &mpk, l);
let public_key = keypair.get_public_key(&mpk);
let mut message1: Vec<Fr> = Vec::new();
let mut message2: Vec<Fr> = Vec::new();
for _i in 0..l {
message1.push(Fr::rand(&mut rng));
message2.push(Fr::rand(&mut rng));
}
let sig = keypair.sign(&mut rng, &message1);
assert_eq!(public_key.verify(&mpk, &message1, &sig), true);
assert_eq!(public_key.verify(&mpk, &message2, &sig), false);
let t = Fr::rand(&mut rng);
let blind_sig = keypair.blind(&mut rng, &t, &sig);
// pick another blinding factor
let t1 = Fr::rand(&mut rng);
// verify blind signatures and provide blinding factor as input
assert_eq!(keypair.public.verify_blind(&mpk, &message1, &t, &blind_sig), true);
assert_eq!(keypair.public.verify_blind(&mpk, &message2, &t, &blind_sig), false);
assert_eq!(keypair.public.verify_blind(&mpk, &message1, &t1, &blind_sig), false);
let rand_sig = keypair.rerandomize_signature(&mut rng, &sig);
assert_eq!(public_key.verify(&mpk, &message1, &rand_sig), true);
}
#[test]
fn blind_unblind_works() {
let mut rng = &mut rand::thread_rng();
let l = 5;
let mpk = setup(&mut rng);
let keypair = BlindKeyPair::<Bls12>::generate(&mut rng, &mpk, l);
let mut message1: Vec<Fr> = Vec::new();
for _i in 0..l {
message1.push(Fr::rand(&mut rng));
}
let signature = keypair.sign(rng, &message1);
let r = Fr::rand(rng);
let blind_sig = keypair.blind(rng, &r, &signature);
let signature1 = keypair.unblind(&r, &blind_sig);
assert_eq!(keypair.get_public_key(&mpk).verify(&mpk, &message1, &signature1), true);
assert_eq!(keypair.get_public_key(&mpk).verify(&mpk, &message1, &blind_sig), false);
assert_eq!(keypair.public.verify_blind(&mpk, &message1, &r, &blind_sig), true);
}
#[test]
fn blind_sign_and_verify_works() {
let mut rng = &mut rand::thread_rng();
let l = 5;
let mpk = setup(&mut rng);
let keypair = BlindKeyPair::<Bls12>::generate(&mut rng, &mpk, l);
let mut message1: Vec<Fr> = Vec::new();
let mut message2: Vec<Fr> = Vec::new();
for _i in 0..l {
message1.push(Fr::rand(&mut rng));
message2.push(Fr::rand(&mut rng));
}
let com_params = keypair.generate_cs_multi_params(&mpk);
let t = Fr::rand(rng);
let com = com_params.commit(&message1, &t);
let signature = keypair.sign_blind(rng, &mpk, com);
let unblinded_sig = keypair.unblind(&t, &signature);
let t1 = Fr::rand(&mut rng);
assert_eq!(keypair.get_public_key(&mpk).verify(&mpk, &message1, &unblinded_sig), true);
assert_eq!(keypair.public.verify_blind(&mpk, &message1, &t, &signature), true);
assert_eq!(keypair.get_public_key(&mpk).verify(&mpk, &message2, &unblinded_sig), false);
assert_eq!(keypair.public.verify_blind(&mpk, &message2, &t, &signature), false);
assert_eq!(keypair.public.verify_blind(&mpk, &message1, &t1, &signature), false);
}
#[test]
fn proof_of_knowledge_of_signature() {
let mut rng = &mut rand::thread_rng();
let l = 5;
let mpk = setup(&mut rng);
let keypair = BlindKeyPair::<Bls12>::generate(&mut rng, &mpk, l);
let mut message1: Vec<Fr> = Vec::new();
for _i in 0..l {
message1.push(Fr::rand(&mut rng));
}
let sig = keypair.sign(&mut rng, &message1);
let proof_state = keypair.public.prove_commitment(rng, &mpk, &sig, None, None);
let challenge = Fr::rand(&mut rng);
let proof = keypair.public.prove_response(&proof_state.clone(), challenge, &mut message1);
assert_eq!(keypair.public.verify_proof(&mpk, proof_state.blindSig, proof, challenge), true);
}
#[test]
fn test_cl_basic_serialize() {
let mut rng = &mut rand::thread_rng();
let l = 5;
let mpk = setup(&mut rng);
let keypair = KeyPair::<Bls12>::generate(&mut rng, &mpk, l);
let blindkeypair = BlindKeyPair::<Bls12>::generate(&mut rng, &mpk, l);
let serialized = serde_json::to_vec(&mpk).unwrap();
//println!("serialized = {:?}", serialized.len());
let _mpk_des: PublicParams<Bls12> = serde_json::from_slice(&serialized).unwrap();
//println!("{}", mpk_des);
//println!("SK => {}", &keypair.secret);
let sk_serialized = serde_json::to_vec(&keypair.secret).unwrap();
//println!("sk_serialized = {:?}", sk_serialized.len());
let sk_des: SecretKey<Bls12> = serde_json::from_slice(&sk_serialized).unwrap();
//println!("{}", sk_des);
assert_eq!(sk_des, keypair.secret);
//println!("PK => {}", &keypair.public);
let pk_serialized = serde_json::to_vec(&keypair.public).unwrap();
//println!("pk_serialized = {:?}", pk_serialized.len());
let _pk_des: PublicKey<Bls12> = serde_json::from_slice(&pk_serialized).unwrap();
//assert_eq!(pk_des, keypair.public);
//println!("{}", &blindkeypair.public);
let bpk_ser = serde_json::to_vec(&blindkeypair.public).unwrap();
//println!("blind pk_ser = {:?}", bpk_ser.len());
let bpk_des: BlindPublicKey<Bls12> = serde_json::from_slice(&bpk_ser).unwrap();
assert_eq!(bpk_des, blindkeypair.public);
let unblind_pk = blindkeypair.get_public_key(&mpk);
//println!("{}", &unblind_pk);
let upk_serialized = serde_json::to_vec(&unblind_pk).unwrap();
//println!("upk_serialized = {:?}", upk_serialized.len());
let upk_des: PublicKey<Bls12> = serde_json::from_slice(&upk_serialized).unwrap();
assert_eq!(upk_des, unblind_pk);
assert_ne!(upk_des, keypair.public);
}
}

View File

@ -1,308 +0,0 @@
// clproto.rs
use std::fmt;
use std::str;
use rand::{thread_rng, Rng};
use bn::{Group, Fr, G1, G2, Gt, pairing};
use clsigs;
use commit_scheme;
use debug_elem_in_hex;
use debug_g1_in_hex;
use debug_g2_in_hex;
use debug_gt_in_hex;
use concat_to_vector;
use bincode::SizeLimit::Infinite;
use bincode::rustc_serialize::encode;
use clsigs::{PublicParams, SignatureD, PublicKeyD, SecretKeyD, hash_g2_to_fr, hash_gt_to_fr};
#[derive(Clone)]
pub struct ProofCV {
pub T: G2,
pub C: G2,
pub s: Vec<Fr>,
pub num_secrets: usize,
pub pub_bases: Vec<G2>
}
/// NIZK for PoK of the opening of a commitment M = g^m0 * Z1^m1 * ... * Zl^ml
/// Arg 1 - secret values
/// Arg 2 - public bases
/// Arg 3 - challenge to include in the proof
pub fn bs_gen_nizk_proof(x: &Vec<Fr>, pub_bases: &Vec<G2>, C: G2) -> ProofCV {
let rng = &mut thread_rng();
let l = x.len(); // number of secrets
let mut t: Vec<Fr> = Vec::new();
for i in 0 .. l {
t.push(Fr::random(rng));
}
// compute the T
let mut T = pub_bases[0] * t[0];
for i in 1 .. l {
T = T + (pub_bases[i] * t[i]);
}
// hash T to get the challenge
let c = hash_g2_to_fr(&T);
// compute s values
let mut s: Vec<Fr> = Vec::new();
for i in 0 .. l {
//println!("(gen proof) i => {}", i);
let _s = (x[i] * c) + t[i];
s.push(_s);
}
return ProofCV { T: T, C: C, s: s, pub_bases: pub_bases.clone(), num_secrets: l };
}
pub fn bs_check_proof_and_gen_signature(mpk: &PublicParams, sk: &SecretKeyD, proof: &ProofCV) -> SignatureD {
if bs_verify_nizk_proof(&proof) {
return bs_compute_blind_signature(&mpk, &sk, proof.C, proof.num_secrets);
} else {
panic!("Invalid proof: could not verify the NIZK proof");
}
}
pub fn bs_verify_nizk_proof(proof: &ProofCV) -> bool {
// if proof is valid, then call part
let c = hash_g2_to_fr(&proof.T);
let l = proof.s.len(); // number of s values
assert!(l <= proof.pub_bases.len());
let mut lhs = proof.pub_bases[0] * proof.s[0];
for i in 1 .. l {
//println!("(in verify proof) i => {}", i);
lhs = lhs + (proof.pub_bases[i] * proof.s[i]);
}
let rhs = (proof.C * c) + proof.T;
return lhs == rhs;
}
// internal function
pub fn bs_compute_blind_signature(mpk: &PublicParams, sk: &SecretKeyD, m: G2, num_secrets: usize) -> SignatureD {
let rng = &mut thread_rng();
let alpha = Fr::random(rng);
let a = mpk.g2 * alpha;
let mut A: Vec<G2> = Vec::new();
let mut B: Vec<G2> = Vec::new();
assert!(sk.z.len() <= num_secrets);
let l = sk.z.len();
for i in 0 .. l {
let _A = a * sk.z[i];
let _B = _A * sk.y;
A.push(_A);
B.push(_B);
}
let b = a * sk.y;
let c = (a * sk.x) + (m * (alpha * sk.x * sk.y));
let sig = SignatureD { a: a, A: A, b: b, B: B, c: c };
return sig;
}
// Prover first randomizes the signature
pub fn prover_generate_blinded_sig(sig: &SignatureD) -> SignatureD {
let rng = &mut thread_rng();
let r = Fr::random(rng);
let rpr = Fr::random(rng);
let a = sig.a * r;
let b = sig.b * r;
let c = (sig.c * r) * rpr;
let mut A: Vec<G2> = Vec::new();
let mut B: Vec<G2> = Vec::new();
assert!(sig.A.len() == sig.B.len());
let l = sig.A.len();
for i in 0 .. l {
A.push(sig.A[i] * r);
B.push(sig.B[i] * r);
}
let bsig = SignatureD { a: a, A: A, b: b, B: B, c: c };
return bsig;
}
#[derive(Clone)]
pub struct CommonParams {
vx: Gt,
vxy: Gt,
vxyi: Vec<Gt>,
pub vs: Gt
}
#[derive(Clone)]
pub struct ProofVS {
T: Gt,
A: Gt,
s: Vec<Fr>,
pub_bases: Vec<Gt>
}
pub fn gen_common_params(mpk: &PublicParams, pk: &PublicKeyD, sig: &SignatureD) -> CommonParams {
let l = sig.B.len();
let vx = pairing(pk.X, sig.a);
let vxy = pairing(pk.X, sig.b);
// generate vector
let mut vxyi: Vec<Gt> = Vec::new();
for i in 0 .. l {
vxyi.push(pairing(pk.X, sig.B[i]));
}
let vs = pairing(mpk.g1, sig.c);
return CommonParams { vx: vx, vxy: vxy, vxyi: vxyi, vs: vs };
}
pub fn vs_gen_nizk_proof(x: &Vec<Fr>, cp: &CommonParams, a: Gt) -> ProofVS {
let rng = &mut thread_rng();
let l = x.len() + 1;
let mut t: Vec<Fr> = Vec::new();
for i in 0 .. l {
t.push(Fr::random(rng));
}
let mut pub_bases: Vec<Gt> = Vec::new();
pub_bases.push(cp.vx); // 1
pub_bases.push(cp.vxy); // u_0
for i in 0 .. cp.vxyi.len() {
pub_bases.push(cp.vxyi[i]); // u_1 ... u_l
}
// compute the T
let mut T = pub_bases[0].pow(t[0]); // vx ^ t0
for i in 1 .. l {
T = T * (pub_bases[i].pow(t[i])); // vxy{i} ^ t{i}
}
// hash T to get the challenge
let c = hash_gt_to_fr(&T);
// compute s values
let mut s: Vec<Fr> = Vec::new();
let _s = c + t[0]; // for vx => s0 = (1*c + t[0])
s.push(_s);
for i in 1 .. l {
//println!("(gen nizk proof) i => {}", i);
let _s = (x[i-1] * c) + t[i];
s.push(_s);
}
return ProofVS { T: T, A: a, s: s, pub_bases: pub_bases };
}
fn part1_verify_proof_vs(proof: &ProofVS) -> bool {
let c = hash_gt_to_fr(&proof.T);
let l = proof.s.len();
assert!(l > 1);
let mut lhs = proof.pub_bases[0].pow(proof.s[0]);
for i in 1 .. l {
lhs = lhs * (proof.pub_bases[i].pow(proof.s[i]));
}
let rhs = proof.A.pow(c) * proof.T;
return lhs == rhs;
}
pub fn vs_verify_blind_sig(mpk: &PublicParams, pk: &PublicKeyD, proof: &ProofVS, sig: &SignatureD) -> bool {
let result0 = part1_verify_proof_vs(&proof);
let mut result1 = true;
let mut result3 = true;
// TODO: optimize verification
// verify second condition
let lhs2 = pairing(pk.Y, sig.a);
let rhs2 = pairing(mpk.g1, sig.b);
let result2 = lhs2 == rhs2;
assert_eq!(sig.A.len(), sig.B.len());
let l = sig.A.len();
for i in 0 .. l {
let lhs1 = pairing(pk.Z[i], sig.a);
let rhs1 = pairing(mpk.g1, sig.A[i]);
if lhs1 != rhs1 {
result1 = false;
}
let lhs3 = pairing(pk.Y, sig.A[i]);
let rhs3 = pairing(mpk.g1, sig.B[i]);
if lhs3 != rhs3 {
result3 = false;
}
}
if !result0 {
println!("ERROR: Failed to verify proof");
}
if !result1 {
println!("ERROR: Failed to verify pairing eq 1");
}
if !result2 {
println!("ERROR: Failed to verify pairing eq 2");
}
if !result3 {
println!("ERROR: Failed to verify pairing eq 3");
}
return result0 && result1 && result2 && result3;
}
#[cfg(test)]
mod tests {
use super::*;
use rand::{Rng, thread_rng};
use bn::{Fr, Group};
use clsigs;
use commit_scheme;
use debug_g2_in_hex;
#[test]
fn efficient_protocols_for_cl_signatures() {
let rng = &mut rand::thread_rng();
let mpk = clsigs::setup_d();
let l = 3;
let m_keypair = clsigs::keygen_d(&mpk, l);
let mut m1 : Vec<Fr> = Vec::new();
for i in 0 .. l+1 {
m1.push(Fr::random(rng));
}
let b = m_keypair.pk.Z2.len();
let mut bases: Vec<G2> = Vec::new();
bases.push(mpk.g2);
for i in 0 .. b {
bases.push(m_keypair.pk.Z2[i]);
}
// generate sample commitment
let mut C = mpk.g2 * m1[0];
for i in 0 .. b {
//println!("index: {}", i);
C = C + (m_keypair.pk.Z2[i] * m1[i+1]);
}
let msg = "Sample Commit output:";
debug_g2_in_hex(msg, &C);
let cm_csp = commit_scheme::setup(b, m_keypair.pk.Z2.clone(), mpk.g2.clone());
let r = m1[0];
let w_com = commit_scheme::commit(&cm_csp, &m1, r);
assert!(commit_scheme::decommit(&cm_csp, &w_com, &m1));
let proof = bs_gen_nizk_proof(&m1, &cm_csp.pub_bases, w_com.c);
let int_sig = bs_check_proof_and_gen_signature(&mpk, &m_keypair.sk, &proof);
assert!(clsigs::verify_d(&mpk, &m_keypair.pk, &m1, &int_sig) == true);
let blind_sigs = prover_generate_blinded_sig(&int_sig);
let common_params1 = gen_common_params(&mpk, &m_keypair.pk, &int_sig);
let proof_vs = vs_gen_nizk_proof(&m1, &common_params1, common_params1.vs);
assert!(vs_verify_blind_sig(&mpk, &m_keypair.pk, &proof_vs, &blind_sigs) == true);
}
}

View File

@ -1,412 +0,0 @@
// clsigs.rs
use std::fmt;
use std::str;
use rand::{thread_rng, Rng};
use bn::{Group, Fr, G1, G2, Gt, pairing};
use debug_elem_in_hex;
use debug_g1_in_hex;
use debug_g2_in_hex;
use debug_gt_in_hex;
use concat_to_vector;
use bincode::SizeLimit::Infinite;
use bincode::rustc_serialize::encode;
use sodiumoxide::crypto::hash::sha512;
use sodiumoxide::randombytes;
pub struct PublicParams {
pub g1: G1,
pub g2: G2
}
#[derive(Copy, Clone)]
pub struct PublicKey {
X: G1,
Y: G1
}
impl PublicKey {
pub fn encode(&self) -> Vec<u8> {
let mut output_buf = Vec::new();
let x_vec: Vec<u8> = encode(&self.X, Infinite).unwrap();
let y_vec: Vec<u8> = encode(&self.Y, Infinite).unwrap();
output_buf.extend(x_vec);
output_buf.extend(y_vec);
return output_buf;
}
}
impl fmt::Display for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let x_vec: Vec<u8> = encode(&self.X, Infinite).unwrap();
let y_vec: Vec<u8> = encode(&self.Y, Infinite).unwrap();
let mut x_s = String::new();
for x in x_vec.iter() {
x_s = format!("{}{:x}", x_s, x);
}
let mut y_s = String::new();
for y in y_vec.iter() {
y_s = format!("{}{:x}", y_s, y);
}
write!(f, "PK : (X=0x{}, Y=0x{})", x_s, y_s)
}
}
#[derive(Copy, Clone)]
pub struct SecretKey {
x: Fr,
y: Fr
}
impl SecretKey {
pub fn encode(&self) -> Vec<u8> {
let mut output_buf = Vec::new();
let x_vec: Vec<u8> = encode(&self.x, Infinite).unwrap();
let y_vec: Vec<u8> = encode(&self.y, Infinite).unwrap();
output_buf.extend(x_vec);
output_buf.extend(y_vec);
return output_buf;
}
}
#[derive(Clone)]
pub struct KeyPair {
pub sk: SecretKey,
pub pk: PublicKey
}
pub struct Signature {
a: G2,
b: G2,
c: G2
}
impl fmt::Display for Signature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let a_vec: Vec<u8> = encode(&self.a, Infinite).unwrap();
let b_vec: Vec<u8> = encode(&self.b, Infinite).unwrap();
let c_vec: Vec<u8> = encode(&self.c, Infinite).unwrap();
let mut a_s = String::new();
for x in a_vec.iter() {
a_s = format!("{}{:x}", a_s, x);
}
let mut b_s = String::new();
for y in b_vec.iter() {
b_s = format!("{}{:x}", b_s, y);
}
let mut c_s = String::new();
for y in c_vec.iter() {
c_s = format!("{}{:x}", c_s, y);
}
write!(f, "Signature : (\na = 0x{},\nb = 0x{},\nc = 0x{}\n)", a_s, b_s, c_s)
}
}
// scheme A - for a single message
pub fn setup_a() -> PublicParams {
let rng = &mut thread_rng();
let g1 = G1::random(rng);
let g2 = G2::random(rng);
let mpk = PublicParams { g1: g1, g2: g2 };
return mpk;
}
pub fn keygen_a(mpk : &PublicParams) -> KeyPair {
let rng = &mut thread_rng();
let x = Fr::random(rng);
let y = Fr::random(rng);
let sk = SecretKey { x: x, y: y };
let pk = PublicKey { X: mpk.g1 * x,
Y: mpk.g1 * y
};
return KeyPair { sk: sk, pk: pk }
}
pub fn sign_a(sk: &SecretKey, m: Fr) -> Signature {
let rng = &mut thread_rng();
let a = G2::random(rng);
let b = a * sk.y;
let c = a * (sk.x + (m * sk.x * sk.y));
let sig = Signature { a: a, b: b, c: c };
return sig;
}
pub fn verify_a(mpk: &PublicParams, pk: &PublicKey, m: Fr, sig: &Signature) -> bool {
let lhs1 = pairing(pk.Y, sig.a);
let rhs1 = pairing(mpk.g1, sig.b);
let lhs2 = pairing(pk.X, sig.a) * (pairing(pk.X, sig.b).pow(m));
let rhs2 = pairing(mpk.g1, sig.c);
return (lhs1 == rhs1) && (lhs2 == rhs2);
}
// scheme D - for a vector of messages
#[derive(Clone)]
pub struct PublicKeyD {
pub X: G1,
pub Y: G1,
pub Z: Vec<G1>,
pub Z2: Vec<G2>,
pub W: Vec<G1>
}
impl PublicKeyD {
pub fn encode(&self) -> Vec<u8> {
let mut output_buf = Vec::new();
let x_vec: Vec<u8> = encode(&self.X, Infinite).unwrap();
let y_vec: Vec<u8> = encode(&self.Y, Infinite).unwrap();
output_buf.extend(x_vec);
output_buf.extend(y_vec);
for i in 0 .. self.Z.len() {
let zi_vec: Vec<u8> = encode(&self.Z[i], Infinite).unwrap();
output_buf.extend(zi_vec);
let z2i_vec: Vec<u8> = encode(&self.Z2[i], Infinite).unwrap();
output_buf.extend(z2i_vec);
let w_vec: Vec<u8> = encode(&self.W[i], Infinite).unwrap();
output_buf.extend(w_vec);
}
return output_buf;
}
}
#[derive(Clone)]
pub struct SecretKeyD {
pub x: Fr,
pub y: Fr,
pub z: Vec<Fr>
}
#[derive(Clone)]
pub struct KeyPairD {
pub sk: SecretKeyD,
pub pk: PublicKeyD
}
#[derive(Clone)]
pub struct SignatureD {
pub a: G2,
pub A: Vec<G2>,
pub b: G2,
pub B: Vec<G2>,
pub c: G2
}
impl SignatureD {
pub fn new(_a: G2, _A: Vec<G2>, _b: G2, _B: Vec<G2>, _c: G2) -> SignatureD {
SignatureD {
a: _a, A: _A, b: _b, B: _B, c: _c
}
}
pub fn hash(&self, prefix: &str) -> Fr {
let mut output_buf: Vec<u8> = Vec::new();
output_buf.extend_from_slice(prefix.as_bytes());
concat_to_vector(&mut output_buf, &self.a);
concat_to_vector(&mut output_buf, &self.b);
concat_to_vector(&mut output_buf, &self.c);
assert_eq!(self.A.len(), self.B.len());
for i in 0 .. self.A.len() {
concat_to_vector(&mut output_buf, &self.A[i]);
concat_to_vector(&mut output_buf, &self.B[i]);
}
// println!("DEBUG: signature len => {}", output_buf.len());
// let's hash the final output_buf
let sha2_digest = sha512::hash(output_buf.as_slice());
let mut hash_buf: [u8; 64] = [0; 64];
hash_buf.copy_from_slice(&sha2_digest[0..64]);
return Fr::interpret(&hash_buf);
}
}
pub fn setup_d() -> PublicParams {
let rng = &mut thread_rng();
let g1 = G1::random(rng);
let g2 = G2::random(rng);
let mpk = PublicParams { g1: g1, g2: g2 };
return mpk;
}
pub fn keygen_d(mpk : &PublicParams, l: usize) -> KeyPairD {
let rng = &mut thread_rng();
let x = Fr::random(rng);
let y = Fr::random(rng);
let X = mpk.g1 * x;
let Y = mpk.g1 * y;
let mut z: Vec<Fr> = Vec::new();
let mut Z: Vec<G1> = Vec::new();
let mut Z2: Vec<G2> = Vec::new();
let mut W: Vec<G1> = Vec::new();
// generate the vector ck of sym keys
for i in 0 .. l {
let _z = Fr::random(rng);
let _Z = mpk.g1 * _z;
let _Z2 = mpk.g2 * _z;
let _W = Y * _z;
z.push(_z);
Z.push(_Z);
Z2.push(_Z2);
W.push(_W);
}
// plus one to Z2
let sk = SecretKeyD { x: x, y: y, z: z };
let pk = PublicKeyD { X: X, Y: Y, Z: Z, Z2: Z2, W: W };
return KeyPairD { sk: sk, pk: pk }
}
pub fn sign_d(mpk: &PublicParams, sk: &SecretKeyD, m: &Vec<Fr>) -> SignatureD {
assert!(m.len() <= sk.z.len()+1);
let l = m.len();
let rng = &mut thread_rng();
//let a = mpk.g2 * Fr::random(rng); // G2::random(rng);
let a = G2::random(rng);
let mut A: Vec<G2> = Vec::new();
let b = a * sk.y;
let mut B: Vec<G2> = Vec::new();
let mut c = (a * (sk.x + (m[0] * sk.x * sk.y)));
for i in 0 .. l-1 {
let _A = a * sk.z[i];
let _B = _A * sk.y;
A.push(_A);
B.push(_B);
c = c + (_A * (m[i+1] * sk.x * sk.y));
}
let sig = SignatureD { a: a, A: A, b: b, B: B, c: c };
return sig;
}
pub fn verify_d_unoptimized(mpk: &PublicParams, pk: &PublicKeyD, m: &Vec<Fr>, sig: &SignatureD) -> bool {
let l = m.len();
// lhs2a and rhs2a checks that sig.b was formed correctly
let lhs2a = pairing(pk.Y, sig.a); // eq2a
let rhs2a = pairing(mpk.g1, sig.b);
let mut result1 = true;
let mut result2b = true;
// lhs3 and rhs3 checks that sig.c was formed correctly
let mut lhs3 = pairing(pk.X, sig.a) * pairing(pk.X, sig.b * m[0]); // eq3
let rhs3 = pairing(mpk.g1, sig.c);
for i in 0 .. l-1 {
// checks that {sig.A}_i was formed correctly
let lhs1 = pairing(pk.Z[i], sig.a); // eq1
let rhs1 = pairing(mpk.g1, sig.A[i]);
if (lhs1 != rhs1) {
result1 = false;
}
let lhs2b = pairing(pk.Y, sig.A[i]); // eq2b
let rhs2b = pairing(mpk.g1, sig.B[i]);
if lhs2b != rhs2b {
result2b = false;
}
lhs3 = lhs3 * pairing(pk.X, sig.B[i] * m[i+1]); // eq3
}
return result1 && (lhs2a == rhs2a) && result2b && (lhs3 == rhs3);
}
// optimized but does not include small exps for security
pub fn verify_d(mpk: &PublicParams, pk: &PublicKeyD, m: &Vec<Fr>, sig: &SignatureD) -> bool {
let l = m.len();
let mut Zis = G1::zero();
let mut Ais = G2::zero();
let mut Bis = G2::zero();
let mut _lhs3 = G2::zero();
for i in 0 .. l-1 {
// checks that {sig.A}_i was formed correctly
let Zis = Zis + pk.Z[i];
let Ais = Ais + sig.A[i];
let Bis = Bis + sig.B[i];
_lhs3 = _lhs3 + (sig.B[i] * m[i+1]);
}
return pairing(Zis, sig.a) *
pairing(pk.Y, Ais + sig.a).inverse() *
pairing(pk.X, sig.a + (sig.b * m[0]) + _lhs3).inverse() ==
pairing(mpk.g1, Ais + -Bis + -sig.b + -sig.c);
}
// NIZK protocol for proving knowledge of a signature
pub fn hash_g2_to_fr(x: &G2) -> Fr {
// TODO: change to serde (instead of rustc_serialize)
let x_vec: Vec<u8> = encode(&x, Infinite).unwrap();
let sha2_digest = sha512::hash(x_vec.as_slice());
let mut hash_buf: [u8; 64] = [0; 64];
hash_buf.copy_from_slice(&sha2_digest[0..64]);
return Fr::interpret(&hash_buf);
}
pub fn hash_gt_to_fr(x: &Gt) -> Fr {
// TODO: change to serde (instead of rustc_serialize)
let x_vec: Vec<u8> = encode(&x, Infinite).unwrap();
let sha2_digest = sha512::hash(x_vec.as_slice());
let mut hash_buf: [u8; 64] = [0; 64];
hash_buf.copy_from_slice(&sha2_digest[0..64]);
return Fr::interpret(&hash_buf);
}
#[cfg(test)]
mod tests {
use super::*;
use bn::{Fr, Group};
#[test]
fn scheme_a_sign_and_verify_works() {
// test ability to sign/verify a single message
let rng = &mut thread_rng();
let mpk = setup_a();
let keypair = keygen_a(&mpk);
let mut m1 = Fr::random(rng);
let mut m2 = Fr::random(rng);
let signature = sign_a(&keypair.sk, m1);
assert!(verify_a(&mpk, &keypair.pk, m1, &signature) == true);
assert!(verify_a(&mpk, &keypair.pk, m2, &signature) == false);
}
#[test]
fn scheme_d_sign_and_verify_works() {
// test ability to sign/verify a vector of messages
let rng = &mut thread_rng();
let mpk = setup_d();
let l = 3;
let keypair = keygen_d(&mpk, l);
let mut m1 : Vec<Fr> = Vec::new();
let mut m2 : Vec<Fr> = Vec::new();
for i in 0 .. l+1 {
m1.push(Fr::random(rng));
m2.push(Fr::random(rng));
}
let signature = sign_d(&mpk, &keypair.sk, &m1);
assert!(verify_d(&mpk, &keypair.pk, &m1, &signature) == true);
assert!(verify_d_unoptimized(&mpk, &keypair.pk, &m1, &signature) == true);
assert!(verify_d(&mpk, &keypair.pk, &m2, &signature) == false);
}
}

View File

@ -1,212 +0,0 @@
// commit_schemes.rs
use std::fmt;
use rand::{thread_rng, Rng};
use bn::{Group, Fr, G1, G2};
use clsigs;
use debug_elem_in_hex;
use bincode::SizeLimit::Infinite;
use bincode::rustc_serialize::encode;
use sodiumoxide::crypto::hash::sha512;
#[derive(Copy, Clone)]
pub struct PublicKey {
g: G2,
h: G2
}
#[derive(Copy, Clone)]
pub struct Commitment {
pub c: G2,
pub r: Fr
}
#[derive(Clone)]
pub struct CSParams {
pub pub_bases: Vec<G2>
}
impl fmt::Display for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let g_vec: Vec<u8> = encode(&self.g, Infinite).unwrap();
let h_vec: Vec<u8> = encode(&self.h, Infinite).unwrap();
let mut g_s = String::new();
for x in g_vec.iter() {
g_s = format!("{}{:x}", g_s, x);
}
let mut h_s = String::new();
for y in h_vec.iter() {
h_s = format!("{}{:x}", h_s, y);
}
write!(f, "PK : (g=0x{}, h=0x{})", g_s, h_s)
}
}
impl fmt::Display for Commitment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let c_vec: Vec<u8> = encode(&self.c, Infinite).unwrap();
let mut c_s = String::new();
for x in c_vec.iter() {
c_s = format!("{}{:x}", c_s, x);
}
let d_vec: Vec<u8> = encode(&self.r, Infinite).unwrap();
let mut d_s = String::new();
for x in d_vec.iter() {
d_s = format!("{}{:x}", d_s, x);
}
write!(f, "Commitment : (c=0x{}, d=0x{})", c_s, d_s)
}
}
/*
Implements the setup algorithm for the Pedersen92 commitment scheme
*/
pub fn ped92_setup() -> PublicKey {
println!("Run Setup...");
let rng = &mut thread_rng();
let g = G2::random(rng);
let h = G2::random(rng);
let pk = PublicKey { g: g, h: h };
println!("{}", pk);
return pk;
}
/*
commit(pk, msg) -> cm where
- pk is the public key generated from setup()
- msg is the message structure for the commitment scheme
- cm is the output commitment message for the given message
*/
pub fn ped92_commit(pk: &PublicKey, m: Fr, R: Option<Fr>) -> Commitment {
let rng = &mut thread_rng();
let r = R.unwrap_or(Fr::random(rng));
//let r = Fr::random(rng);
//let m = msg.hash();
let p = "commit -> m";
debug_elem_in_hex(p, &m);
// c = g^m * h^r
let c = (pk.g * m) + (pk.h * r);
// return (c, r) <- d=r
let commitment = Commitment { c: c, r: r };
// debugging
println!("{}", commitment);
return commitment;
}
/*
decommit(pk, cm, msg) -> bool where
- pk is the public key generated from setup()
- cm is the commitment
- m is the message to validate
- outputs T/F for whether the cm is a valid commitment to the msg
*/
pub fn ped92_decommit(pk: &PublicKey, cm: &Commitment, m: Fr) -> bool {
let p = "decommit -> m";
debug_elem_in_hex(p, &m);
let dm = (pk.g * m) + (pk.h * cm.r);
return dm == cm.c;
}
/*
Implements the setup algorithm for the Pedersen92 commitment scheme over
a vector of messages.
*/
pub fn setup(len: usize, pub_bases: Vec<G2>, h: G2) -> CSParams {
let rng = &mut thread_rng();
//let base_h = h.unwrap_or(G2::random(rng));
let mut p: Vec<G2> = Vec::new();
p.push(h);
let _p = pub_bases;
for i in 0 .. _p.len() {
p.push(_p[i]);
}
return CSParams { pub_bases: p };
}
pub fn setup_gen_params(len: usize) -> CSParams {
let rng = &mut thread_rng();
let mut p: Vec<G2> = Vec::new();
for i in 0 .. len {
p.push(G2::random(rng));
}
return CSParams { pub_bases: p };
}
pub fn commit(csp: &CSParams, x: &Vec<Fr>, r: Fr) -> Commitment {
let rng = &mut thread_rng();
//let r = R.unwrap_or(Fr::random(rng));
// c = g1^m1 * ... * gn^mn * h^r
//println!("(commit) index: 0");
let mut c = (csp.pub_bases[0] * r);
for i in 1 .. x.len() {
//println!("(commit) index: {}", i);
c = c + (csp.pub_bases[i] * x[i]);
}
// return (c, r) <- r
let commitment = Commitment { c: c, r: r };
// debugging
//println!("{}", commitment);
return commitment;
}
pub fn decommit(csp: &CSParams, cm: &Commitment, x: &Vec<Fr>) -> bool {
let l = x.len();
// pub_base[0] => h, x[0] => r
// check that cm.r == x[0]
// assert!(cm.r == x[0]);
let mut dc = csp.pub_bases[0] * cm.r;
for i in 1 .. l {
dc = dc + (csp.pub_bases[i] * x[i]);
}
return dc == cm.c && cm.r == x[0];
}
#[cfg(test)]
mod tests {
use super::*;
use bn::{Fr, Group};
#[test]
fn commit_one_message_works() {
let rng = &mut thread_rng();
let pk = ped92_setup();
let m1 = Fr::random(rng);
let m2 = m1 + Fr::from_str("1").unwrap();
let r = Fr::random(rng);
let c = ped92_commit(&pk, m1, Some(r));
assert!(ped92_decommit(&pk, &c, m1) == true);
assert!(ped92_decommit(&pk, &c, m2) == false);
}
#[test]
fn commit_n_message_works() {
let rng = &mut thread_rng();
let len = 3;
let csp = setup_gen_params(len);
let mut m: Vec<Fr> = Vec::new();
for i in 0 .. len {
m.push(Fr::random(rng));
}
let r = m[0];
let c = commit(&csp, &m, r);
assert!(decommit(&csp, &c, &m) == true);
}
}

525
src/ffishim.rs Normal file
View File

@ -0,0 +1,525 @@
#[no_mangle]
pub mod ffishim {
extern crate libc;
use bidirectional;
use ff::ScalarEngine;
use pairing::bls12_381::Bls12;
use serde::Deserialize;
use libc::c_char;
use std::ffi::{CStr, CString};
use std::str;
// use channels::{ChannelcloseM, ResultBoltType, BoltError};
fn error_message(s: String) -> *mut c_char {
let ser = ["{\'error\':\'", &s, "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
macro_rules! bolt_try {
($e:expr) => (match $e {
Ok(val) => val.unwrap(),
Err(err) => return error_message(err),
});
}
macro_rules! handle_errors {
($e:expr) => (match $e {
Ok(val) => val,
Err(err) => return error_message(err.to_string()),
});
}
pub type ResultSerdeType<T> = Result<T, serde_json::error::Error>;
// fn deserialize_object<'a, T>(serialized: *mut c_char) -> T
// where
// T: Deserialize<'a>,
// {
// let bytes = unsafe { CStr::from_ptr(serialized).to_bytes() };
// let string: &str = str::from_utf8(bytes).unwrap(); // make sure the bytes are UTF-8
// serde_json::from_str(&string).unwrap()
// }
//
// fn deserialize_optional_object<'a, T>(serialized: *mut c_char) -> Option<T>
// where
// T: Deserialize<'a>,
// {
// let bytes = unsafe { CStr::from_ptr(serialized).to_bytes() };
// let string: &str = str::from_utf8(bytes).unwrap(); // make sure the bytes are UTF-8
// Some(serde_json::from_str(&string).unwrap())
// }
fn deserialize_result_object<'a, T>(serialized: *mut c_char) -> ResultSerdeType<T>
where
T: Deserialize<'a>,
{
let bytes = unsafe { CStr::from_ptr(serialized).to_bytes() };
let string: &str = str::from_utf8(bytes).unwrap(); // make sure the bytes are UTF-8
serde_json::from_str(&string)
}
#[no_mangle]
pub extern fn ffishim_free_string(pointer: *mut c_char) {
unsafe {
if pointer.is_null() { return; }
CString::from_raw(pointer)
};
}
#[no_mangle]
pub extern fn ffishim_bidirectional_wtp_check_wpk(ser_wpk: *mut c_char) -> *mut c_char {
let wpk_result: ResultSerdeType<secp256k1::PublicKey> = deserialize_result_object(ser_wpk);
let _wpk = handle_errors!(wpk_result);
let res = true;
let ser = ["{\'result\':\'", serde_json::to_string(&res).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_channel_setup(channel_name: *const c_char, third_party_support: u32) -> *mut c_char {
let bytes = unsafe { CStr::from_ptr(channel_name).to_bytes() };
let name: &str = str::from_utf8(bytes).unwrap(); // make sure the bytes are UTF-8
let mut tps = false;
if third_party_support > 1 {
tps = true;
}
let channel_state = bidirectional::ChannelState::<Bls12>::new(name.to_string(), tps);
let ser = ["{\'channel_state\':\'", serde_json::to_string(&channel_state).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
// INIT
#[no_mangle]
pub extern fn ffishim_bidirectional_init_merchant(ser_channel_state: *mut c_char, name_ptr: *const c_char) -> *mut c_char {
let rng = &mut rand::thread_rng();
let channel_state_result: ResultSerdeType<bidirectional::ChannelState<Bls12>> = deserialize_result_object(ser_channel_state);
let mut channel_state = handle_errors!(channel_state_result);
let bytes = unsafe { CStr::from_ptr(name_ptr).to_bytes() };
let name: &str = str::from_utf8(bytes).unwrap(); // make sure the bytes are UTF-8
let (channel_token, merch_state, channel_state) = bidirectional::init_merchant(rng, &mut channel_state, name);
let ser = ["{\'channel_token\':\'", serde_json::to_string(&channel_token).unwrap().as_str(), "\', \'merch_state\':\'", serde_json::to_string(&merch_state).unwrap().as_str(), "\', \'channel_state\':\'", serde_json::to_string(&channel_state).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_init_customer(ser_channel_token: *mut c_char, balance_customer: i64, balance_merchant: i64, name_ptr: *const c_char) -> *mut c_char {
let rng = &mut rand::thread_rng();
// Deserialize the channel token
let channel_token_result: ResultSerdeType<bidirectional::ChannelToken<Bls12>> = deserialize_result_object(ser_channel_token);
let mut channel_token = handle_errors!(channel_token_result);
// Deserialize the name
let bytes = unsafe { CStr::from_ptr(name_ptr).to_bytes() };
let name: &str = str::from_utf8(bytes).unwrap(); // make sure the bytes are UTF-8
// We change the channel state
let cust_state = bidirectional::init_customer(rng, &mut channel_token, balance_customer, balance_merchant, name);
let ser = ["{\'cust_state\':\'", serde_json::to_string(&cust_state).unwrap().as_str(), "\', \'channel_token\':\'", serde_json::to_string(&channel_token).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
// ESTABLISH
#[no_mangle] // bidirectional::establish_customer_generate_proof(rng, &mut channel_token, &mut cust_state);
pub extern fn ffishim_bidirectional_establish_customer_generate_proof(ser_channel_token: *mut c_char, ser_customer_state: *mut c_char) -> *mut c_char {
let rng = &mut rand::thread_rng();
// Deserialize the channel token
let channel_token_result: ResultSerdeType<bidirectional::ChannelToken<Bls12>> = deserialize_result_object(ser_channel_token);
let mut channel_token = handle_errors!(channel_token_result);
// Deserialize the cust state
let cust_state_result: ResultSerdeType<bidirectional::CustomerState<Bls12>> = deserialize_result_object(ser_customer_state);
let mut cust_state = handle_errors!(cust_state_result);
let (com, com_proof) = bidirectional::establish_customer_generate_proof(rng, &mut channel_token, &mut cust_state);
let ser = ["{\'cust_state\':\'", serde_json::to_string(&cust_state).unwrap().as_str(),
"\', \'channel_token\':\'", serde_json::to_string(&channel_token).unwrap().as_str(),
"\', \'com\':\'", serde_json::to_string(&com).unwrap().as_str(),
"\', \'com_proof\':\'", serde_json::to_string(&com_proof).unwrap().as_str(),
"\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_establish_merchant_issue_close_token(ser_channel_state: *mut c_char, ser_com: *mut c_char, ser_com_proof: *mut c_char, ser_channel_id: *mut c_char, init_cust_bal: i64, init_merch_bal: i64, ser_merch_state: *mut c_char) -> *mut c_char {
let rng = &mut rand::thread_rng();
// Deserialize the channel state
let channel_state_result: ResultSerdeType<bidirectional::ChannelState<Bls12>> = deserialize_result_object(ser_channel_state);
let channel_state = handle_errors!(channel_state_result);
// Deserialize the com proof
let com_result: ResultSerdeType<bidirectional::Commitment<Bls12>> = deserialize_result_object(ser_com);
let com = handle_errors!(com_result);
// Deserialize the com proof
let com_proof_result: ResultSerdeType<bidirectional::CommitmentProof<Bls12>> = deserialize_result_object(ser_com_proof);
let com_proof = handle_errors!(com_proof_result);
// Deserialize the merchant state
let merch_state_result: ResultSerdeType<bidirectional::MerchantState<Bls12>> = deserialize_result_object(ser_merch_state);
let merch_state = handle_errors!(merch_state_result);
// Deserialize the pk_c
let channel_id_result: ResultSerdeType<<Bls12 as ScalarEngine>::Fr> = deserialize_result_object(ser_channel_id);
let channel_id_fr = handle_errors!(channel_id_result);
let close_token = bolt_try!(bidirectional::establish_merchant_issue_close_token(rng, &channel_state, &com, &com_proof, &channel_id_fr, init_cust_bal, init_merch_bal, &merch_state));
let ser = ["{\'close_token\':\'", serde_json::to_string(&close_token).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_establish_merchant_issue_pay_token(ser_channel_state: *mut c_char, ser_com: *mut c_char, ser_merch_state: *mut c_char) -> *mut c_char {
let rng = &mut rand::thread_rng();
// Deserialize the channel state
let channel_state_result: ResultSerdeType<bidirectional::ChannelState<Bls12>> = deserialize_result_object(ser_channel_state);
let channel_state = handle_errors!(channel_state_result);
// Deserialize the commitment
let com_result: ResultSerdeType<bidirectional::Commitment<Bls12>> = deserialize_result_object(ser_com);
let com = handle_errors!(com_result);
// Deserialize the merchant state
let merch_state_result: ResultSerdeType<bidirectional::MerchantState<Bls12>> = deserialize_result_object(ser_merch_state);
let merch_state = handle_errors!(merch_state_result);
let pay_token = bidirectional::establish_merchant_issue_pay_token(rng, &channel_state, &com, &merch_state);
let ser = ["{\'pay_token\':\'", serde_json::to_string(&pay_token).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_verify_close_token(ser_channel_state: *mut c_char, ser_customer_state: *mut c_char, ser_close_token: *mut c_char) -> *mut c_char {
// Deserialize the channel state
let channel_state_result: ResultSerdeType<bidirectional::ChannelState<Bls12>> = deserialize_result_object(ser_channel_state);
let mut channel_state = handle_errors!(channel_state_result);
// Deserialize the cust state
let cust_state_result: ResultSerdeType<bidirectional::CustomerState<Bls12>> = deserialize_result_object(ser_customer_state);
let mut cust_state = handle_errors!(cust_state_result);
// Deserialize the close token
let close_result: ResultSerdeType<bidirectional::Signature<Bls12>> = deserialize_result_object(ser_close_token);
let close_token = handle_errors!(close_result);
let is_close_token_valid = cust_state.verify_close_token(&mut channel_state, &close_token);
let ser = ["{\'cust_state\':\'", serde_json::to_string(&cust_state).unwrap().as_str(),
"\', \'is_token_valid\':\'", serde_json::to_string(&is_close_token_valid).unwrap().as_str(),
"\', \'channel_state\':\'", serde_json::to_string(&channel_state).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_establish_customer_final(ser_channel_state: *mut c_char, ser_customer_state: *mut c_char, ser_pay_token: *mut c_char) -> *mut c_char {
// Deserialize the channel state
let channel_state_result: ResultSerdeType<bidirectional::ChannelState<Bls12>> = deserialize_result_object(ser_channel_state);
let mut channel_state = handle_errors!(channel_state_result);
// Deserialize the cust state
let cust_state_result: ResultSerdeType<bidirectional::CustomerState<Bls12>> = deserialize_result_object(ser_customer_state);
let mut cust_state = handle_errors!(cust_state_result);
// Deserialize the custdata
let pay_token_result: ResultSerdeType<bidirectional::Signature<Bls12>> = deserialize_result_object(ser_pay_token);
let pay_token = handle_errors!(pay_token_result);
let is_channel_established = bidirectional::establish_customer_final(&mut channel_state, &mut cust_state, &pay_token);
let ser = ["{\'cust_state\':\'", serde_json::to_string(&cust_state).unwrap().as_str(),
"\', \'is_established\':\'", serde_json::to_string(&is_channel_established).unwrap().as_str(),
"\', \'channel_state\':\'", serde_json::to_string(&channel_state).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
// PAY
#[no_mangle]
pub extern fn ffishim_bidirectional_pay_generate_payment_proof(ser_channel_state: *mut c_char, ser_customer_state: *mut c_char, amount: i64) -> *mut c_char {
let rng = &mut rand::thread_rng();
// Deserialize the channel state
let channel_state_result: ResultSerdeType<bidirectional::ChannelState<Bls12>> = deserialize_result_object(ser_channel_state);
let channel_state = handle_errors!(channel_state_result);
// Deserialize the cust state
let cust_state_result: ResultSerdeType<bidirectional::CustomerState<Bls12>> = deserialize_result_object(ser_customer_state);
let cust_state = handle_errors!(cust_state_result);
// Generate the payment proof
let (payment, new_cust_state) = bidirectional::generate_payment_proof(rng, &channel_state, &cust_state, amount);
// Serialize the results and return to caller
let ser = ["{\'payment\':\'", serde_json::to_string(&payment).unwrap().as_str(),
"\', \'cust_state\':\'", serde_json::to_string(&new_cust_state).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_pay_verify_payment_proof(ser_channel_state: *mut c_char, ser_pay_proof: *mut c_char, ser_merch_state: *mut c_char) -> *mut c_char {
let rng = &mut rand::thread_rng();
// Deserialize the channel state
let channel_state_result: ResultSerdeType<bidirectional::ChannelState<Bls12>> = deserialize_result_object(ser_channel_state);
let channel_state = handle_errors!(channel_state_result);
// Deserialize the payment proof
let payment_result: ResultSerdeType<bidirectional::Payment<Bls12>> = deserialize_result_object(ser_pay_proof);
let payment = handle_errors!(payment_result);
// Deserialize the merch state
let merch_state_result: ResultSerdeType<bidirectional::MerchantState<Bls12>> = deserialize_result_object(ser_merch_state);
let mut merch_state = handle_errors!(merch_state_result);
let close_token = bidirectional::verify_payment_proof(rng, &channel_state, &payment, &mut merch_state);
let ser = ["{\'close_token\':\'", serde_json::to_string(&close_token).unwrap().as_str(),
"\', \'merch_state\':\'", serde_json::to_string(&merch_state).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_pay_verify_multiple_payment_proofs(ser_channel_state: *mut c_char, ser_sender_pay_proof: *mut c_char, ser_receiver_pay_proof: *mut c_char, ser_merch_state: *mut c_char) -> *mut c_char {
let rng = &mut rand::thread_rng();
// Deserialize the channel state
let channel_state_result: ResultSerdeType<bidirectional::ChannelState<Bls12>> = deserialize_result_object(ser_channel_state);
let channel_state = handle_errors!(channel_state_result);
// Deserialize the payment proofs
let sender_payment_result: ResultSerdeType<bidirectional::Payment<Bls12>> = deserialize_result_object(ser_sender_pay_proof);
let sender_payment = handle_errors!(sender_payment_result);
let receiver_payment_result: ResultSerdeType<bidirectional::Payment<Bls12>> = deserialize_result_object(ser_receiver_pay_proof);
let receiver_payment = handle_errors!(receiver_payment_result);
// Deserialize the merch state
let merch_state_result: ResultSerdeType<bidirectional::MerchantState<Bls12>> = deserialize_result_object(ser_merch_state);
let mut merch_state = handle_errors!(merch_state_result);
let close_token_result = bidirectional::verify_multiple_payment_proofs(rng, &channel_state, &sender_payment, &receiver_payment, &mut merch_state);
let (sender_close_token, receiver_cond_close_token) = handle_errors!(close_token_result).unwrap();
let ser = ["{\'sender_close_token\':\'", serde_json::to_string(&sender_close_token).unwrap().as_str(),
"\', \'receiver_cond_close_token\':\'", serde_json::to_string(&receiver_cond_close_token).unwrap().as_str(),
"\', \'merch_state\':\'", serde_json::to_string(&merch_state).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_pay_generate_revoke_token(ser_channel_state: *mut c_char, ser_cust_state: *mut c_char, ser_new_cust_state: *mut c_char, ser_close_token: *mut c_char) -> *mut c_char {
// Deserialize the channel state
let channel_state_result: ResultSerdeType<bidirectional::ChannelState<Bls12>> = deserialize_result_object(ser_channel_state);
let channel_state = handle_errors!(channel_state_result);
// Deserialize the cust state
let cust_state_result: ResultSerdeType<bidirectional::CustomerState<Bls12>> = deserialize_result_object(ser_cust_state);
let mut cust_state = handle_errors!(cust_state_result);
// Deserialize the cust state
let new_cust_state_result: ResultSerdeType<bidirectional::CustomerState<Bls12>> = deserialize_result_object(ser_new_cust_state);
let new_cust_state = handle_errors!(new_cust_state_result);
// Deserialize the close token
let close_token_result: ResultSerdeType<bidirectional::Signature<Bls12>> = deserialize_result_object(ser_close_token);
let close_token = handle_errors!(close_token_result);
let revoke_token = bidirectional::generate_revoke_token(&channel_state, &mut cust_state, new_cust_state, &close_token);
let ser = ["{\'revoke_token\':\'", serde_json::to_string(&revoke_token).unwrap().as_str(),
"\', \'cust_state\':\'", serde_json::to_string(&cust_state).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_pay_verify_revoke_token(ser_revoke_token: *mut c_char, ser_merch_state: *mut c_char) -> *mut c_char {
// Deserialize the revoke token
let revoke_token_result: ResultSerdeType<bidirectional::RevokeToken> = deserialize_result_object(ser_revoke_token);
let revoke_token = handle_errors!(revoke_token_result);
// Deserialize the cust state
let merch_state_result: ResultSerdeType<bidirectional::MerchantState<Bls12>> = deserialize_result_object(ser_merch_state);
let mut merch_state = handle_errors!(merch_state_result);
// send revoke token and get pay-token in response
let pay_token_result = bidirectional::verify_revoke_token(&revoke_token, &mut merch_state);
let pay_token = handle_errors!(pay_token_result);
let ser = ["{\'pay_token\':\'", serde_json::to_string(&pay_token.unwrap()).unwrap().as_str(),
"\', \'merch_state\':\'", serde_json::to_string(&merch_state).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_pay_verify_multiple_revoke_tokens(ser_sender_revoke_token: *mut c_char, ser_receiver_revoke_token: *mut c_char, ser_merch_state: *mut c_char) -> *mut c_char {
// Deserialize the revoke tokens
let sender_revoke_token_result: ResultSerdeType<bidirectional::RevokeToken> = deserialize_result_object(ser_sender_revoke_token);
let sender_revoke_token = handle_errors!(sender_revoke_token_result);
let receiver_revoke_token_result: ResultSerdeType<bidirectional::RevokeToken> = deserialize_result_object(ser_receiver_revoke_token);
let receiver_revoke_token = handle_errors!(receiver_revoke_token_result);
// Deserialize the cust state
let merch_state_result: ResultSerdeType<bidirectional::MerchantState<Bls12>> = deserialize_result_object(ser_merch_state);
let mut merch_state = handle_errors!(merch_state_result);
// send revoke token and get pay-token in response
let pay_token_result = bidirectional::verify_multiple_revoke_tokens(&sender_revoke_token, &receiver_revoke_token, &mut merch_state);
let (sender_pay_token, receiver_pay_token) = handle_errors!(pay_token_result).unwrap();
let ser = ["{\'sender_pay_token\':\'", serde_json::to_string(&sender_pay_token).unwrap().as_str(),
"\', \'receiver_pay_token\':\'", serde_json::to_string(&receiver_pay_token).unwrap().as_str(),
"\', \'merch_state\':\'", serde_json::to_string(&merch_state).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_pay_verify_payment_token(ser_channel_state: *mut c_char, ser_cust_state: *mut c_char, ser_pay_token: *mut c_char) -> *mut c_char {
// Deserialize the channel state
let channel_state_result: ResultSerdeType<bidirectional::ChannelState<Bls12>> = deserialize_result_object(ser_channel_state);
let channel_state = handle_errors!(channel_state_result);
// Deserialize the cust state
let cust_state_result: ResultSerdeType<bidirectional::CustomerState<Bls12>> = deserialize_result_object(ser_cust_state);
let mut cust_state = handle_errors!(cust_state_result);
// Deserialize the pay token
let pay_token_result: ResultSerdeType<bidirectional::Signature<Bls12>> = deserialize_result_object(ser_pay_token);
let pay_token = handle_errors!(pay_token_result);
// verify the pay token and update internal state
let is_pay_valid = cust_state.verify_pay_token(&channel_state, &pay_token);
let ser = ["{\'cust_state\':\'", serde_json::to_string(&cust_state).unwrap().as_str(),
"\', \'is_pay_valid\':\'", serde_json::to_string(&is_pay_valid).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
// CLOSE
#[no_mangle]
pub extern fn ffishim_bidirectional_customer_close(ser_channel_state: *mut c_char, ser_cust_state: *mut c_char) -> *mut c_char {
// Deserialize the channel state
let channel_state_result: ResultSerdeType<bidirectional::ChannelState<Bls12>> = deserialize_result_object(ser_channel_state);
let channel_state = handle_errors!(channel_state_result);
// Deserialize the cust state
let cust_state_result: ResultSerdeType<bidirectional::CustomerState<Bls12>> = deserialize_result_object(ser_cust_state);
let cust_state = handle_errors!(cust_state_result);
let cust_close = bidirectional::customer_close(&channel_state, &cust_state);
let ser = ["{\'cust_close\':\'", serde_json::to_string(&cust_close).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
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_address: *const c_char, ser_cust_close: *mut c_char, ser_merch_state: *mut c_char) -> *mut c_char {
// Deserialize the channel state
let channel_state_result: ResultSerdeType<bidirectional::ChannelState<Bls12>> = deserialize_result_object(ser_channel_state);
let channel_state = handle_errors!(channel_state_result);
// Deserialize the channel token
let channel_token_result: ResultSerdeType<bidirectional::ChannelToken<Bls12>> = deserialize_result_object(ser_channel_token);
let channel_token = handle_errors!(channel_token_result);
// Deserialize the customer close structure
let cust_close_result: ResultSerdeType<bidirectional::ChannelcloseC<Bls12>> = deserialize_result_object(ser_cust_close);
let cust_close = handle_errors!(cust_close_result);
// Deserialize the merch state
let merch_state_result: ResultSerdeType<bidirectional::MerchantState<Bls12>> = deserialize_result_object(ser_merch_state);
let merch_state = handle_errors!(merch_state_result);
// Deserialize the destination address as a string
let ser_addr_bytes = unsafe { CStr::from_ptr(ser_address).to_bytes() };
let address: &str = str::from_utf8(ser_addr_bytes).unwrap(); // make sure the bytes are UTF-8
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 merch_close: bidirectional::ChannelcloseM = merch_state.sign_revoke_message(address.to_string(), &keys.revoke_token);
let ser = ["{\'wpk\':\'", serde_json::to_string(&keys.wpk).unwrap().as_str(),
"\', \'merch_close\':\'", serde_json::to_string(&merch_close).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_wtp_verify_cust_close_message(ser_channel_token: *mut c_char, ser_wpk: *mut c_char, ser_close_msg: *mut c_char, ser_close_token: *mut c_char) -> *mut c_char {
// Deserialize the channel token
let channel_token_result: ResultSerdeType<bidirectional::ChannelToken<Bls12>> = deserialize_result_object(ser_channel_token);
let channel_token = handle_errors!(channel_token_result);
// Deserialize the wpk
let wpk_result: ResultSerdeType<secp256k1::PublicKey> = deserialize_result_object(ser_wpk);
let wpk = handle_errors!(wpk_result);
// Deserialize the close wallet
let close_msg_result: ResultSerdeType<bidirectional::Wallet<Bls12>> = deserialize_result_object(ser_close_msg);
let close_msg = handle_errors!(close_msg_result);
// Deserialize the close token
let close_token_result: ResultSerdeType<bidirectional::Signature<Bls12>> = deserialize_result_object(ser_close_token);
let close_token = handle_errors!(close_token_result);
// check the signatures
let token_valid = bidirectional::wtp_verify_cust_close_message(&channel_token, &wpk, &close_msg, &close_token);
let ser = ["{\"result\":\"", serde_json::to_string(&token_valid).unwrap().as_str(), "\"}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_wtp_verify_merch_close_message(ser_channel_token: *mut c_char, ser_wpk: *mut c_char, ser_merch_close: *mut c_char) -> *mut c_char {
// Deserialize the channel token
let channel_token_result: ResultSerdeType<bidirectional::ChannelToken<Bls12>> = deserialize_result_object(ser_channel_token);
let channel_token = handle_errors!(channel_token_result);
// Deserialize the wpk
let wpk_result: ResultSerdeType<secp256k1::PublicKey> = deserialize_result_object(ser_wpk);
let wpk = handle_errors!(wpk_result);
// Deserialize the merch close
//let revoke_token: secp256k1::Signature = deserialize_object(ser_revoke_token);
let merch_close_result: ResultSerdeType<bidirectional::ChannelcloseM> = deserialize_result_object(ser_merch_close);
let merch_close = handle_errors!(merch_close_result);
let revoke_token_valid = bidirectional::wtp_verify_revoke_message(&wpk, &merch_close.revoke.unwrap());
let merch_close_valid = bidirectional::wtp_verify_merch_close_message(&channel_token, &merch_close);
let token_valid = revoke_token_valid && merch_close_valid;
let ser = ["{\'result\':\'", serde_json::to_string(&token_valid).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
}

2087
src/lib.rs

File diff suppressed because it is too large Load Diff

428
src/nizk.rs Normal file
View File

@ -0,0 +1,428 @@
extern crate pairing;
extern crate rand;
use super::*;
use rand::Rng;
use cl::{Signature, PublicParams, setup, BlindKeyPair, SignatureProof, BlindPublicKey};
use ped92::{Commitment, CSMultiParams, CommitmentProof};
use pairing::{Engine, CurveProjective};
use wallet::Wallet;
use ccs08::{SecretParamsUL, ParamsUL, ProofUL};
use serde::{Serialize, Deserialize};
use util;
/// NIZKProof is the object that represents the NIZK Proof of Knowledge during the payment and closing protocol
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize, \
<E as pairing::Engine>::Fqk: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>, \
<E as pairing::Engine>::Fqk: serde::Deserialize<'de>"
))]
pub struct NIZKProof<E: Engine> {
pub sig: Signature<E>,
pub sigProof: SignatureProof<E>,
pub comProof: CommitmentProof<E>,
pub rpBC: ProofUL<E>,
pub rpBM: ProofUL<E>,
}
/// NIZKPublicParams are public parameters to perform a NIZK Proof of Knowledge during the payment and closing protocol
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>"
))]
pub struct NIZKPublicParams<E: Engine> {
pub mpk: PublicParams<E>,
pub pk: BlindPublicKey<E>,
pub comParams: CSMultiParams<E>,
pub rpParams: ParamsUL<E>,
}
/// NIZKSecretParams are secret parameters to perform the verification of a NIZK Proof of Knowledge during the payment and closing protocol
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>"
))]
pub struct NIZKSecretParams<E: Engine> {
pub pubParams: NIZKPublicParams<E>,
pub keypair: BlindKeyPair<E>,
pub rpParams: SecretParamsUL<E>,
}
impl<E: Engine> NIZKSecretParams<E> {
/// Basic setup for the NIZKPublicParams
/// Takes as input a random generator and the length of the message which should be 4 during payment protocol and 5 for the closing protocol
pub fn setup<R: Rng>(rng: &mut R, messageLength: usize) -> Self {
let mpk = setup(rng);
let keypair = BlindKeyPair::<E>::generate(rng, &mpk, messageLength);
let comParams = keypair.generate_cs_multi_params(&mpk);
let u = 128; //TODO: make u and l configurable
let l = 9;
let rpParams = SecretParamsUL::setup_ul(rng, u, l, comParams.clone());
let pubParams = NIZKPublicParams { mpk, pk: keypair.public.clone(), comParams, rpParams: rpParams.pubParams.clone() };
NIZKSecretParams { pubParams, keypair, rpParams }
}
/**
Verify a NIZK Proof of Knowledge during payment or closing protocol
Input:
proof: A NIZK proof created by the Customer
epsilon: The transaction amount of the payment
com: Commitment of the new wallet that needs to be signed
wpk: reveal of wallet public key of the old wallet.
*/
pub fn verify(&self, proof: NIZKProof<E>, epsilon: E::Fr, com: &Commitment<E>, wpk: E::Fr) -> bool {
//verify signature is not the identity
let r0 = proof.sig.h != E::G1::one();
//compute challenge
let challenge = NIZKPublicParams::<E>::hash(proof.sigProof.a, vec! {proof.comProof.T, proof.rpBC.D, proof.rpBM.D});
//verify knowledge of signature
let mut r1 = self.keypair.public.verify_proof(&self.pubParams.mpk, proof.sig, proof.sigProof.clone(), challenge);
let mut wpkc = wpk.clone();
wpkc.mul_assign(&challenge.clone());
r1 = r1 && proof.sigProof.zsig[1] == wpkc;
//verify knowledge of commitment
let r2 = proof.comProof.verify_proof(&self.pubParams.comParams, &com.c.clone(), &challenge, None);
//verify range proofs
let r3 = self.rpParams.verify_ul(&proof.rpBC.clone(), challenge.clone(), 3);
let r4 = self.rpParams.verify_ul(&proof.rpBM.clone(), challenge.clone(), 4);
//verify linear relationship
let mut r5 = proof.comProof.z[1] == proof.sigProof.zsig[0];
let mut zsig2 = proof.sigProof.zsig[2].clone();
let mut epsC = epsilon.clone();
epsC.mul_assign(&challenge.clone());
zsig2.sub_assign(&epsC.clone());
r5 = r5 && proof.comProof.z[3] == zsig2;
let mut zsig3 = proof.sigProof.zsig[3].clone();
zsig3.add_assign(&epsC.clone());
r5 = r5 && proof.comProof.z[4] == zsig3;
r0 && r1 && r2 && r3 && r4 && r5
}
}
impl<E: Engine> NIZKPublicParams<E> {
/** This method can be called to create the proof during the payment and closing protocol
Input:
rng: random generator
oldWallet: This is the wallet before payment occurs
newWallet: This is the new state of the wallet after payment
newWalletCom: A commitment of the new wallet
rPrime: blinding value of commitment of new wallet
paymentToken: A blind signature on the old wallet
Output:
NIZKProof: a proof that can be verified by the merchant during payment or closing protocol
*/
pub fn prove<R: Rng>(&self, rng: &mut R, oldWallet: Wallet<E>, newWallet: Wallet<E>,
newWalletCom: Commitment<E>, rPrime: E::Fr, paymentToken: &Signature<E>) -> NIZKProof<E> {
//Commitment phase
//commit commitment
let w_len = newWallet.as_fr_vec().len();
let diff = self.comParams.pub_bases.len() - w_len;
let max = match diff > 1 {
true => w_len,
false => self.comParams.pub_bases.len()
};
let (D, t) = CommitmentProof::<E>::prove_commitment(rng, &self.comParams, &newWallet.as_fr_vec(), None);
//commit signature
let zero = E::Fr::zero();
let tOptional = match max > 4 {
true => Some(vec!(t[1], zero, t[3].clone(), t[4].clone())),
false => Some(vec!(t[1], zero, t[3].clone()))
};
let proofState = self.pk.prove_commitment(rng, &self.mpk, &paymentToken, tOptional, None);
//commit range proof
let rpStateBC = self.rpParams.prove_ul_commitment(rng, newWallet.bc.clone(), 3, None, None);
let rpStateBM = self.rpParams.prove_ul_commitment(rng, newWallet.bm.clone(), 4, None, None);
//Compute challenge
let challenge = NIZKPublicParams::<E>::hash(proofState.a, vec! {D, rpStateBC.D, rpStateBM.D});
//Response phase
//response for signature
let oldWalletVec = oldWallet.as_fr_vec();
let sigProof = self.pk.prove_response(&proofState, challenge, &mut oldWalletVec.clone());
//response commitment
let newWalletVec = newWallet.as_fr_vec();
let comProof = CommitmentProof::<E>::prove_response(&newWalletVec, &rPrime, D, &t, &challenge);
//response range proof
let mut vec01 = newWalletVec[0..2].to_vec();
let mut vecWithout2 = vec01.clone();
let mut vec3 = newWalletVec[3..].to_vec();
vecWithout2.append(&mut vec3);
let vec2 = newWalletVec[2].clone();
vec01.push(vec2);
if newWalletVec.len() > 4 {
let mut vec4 = newWalletVec[4..].to_vec();
vec01.append(&mut vec4);
}
let rpBC = self.rpParams.prove_ul_response(rPrime.clone(), newWalletCom.clone(), &rpStateBC, challenge.clone(), 3, vecWithout2.to_vec());
let rpBM = self.rpParams.prove_ul_response(rPrime.clone(), newWalletCom.clone(), &rpStateBM, challenge.clone(), 4, vec01.to_vec());
NIZKProof { sig: proofState.blindSig, sigProof, comProof, rpBC, rpBM }
}
fn hash(a: E::Fqk, T: Vec<E::G1>) -> E::Fr {
let mut x_vec: Vec<u8> = Vec::new();
x_vec.extend(format!("{}", a).bytes());
for t in T {
x_vec.extend(format!("{}", t).bytes());
}
util::hash_to_fr::<E>(x_vec)
}
}
///
/// Verify PoK for the opening of a commitment during the establishment protocol
///
pub fn verify_opening<E: Engine>(com_params: &CSMultiParams<E>, com: &E::G1, proof: &CommitmentProof<E>, channelId: &E::Fr, init_cust: i64, init_merch: i64) -> bool {
let xvec: Vec<E::G1> = vec![proof.T.clone(), com.clone()];
let challenge = util::hash_g1_to_fr::<E>(&xvec);
// compute the
let com_equal = proof.verify_proof(com_params, com, &challenge, Some(vec!{None, Some(channelId.clone()), None, Some(util::convert_int_to_fr::<E>(init_cust as i64)), Some(util::convert_int_to_fr::<E>(init_merch as i64))}));
return com_equal;
}
#[cfg(test)]
mod tests {
use super::*;
use pairing::bls12_381::{Bls12, Fr};
use util::convert_int_to_fr;
use ff::PrimeField;
#[test]
fn nizk_proof_works() {
let rng = &mut rand::thread_rng();
let channelId = Fr::rand(rng);
let wpk = Fr::rand(rng);
let wpkprime = Fr::rand(rng);
let bc = rng.gen_range(100, 1000);
let mut bc2 = bc.clone();
let bm = rng.gen_range(100, 1000);
let mut bm2 = bm.clone();
let epsilon = rng.gen_range(1, 100);
bc2 -= epsilon;
bm2 += epsilon;
let r = Fr::rand(rng);
let rprime = Fr::rand(rng);
let secParams = NIZKSecretParams::<Bls12>::setup(rng, 4);
let wallet1 = Wallet { channelId: channelId, wpk, bc, bm, close: None };
let commitment1 = secParams.pubParams.comParams.commit(&wallet1.as_fr_vec(), &r);
let wallet2 = Wallet { channelId: channelId, wpk: wpkprime, bc: bc2, bm: bm2, close: None };
let commitment2 = secParams.pubParams.comParams.commit(&wallet2.as_fr_vec(), &rprime);
let blindPaymentToken = secParams.keypair.sign_blind(rng, &secParams.pubParams.mpk, commitment1.clone());
let paymentToken = secParams.keypair.unblind(&r, &blindPaymentToken);
let proof = secParams.pubParams.prove(rng, wallet1, wallet2,
commitment2.clone(), rprime, &paymentToken);
let fr = convert_int_to_fr::<Bls12>(epsilon);
assert_eq!(secParams.verify(proof, fr, &commitment2, wpk), true);
}
#[test]
fn nizk_proof_negative_value_works() {
let rng = &mut rand::thread_rng();
let channelId = Fr::rand(rng);
let wpk = Fr::rand(rng);
let wpkprime = Fr::rand(rng);
let bc = rng.gen_range(100, 1000);
let mut bc2 = bc.clone();
let bm = rng.gen_range(100, 1000);
let mut bm2 = bm.clone();
let epsilon = rng.gen_range(-100, -1);
bc2 -= epsilon;
bm2 += epsilon;
let r = Fr::rand(rng);
let rprime = Fr::rand(rng);
let secParams = NIZKSecretParams::<Bls12>::setup(rng, 4);
let wallet1 = Wallet { channelId: channelId, wpk, bc, bm, close: None };
let commitment1 = secParams.pubParams.comParams.commit(&wallet1.as_fr_vec(), &r);
let wallet2 = Wallet { channelId: channelId, wpk: wpkprime, bc: bc2, bm: bm2, close: None };
let commitment2 = secParams.pubParams.comParams.commit(&wallet2.as_fr_vec(), &rprime);
let blindPaymentToken = secParams.keypair.sign_blind(rng, &secParams.pubParams.mpk, commitment1.clone());
let paymentToken = secParams.keypair.unblind(&r, &blindPaymentToken);
let proof = secParams.pubParams.prove(rng, wallet1, wallet2,
commitment2.clone(), rprime, &paymentToken);
let fr = convert_int_to_fr::<Bls12>(epsilon);
assert_eq!(secParams.verify(proof, fr, &commitment2, wpk), true);
}
#[test]
fn nizk_proof_close_works() {
let rng = &mut rand::thread_rng();
let channelId = Fr::rand(rng);
let wpk = Fr::rand(rng);
let wpkprime = Fr::rand(rng);
let bc = rng.gen_range(100, 1000);
let mut bc2 = bc.clone();
let bm = rng.gen_range(100, 1000);
let mut bm2 = bm.clone();
let epsilon = rng.gen_range(1, 100);
bc2 -= epsilon;
bm2 += epsilon;
let r = Fr::rand(rng);
let rprime = Fr::rand(rng);
let _closeToken = Fr::rand(rng);
let secParams = NIZKSecretParams::<Bls12>::setup(rng, 5);
let wallet1 = Wallet { channelId: channelId, wpk, bc, bm, close: None };
let commitment1 = secParams.pubParams.comParams.commit(&wallet1.as_fr_vec(), &r);
let wallet2 = Wallet { channelId: channelId, wpk: wpkprime, bc: bc2, bm: bm2, close: Some(_closeToken) };
let commitment2 = secParams.pubParams.comParams.commit(&wallet2.as_fr_vec(), &rprime);
let blindPaymentToken = secParams.keypair.sign_blind(rng, &secParams.pubParams.mpk, commitment1.clone());
let paymentToken = secParams.keypair.unblind(&r, &blindPaymentToken);
let blindCloseToken = secParams.keypair.sign_blind(rng, &secParams.pubParams.mpk, commitment2.clone());
let closeToken = secParams.pubParams.pk.unblind(&rprime, &blindCloseToken);
// verify the blind signatures
let pk = secParams.keypair.get_public_key(&secParams.pubParams.mpk);
assert!(pk.verify(&secParams.pubParams.mpk, &wallet1.as_fr_vec(), &paymentToken));
println!("close => {}", &wallet2);
assert!(pk.verify(&secParams.pubParams.mpk, &wallet2.as_fr_vec(), &closeToken));
let proof = secParams.pubParams.prove(rng, wallet1, wallet2,
commitment2.clone(), rprime, &paymentToken);
assert_eq!(secParams.verify(proof, Fr::from_str(&epsilon.to_string()).unwrap(), &commitment2, wpk), true);
}
#[test]
fn nizk_proof_false_statements() {
let rng = &mut rand::thread_rng();
let channelId = Fr::rand(rng);
let wpk = Fr::rand(rng);
let wpkprime = Fr::rand(rng);
let bc = rng.gen_range(100, 1000);
let mut bc2 = bc.clone();
let bm = rng.gen_range(100, 1000);
let mut bm2 = bm.clone();
let epsilon = rng.gen_range(1, 100);
bc2 -= epsilon;
bm2 += epsilon;
let r = Fr::rand(rng);
let rprime = Fr::rand(rng);
let secParams = NIZKSecretParams::<Bls12>::setup(rng, 4);
let wallet1 = Wallet { channelId: channelId, wpk, bc, bm, close: None };
let bc2Prime = bc.clone();
let wallet3 = Wallet { channelId: channelId, wpk: wpkprime, bc: bc2Prime, bm: bm2, close: None };
let commitment1 = secParams.pubParams.comParams.commit(&wallet1.as_fr_vec().clone(), &r);
let commitment2 = secParams.pubParams.comParams.commit(&wallet3.as_fr_vec(), &rprime);
let blindPaymentToken = secParams.keypair.sign_blind(rng, &secParams.pubParams.mpk, commitment1.clone());
let paymentToken = secParams.keypair.unblind(&r, &blindPaymentToken);
let proof = secParams.pubParams.prove(rng, wallet1.clone(), wallet3, commitment2.clone(), rprime, &paymentToken);
assert_eq!(secParams.verify(proof, Fr::from_str(&epsilon.to_string()).unwrap(), &commitment2, wpk), false);
let bm2Prime = bm.clone();
let wallet4 = Wallet { channelId: channelId, wpk: wpkprime, bc: bc2, bm: bm2Prime, close: None };
let commitment2 = secParams.pubParams.comParams.commit(&wallet4.as_fr_vec(), &rprime);
let proof = secParams.pubParams.prove(rng, wallet1.clone(), wallet4, commitment2.clone(), rprime, &paymentToken);
assert_eq!(secParams.verify(proof, Fr::from_str(&epsilon.to_string()).unwrap(), &commitment2, wpk), false);
let wallet5 = Wallet { channelId: Fr::rand(rng), wpk: wpkprime, bc: bc2, bm: bm2, close: None };
let commitment2 = secParams.pubParams.comParams.commit(&wallet5.as_fr_vec(), &rprime);
let proof = secParams.pubParams.prove(rng, wallet1.clone(), wallet5, commitment2.clone(), rprime, &paymentToken);
assert_eq!(secParams.verify(proof, Fr::from_str(&epsilon.to_string()).unwrap(), &commitment2, wpk), false);
}
#[test]
fn nizk_proof_commitment_opening_works() {
let rng = &mut rand::thread_rng();
let channelId = Fr::rand(rng);
let wpk = Fr::rand(rng);
let t = Fr::rand(rng);
let bc = rng.gen_range(100, 1000);
let bm = rng.gen_range(100, 1000);
let wallet = Wallet::<Bls12> { channelId: channelId, wpk: wpk, bc: bc, bm: bm, close: None };
let secParams = NIZKSecretParams::<Bls12>::setup(rng, 4);
let com = secParams.pubParams.comParams.commit(&wallet.as_fr_vec().clone(), &t);
let com_proof = CommitmentProof::<Bls12>::new(rng, &secParams.pubParams.comParams,
&com.c, &wallet.as_fr_vec(), &t, &vec![1, 3, 4]);
assert!(verify_opening(&secParams.pubParams.comParams, &com.c, &com_proof, &channelId.clone(), bc, bm));
}
#[test]
fn nizk_proof_false_commitment() {
let rng = &mut rand::thread_rng();
let channelId = Fr::rand(rng);
let wpk = Fr::rand(rng);
let t = Fr::rand(rng);
let bc = rng.gen_range(100, 1000);
let bc2 = rng.gen_range(100, 1000);
let bm = rng.gen_range(100, 1000);
let wallet1 = Wallet::<Bls12> { channelId: channelId, wpk: wpk, bc: bc, bm: bm, close: None };
let wallet2 = Wallet::<Bls12> { channelId: channelId, wpk: wpk, bc: bc2, bm: bm, close: None };
let secParams = NIZKSecretParams::<Bls12>::setup(rng, 4);
let com1 = secParams.pubParams.comParams.commit(&wallet1.as_fr_vec().clone(), &t);
let com2 = secParams.pubParams.comParams.commit(&wallet2.as_fr_vec().clone(), &t);
let com1_proof = CommitmentProof::<Bls12>::new(rng, &secParams.pubParams.comParams,
&com1.c, &wallet1.as_fr_vec(), &t, &vec![1, 3, 4]);
assert!(verify_opening(&secParams.pubParams.comParams, &com1.c, &com1_proof, &channelId.clone(), bc, bm));
assert!(!verify_opening(&secParams.pubParams.comParams, &com2.c, &com1_proof, &channelId.clone(), bc2, bm));
}
#[test]
fn test_nizk_serialization() {
let mut rng = &mut rand::thread_rng();
let l = 5;
let mpk = setup(&mut rng);
let blindkeypair = BlindKeyPair::<Bls12>::generate(&mut rng, &mpk, l);
let comParams = blindkeypair.generate_cs_multi_params(&mpk);
let u = 256; //TODO: optimize u?
let l = 8;
let rpParams = ccs08::SecretParamsUL::setup_ul(rng, u, l, comParams.clone());
let nizk_params = NIZKPublicParams { mpk: mpk, pk: blindkeypair.public, comParams: comParams, rpParams: rpParams.pubParams.clone() };
let is_serialized = serde_json::to_vec(&nizk_params).unwrap();
println!("NIZK Struct len: {}", is_serialized.len());
// deserialize
}
}

View File

@ -1,60 +0,0 @@
/*
One-time encryption - keyspace of the OTE is also the range of the pseudo-random function
*/
use std::fmt;
use bn::{Group, Fr, G1};
use rand;
pub struct OTMessage {
pub m1: G1,
pub m2: G1
}
pub struct OTCiphertext {
c1: G1,
c2: G1
}
pub fn keygen() -> G1 {
let rng = &mut rand::thread_rng();
let k = G1::random(rng);
return k;
}
// encryption scheme can be implemented by encoding the plaintext as an element in a cyclic group G
// and multiplying by a random group element.
pub fn otenc(k: G1, m: &OTMessage) -> OTCiphertext {
let c1 = k + m.m1;
let c2 = k + m.m2;
assert!(c1 != c2);
return OTCiphertext { c1: c1, c2: c2 };
}
pub fn otdec(k: G1, c: &OTCiphertext) -> OTMessage {
let x = c.c1 - k;
let y = c.c2 - k;
return OTMessage { m1: x, m2: y};
}
#[cfg(test)]
mod tests {
use super::*;
use rand::{Rng, thread_rng};
use bn::{G1, Group};
#[test]
fn one_time_enc_dec_works() {
let rng = &mut rand::thread_rng();
// Test the OTE scheme
let k = keygen();
let x = G1::random(rng);
let y = G1::random(rng);
let m = OTMessage { m1: x, m2: y };
let c = otenc(k, &m);
let orig_m = otdec(k, &c);
assert!(m.m1 == orig_m.m1 && m.m2 == orig_m.m2);
}
}

373
src/ped92.rs Normal file
View File

@ -0,0 +1,373 @@
// ped92.rs
use rand::Rng;
use pairing::{Engine, CurveProjective};
use ff::{Rand, Field, PrimeField};
use std::fmt;
use util::is_vec_g1_equal;
use serde::{Serialize, Deserialize};
use util;
#[derive(Clone)]
pub struct CSParams<E: Engine> {
pub g: E::G1,
pub h: E::G1,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as pairing::Engine>::G1: serde::Serialize"))]
#[serde(bound(deserialize = "<E as pairing::Engine>::G1: serde::Deserialize<'de>"))]
pub struct Commitment<E: Engine> {
pub c: E::G1,
}
impl<E: Engine> PartialEq for Commitment<E> {
fn eq(&self, other: &Commitment<E>) -> bool {
self.c == other.c
}
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as pairing::Engine>::G1: serde::Serialize"))]
#[serde(bound(deserialize = "<E as pairing::Engine>::G1: serde::Deserialize<'de>"))]
pub struct CSMultiParams<E: Engine> {
pub pub_bases: Vec<E::G1>
}
impl<E: Engine> PartialEq for CSMultiParams<E> {
fn eq(&self, other: &CSMultiParams<E>) -> bool {
is_vec_g1_equal::<E>(&self.pub_bases, &other.pub_bases)
}
}
impl<E: Engine> fmt::Display for CSMultiParams<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut y_str = String::new();
let mut i = 0;
for y in self.pub_bases.iter() {
y_str = format!("{}\n{} => {}", y_str, i, y);
i += 1;
}
write!(f, "CSMultiParams : (\n{}\n)", y_str)
}
}
impl<E: Engine> fmt::Display for Commitment<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Commitment : (c={})", &self.c)
}
}
impl<E: Engine> CSParams<E> {
/*
Implements the setup algorithm for the Pedersen92 commitment scheme
*/
pub fn setup<R: Rng>(rng: &mut R) -> Self {
let g = E::G1::rand(rng);
let h = E::G1::rand(rng);
CSParams { g, h }
}
/*
commit(pk, msg) -> cm where
- pk is the public key generated from setup()
- msg is the message structure for the commitment scheme
- cm is the output commitment message for the given message
*/
pub fn commit<R: Rng>(&self, rng: &mut R, m: E::Fr, R: Option<E::Fr>) -> Commitment<E> {
let r = R.unwrap_or(E::Fr::rand(rng));
// c = g^m * h^r
let mut c = self.g.clone();
c.mul_assign(m.clone());
let mut h = self.h.clone();
h.mul_assign(r.clone());
c.add_assign(&h);
Commitment { c }
}
/*
decommit(csp, cm, msg) -> bool where
- cm is the commitment
- m is the message to validate
- outputs T/F for whether the cm is a valid commitment to the msg
*/
pub fn decommit(&self, cm: &Commitment<E>, m: &E::Fr, r: &E::Fr) -> bool {
let mut dm = self.g.clone();
dm.mul_assign(m.clone());
let mut h = self.h.clone();
h.mul_assign(r.clone());
dm.add_assign(&h);
dm == cm.c
}
}
impl<E: Engine> CSMultiParams<E> {
/*
Implements the setup algorithm for the Pedersen92 commitment scheme over
a vector of messages of length len.
*/
pub fn setup_gen_params<R: Rng>(rng: &mut R, len: usize) -> Self {
let mut p: Vec<E::G1> = Vec::new();
// 1 extra base element for the random parameter
for _i in 0..len + 1 {
p.push(E::G1::rand(rng));
}
CSMultiParams { pub_bases: p }
}
pub fn commit(&self, x: &Vec<E::Fr>, r: &E::Fr) -> Commitment<E> {
// c = g1^m1 * ... * gn^mn * h^r
let mut c = self.pub_bases[0].clone();
let p_len = self.pub_bases.len();
c.mul_assign(r.clone());
//println!("commit => x.len = {}, p.len = {}", x.len(), p_len);
for i in 0..x.len() {
if (i < p_len) {
let mut basis = self.pub_bases[i + 1];
basis.mul_assign(x[i]);
c.add_assign(&basis);
}
}
Commitment { c }
}
pub fn extend_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();
c.mul_assign(x.clone());
c.add_assign(&com.c);
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
let mut dc = self.pub_bases[0].clone();
dc.mul_assign(r.clone());
for i in 0..l {
let mut basis = self.pub_bases[i+1];
basis.mul_assign(x[i]);
dc.add_assign(&basis);
}
return dc == cm.c;
}
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize, \
<E as pairing::Engine>::G1: serde::Serialize, \
<E as pairing::Engine>::G2: serde::Serialize"
))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>, \
<E as pairing::Engine>::G1: serde::Deserialize<'de>, \
<E as pairing::Engine>::G2: serde::Deserialize<'de>"
))]
pub struct CommitmentProof<E: Engine> {
pub T: E::G1,
pub z: Vec<E::Fr>,
}
impl<E: Engine> CommitmentProof<E> {
pub fn new<R: Rng>(csprng: &mut R, com_params: &CSMultiParams<E>, com: &E::G1, wallet: &Vec<E::Fr>, r: &E::Fr, reveal_index: &Vec<usize>) -> Self {
let mut rt = Vec::new();
for i in 0..wallet.len() + 1 {
if reveal_index.contains(&i) {
rt.push(E::Fr::zero());
} else {
rt.push(E::Fr::rand(csprng));
}
}
let (Tvals, t) = CommitmentProof::<E>::prove_commitment::<R>(csprng, com_params, wallet, Some(rt));
// compute the challenge
let x: Vec<E::G1> = vec![Tvals, com.clone()];
let challenge = util::hash_g1_to_fr::<E>(&x);
// compute the response
CommitmentProof::<E>::prove_response(wallet, r, Tvals, &t, &challenge)
}
pub fn prove_commitment<R: Rng>(csprng: &mut R, com_params: &CSMultiParams<E>, wallet: &Vec<E::Fr>, tOptional: Option<Vec<E::Fr>>) -> (E::G1, Vec<E::Fr>) {
let mut Tvals = E::G1::zero();
assert!(wallet.len() <= com_params.pub_bases.len());
let mut t = tOptional.unwrap_or(Vec::<E::Fr>::with_capacity(wallet.len() + 1));
// aspects of wallet being revealed
for i in 0..wallet.len() + 1 {
if t.len() == i {
t.push(E::Fr::rand(csprng));
}
let ti = t[i].clone();
let mut gt = com_params.pub_bases[i].clone();
gt.mul_assign(ti.into_repr());
Tvals.add_assign(&gt);
}
(Tvals, t)
}
pub fn prove_response(wallet: &Vec<E::Fr>, r: &E::Fr, Tvals: E::G1, t: &Vec<E::Fr>, challenge: &E::Fr) -> CommitmentProof<E> {
let mut z: Vec<E::Fr> = Vec::new();
let mut z0 = r.clone();
z0.mul_assign(&challenge);
z0.add_assign(&t[0]);
z.push(z0);
for i in 1..t.len() {
let mut zi = wallet[i - 1].clone();
zi.mul_assign(&challenge);
zi.add_assign(&t[i]);
z.push(zi);
}
CommitmentProof {
T: Tvals, // commitment challenge
z: z, // response values
}
}
pub fn verify_proof(&self, com_params: &CSMultiParams<E>, com: &<E as Engine>::G1, challenge: &E::Fr, revealOption: Option<Vec<Option<E::Fr>>>) -> bool {
let mut comc = com.clone();
let T = self.T.clone();
comc.mul_assign(challenge.into_repr());
comc.add_assign(&T);
let mut x = E::G1::zero();
let reveal = revealOption.unwrap_or(vec!{});
let mut revealBool = true;
for i in 0..self.z.len() {
let mut base = com_params.pub_bases[i].clone();
base.mul_assign(self.z[i].into_repr());
x.add_assign(&base);
if reveal.len() > i && reveal[i].is_some() {
let mut el = reveal[i].unwrap();
el.mul_assign(&challenge.clone());
revealBool = revealBool && self.z[i] == el;
}
}
revealBool && comc == x
}
}
#[cfg(test)]
mod tests {
use super::*;
use pairing::bls12_381::{Bls12, Fr, G1};
use rand::thread_rng;
use ff::Field;
use wallet::Wallet;
#[test]
fn commit_one_message_works() {
let rng = &mut thread_rng();
let csp = CSParams::<Bls12>::setup(rng);
let m1 = Fr::rand(rng);
let mut m2 = m1.clone();
m2.add_assign(&Fr::one());
let r = Fr::rand(rng);
let c = csp.commit(rng, m1, Some(r));
assert_eq!(csp.decommit(&c, &m1, &r), true);
assert_eq!(csp.decommit(&c, &m2, &r), false);
}
#[test]
fn commit_n_message_works() {
let rng = &mut thread_rng();
let len = 3;
let csp = CSMultiParams::<Bls12>::setup_gen_params(rng, len);
let mut m: Vec<Fr> = Vec::new();
for _i in 0..len {
m.push(Fr::rand(rng));
}
let r = Fr::rand(rng);
let c = csp.commit(&m, &r);
assert_eq!(csp.decommit(&c, &m, &r), true);
let mut r1 = r.clone();
r1.add_assign(&Fr::one());
assert_eq!(csp.decommit(&c, &m, &r1), false);
}
#[test]
fn commit_variable_messages_works() {
let rng = &mut thread_rng();
let len = 5;
let csp = CSMultiParams::<Bls12>::setup_gen_params(rng, len);
let mut m1: Vec<Fr> = Vec::new();
for _i in 0..len-1 {
m1.push(Fr::rand(rng));
}
let extra_m = Fr::rand(rng);
let r = Fr::rand(rng);
let c1 = csp.commit(&m1, &r);
assert_eq!(csp.decommit(&c1, &m1, &r), true);
let mut r1 = r.clone();
r1.add_assign(&Fr::one());
assert_eq!(csp.decommit(&c1, &m1, &r1), false);
// let's add another message
let mut m2 = m1.clone();
m2.push(extra_m);
let c2 = csp.commit(&m2, &r);
assert_eq!(csp.decommit(&c2, &m2, &r), true);
}
#[test]
fn test_csp_basic_serialize() {
let rng = &mut rand::thread_rng();
let len = 5;
let csp = CSMultiParams::<Bls12>::setup_gen_params(rng, len);
let serialized = serde_json::to_string(&csp).unwrap();
let _csp_des: CSMultiParams<Bls12> = serde_json::from_str(&serialized).unwrap();
}
#[test]
fn test_proof_commitment() {
let rng = &mut rand::thread_rng();
let channelId = Fr::rand(rng);
let wpk = Fr::rand(rng);
let t = Fr::rand(rng);
let bc = rng.gen_range(100, 1000);
let bm = rng.gen_range(100, 1000);
let wallet = Wallet::<Bls12> { channelId: channelId, wpk: wpk, bc: bc, bm: bm, close: None };
let comParams = CSMultiParams::setup_gen_params(rng, 4);
let com = comParams.commit(&wallet.as_fr_vec().clone(), &t);
let proof = CommitmentProof::<Bls12>::new(rng, &comParams, &com.c, &wallet.as_fr_vec(), &t, &vec!{});
let xvec: Vec<G1> = vec![proof.T.clone(), com.c];
let challenge = util::hash_g1_to_fr::<Bls12>(&xvec);
assert_eq!(proof.verify_proof(&comParams, &com.c, &challenge, None), true);
}
// add tests for extend/remove commits dynamically
}

View File

@ -1,46 +0,0 @@
/*
Pseudo-random Function (PRF) using Dodis-Yampolskiy PRF to support proofs of knowledge.
Properties:
- strong pr-image resistance
*/
use rand;
use bn::{Group, Fr, G1};
pub struct PRFKey {
s: Fr,
g: G1
}
// initialize the PRF with a seed and an optional generator
pub fn init_prf(s: Fr, gen: Option<G1>) -> PRFKey {
let rng = &mut rand::thread_rng();
let g = gen.unwrap_or(G1::random(rng));
return PRFKey { s: s, g: g };
}
// compute the PRF given the key and an input
pub fn compute(key: &PRFKey, x: Fr) -> G1 {
let r = key.s + x;
return key.g * r.inverse().unwrap();
}
#[cfg(test)]
mod tests {
use super::*;
use rand::{Rng, thread_rng};
use bn::{Fr, G1, Group};
#[test]
fn prf_works() {
let rng = &mut rand::thread_rng();
let s = Fr::random(rng);
let key = init_prf(s, None);
let x = Fr::random(rng);
let y = compute(&key, x);
let z = compute(&key, x + Fr::from_str("1").unwrap());
// confirm that PRF(k, x) != PRF(k, x+1)
assert!(y != z);
}
}

View File

@ -1,97 +0,0 @@
/*
Symmetric Key Encryption Scheme.
*/
use std::fmt;
use sodiumoxide;
use sodiumoxide::init;
use sodiumoxide::crypto::secretbox;
pub struct SymCT {
nonce: secretbox::Nonce,
ciphertext: Vec<u8>
}
impl fmt::Display for SymCT {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut y_s = String::new();
for y in self.ciphertext.iter() {
y_s = format!("{}{:x}", y_s, y);
}
write!(f, "CT : (ct=0x{})", y_s)
}
}
#[derive(Clone)]
pub struct SymKey {
key: secretbox::Key,
l: i32
}
pub fn init_mod() {
sodiumoxide::init();
}
pub fn keygen(l: i32) -> SymKey {
// TODO: make sure key is a l-bit key
return SymKey { key: secretbox::gen_key(), l: l };
}
pub fn encrypt(key: &SymKey, plaintext: &String) -> SymCT {
let nonce = secretbox::gen_nonce();
let pt = plaintext.as_bytes();
let ct = secretbox::seal(pt, &nonce, &key.key);
return SymCT { nonce: nonce, ciphertext: ct };
}
pub fn decrypt(key: &SymKey, ciphertext: &SymCT) -> String {
let nonce = ciphertext.nonce;
let pt = secretbox::open(&ciphertext.ciphertext, &nonce, &key.key).unwrap();
let plaintext = String::from_utf8(pt).expect("Found invalid UTF-8");
return plaintext;
}
#[cfg(test)]
mod tests {
use super::*;
use rand::{Rng, thread_rng};
#[test]
fn symenc_dec_works() {
init_mod();
// SymKeyEnc tests
// TODO: figure out how to apply this to secretbox
let l = 128;
let key1 = keygen(l);
// println!("key: {:?}", key);
let pt1 = String::from("hello world");
let ciphertext = encrypt(&key1, &pt1);
//println!("{}", ciphertext);
let pt2 = decrypt(&key1, &ciphertext);
//println!("Recovered plaintext: {}", pt2);
assert!(pt1 == pt2);
}
#[test]
#[should_panic]
fn symenc_dec_should_fail() {
init_mod();
// SymKeyEnc tests
// TODO: figure out how to apply this to secretbox
let l = 128;
let key1 = keygen(l);
let key2 = keygen(l);
let pt1 = String::from("hello world");
let ciphertext = encrypt(&key1, &pt1);
let pt3 = decrypt(&key2, &ciphertext);
// should fail and panic
}
}

189
src/util.rs Normal file
View File

@ -0,0 +1,189 @@
use super::*;
use pairing::Engine;
use ff::{PrimeField};
use sha2::Digest;
pub fn is_vec_fr_equal<E: Engine>(a: &Vec<E::Fr>, b: &Vec<E::Fr>) -> bool {
(a.len() == b.len()) &&
a.iter()
.zip(b)
.all(|(a, b)| a == b)
}
pub fn is_vec_g1_equal<E: Engine>(a: &Vec<E::G1>, b: &Vec<E::G1>) -> bool {
(a.len() == b.len()) &&
a.iter()
.zip(b)
.all(|(a, b)| a == b)
}
pub fn is_vec_g2_equal<E: Engine>(a: &Vec<E::G2>, b: &Vec<E::G2>) -> bool {
(a.len() == b.len()) &&
a.iter()
.zip(b)
.all(|(a, b)| a == b)
}
pub fn hash_g1_to_fr<E: Engine>(x: &Vec<E::G1>) -> E::Fr {
let mut x_vec: Vec<u8> = Vec::new();
for i in x.iter() {
x_vec.extend(format!("{}", i).bytes());
}
hash_to_fr::<E>(x_vec)
}
pub fn hash_g2_to_fr<E: Engine>(x: &E::G2) -> E::Fr {
let mut x_vec: Vec<u8> = Vec::new();
x_vec.extend(format!("{}", x).bytes());
hash_to_fr::<E>(x_vec)
}
pub fn fmt_bytes_to_int(bytearray: [u8; 64]) -> String {
let mut result: String = "".to_string();
for byte in bytearray.iter() {
// Decide if you want upper- or lowercase results,
// padding the values to two characters, spaces
// between bytes, etc.
let s = format!("{}", *byte as u8);
result = result + &s;
}
let s = match result.starts_with('0') {
true => result[1..].to_string(),
false => result.to_string()
};
return s;
}
pub fn compute_the_hash<E: Engine>(bytes: &Vec<u8>) -> E::Fr {
let mut hasher = sha2::Sha512::new();
hasher.input(&bytes.as_slice());
let sha2_digest = hasher.result();
let mut hash_buf: [u8; 64] = [0; 64];
hash_buf.copy_from_slice(&sha2_digest[0..64]);
let hexresult = fmt_bytes_to_int(hash_buf);
return E::Fr::from_str(&hexresult).unwrap();
}
pub fn hash_to_fr<E: Engine>(byteVec: Vec<u8>) -> E::Fr {
return compute_the_hash::<E>(&byteVec);
}
pub fn hash_pubkey_to_fr<E: Engine>(wpk: &secp256k1::PublicKey) -> E::Fr {
let x_slice = wpk.serialize_uncompressed();
return compute_the_hash::<E>(&x_slice.to_vec());
}
pub fn convert_int_to_fr<E: Engine>(value: i64) -> E::Fr {
if value > 0 {
return E::Fr::from_str(value.to_string().as_str()).unwrap();
} else {
// negative value
let value2 = value * -1;
let mut res = E::Fr::zero();
let val = E::Fr::from_str(value2.to_string().as_str()).unwrap();
res.sub_assign(&val);
return res;
}
}
pub fn compute_pub_key_fingerprint(wpk: &secp256k1::PublicKey) -> String {
let x_slice = wpk.serialize();
let mut hasher = sha2::Sha512::new();
hasher.input(&x_slice.to_vec());
let sha2_digest = hasher.result();
// let sha2_digest = sha512::hash(&x_slice);
let h = format!("{:x}", HexSlice::new(&sha2_digest[0..16]));
return h;
}
pub fn hash_buffer_to_fr<'a, E: Engine>(prefix: &'a str, buf: &[u8; 64]) -> E::Fr {
let mut input_buf = Vec::new();
input_buf.extend_from_slice(prefix.as_bytes());
input_buf.extend_from_slice(buf);
return compute_the_hash::<E>(&input_buf);
}
pub fn hash_to_slice(input_buf: &Vec<u8>) -> [u8; 32] {
let mut hasher = sha2::Sha512::new();
hasher.input(&input_buf.as_slice());
let sha2_digest = hasher.result();
let mut hash_buf: [u8; 32] = [0; 32];
hash_buf.copy_from_slice(&sha2_digest[0..32]);
return hash_buf;
}
#[derive(Clone, Serialize, Deserialize)]
pub struct RevokedMessage {
pub msgtype: String,
pub wpk: secp256k1::PublicKey
}
impl RevokedMessage {
pub fn new(_msgtype: String, _wpk: secp256k1::PublicKey) -> RevokedMessage {
RevokedMessage {
msgtype: _msgtype, wpk: _wpk
}
}
pub fn hash<E: Engine>(&self) -> Vec<E::Fr> {
let mut v: Vec<E::Fr> = Vec::new();
let mut input_buf = Vec::new();
input_buf.extend_from_slice(self.msgtype.as_bytes());
v.push(hash_to_fr::<E>(input_buf));
v.push(hash_pubkey_to_fr::<E>(&self.wpk));
return v;
}
// return a message digest (32-bytes)
pub fn hash_to_slice(&self) -> [u8; 32] {
let mut input_buf = Vec::new();
input_buf.extend_from_slice(self.msgtype.as_bytes());
input_buf.extend_from_slice(&self.wpk.serialize_uncompressed());
return hash_to_slice(&input_buf);
}
}
#[cfg(test)]
mod tests {
use super::*;
use pairing::bls12_381::{Bls12, G2};
use pairing::CurveProjective;
#[test]
fn hash_g2_to_fr_works() {
let mut two = G2::one();
two.double();
assert_eq!(format!("{}", hash_g2_to_fr::<Bls12>(&two).into_repr()),
"0x27cd26f702a777dbf782534ae6bf2ec4aa6cb4617c8366f10f59bef13beb8c56");
}
#[test]
fn hash_to_fr_works() {
let mut two = G2::one();
two.double();
let mut x_vec: Vec<u8> = Vec::new();
x_vec.extend(format!("{}", two).bytes());
assert_eq!(format!("{}", hash_to_fr::<Bls12>(x_vec).into_repr()),
"0x27cd26f702a777dbf782534ae6bf2ec4aa6cb4617c8366f10f59bef13beb8c56");
}
#[test]
fn fmt_byte_to_int_works() {
assert_eq!(fmt_bytes_to_int([12, 235, 23, 123, 13, 43, 12, 235, 23, 123, 13, 43, 12, 235, 23, 123, 13, 43, 12, 235, 23, 123, 13, 43, 12, 235, 23, 123, 13, 43, 12, 235, 23, 123, 13, 43, 12, 235, 23, 123, 13, 43, 12, 235, 23, 123, 13, 43, 12, 235, 23, 123, 13, 43, 12, 235, 23, 123, 13, 43, 12, 235, 23, 123]),
"122352312313431223523123134312235231231343122352312313431223523123134312235231231343122352312313431223523123134312235231231343122352312313431223523123");
}
#[test]
fn convert_int_to_fr_works() {
assert_eq!(format!("{}", convert_int_to_fr::<Bls12>(1).into_repr()),
"0x0000000000000000000000000000000000000000000000000000000000000001");
assert_eq!(format!("{}", convert_int_to_fr::<Bls12>(-1).into_repr()),
"0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000");
assert_eq!(format!("{}", convert_int_to_fr::<Bls12>(365).into_repr()),
"0x000000000000000000000000000000000000000000000000000000000000016d");
}
}

50
src/wallet.rs Normal file
View File

@ -0,0 +1,50 @@
extern crate pairing;
use super::*;
use pairing::Engine;
use ff::PrimeField;
use util::hash_to_fr;
use std::fmt;
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "<E as ff::ScalarEngine>::Fr: serde::Serialize"))]
#[serde(bound(deserialize = "<E as ff::ScalarEngine>::Fr: serde::Deserialize<'de>"))]
pub struct Wallet<E: Engine> {
pub channelId: E::Fr,
pub wpk: E::Fr,
pub bc: i64,
pub bm: i64,
pub close: Option<E::Fr>,
}
impl<E: Engine> Wallet<E> {
pub fn as_fr_vec(&self) -> Vec<E::Fr> {
if self.close.is_some() {
vec!(self.channelId, self.wpk, E::Fr::from_str(&self.bc.to_string()).unwrap(), E::Fr::from_str(&self.bm.to_string()).unwrap(), self.close.unwrap())
} else {
vec!(self.channelId, self.wpk, E::Fr::from_str(&self.bc.to_string()).unwrap(), E::Fr::from_str(&self.bm.to_string()).unwrap())
}
}
pub fn without_close(&self) -> Vec<E::Fr> {
vec!(self.channelId, 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());
return self.as_fr_vec();
}
}
impl<E: Engine> fmt::Display for Wallet<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.close.is_some() {
let close_str = self.close.unwrap();
write!(f, "Wallet : (\nchannelId={}\nwpk={}\nbc={}\nbm={}\nclose={}\n)", &self.channelId, &self.wpk, &self.bc, &self.bm, close_str)
} else {
write!(f, "Wallet : (\nchannelId={}\nwpk={}\nbc={}\nbm={}\nclose=None\n)", &self.channelId, &self.wpk, &self.bc, &self.bm)
}
}
}