33
44#include " ../Gadgets/MatchingGadgets.h"
55
6+ struct OrderState
7+ {
8+ Order order;
9+ Account account;
10+ BalanceLeaf balanceLeafS;
11+ BalanceLeaf balanceLeafB;
12+ TradeHistoryLeaf tradeHistoryLeaf;
13+ };
14+
15+ OrderState setOrderState (const OrderState& orderState,
16+ const FieldT& orderID,
17+ const FieldT& amountS, const FieldT& amountB, bool buy,
18+ const FieldT& balanceS,
19+ const FieldT& filled, bool cancelled, const FieldT& tradeHistoryOrderID,
20+ bool allOrNone = false )
21+ {
22+ OrderState newOrderState (orderState);
23+ newOrderState.order .orderID = orderID;
24+ newOrderState.order .amountS = amountS;
25+ newOrderState.order .amountB = amountB;
26+ newOrderState.order .buy = buy ? 1 : 0 ;
27+ newOrderState.order .allOrNone = allOrNone ? 1 : 0 ;
28+ newOrderState.balanceLeafS .balance = balanceS;
29+ newOrderState.balanceLeafB .balance = 0 ;
30+ newOrderState.tradeHistoryLeaf .filled = filled;
31+ newOrderState.tradeHistoryLeaf .cancelled = cancelled ? 1 : 0 ;
32+ newOrderState.tradeHistoryLeaf .orderID = tradeHistoryOrderID;
33+ return newOrderState;
34+ };
35+
36+ bool lt (const FieldT& A, const FieldT& B)
37+ {
38+ return toBigInt (A) < toBigInt (B);
39+ }
40+
41+ bool lte (const FieldT& A, const FieldT& B)
42+ {
43+ return toBigInt (A) <= toBigInt (B);
44+ }
45+
46+ FieldT muldiv (const FieldT& V, const FieldT& N, const FieldT& D)
47+ {
48+ return toFieldElement (validate (toBigInt (V) * toBigInt (N)) / toBigInt (D));
49+ }
50+
651TEST_CASE (" RequireFillRate" , " [RequireFillRateGadget]" )
752{
853 unsigned int maxLength = NUM_BITS_AMOUNT;
@@ -129,6 +174,212 @@ TEST_CASE("RequireFillRate", "[RequireFillRateGadget]")
129174 }}
130175}
131176
177+ TEST_CASE (" RequireFillLimit" , " [RequireFillLimitGadget]" )
178+ {
179+ unsigned int numIterations = 1024 ;
180+
181+ RingSettlementBlock block = getRingSettlementBlock ();
182+ REQUIRE (block.ringSettlements .size () > 0 );
183+ const RingSettlement& ringSettlement = block.ringSettlements [0 ];
184+
185+ const Order& order = ringSettlement.ring .orderA ;
186+ const Account& account = ringSettlement.accountUpdate_A .before ;
187+ const BalanceLeaf& balanceLeafS = ringSettlement.balanceUpdateS_A .before ;
188+ const BalanceLeaf& balanceLeafB = ringSettlement.balanceUpdateB_A .before ;
189+ const TradeHistoryLeaf& tradeHistoryLeaf = ringSettlement.tradeHistoryUpdate_A .before ;
190+ const OrderState _orderState = {order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf};
191+
192+ unsigned int numTradeHistoryLeafs = pow (2 , NUM_BITS_TRADING_HISTORY);
193+ const FieldT orderID = rand () % numTradeHistoryLeafs;
194+
195+ enum class Expected
196+ {
197+ Valid,
198+ Invalid,
199+ Automatic
200+ };
201+ auto checkFillLimitChecked = [_orderState](const FieldT& _amountS, const FieldT& _amountB, bool _buy, unsigned int _orderID,
202+ unsigned int _tradeHistoryOrderID, const FieldT& _filled, bool _cancelled,
203+ const FieldT& _fillAmountS, const FieldT& _fillAmountB,
204+ Expected expected = Expected::Automatic)
205+ {
206+ protoboard<FieldT> pb;
207+
208+ OrderState orderState = setOrderState (
209+ _orderState,
210+ _orderID,
211+ _amountS, _amountB, _buy,
212+ getMaxFieldElement (NUM_BITS_AMOUNT),
213+ _filled, _cancelled, _tradeHistoryOrderID
214+ );
215+
216+ VariableT fillAmountS = make_variable (pb, _fillAmountS, " fillAmountS" );
217+ VariableT fillAmountB = make_variable (pb, _fillAmountB, " fillAmountB" );
218+
219+ Constants constants (pb, " constants" );
220+
221+ VariableT exchangeID = make_variable (pb, 0 , " exchangeID" );
222+ VariableT timestamp = make_variable (pb, 0 , " timestamp" );
223+
224+ jubjub::Params params;
225+ OrderGadget order (pb, params, constants, exchangeID, " .order" );
226+ order.generate_r1cs_witness (orderState.order , orderState.account , orderState.balanceLeafS , orderState.balanceLeafB , orderState.tradeHistoryLeaf );
227+
228+ RequireFillLimitGadget requireFillLimit (pb, constants, order, fillAmountS, fillAmountB, " requireFillRateGadget" );
229+ requireFillLimit.generate_r1cs_constraints ();
230+ requireFillLimit.generate_r1cs_witness ();
231+
232+ // Simulate
233+ FieldT limit;
234+ FieldT filledAfter;
235+ if (_buy)
236+ {
237+ limit = (pb.val (order.tradeHistory .getCancelled ()) == 1 ) ? pb.val (order.tradeHistory .getFilled ()) : _amountB;
238+ filledAfter = pb.val (order.tradeHistory .getFilled ()) + _fillAmountB;
239+ }
240+ else
241+ {
242+ limit = (pb.val (order.tradeHistory .getCancelled ()) == 1 ) ? pb.val (order.tradeHistory .getFilled ()) : _amountS;
243+ filledAfter = pb.val (order.tradeHistory .getFilled ()) + _fillAmountS;
244+ }
245+ bool expectedAutomaticValid = lte (filledAfter, limit);
246+
247+ bool expectedValid = false ;
248+ if (expected == Expected::Automatic)
249+ {
250+ expectedValid = expectedAutomaticValid;
251+ }
252+ else
253+ {
254+ expectedValid = (expected == Expected::Valid) ? true : false ;
255+ }
256+ REQUIRE (expectedAutomaticValid == expectedValid);
257+
258+ REQUIRE (pb.is_satisfied () == expectedValid);
259+ if (expectedValid)
260+ {
261+ REQUIRE ((pb.val (requireFillLimit.getFilledAfter ()) == filledAfter));
262+ }
263+ };
264+
265+ unsigned int n = NUM_BITS_AMOUNT;
266+ FieldT maxAmount = getMaxFieldElement (NUM_BITS_AMOUNT);
267+
268+ SECTION (" order: 1/1, fill: 1/1" )
269+ {
270+ checkFillLimitChecked (1 , 1 , true , 0 ,
271+ 0 , 0 , false ,
272+ 1 , 1 , Expected::Valid);
273+ }
274+
275+ SECTION (" order: max/max, fill: max/max" )
276+ {
277+ checkFillLimitChecked (maxAmount, maxAmount, true , 0 ,
278+ 0 , 0 , false ,
279+ 1 , 1 , Expected::Valid);
280+ }
281+
282+ SECTION (" order: 1/1, fill: max/max" )
283+ {
284+ checkFillLimitChecked (1 , 1 , true , 0 ,
285+ 0 , 0 , false ,
286+ maxAmount, maxAmount, Expected::Invalid);
287+ }
288+
289+ SECTION (" order: max/max, fill: 1/1" )
290+ {
291+ checkFillLimitChecked (maxAmount, maxAmount, true , 0 ,
292+ 0 , 0 , false ,
293+ 1 , 1 , Expected::Valid);
294+ }
295+
296+ SECTION (" order: max/1, fill: max/1" )
297+ {
298+ checkFillLimitChecked (maxAmount, 1 , true , 0 ,
299+ 0 , 0 , false ,
300+ maxAmount, 1 , Expected::Valid);
301+ }
302+
303+ SECTION (" order: 1/max, fill: 1/max" )
304+ {
305+ checkFillLimitChecked (1 , maxAmount, true , 0 ,
306+ 0 , 0 , false ,
307+ 1 , maxAmount, Expected::Valid);
308+ }
309+
310+ SECTION (" cancelled" )
311+ {
312+ unsigned int orderID = rand () % NUM_BITS_ORDERID;
313+ checkFillLimitChecked (1 , 1 , true , orderID,
314+ orderID, 0 , true ,
315+ 1 , 1 , Expected::Invalid);
316+ }
317+
318+ SECTION (" reused" )
319+ {
320+ unsigned int orderID = rand () % NUM_BITS_TRADING_HISTORY;
321+ for (unsigned int i = 0 ; i < 2 ; i++)
322+ {
323+ checkFillLimitChecked (maxAmount, maxAmount, true , orderID + numTradeHistoryLeafs,
324+ orderID, maxAmount, i % 2 ,
325+ maxAmount, maxAmount, Expected::Valid);
326+ }
327+ }
328+
329+ SECTION (" trimmed" )
330+ {
331+ unsigned int orderID = rand () % NUM_BITS_TRADING_HISTORY;
332+ checkFillLimitChecked (1 , 1 , true , orderID,
333+ orderID + numTradeHistoryLeafs, maxAmount, false ,
334+ 1 , 1 , Expected::Invalid);
335+ }
336+
337+ SECTION (" Fill limits" )
338+ {
339+ unsigned int orderID = rand () % NUM_BITS_TRADING_HISTORY;
340+ unsigned int amountS = 1000 ;
341+ unsigned int amountB = 100 ;
342+
343+ // buy order
344+ for (unsigned int filled = 0 ; filled < amountB * 2 ; filled += 10 )
345+ {
346+ for (unsigned int fillB = 0 ; fillB < amountB * 2 ; fillB += 10 )
347+ {
348+ bool expectedValid = (filled + fillB <= amountB);
349+ checkFillLimitChecked (amountS, amountB, true , orderID,
350+ orderID, filled, false ,
351+ fillB * 9 , fillB,
352+ expectedValid ? Expected::Valid : Expected::Invalid);
353+ }
354+ }
355+
356+ // sell order
357+ for (unsigned int filled = 0 ; filled < amountS * 2 ; filled += 100 )
358+ {
359+ for (unsigned int fillS = 0 ; fillS < amountS * 2 ; fillS += 100 )
360+ {
361+ bool expectedValid = (filled + fillS <= amountS);
362+ checkFillLimitChecked (amountS, amountB, false , orderID,
363+ orderID, filled, false ,
364+ fillS, fillS / 9 ,
365+ expectedValid ? Expected::Valid : Expected::Invalid);
366+ }
367+ }
368+ }
369+
370+ SECTION (" Random" )
371+ {
372+ for (unsigned int i = 0 ; i < numIterations; i++)
373+ {
374+ bool buy = i % 2 ;
375+ bool cancelled = i % 5 ;
376+ checkFillLimitChecked (getRandomFieldElement (n), getRandomFieldElement (n), buy, rand () % NUM_BITS_ORDERID,
377+ rand () % NUM_BITS_ORDERID, getRandomFieldElement (n), cancelled,
378+ getRandomFieldElement (n), getRandomFieldElement (n));
379+ }
380+ }
381+ }
382+
132383TEST_CASE (" FeeCalculator" , " [FeeCalculatorGadget]" )
133384{
134385 unsigned int maxLength = NUM_BITS_AMOUNT;
@@ -216,14 +467,6 @@ TEST_CASE("FeeCalculator", "[FeeCalculatorGadget]")
216467 }
217468}
218469
219- struct OrderState
220- {
221- Order order;
222- Account account;
223- BalanceLeaf balanceLeafS;
224- BalanceLeaf balanceLeafB;
225- TradeHistoryLeaf tradeHistoryLeaf;
226- };
227470
228471namespace Simulator
229472{
@@ -246,21 +489,6 @@ namespace Simulator
246489 bool valid;
247490 };
248491
249- bool lt (const FieldT& A, const FieldT& B)
250- {
251- return toBigInt (A) < toBigInt (B);
252- }
253-
254- bool lte (const FieldT& A, const FieldT& B)
255- {
256- return toBigInt (A) <= toBigInt (B);
257- }
258-
259- FieldT muldiv (const FieldT& V, const FieldT& N, const FieldT& D)
260- {
261- return toFieldElement (validate (toBigInt (V) * toBigInt (N)) / toBigInt (D));
262- }
263-
264492 TradeHistory getTradeHistory (const OrderState& orderState)
265493 {
266494 FieldT filled = lt (orderState.tradeHistoryLeaf .orderID , orderState.order .orderID ) ? 0 : orderState.tradeHistoryLeaf .filled ;
@@ -351,27 +579,6 @@ namespace Simulator
351579
352580TEST_CASE (" OrderMatching" , " [OrderMatchingGadget]" )
353581{
354- auto setOrderState = [](const OrderState& orderState,
355- const FieldT& orderID,
356- const FieldT& amountS, const FieldT& amountB, bool buy,
357- const FieldT& balanceS,
358- const FieldT& filled, bool cancelled, const FieldT& tradeHistoryOrderID,
359- bool allOrNone = false )
360- {
361- OrderState newOrderState (orderState);
362- newOrderState.order .orderID = orderID;
363- newOrderState.order .amountS = amountS;
364- newOrderState.order .amountB = amountB;
365- newOrderState.order .buy = buy ? 1 : 0 ;
366- newOrderState.order .allOrNone = allOrNone ? 1 : 0 ;
367- newOrderState.balanceLeafS .balance = balanceS;
368- newOrderState.balanceLeafB .balance = 0 ;
369- newOrderState.tradeHistoryLeaf .filled = filled;
370- newOrderState.tradeHistoryLeaf .cancelled = cancelled ? 1 : 0 ;
371- newOrderState.tradeHistoryLeaf .orderID = tradeHistoryOrderID;
372- return newOrderState;
373- };
374-
375582 enum class ExpectedValid
376583 {
377584 Valid,
@@ -427,10 +634,10 @@ TEST_CASE("OrderMatching", "[OrderMatchingGadget]")
427634 REQUIRE (pb.is_satisfied () == (expectedSatisfied && expectedValidValue));
428635 if (expectedSatisfied && expectedValidValue)
429636 {
430- REQUIRE ((pb. val (orderMatching. getFillA_S ()) == pb. val (expectedFillS_A)) );
431- REQUIRE ((pb.val (orderMatching.getFillA_B ()) == pb.val (expectedFillS_B)));
432- REQUIRE ((pb. val (orderMatching. getFillB_S ()) == pb. val (expectedFillS_B)) );
433- REQUIRE ((pb.val (orderMatching.getFillB_B ()) == pb.val (expectedFillS_A)));
637+ auto tradeHistory_A = Simulator::getTradeHistory (orderStateA );
638+ REQUIRE ((pb.val (orderMatching.getFilledAfter_A ()) == (tradeHistory_A. filled + (orderStateA. order . buy == 1 ? pb.val (expectedFillS_B) : pb. val (expectedFillS_A)) )));
639+ auto tradeHistory_B = Simulator::getTradeHistory (orderStateB );
640+ REQUIRE ((pb.val (orderMatching.getFilledAfter_B ()) == (tradeHistory_B. filled + (orderStateB. order . buy == 1 ? pb.val (expectedFillS_A) : pb. val (expectedFillS_B)) )));
434641 }
435642 };
436643
0 commit comments