Merge pull request #7 from boltlabs-inc/master
Sync'ing with Bolt Labs - libbolt fork
This commit is contained in:
commit
2561ece47f
|
@ -1 +1,4 @@
|
|||
.idea/
|
||||
target
|
||||
Cargo.lock
|
||||
py/__pycache__/
|
29
Cargo.toml
29
Cargo.toml
|
@ -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"]
|
||||
|
||||
|
|
28
Makefile
28
Makefile
|
@ -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
264
README.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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!("******************************************");
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
File diff suppressed because it is too large
Load Diff
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_ ]
|
|
@ -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_ ]
|
|
@ -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_
|
File diff suppressed because it is too large
Load Diff
|
@ -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_
|
|
@ -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_
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -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()
|
|
@ -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()
|
|
@ -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, ¶ms, 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, ¶ms, &sig, None, None);
|
||||
let state1 = kp.public.prove_commitment(rng, ¶ms, &sig, None, None);
|
||||
let state2 = kp.public.prove_commitment(rng, ¶ms, &sig, None, None);
|
||||
let state3 = kp.public.prove_commitment(rng, ¶ms, &sig, None, None);
|
||||
let state4 = kp.public.prove_commitment(rng, ¶ms, &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()))
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
308
src/clproto.rs
308
src/clproto.rs
|
@ -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);
|
||||
}
|
||||
}
|
412
src/clsigs.rs
412
src/clsigs.rs
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
2087
src/lib.rs
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
}
|
||||
}
|
60
src/ote.rs
60
src/ote.rs
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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(>);
|
||||
}
|
||||
(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
|
||||
}
|
46
src/prf.rs
46
src/prf.rs
|
@ -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);
|
||||
}
|
||||
}
|
97
src/sym.rs
97
src/sym.rs
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue