iden3 / circomlib Goto Github PK
View Code? Open in Web Editor NEWLibrary of basic circuits for circom
Library of basic circuits for circom
I understand how IsZero
works, but why can't we just constrain a signal like s === 0
? This would halve the number of constraints for checking that a value IsZero
, and could pass on significant savings.
This should form a valid R1CS constraint. I.e., for a constraint
Hi!
I am looking to build a circuit that verifies ECDSA signatures (NIST P-256 curve). Any pointers on how to proceed would be much appreciated.
Thanks,
Deepak
This would make updating and transitioning to circom2 easier, as hardhat-circom will support compiling with either version.
Hi Jordi,
Would you be interested in a pull request to add new circuits and JS code for the following features?
All the code is here: https://github.com/weijiekoh/snarschain
PedersenHash
circuit
EncodePedersenPoint
circuitEncodePedersenPoint
circuit
x
and y
(32-byte bigInts)x
, but its most significant 8 bits are the 8 most significant bits of y
.JoinHashes
circuit
left
and right
left
concatenated with the 128 LSBs of right
PedersenHashDouble
circuit
left
and right
, a 32-byte bigInt eachout[2]
: the x- and y-outputs of a Pedersen circuitEncodePedersenPoint
circuitThese circuits achieve two goals:
Have an easy-to-use Pedersen hash circuit which outputs 32 bytes. Since the Pedersen hash function outputs a point on the BabyJub curve, which is symmetrical across the y-axis, the only relevant data is the x-value and the sign of the y-value. However, the babyJub.packPoint()
function will cause an integer overflow within circom, so the next best option is to return 32 bytes consisting of the most significant 8 bits of y and the least significant 248 bits of x.
Have an easy-to-use Pedersen hash circuit which can be plugged in to a Merkle tree validator circuit. Since it needs to hash two 32-byte values, it takes 16 bytes from each input and then pipes them into the Pedersen single-input hash circuit described above.
I'm curious about what you think. If you like the idea, I'm happy to package what I've written as a PR to circomlib
and submit that. I'm also keen to improve EncodePedersenPoint
and JoinHashes
if they are incorrect or insecure.
Thanks!
// test.circom
include "../circomlib/circuits/bitify.circom";
include "../circomlib/circuits/escalarmulfix.circom";
template PublicKey() {
signal private input in;
signal output out[2];
component privBits = Num2Bits(253);
privBits.in <== in;
var BASE8 = [
5299619240641551281634865583518297030282874472190772894086521144482721001553,
16950150798460657717958625567821834550301663161624707787222815936182638968203
];
component mulFix = EscalarMulFix(253, BASE8);
for (var i = 0; i < 253; i++) {
mulFix.e[i] <== privBits.out[i];
}
out[0] <== mulFix.out[0];
out[1] <== mulFix.out[1];
}
component main = PublicKey();
I encountered the following problem, when I try to execute circom test.circom --r1cs --wasm --sym
to compile the circuit above.
test.circom:11,2-14,3 Sizes in assignment must be the same
{
"type": "OP",
"op": "=",
"values": [
{
"type": "DECLARE",
"declareType": "VARIABLE",
"name": {
"type": "VARIABLE",
"name": "BASE8",
"selectors": [],
"first_line": 11,
"first_column": 6,
"last_line": 11,
"last_column": 11
},
"first_line": 11,
"first_column": 2,
"last_line": 11,
"last_column": 11,
"refId": 3
},
{
"type": "ARRAY",
"values": [
{
"type": "NUMBER",
"value": "5299619240641551281634865583518297030282874472190772894086521144482721001553",
"first_line": 12,
"first_column": 4,
"last_line": 12,
"last_column": 80
},
{
"type": "NUMBER",
"value": "16950150798460657717958625567821834550301663161624707787222815936182638968203",
"first_line": 13,
"first_column": 4,
"last_line": 13,
"last_column": 81
}
],
"first_line": 11,
"first_column": 14,
"last_line": 14,
"last_column": 3
}
],
"first_line": 11,
"first_column": 2,
"last_line": 14,
"last_column": 3
}
=========== environment ===========
circom: 0.5.45
node: V14.15.0
Hello,
I was thinking, would it be possible to improve the current Readme.md in the circuits folder to explain what every single component does in better details, to facilitate easier user experience for new users?
With lower values of t
there are some tricks that can be applied to reduce gas cost.
add
instead of addmod
in mds multiplication and round constant phases. bn254 base field is suitable for deferred reductions upto 5 since 2**256 / Q ~= 5.29
Applying these for (3, 8, 55)
setup, I'm observing reduction of gas cost from 35k to 21k. Here you can check the example.
Just cloned the repo, ran npm i
, and mocha --grep "MiMC Circuit test"
and getting
MiMC Circuit test
1) "before all" hook for "Should check constrain"
0 passing (310ms)
1 failing
1) MiMC Circuit test
"before all" hook for "Should check constrain":
LinkError: WebAssembly.instantiate(): Import #1 module="runtime" function="printErrorMessage" error: function import requires a callable
at builder (node_modules/circom_tester/wasm/witness_calculator.js:19:40)
at async wasm_tester (node_modules/circom_tester/wasm/tester.js:60:16)
at async Context.<anonymous> (test/mimccircuit.js:15:19)
Any idea what's happening? This is on a macbook pro m1.
In circomlib/circuits/mux1.circom
it is possible to specify an input that is not equal 0 or 1 and get an arbitrary output instead of one of inputs. The following check should be added at line 24:
s * (1 - s) === 0
Possibly other multiplexer implementations are also affected
https://github.com/iden3/circomlib/blob/master/circuits/babyjub.circom#L96-L97
Why in this circuit is there a check that the private key is within 253 bits? I thought valid private keys are within 254 bit prime field corresponding to BN254?
Hi!I am a newbie here.
According to the description of the circom documentation webpage,the circom language is parametric to param p and param p can be changed without affecting the rest of the language using GLOBAL_FIELD_P. How can I use GLOBAL_FIELD_P to modify param p ?I don't know how to accomplish this.
https://github.com/iden3/circomlib/blob/master/src/mimcsponge_gencontract.js
The contract generated by mimcsponge_gencontracts.js is quite cheap. But because it's an external library which uses public
function we have to pay more than 1400 gas compared to when if we use internal
function by including the library inside the contract.
Therefore it'll be really good if we have the solidity inline-assembly version of the MiMC.sol. It is hard to make other contracts include the library with only the asm.
This seems to be going MSB to LSB where it should be going LSB to MSB..
circomlib/circuits/smt/smtverifier.circom
Line 108 in cff5ab6
I am working on a circuit to verify ed25519 signatures. The problem I am facing is that the Global_P hardcoded in the compiler is set according to altbn128, which is different from the finite field prime used by ed25519. Circom applies a modulo Global_P for every operation which means I can't perform arithmetic operations according to ed25519.
Is there a workaround for this?
Inside the SHA-256 > main.circom
There is a line - include "sha256_2.jaz";
And this is the code of sha256_2.jaz -
`
component num2bits[2] = Num2Bits(216);
component bits2num = Bits2Num(216);
num2bits[0].inp <== a;
num2bits[1].inp <== b;
component sha256compression = Sha256compression() ;
var i;
for (i=0; i<216; i++) {
sha256compression.inp[i] <== num2bits[0].out[i];
sha256compression.inp[i+216] <== num2bits[1].out[i];
}
[for (i=432; i<247; i++) {](https://hexang.com/yh/circom/-/blob/281b52339c2717a1b3a519b12661912220f35d89/circuits/sha256/sha256_2.jaz)
sha256compression.inp[i] <== 0;
}
`
What is the use of second loop? Because by definition it does nothing.
Hi
I am a newbie here,
How to send strings or array characters as input?
This is state change diagram in source file (smtprocessor.circom
):
Insert to a used leaf.
=====================
STATE OLD STATE NEW STATE
===== ========= =========
oldRoot newRoot
▲ ▲
│ │
┌───────┐ ┏━━━┻━━━┓ ┌───────┐ ┏━━━┻━━━┓
top │Sibling├────▶┃ Hash ┃◀─┐ │Sibling├────▶┃ Hash ┃◀─┐
└───────┘ ┗━━━━━━━┛ │ └───────┘ ┗━━━━━━━┛ │
│ │
│ │
┏━━━┻━━━┓ ┌───────┐ ┏━━━┻━━━┓ ┌───────┐
top ┌─────▶┃ Hash ┃◀──┤Sibling│ ┌─────▶┃ Hash ┃◀──┤Sibling│
│ ┗━━━━━━━┛ └───────┘ │ ┗━━━━━━━┛ └───────┘
│ │
│ │
┌───────┐ ┏━━━┻━━━┓ ┌───────┐ ┏━━━┻━━━┓
top │Sibling├──▶┃ Hash ┃◀─────┐ │Sibling├──▶┃ Hash ┃◀─────┐
└───────┘ ┗━━━━━━━┛ │ └───────┘ ┗━━━━━━━┛ │
│ │
│ │
┌────┴────┐ ┏━━━┻━━━┓ ┌───────┐
bot │Old1Leaf │ ┌─────▶┃ Hash ┃◀──┼─ 0 │
└─────────┘ │ ┗━━━━━━━┛ └───────┘
│
│
┏━━━━━━━┓ ┌───────┐ ┏━━━┻━━━┓
bot ┃ Hash ┃ │ 0 ─┼──▶┃ Hash ┃◀─────┐
┗━━━━━━━┛ └───────┘ ┗━━━━━━━┛ │
│
│
┏━━━━━━━┓ ┏━━━┻━━━┓ ┌───────┐
bot ┃ Hash ┃ ┌─────▶┃ Hash ┃◀──│ 0 │
┗━━━━━━━┛ │ ┗━━━━━━━┛ └───────┘
│
│
┏━━━━━━━┓ ┌─────────┐ ┏━━━┻━━━┓ ┌─────────┐
new1 ┃ Hash ┃ │Old1Leaf ├──▶┃ Hash ┃◀──│New1Leaf │
┗━━━━━━━┛ └─────────┘ ┗━━━━━━━┛ └─────────┘
┏━━━━━━━┓ ┏━━━━━━━┓
na ┃ Hash ┃ ┃ Hash ┃
┗━━━━━━━┛ ┗━━━━━━━┛
┏━━━━━━━┓ ┏━━━━━━━┓
na ┃ Hash ┃ ┃ Hash ┃
┗━━━━━━━┛ ┗━━━━━━━┛
In bot state, Old1Leaf suddenly is suddenly changed to Hash of 0, and in new1 state, old1leaf value and new1leaf value are combined to hash.
In my understanding, when old1leaf is changed to new1leaf, new SMT root hash have to combined only with new1leaf and new child, comparing with previous old SMT root hash is combined only with old1leaf and old child.
Is this kind of insertion case? But why were nodes suddenly created and inserted in the middle of highest bot level and new1 level?
I need a little more explanation. I'm trying to utilize this in my rollup code, but I've been looking at the SMTprocessor code implementation for 3 days trying to understand the internals.
And refering to this code block:
component topSwitcher = Switcher();
topSwitcher.sel <== fnc[0]*fnc[1];
topSwitcher.L <== levels[0].oldRoot;
topSwitcher.R <== levels[0].newRoot;
component checkOldInput = ForceEqualIfEnabled();
checkOldInput.enabled <== enabled;
checkOldInput.in[0] <== oldRoot;
checkOldInput.in[1] <== topSwitcher.outL;
Contrary to its initial intention, I think it does not seem for fnc[1] to be used very meaningfully.
Because I cannot find any DELETE mechanism described below block:
Fnction
fnc[0] fnc[1]
0 0 NOP
0 1 UPDATE
1 0 INSERT
1 1 DELETE
in this block:
component keysOk = MultiAND(3);
keysOk.in[0] <== 1-fnc[0];
keysOk.in[1] <== fnc[1];
keysOk.in[2] <== 1-areKeyEquals.out;
keysOk.out === 0;
When fnc[0] == 0, fnc[1] == 1, areKeyEquals.out == 0, it will cause circuit error.
In other words, it appears to be code to check that the keys are not different from each other.
If the keys are the same, this constraint must pass. Even if the keys are different, (0,0) NOP, (1,0) INSERT, and (1,1) DELETE will pass.
Why did you create this constraint with MultiAND?
I looked and studied as much as I could, but I couldn't understand it in the end because I lacked understanding.
This is an amazing library, but it's unfortunate that the users like me are inexperienced... :(
Thank you for your work.
I'd like to convert my 256 bits data to bits array by Num2Bits
. This is my input
77566169416202731942676121969132614529774733997845438300974228917086841813606
// exact 256 bits length
My circom code,
template Example_Num2Bits(nBits) {
signal input data
component external_nullifier_bits = Num2Bits(nBits);
external_nullifier_bits.in <== data;
}
component main = Example_Num2Bits(256)
I got this error when calculating witness,
ERROR: Error: Constraint doesn't match main.external_nullifier_bits: /home/ubuntu/zk/circomlib/circuits/bitify.circom:35:4 -> 11901440800684906275936904733360789264129640796597335269879616357359416326755 != 77566169416202731942676121969132614529774733997845438300974228917086841813606
The value converted by Num2Bits
is 11901440800684906275936904733360789264129640796597335269879616357359416326755
No idea what happened.
-p bls12381
to compile the circuit with circomThen i want to use my js to generate the witness, but it meets error, which is currentStateCommitmentHasher.hash === currentStateCommitment;
my js code is using poseidon hash with poseidon([0, 0, 0, 0, 0])
like this call.
This error only happen in the case of using bls12381 to compile the circuit, but is normal in bn128. Also, i find the source code of poseidon in circomlib and it uses the const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
. I think it cause the error, So i want to know if there is an implement of bls12381 with poseidon?
The template SegmentMulFix()
computes in Montgomery form the point V*base
where V = (3*nWindows-bit input_value) + 2^(3*nWindows+1)+2^(3*nWindows-3)+2^(3*nWindows-6)+..+1
. Therefore, for nWindows>82
we can have input_value as big as 2^(249)-1
and the value of V
exceeding 2^(250)+2^(249)+2^(246)
, which is bigger than the prime subgroup order, possibly causing an overflow and infinity point computation in Montgomery. For such inputs the template is satisfied for some invalid input-output tuples and thus is flawed. Thus the nWindow
value should not exceed 82. Currently it is set to 83 for many inputs in EscalarMulFix
.
The simplest way to fix it is to use at most 246-bit segments and thus at most 82 windows.
In this circom file , NOT gate is setting to be :
template NOT() {
signal input in;
signal output out;
out <== 1 + in - 2*in;
}
Why not just directly setting that as : out <== 1 - in ?
This is the version of the environment I am using:
Maybe I'm using the wrong method, the hash I generate with the pedersen tool in circomlibjs
doesn't match the hash generated by pedersen in circom
, so generating witness keeps failing.
This has been bothering me for a long time, I would like to ask for help, thank you very much!
circomlib: ^2.0.5
circomlibjs: ^0.1.7
I had some problems reproducing the Tornado Cash MerkleTree circom
, So I first tested the pedersen hash circom and then ran into problems, here is the circom used for testing:
pragma circom 2.0.0;
include "../node_modules/circomlib/circuits/pedersen.circom";
include "../node_modules/circomlib/circuits/bitify.circom";
template CommitmentHasher() {
signal input secret;
signal input commitment; // I change here `outpu => input` for test
component hasher = Pedersen(248);
component secretBits = Num2Bits(248);
secretBits.in <== secret;
for (var i = 0; i < 248; i++) {
hasher.in[i] <== secretBits.out[i];
}
commitment === hasher.out[0]; // I change here `<== => ===` for test
}
This is the code I used to generate the test input.json file:
const crypto = require("crypto");
const { groth16 } = require("snarkjs");
const circomlibjs = require("circomlibjs");
/** Generate random buffer of specified byte length */
const rbuffer = (nbytes) => crypto.randomBytes(nbytes);
/** Compute pedersen hash */
async function pedersenHash(data) {
let babyJub = await circomlibjs.buildBabyjub()
let ph = await circomlibjs.buildPedersenHash()
return babyJub.unpackPoint(ph.hash(data))[0];
}
function toBigIntLE(buf) {
const reversed = Buffer.from(buf);
reversed.reverse();
const hex = reversed.toString("hex");
if (hex.length === 0) {
return BigInt(0);
}
return BigInt(`0x${hex}`);
}
const rbuf = utils.rbuffer(31);
// generate circom input as follow
const secret = utils.toBigIntLE(rbuf).toString();
const commitment = await utils.pedersenHash(rbuf);
Hi, I am getting the error below when trying to use the EdDSA and Num2Bits modules.
$ snarkjs calculatewitness
Error: Constraint doesn't match: 1 != 0
...
input.json
{ "M": 87521618088882671231069284
, "A": 55827404635365964770721635799969315856041517429760659189665076684285324169570
, "R": 103858265219596690085663683153809217622835068605340394886551250625399267718227
, "S": 11198587322283910980185217763598004772992920884777713219704247405759630588676
}
circuit.circom
include "./circomlib/circuits/bitify.circom";
include "./circomlib/circuits/eddsa.circom";
template Test(n) {
signal input M;
signal input A;
signal input R;
signal input S;
var i;
component eddsaVer = EdDSAVerifier(n);
component bM = Num2Bits(n);
component bA = Num2Bits(256);
component bR = Num2Bits(256);
component bS = Num2Bits(256);
bM.in <== M;
bA.in <== A;
bR.in <== R;
bS.in <== S;
for (i=0; i<n; i++) {
eddsaVer.msg[i] <== bM.out[i];
}
for (i=0; i<256; i++) {
eddsaVer.A[i] <== bA.out[i];
eddsaVer.R8[i] <== bR.out[i];
eddsaVer.S[i] <== bS.out[i];
}
}
component main = Test(88);
When I run this script:
console.log(eddsa.verifyMiMCSponge(msgHash, signature, pubKey));
First problem would happen in 'bigInt' with 'ReferenceError: bigInt is not defined'
code:
let Pright = babyJub.mulPointEscalar(A, hm.times(bigInt("8")));
After I change bigInt to BigInt, it would run error below:
/Users/howard/Desktop/circom-playground/node_modules/circomlib/src/eddsa.js:207
let Pright = babyJub.mulPointEscalar(A, hm.times(BigInt("8")));
^
TypeError: hm.times is not a function
at Object.verifyMiMCSponge (/Users/howard/Desktop/circom-playground/node_modules/circomlib/src/eddsa.js:207:48)
at Object.<anonymous> (/Users/howard/Desktop/circom-playground/eddsa/js/index.js:28:19)
sorry, I can't really understand how these cryptography works under the hood.
I was trying to reproduce the constants as described at the top here:
https://github.com/iden3/circomlib/blob/master/circuits/poseidon_constants.circom
by running
sage generate_parameters_grain.sage 1 0 254 2 8 56 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
I see the discrepancy in the result, i.e. the 3rd constant that sage outputs is
0x1e28a1d935698ad1142e51182bb54cf4a00ea5aabd6268bd317ea977cc154a30
whereas in your file it is
0x250f5116a417d76aaa422952fcc5b33329f7714fc26d56c0432507fc740a87c4
Could you pls explain how you obtained those constants? Thanks.
Hey folks,
I am looking for a zkp circuit to verify an ethereum signature. I think that's ECDSA - secp256k1? I need to use for my hackathon project. Is there a implementation for the same already available somewhere?
As a PoC I can use EdDSA signatures as well for my project, however the EdDSA test
tests in this repo are failing. Can you help me fix them?
Thank you.
pedersen_old.circom
26: var nexps = ((n-1) \ 250) + 1;
escalarmulfix.circom
238: var nsegments = (n-1)\246 +1; // 249 probably would work. But I'm not sure and for security I keep 246
254: nWindows = ((nseg - 1)\3)+1;
gates.circom
81: var n1 = n\2;
82: var n2 = n-n\2;
pedersen.circom
192: var nSegments = ((n-1)\200)+1;
202: nWindows = ((nBits - 1)\4)+1;
escalarmulany.circom
135: var nsegments = (n-1)\148 +1;
sha256/sha256.circom
16: nBlocks = ((nBits + 64)\512)+1;
Originally:
template Num2Bits(n) {
signal input in;
signal output out[n];
var lc1=0;
var e2=1;
for (var i = 0; i<n; i++) {
out[i] <-- (in >> i) & 1;
out[i] * (out[i] -1 ) === 0;
lc1 += out[i] * e2;
e2 = e2+e2;
}
lc1 === in;
}
But I think the last check is not secure, since lc1 is not a singal.
A possible modification would be:
template Num2Bits(n) {
signal input in;
signal output out[n];
signal lc1[n+1];
lc1[0] <== 0;
var e2 = 1;
for (var i = 0; i<n; i++) {
out[i] <-- (in >> i) & 1;
out[i] * (out[i] -1 ) === 0;
lc1[i+1] <== out[i] * e2 + lc1[i];
e2 = e2 + e2;
}
lc1[n] === in;
}
Hi,
Is the circomlib2 branch production-ready? I noticed it has a number of improvements over the master branch, and I was wondering if it's considered as safe for production as the master branch.
Thanks!
All templates should have an output, for instance:
circomlib/circuits/aliascheck.circom
Line 31 in d6e6a3b
Let's see an example.
Using bitify & sha256 circuits.
sha256 circuit expects bit array input. In the array the lower index, 0, corresponds to the higher bit, the higher index, lets say 7th corresponds to lower bit.
Example:
237 (decimal) = 0xed = 11101101 (binay)
the input signal file would be:
{ "number" = ["1","1","1","0","1","1","0","1"] }
visually correct, but the mapping signal would be:
number[0]=1;
number[1]=0;
number[2]=1;
number[3]=1;
number[4]=0;
number[5]=1;
number[6]=1;
number[7]=1;
That does not match the array order in the JSON format. Anyway SAH256 accepts that order to compute the proper hash.
On the other hand Num2bits operation in bitify libray would do the follwing transformation:
Numb2bits(237)
num2bits.out[0]=1;
num2bits.out[1]=1;
num2bits.out[2]=1;
num2bits.out[3]=0;
num2bits.out[4]=1;
num2bits.out[5]=1;
num2bits.out[6]=0;
num2bits.out[7]=1;
What looks correct as the array index corresponds with the bit weigth in the binary form.
Unfortunatelly SHA256 expects bit input order in the signal as the reading order and not in the logical order (like Num2bits works) so
sha256.in <== num2bits.out
does not work, and it requires a bit swap.
Line 8 in d91afa8
In this circuit:
pragma circom 2.0.0;
include "../circomlib/circuits/eddsamimc.circom";
include "../circomlib/circuits/mimc.circom";
template VerifyEdDSAMiMC(k) {
signal input from_x;
signal input from_y;
signal input R8x;
signal input R8y;
signal input S;
signal input preimage[k];
// hash them inside the circuit
component M = MultiMiMC7(k, 91);
M.in[0] <== preimage[0];
M.in[1] <== preimage[1];
M.in[2] <== preimage[2];
component verifier = EdDSAMiMCVerifier();
verifier.enabled <== 1;
verifier.Ax <== from_x;
verifier.Ay <== from_y;
verifier.R8x <== R8x;
verifier.R8y <== R8y;
verifier.S <== S;
verifier.M <== M.out;
}
component main {public [from_x, from_y, R8x, R8y, S]} = VerifyEdDSAMiMC(3);
this error appears upon compilation:
error[T3001]: Exception caused by invalid access
┌─ "circuit.circom":27:20
│
27 │ verifier.M <== M.out;
│ ^^^^^ found here
│
= call trace:
->VerifyEdDSAMiMC
unless I add
...
component M = MultiMiMC7(k, 91);
M.in[0] <== preimage[0];
M.in[1] <== preimage[1];
M.in[2] <== preimage[2];
M.k <== 0; // THIS HERE
...
why is it so?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.