Skip to content

Commit 9dac6d1

Browse files
authored
Calldata compression (#1)
* Added transformation of ring data to make the it easier to compress * Fixed ring settlement without DA * Renamed numElements to blockSize for consistency * More flexible ring data transformation * Small data transform change
1 parent 3650674 commit 9dac6d1

File tree

6 files changed

+243
-34
lines changed

6 files changed

+243
-34
lines changed

Circuits/RingSettlementCircuit.h

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,91 @@ using namespace ethsnarks;
1818
namespace Loopring
1919
{
2020

21+
class TransformRingSettlementDataGadget : public GadgetT
22+
{
23+
public:
24+
25+
const unsigned int ringSize = 25 * 8;
26+
27+
VariableArrayT data;
28+
Bitstream transformedData;
29+
unsigned int numRings;
30+
31+
std::vector<XorArrayGadget> xorGadgets;
32+
33+
TransformRingSettlementDataGadget(
34+
ProtoboardT& pb,
35+
const std::string& prefix
36+
) :
37+
GadgetT(pb, prefix)
38+
{
39+
numRings = 0;
40+
}
41+
42+
VariableArrayT result()
43+
{
44+
return flatten(transformedData.data);
45+
}
46+
47+
void generate_r1cs_witness()
48+
{
49+
for (unsigned int i = 0; i < xorGadgets.size(); i++)
50+
{
51+
xorGadgets[i].generate_r1cs_witness();
52+
}
53+
}
54+
55+
void generate_r1cs_constraints(unsigned int numRings, const VariableArrayT& data)
56+
{
57+
this->numRings = numRings;
58+
this->data = data;
59+
assert(numRings > 0);
60+
assert(numRings * ringSize == data.size());
61+
62+
// XOR compress
63+
Bitstream compressedData;
64+
compressedData.add(subArray(data, 0, ringSize));
65+
for (unsigned int i = 1; i < numRings; i++)
66+
{
67+
unsigned int previousRingStart = (i - 1) * ringSize;
68+
unsigned int ringStart = i * ringSize;
69+
70+
xorGadgets.emplace_back(pb, subArray(data, previousRingStart, 5 * 8),
71+
subArray(data, ringStart, 5 * 8),
72+
std::string("xor_") + std::to_string(i));
73+
xorGadgets.back().generate_r1cs_constraints();
74+
compressedData.add(xorGadgets.back().result());
75+
compressedData.add(subArray(data, ringStart + 5 * 8, ringSize - 5 * 8));
76+
}
77+
78+
// Transform
79+
struct Range
80+
{
81+
unsigned int offset;
82+
unsigned int length;
83+
};
84+
std::vector<std::vector<Range>> ranges;
85+
ranges.push_back({{0, 40}}); // ringMatcherID + fFee + tokenID
86+
ranges.push_back({{40, 40}}); // orderA.orderID + orderB.orderID
87+
ranges.push_back({{80, 40}}); // orderA.accountID + orderB.accountID
88+
ranges.push_back({{120, 8}, {160, 8}}); // orderA.tokenS + orderB.tokenS
89+
ranges.push_back({{128, 24},{168, 24}}); // orderA.fillS + orderB.fillS
90+
ranges.push_back({{152, 8}}); // orderA.data
91+
ranges.push_back({{192, 8}}); // orderB.data
92+
for (const std::vector<Range>& subRanges : ranges)
93+
{
94+
for (unsigned int i = 0; i < numRings; i++)
95+
{
96+
for (const Range& subRange : subRanges)
97+
{
98+
unsigned int ringStart = i * ringSize;
99+
transformedData.add(subArray(flatten(compressedData.data), ringStart + subRange.offset, subRange.length));
100+
}
101+
}
102+
}
103+
}
104+
};
105+
21106
class RingSettlementGadget : public GadgetT
22107
{
23108
public:
@@ -49,6 +134,9 @@ class RingSettlementGadget : public GadgetT
49134
OrderGadget orderA;
50135
OrderGadget orderB;
51136

137+
ForceNotEqualGadget accountA_neq_ringMatcher;
138+
ForceNotEqualGadget accountB_neq_ringMatcher;
139+
52140
OrderMatchingGadget orderMatching;
53141

54142
TernaryGadget uFillS_A;
@@ -147,6 +235,9 @@ class RingSettlementGadget : public GadgetT
147235
orderA(pb, params, constants, _exchangeID, FMT(prefix, ".orderA")),
148236
orderB(pb, params, constants, _exchangeID, FMT(prefix, ".orderB")),
149237

238+
accountA_neq_ringMatcher(pb, orderA.accountID.packed, minerAccountID.packed, FMT(prefix, ".accountA_neq_ringMatcher")),
239+
accountB_neq_ringMatcher(pb, orderB.accountID.packed, minerAccountID.packed, FMT(prefix, ".accountB_neq_ringMatcher")),
240+
150241
// Match orders
151242
orderMatching(pb, constants, _timestamp, orderA, orderB, FMT(prefix, ".orderMatching")),
152243

@@ -350,11 +441,16 @@ class RingSettlementGadget : public GadgetT
350441
orderA.generate_r1cs_witness(ringSettlement.ring.orderA,
351442
ringSettlement.accountUpdate_A.before,
352443
ringSettlement.balanceUpdateS_A.before,
353-
ringSettlement.balanceUpdateB_A.before);
444+
ringSettlement.balanceUpdateB_A.before,
445+
ringSettlement.tradeHistoryUpdate_A.before);
354446
orderB.generate_r1cs_witness(ringSettlement.ring.orderB,
355447
ringSettlement.accountUpdate_B.before,
356448
ringSettlement.balanceUpdateS_B.before,
357-
ringSettlement.balanceUpdateB_B.before);
449+
ringSettlement.balanceUpdateB_B.before,
450+
ringSettlement.tradeHistoryUpdate_B.before);
451+
452+
accountA_neq_ringMatcher.generate_r1cs_witness();
453+
accountB_neq_ringMatcher.generate_r1cs_witness();
358454

359455
// Match orders
360456
orderMatching.generate_r1cs_witness();
@@ -457,6 +553,9 @@ class RingSettlementGadget : public GadgetT
457553
orderA.generate_r1cs_constraints();
458554
orderB.generate_r1cs_constraints();
459555

556+
accountA_neq_ringMatcher.generate_r1cs_constraints();
557+
accountB_neq_ringMatcher.generate_r1cs_constraints();
558+
460559
// Match orders
461560
orderMatching.generate_r1cs_constraints();
462561

@@ -529,6 +628,8 @@ class RingSettlementCircuit : public GadgetT
529628
std::vector<RingSettlementGadget*> ringSettlements;
530629

531630
libsnark::dual_variable_gadget<FieldT> publicDataHash;
631+
Bitstream dataAvailabityData;
632+
TransformRingSettlementDataGadget transformData;
532633
PublicDataGadget publicData;
533634

534635
Constants constants;
@@ -555,6 +656,7 @@ class RingSettlementCircuit : public GadgetT
555656
GadgetT(pb, prefix),
556657

557658
publicDataHash(pb, 256, FMT(prefix, ".publicDataHash")),
659+
transformData(pb, FMT(prefix, ".transformData")),
558660
publicData(pb, publicDataHash, FMT(prefix, ".publicData")),
559661

560662
constants(pb, FMT(prefix, ".constants")),
@@ -644,7 +746,7 @@ class RingSettlementCircuit : public GadgetT
644746
if (onchainDataAvailability)
645747
{
646748
// Store data from ring settlement
647-
publicData.add(ringSettlements.back()->getPublicData());
749+
dataAvailabityData.add(ringSettlements.back()->getPublicData());
648750
}
649751
}
650752

@@ -662,6 +764,13 @@ class RingSettlementCircuit : public GadgetT
662764
FMT(annotation_prefix, ".updateAccount_O"));
663765
updateAccount_O->generate_r1cs_constraints();
664766

767+
if (onchainDataAvailability)
768+
{
769+
// Transform the data
770+
transformData.generate_r1cs_constraints(numRings, flattenReverse(dataAvailabityData.data));
771+
publicData.add(flattenReverse({transformData.result()}));
772+
}
773+
665774
// Check the input hash
666775
publicDataHash.generate_r1cs_constraints(true);
667776
publicData.generate_r1cs_constraints();
@@ -715,6 +824,10 @@ class RingSettlementCircuit : public GadgetT
715824
updateAccount_P->generate_r1cs_witness(block.accountUpdate_P.proof);
716825
updateAccount_O->generate_r1cs_witness(block.accountUpdate_O.proof);
717826

827+
if (onchainDataAvailability)
828+
{
829+
transformData.generate_r1cs_witness();
830+
}
718831
publicData.generate_r1cs_witness();
719832

720833
return true;

Gadgets/MathGadgets.h

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,92 @@ class NotGadget : public GadgetT
497497
}
498498
};
499499

500+
class XorGadget : public GadgetT
501+
{
502+
public:
503+
VariableT A;
504+
VariableT B;
505+
VariableT C;
506+
507+
XorGadget(
508+
ProtoboardT& pb,
509+
const VariableT& _A,
510+
const VariableT& _B,
511+
const std::string& prefix
512+
) :
513+
GadgetT(pb, prefix),
514+
515+
A(_A),
516+
B(_B),
517+
518+
C(make_variable(pb, FMT(prefix, ".C")))
519+
{
520+
521+
}
522+
523+
const VariableT& result() const
524+
{
525+
return C;
526+
}
527+
528+
void generate_r1cs_witness()
529+
{
530+
pb.val(C) = pb.val(A) + pb.val(B) - ((pb.val(A) == FieldT::one() && pb.val(B) == FieldT::one()) ? 2 : 0);
531+
}
532+
533+
void generate_r1cs_constraints()
534+
{
535+
pb.add_r1cs_constraint(ConstraintT(2 * A, B, A + B - C), FMT(annotation_prefix, ".A ^ B == C"));
536+
}
537+
};
538+
539+
class XorArrayGadget : public GadgetT
540+
{
541+
public:
542+
VariableArrayT A;
543+
VariableArrayT B;
544+
VariableArrayT C;
545+
546+
XorArrayGadget(
547+
ProtoboardT& pb,
548+
VariableArrayT _A,
549+
VariableArrayT _B,
550+
const std::string& prefix
551+
) :
552+
GadgetT(pb, prefix),
553+
554+
A(_A),
555+
B(_B),
556+
557+
C(make_var_array(pb, A.size(), FMT(prefix, ".C")))
558+
{
559+
assert(A.size() == B.size());
560+
}
561+
562+
const VariableArrayT& result() const
563+
{
564+
return C;
565+
}
566+
567+
void generate_r1cs_witness()
568+
{
569+
for (unsigned int i = 0; i < C.size(); i++)
570+
{
571+
pb.val(C[i]) = pb.val(A[i]) + pb.val(B[i]) - ((pb.val(A[i]) == FieldT::one() && pb.val(B[i]) == FieldT::one()) ? 2 : 0);
572+
}
573+
// printBits("A: ", A.get_bits(pb));
574+
// printBits("B: ", B.get_bits(pb));
575+
// printBits("C: ", C.get_bits(pb));
576+
}
577+
578+
void generate_r1cs_constraints()
579+
{
580+
for (unsigned int i = 0; i < C.size(); i++)
581+
{
582+
pb.add_r1cs_constraint(ConstraintT(2 * A[i], B[i], A[i] + B[i] - C[i]), FMT(annotation_prefix, ".A ^ B == C"));
583+
}
584+
}
585+
};
500586

501587
class EqualGadget : public GadgetT
502588
{
@@ -1030,6 +1116,23 @@ class SignatureVerifier : public GadgetT
10301116
}
10311117
};
10321118

1119+
class Bitstream
1120+
{
1121+
public:
1122+
1123+
std::vector<VariableArrayT> data;
1124+
1125+
void add(const VariableArrayT& bits)
1126+
{
1127+
data.push_back(bits);
1128+
}
1129+
1130+
void add(const std::vector<VariableArrayT>& bits)
1131+
{
1132+
data.insert(data.end(), bits.begin(), bits.end());
1133+
}
1134+
};
1135+
10331136
class PublicDataGadget : public GadgetT
10341137
{
10351138
public:

Gadgets/OrderGadgets.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ class OrderGadget : public GadgetT
137137
}
138138

139139
void generate_r1cs_witness(const Order& order, const Account& account,
140-
const BalanceLeaf& balanceLeafS, const BalanceLeaf& balanceLeafB)
140+
const BalanceLeaf& balanceLeafS, const BalanceLeaf& balanceLeafB,
141+
const TradeHistoryLeaf& tradeHistoryLeaf)
141142
{
142143
exchangeID.bits.fill_with_bits_of_field_element(pb, order.exchangeID);
143144
exchangeID.generate_r1cs_witness_from_bits();
@@ -184,9 +185,9 @@ class OrderGadget : public GadgetT
184185
amountS_notZero.generate_r1cs_witness();
185186
amountB_notZero.generate_r1cs_witness();
186187

187-
pb.val(tradeHistoryFilled) = order.tradeHistoryFilled;
188-
pb.val(tradeHistoryCancelled) = order.tradeHistoryCancelled;
189-
pb.val(tradeHistoryOrderID) = order.tradeHistoryOrderID;
188+
pb.val(tradeHistoryFilled) = tradeHistoryLeaf.filled;
189+
pb.val(tradeHistoryCancelled) = tradeHistoryLeaf.cancelled;
190+
pb.val(tradeHistoryOrderID) = tradeHistoryLeaf.orderID;
190191

191192
tradeHistory.generate_r1cs_witness();
192193

Utils/Data.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,6 @@ class Order
171171
ethsnarks::FieldT rebateBips;
172172

173173
Signature signature;
174-
175-
ethsnarks::FieldT tradeHistoryFilled;
176-
ethsnarks::FieldT tradeHistoryCancelled;
177-
ethsnarks::FieldT tradeHistoryOrderID;
178174
};
179175

180176
void from_json(const json& j, Order& order)
@@ -200,10 +196,6 @@ void from_json(const json& j, Order& order)
200196
order.rebateBips = ethsnarks::FieldT(j.at("rebateBips"));
201197

202198
order.signature = j.at("signature").get<Signature>();
203-
204-
order.tradeHistoryFilled = ethsnarks::FieldT(j.at("tradeHistoryFilled").get<std::string>().c_str());
205-
order.tradeHistoryCancelled = ethsnarks::FieldT(j.at("tradeHistoryCancelled"));
206-
order.tradeHistoryOrderID = ethsnarks::FieldT(j.at("tradeHistoryOrderID"));
207199
}
208200

209201
class Ring

0 commit comments

Comments
 (0)