Skip to content

Commit 3650674

Browse files
committed
Initial commit
1 parent a03957c commit 3650674

22 files changed

+28571
-1
lines changed

Circuits/DepositCircuit.h

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
#ifndef _DEPOSITCIRCUIT_H_
2+
#define _DEPOSITCIRCUIT_H_
3+
4+
#include "../Utils/Constants.h"
5+
#include "../Utils/Data.h"
6+
7+
#include "../ThirdParty/BigInt.hpp"
8+
#include "ethsnarks.hpp"
9+
#include "utils.hpp"
10+
#include "gadgets/sha256_many.hpp"
11+
12+
using namespace ethsnarks;
13+
14+
namespace Loopring
15+
{
16+
17+
class DepositGadget : public GadgetT
18+
{
19+
public:
20+
const Constants& constants;
21+
22+
VariableArrayT accountID;
23+
VariableArrayT tokenID;
24+
25+
libsnark::dual_variable_gadget<FieldT> amount;
26+
libsnark::dual_variable_gadget<FieldT> publicKeyX;
27+
libsnark::dual_variable_gadget<FieldT> publicKeyY;
28+
29+
BalanceState balanceBefore;
30+
AccountState accountBefore;
31+
VariableT uncappedBalanceAfter;
32+
MinGadget cappedBalanceAfter;
33+
BalanceState balanceAfter;
34+
UpdateBalanceGadget updateBalance;
35+
AccountState accountAfter;
36+
UpdateAccountGadget updateAccount;
37+
38+
DepositGadget(
39+
ProtoboardT& pb,
40+
const Constants& _constants,
41+
const VariableT& root,
42+
const std::string& prefix
43+
) :
44+
GadgetT(pb, prefix),
45+
46+
constants(_constants),
47+
48+
accountID(make_var_array(pb, TREE_DEPTH_ACCOUNTS, FMT(prefix, ".accountID"))),
49+
tokenID(make_var_array(pb, TREE_DEPTH_TOKENS, FMT(prefix, ".tokenID"))),
50+
51+
amount(pb, NUM_BITS_AMOUNT, FMT(prefix, ".amount")),
52+
publicKeyX(pb, 256, FMT(prefix, ".publicKeyX")),
53+
publicKeyY(pb, 256, FMT(prefix, ".publicKeyY")),
54+
55+
// Balance
56+
balanceBefore({
57+
make_variable(pb, FMT(prefix, ".before.balance")),
58+
make_variable(pb, FMT(prefix, ".tradingHistoryRoot"))
59+
}),
60+
uncappedBalanceAfter(make_variable(pb, FMT(prefix, ".uncappedBalanceAfter"))),
61+
cappedBalanceAfter(pb, uncappedBalanceAfter, constants.maxAmount, NUM_BITS_AMOUNT + 1, FMT(prefix, ".cappedBalanceAfter")),
62+
balanceAfter({
63+
cappedBalanceAfter.result(),
64+
balanceBefore.tradingHistory
65+
}),
66+
// Account
67+
accountBefore({
68+
make_variable(pb, FMT(prefix, ".publicKeyX_before")),
69+
make_variable(pb, FMT(prefix, ".publicKeyY_before")),
70+
make_variable(pb, FMT(prefix, ".nonce")),
71+
make_variable(pb, FMT(prefix, ".balancesRoot_before"))
72+
}),
73+
// Update balance
74+
updateBalance(pb, accountBefore.balancesRoot, tokenID, balanceBefore, balanceAfter, FMT(prefix, ".updateBalance")),
75+
accountAfter({
76+
publicKeyX.packed,
77+
publicKeyY.packed,
78+
accountBefore.nonce,
79+
updateBalance.getNewRoot()
80+
}),
81+
// Update account
82+
updateAccount(pb, root, accountID, accountBefore, accountAfter, FMT(prefix, ".updateAccount"))
83+
{
84+
85+
}
86+
87+
const VariableT getNewAccountsRoot() const
88+
{
89+
return updateAccount.result();
90+
}
91+
92+
const std::vector<VariableArrayT> getOnchainData() const
93+
{
94+
return {constants.accountPadding, accountID,
95+
publicKeyX.bits, publicKeyY.bits,
96+
tokenID,
97+
amount.bits};
98+
}
99+
100+
void generate_r1cs_witness(const Deposit& deposit)
101+
{
102+
accountID.fill_with_bits_of_field_element(pb, deposit.accountUpdate.accountID);
103+
tokenID.fill_with_bits_of_field_element(pb, deposit.balanceUpdate.tokenID);
104+
105+
amount.bits.fill_with_bits_of_field_element(pb, deposit.amount);
106+
amount.generate_r1cs_witness_from_bits();
107+
publicKeyX.bits.fill_with_bits_of_field_element(pb, deposit.accountUpdate.after.publicKey.x);
108+
publicKeyX.generate_r1cs_witness_from_bits();
109+
publicKeyY.bits.fill_with_bits_of_field_element(pb, deposit.accountUpdate.after.publicKey.y);
110+
publicKeyY.generate_r1cs_witness_from_bits();
111+
112+
pb.val(balanceBefore.balance) = deposit.balanceUpdate.before.balance;
113+
pb.val(balanceBefore.tradingHistory) = deposit.balanceUpdate.before.tradingHistoryRoot;
114+
115+
pb.val(uncappedBalanceAfter) = deposit.balanceUpdate.before.balance + deposit.amount;
116+
cappedBalanceAfter.generate_r1cs_witness();
117+
118+
pb.val(accountBefore.publicKeyX) = deposit.accountUpdate.before.publicKey.x;
119+
pb.val(accountBefore.publicKeyY) = deposit.accountUpdate.before.publicKey.y;
120+
pb.val(accountBefore.nonce) = deposit.accountUpdate.before.nonce;
121+
pb.val(accountBefore.balancesRoot) = deposit.accountUpdate.before.balancesRoot;
122+
123+
updateBalance.generate_r1cs_witness(deposit.balanceUpdate.proof);
124+
updateAccount.generate_r1cs_witness(deposit.accountUpdate.proof);
125+
}
126+
127+
void generate_r1cs_constraints()
128+
{
129+
amount.generate_r1cs_constraints(true);
130+
publicKeyX.generate_r1cs_constraints(true);
131+
publicKeyY.generate_r1cs_constraints(true);
132+
133+
pb.add_r1cs_constraint(ConstraintT(balanceBefore.balance + amount.packed, 1, uncappedBalanceAfter), "balanceBefore + amount == uncappedBalanceAfter");
134+
cappedBalanceAfter.generate_r1cs_constraints();
135+
136+
updateBalance.generate_r1cs_constraints();
137+
updateAccount.generate_r1cs_constraints();
138+
}
139+
};
140+
141+
class DepositCircuit : public GadgetT
142+
{
143+
public:
144+
145+
unsigned int numAccounts;
146+
std::vector<DepositGadget> deposits;
147+
148+
libsnark::dual_variable_gadget<FieldT> publicDataHash;
149+
PublicDataGadget publicData;
150+
151+
Constants constants;
152+
153+
libsnark::dual_variable_gadget<FieldT> exchangeID;
154+
libsnark::dual_variable_gadget<FieldT> merkleRootBefore;
155+
libsnark::dual_variable_gadget<FieldT> merkleRootAfter;
156+
157+
VariableArrayT depositBlockHashStart;
158+
libsnark::dual_variable_gadget<FieldT> startIndex;
159+
libsnark::dual_variable_gadget<FieldT> count;
160+
161+
std::vector<sha256_many> hashers;
162+
163+
DepositCircuit(ProtoboardT& pb, const std::string& prefix) :
164+
GadgetT(pb, prefix),
165+
166+
publicDataHash(pb, 256, FMT(prefix, ".publicDataHash")),
167+
publicData(pb, publicDataHash, FMT(prefix, ".publicData")),
168+
169+
constants(pb, FMT(prefix, ".constants")),
170+
171+
exchangeID(pb, 32, FMT(prefix, ".exchangeID")),
172+
merkleRootBefore(pb, 256, FMT(prefix, ".merkleRootBefore")),
173+
merkleRootAfter(pb, 256, FMT(prefix, ".merkleRootAfter")),
174+
175+
depositBlockHashStart(make_var_array(pb, 256, FMT(prefix, ".depositBlockHashStart"))),
176+
startIndex(pb, 32, FMT(prefix, ".startIndex")),
177+
count(pb, 32, FMT(prefix, ".count"))
178+
{
179+
180+
}
181+
182+
~DepositCircuit()
183+
{
184+
185+
}
186+
187+
void generate_r1cs_constraints(int numAccounts)
188+
{
189+
this->numAccounts = numAccounts;
190+
191+
pb.set_input_sizes(1);
192+
193+
constants.generate_r1cs_constraints();
194+
195+
exchangeID.generate_r1cs_constraints(true);
196+
merkleRootBefore.generate_r1cs_constraints(true);
197+
merkleRootAfter.generate_r1cs_constraints(true);
198+
199+
publicData.add(exchangeID.bits);
200+
publicData.add(merkleRootBefore.bits);
201+
publicData.add(merkleRootAfter.bits);
202+
publicData.add(flattenReverse({depositBlockHashStart}));
203+
for (size_t j = 0; j < numAccounts; j++)
204+
{
205+
VariableT depositAccountsRoot = (j == 0) ? merkleRootBefore.packed : deposits.back().getNewAccountsRoot();
206+
deposits.emplace_back(
207+
pb,
208+
constants,
209+
depositAccountsRoot,
210+
std::string("deposit_") + std::to_string(j)
211+
);
212+
deposits.back().generate_r1cs_constraints();
213+
214+
VariableArrayT depositBlockHash = (j == 0) ? depositBlockHashStart : hashers.back().result().bits;
215+
216+
// Hash data from deposit
217+
std::vector<VariableArrayT> depositData = deposits.back().getOnchainData();
218+
std::vector<VariableArrayT> hashBits;
219+
hashBits.push_back(flattenReverse({depositBlockHash}));
220+
hashBits.insert(hashBits.end(), depositData.begin(), depositData.end());
221+
hashers.emplace_back(pb, flattenReverse(hashBits), std::string("hash_") + std::to_string(j));
222+
hashers.back().generate_r1cs_constraints();
223+
}
224+
225+
// Add the block hash
226+
publicData.add(flattenReverse({hashers.back().result().bits}));
227+
publicData.add(startIndex.bits);
228+
publicData.add(count.bits);
229+
230+
// Check the input hash
231+
publicDataHash.generate_r1cs_constraints(true);
232+
publicData.generate_r1cs_constraints();
233+
234+
// Check the new merkle root
235+
forceEqual(pb, deposits.back().getNewAccountsRoot(), merkleRootAfter.packed, "newMerkleRoot");
236+
}
237+
238+
void printInfo()
239+
{
240+
std::cout << pb.num_constraints() << " constraints (" << (pb.num_constraints() / numAccounts) << "/deposit)" << std::endl;
241+
}
242+
243+
bool generateWitness(const DepositBlock& block)
244+
{
245+
constants.generate_r1cs_witness();
246+
247+
exchangeID.bits.fill_with_bits_of_field_element(pb, block.exchangeID);
248+
exchangeID.generate_r1cs_witness_from_bits();
249+
250+
merkleRootBefore.bits.fill_with_bits_of_field_element(pb, block.merkleRootBefore);
251+
merkleRootBefore.generate_r1cs_witness_from_bits();
252+
merkleRootAfter.bits.fill_with_bits_of_field_element(pb, block.merkleRootAfter);
253+
merkleRootAfter.generate_r1cs_witness_from_bits();
254+
255+
// Store the starting hash
256+
for (unsigned int i = 0; i < 256; i++)
257+
{
258+
pb.val(depositBlockHashStart[255 - i]) = block.startHash.test_bit(i);
259+
}
260+
// printBits("start hash input: 0x", depositBlockHashStart.get_bits(pb), true);
261+
262+
startIndex.bits.fill_with_bits_of_field_element(pb, block.startIndex);
263+
startIndex.generate_r1cs_witness_from_bits();
264+
count.bits.fill_with_bits_of_field_element(pb, block.count);
265+
count.generate_r1cs_witness_from_bits();
266+
267+
for(unsigned int i = 0; i < block.deposits.size(); i++)
268+
{
269+
deposits[i].generate_r1cs_witness(block.deposits[i]);
270+
}
271+
272+
for (auto& hasher : hashers)
273+
{
274+
hasher.generate_r1cs_witness();
275+
}
276+
printBits("DepositBlockHash: 0x", hashers.back().result().bits.get_bits(pb));
277+
278+
publicData.generate_r1cs_witness();
279+
280+
return true;
281+
}
282+
};
283+
284+
}
285+
286+
#endif

0 commit comments

Comments
 (0)