diff --git a/Circuits/RingSettlementCircuit.h b/Circuits/RingSettlementCircuit.h index 2781de6..92cb8de 100644 --- a/Circuits/RingSettlementCircuit.h +++ b/Circuits/RingSettlementCircuit.h @@ -18,6 +18,91 @@ using namespace ethsnarks; namespace Loopring { +class TransformRingSettlementDataGadget : public GadgetT +{ +public: + + const unsigned int ringSize = 25 * 8; + + VariableArrayT data; + Bitstream transformedData; + unsigned int numRings; + + std::vector xorGadgets; + + TransformRingSettlementDataGadget( + ProtoboardT& pb, + const std::string& prefix + ) : + GadgetT(pb, prefix) + { + numRings = 0; + } + + VariableArrayT result() + { + return flatten(transformedData.data); + } + + void generate_r1cs_witness() + { + for (unsigned int i = 0; i < xorGadgets.size(); i++) + { + xorGadgets[i].generate_r1cs_witness(); + } + } + + void generate_r1cs_constraints(unsigned int numRings, const VariableArrayT& data) + { + this->numRings = numRings; + this->data = data; + assert(numRings > 0); + assert(numRings * ringSize == data.size()); + + // XOR compress + Bitstream compressedData; + compressedData.add(subArray(data, 0, ringSize)); + for (unsigned int i = 1; i < numRings; i++) + { + unsigned int previousRingStart = (i - 1) * ringSize; + unsigned int ringStart = i * ringSize; + + xorGadgets.emplace_back(pb, subArray(data, previousRingStart, 5 * 8), + subArray(data, ringStart, 5 * 8), + std::string("xor_") + std::to_string(i)); + xorGadgets.back().generate_r1cs_constraints(); + compressedData.add(xorGadgets.back().result()); + compressedData.add(subArray(data, ringStart + 5 * 8, ringSize - 5 * 8)); + } + + // Transform + struct Range + { + unsigned int offset; + unsigned int length; + }; + std::vector> ranges; + ranges.push_back({{0, 40}}); // ringMatcherID + fFee + tokenID + ranges.push_back({{40, 40}}); // orderA.orderID + orderB.orderID + ranges.push_back({{80, 40}}); // orderA.accountID + orderB.accountID + ranges.push_back({{120, 8}, {160, 8}}); // orderA.tokenS + orderB.tokenS + ranges.push_back({{128, 24},{168, 24}}); // orderA.fillS + orderB.fillS + ranges.push_back({{152, 8}}); // orderA.data + ranges.push_back({{192, 8}}); // orderB.data + for (const std::vector& subRanges : ranges) + { + for (unsigned int i = 0; i < numRings; i++) + { + for (const Range& subRange : subRanges) + { + unsigned int ringStart = i * ringSize; + transformedData.add(subArray(flatten(compressedData.data), ringStart + subRange.offset, subRange.length)); + } + } + } + } +}; + class RingSettlementGadget : public GadgetT { public: @@ -49,6 +134,9 @@ class RingSettlementGadget : public GadgetT OrderGadget orderA; OrderGadget orderB; + ForceNotEqualGadget accountA_neq_ringMatcher; + ForceNotEqualGadget accountB_neq_ringMatcher; + OrderMatchingGadget orderMatching; TernaryGadget uFillS_A; @@ -147,6 +235,9 @@ class RingSettlementGadget : public GadgetT orderA(pb, params, constants, _exchangeID, FMT(prefix, ".orderA")), orderB(pb, params, constants, _exchangeID, FMT(prefix, ".orderB")), + accountA_neq_ringMatcher(pb, orderA.accountID.packed, minerAccountID.packed, FMT(prefix, ".accountA_neq_ringMatcher")), + accountB_neq_ringMatcher(pb, orderB.accountID.packed, minerAccountID.packed, FMT(prefix, ".accountB_neq_ringMatcher")), + // Match orders orderMatching(pb, constants, _timestamp, orderA, orderB, FMT(prefix, ".orderMatching")), @@ -350,11 +441,16 @@ class RingSettlementGadget : public GadgetT orderA.generate_r1cs_witness(ringSettlement.ring.orderA, ringSettlement.accountUpdate_A.before, ringSettlement.balanceUpdateS_A.before, - ringSettlement.balanceUpdateB_A.before); + ringSettlement.balanceUpdateB_A.before, + ringSettlement.tradeHistoryUpdate_A.before); orderB.generate_r1cs_witness(ringSettlement.ring.orderB, ringSettlement.accountUpdate_B.before, ringSettlement.balanceUpdateS_B.before, - ringSettlement.balanceUpdateB_B.before); + ringSettlement.balanceUpdateB_B.before, + ringSettlement.tradeHistoryUpdate_B.before); + + accountA_neq_ringMatcher.generate_r1cs_witness(); + accountB_neq_ringMatcher.generate_r1cs_witness(); // Match orders orderMatching.generate_r1cs_witness(); @@ -457,6 +553,9 @@ class RingSettlementGadget : public GadgetT orderA.generate_r1cs_constraints(); orderB.generate_r1cs_constraints(); + accountA_neq_ringMatcher.generate_r1cs_constraints(); + accountB_neq_ringMatcher.generate_r1cs_constraints(); + // Match orders orderMatching.generate_r1cs_constraints(); @@ -529,6 +628,8 @@ class RingSettlementCircuit : public GadgetT std::vector ringSettlements; libsnark::dual_variable_gadget publicDataHash; + Bitstream dataAvailabityData; + TransformRingSettlementDataGadget transformData; PublicDataGadget publicData; Constants constants; @@ -555,6 +656,7 @@ class RingSettlementCircuit : public GadgetT GadgetT(pb, prefix), publicDataHash(pb, 256, FMT(prefix, ".publicDataHash")), + transformData(pb, FMT(prefix, ".transformData")), publicData(pb, publicDataHash, FMT(prefix, ".publicData")), constants(pb, FMT(prefix, ".constants")), @@ -644,7 +746,7 @@ class RingSettlementCircuit : public GadgetT if (onchainDataAvailability) { // Store data from ring settlement - publicData.add(ringSettlements.back()->getPublicData()); + dataAvailabityData.add(ringSettlements.back()->getPublicData()); } } @@ -662,6 +764,13 @@ class RingSettlementCircuit : public GadgetT FMT(annotation_prefix, ".updateAccount_O")); updateAccount_O->generate_r1cs_constraints(); + if (onchainDataAvailability) + { + // Transform the data + transformData.generate_r1cs_constraints(numRings, flattenReverse(dataAvailabityData.data)); + publicData.add(flattenReverse({transformData.result()})); + } + // Check the input hash publicDataHash.generate_r1cs_constraints(true); publicData.generate_r1cs_constraints(); @@ -715,6 +824,10 @@ class RingSettlementCircuit : public GadgetT updateAccount_P->generate_r1cs_witness(block.accountUpdate_P.proof); updateAccount_O->generate_r1cs_witness(block.accountUpdate_O.proof); + if (onchainDataAvailability) + { + transformData.generate_r1cs_witness(); + } publicData.generate_r1cs_witness(); return true; diff --git a/Gadgets/MathGadgets.h b/Gadgets/MathGadgets.h index c50b28a..e3e5d8f 100644 --- a/Gadgets/MathGadgets.h +++ b/Gadgets/MathGadgets.h @@ -497,6 +497,92 @@ class NotGadget : public GadgetT } }; +class XorGadget : public GadgetT +{ +public: + VariableT A; + VariableT B; + VariableT C; + + XorGadget( + ProtoboardT& pb, + const VariableT& _A, + const VariableT& _B, + const std::string& prefix + ) : + GadgetT(pb, prefix), + + A(_A), + B(_B), + + C(make_variable(pb, FMT(prefix, ".C"))) + { + + } + + const VariableT& result() const + { + return C; + } + + void generate_r1cs_witness() + { + pb.val(C) = pb.val(A) + pb.val(B) - ((pb.val(A) == FieldT::one() && pb.val(B) == FieldT::one()) ? 2 : 0); + } + + void generate_r1cs_constraints() + { + pb.add_r1cs_constraint(ConstraintT(2 * A, B, A + B - C), FMT(annotation_prefix, ".A ^ B == C")); + } +}; + +class XorArrayGadget : public GadgetT +{ +public: + VariableArrayT A; + VariableArrayT B; + VariableArrayT C; + + XorArrayGadget( + ProtoboardT& pb, + VariableArrayT _A, + VariableArrayT _B, + const std::string& prefix + ) : + GadgetT(pb, prefix), + + A(_A), + B(_B), + + C(make_var_array(pb, A.size(), FMT(prefix, ".C"))) + { + assert(A.size() == B.size()); + } + + const VariableArrayT& result() const + { + return C; + } + + void generate_r1cs_witness() + { + for (unsigned int i = 0; i < C.size(); i++) + { + 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); + } + // printBits("A: ", A.get_bits(pb)); + // printBits("B: ", B.get_bits(pb)); + // printBits("C: ", C.get_bits(pb)); + } + + void generate_r1cs_constraints() + { + for (unsigned int i = 0; i < C.size(); i++) + { + pb.add_r1cs_constraint(ConstraintT(2 * A[i], B[i], A[i] + B[i] - C[i]), FMT(annotation_prefix, ".A ^ B == C")); + } + } +}; class EqualGadget : public GadgetT { @@ -1030,6 +1116,23 @@ class SignatureVerifier : public GadgetT } }; +class Bitstream +{ +public: + + std::vector data; + + void add(const VariableArrayT& bits) + { + data.push_back(bits); + } + + void add(const std::vector& bits) + { + data.insert(data.end(), bits.begin(), bits.end()); + } +}; + class PublicDataGadget : public GadgetT { public: diff --git a/Gadgets/OrderGadgets.h b/Gadgets/OrderGadgets.h index b4aacee..363bdea 100644 --- a/Gadgets/OrderGadgets.h +++ b/Gadgets/OrderGadgets.h @@ -137,7 +137,8 @@ class OrderGadget : public GadgetT } void generate_r1cs_witness(const Order& order, const Account& account, - const BalanceLeaf& balanceLeafS, const BalanceLeaf& balanceLeafB) + const BalanceLeaf& balanceLeafS, const BalanceLeaf& balanceLeafB, + const TradeHistoryLeaf& tradeHistoryLeaf) { exchangeID.bits.fill_with_bits_of_field_element(pb, order.exchangeID); exchangeID.generate_r1cs_witness_from_bits(); @@ -184,9 +185,9 @@ class OrderGadget : public GadgetT amountS_notZero.generate_r1cs_witness(); amountB_notZero.generate_r1cs_witness(); - pb.val(tradeHistoryFilled) = order.tradeHistoryFilled; - pb.val(tradeHistoryCancelled) = order.tradeHistoryCancelled; - pb.val(tradeHistoryOrderID) = order.tradeHistoryOrderID; + pb.val(tradeHistoryFilled) = tradeHistoryLeaf.filled; + pb.val(tradeHistoryCancelled) = tradeHistoryLeaf.cancelled; + pb.val(tradeHistoryOrderID) = tradeHistoryLeaf.orderID; tradeHistory.generate_r1cs_witness(); diff --git a/Utils/Data.h b/Utils/Data.h index 3fd6ed1..a038570 100644 --- a/Utils/Data.h +++ b/Utils/Data.h @@ -171,10 +171,6 @@ class Order ethsnarks::FieldT rebateBips; Signature signature; - - ethsnarks::FieldT tradeHistoryFilled; - ethsnarks::FieldT tradeHistoryCancelled; - ethsnarks::FieldT tradeHistoryOrderID; }; void from_json(const json& j, Order& order) @@ -200,10 +196,6 @@ void from_json(const json& j, Order& order) order.rebateBips = ethsnarks::FieldT(j.at("rebateBips")); order.signature = j.at("signature").get(); - - order.tradeHistoryFilled = ethsnarks::FieldT(j.at("tradeHistoryFilled").get().c_str()); - order.tradeHistoryCancelled = ethsnarks::FieldT(j.at("tradeHistoryCancelled")); - order.tradeHistoryOrderID = ethsnarks::FieldT(j.at("tradeHistoryOrderID")); } class Ring diff --git a/main.cpp b/main.cpp index 9df4222..da7edb6 100644 --- a/main.cpp +++ b/main.cpp @@ -287,10 +287,10 @@ int main (int argc, char **argv) // Read meta data int blockType = input["blockType"].get(); - unsigned int numElements = input["numElements"].get(); + unsigned int blockSize = input["blockSize"].get(); bool onchainDataAvailability = input["onchainDataAvailability"].get(); std::string strOnchainDataAvailability = onchainDataAvailability ? "_DA_" : "_"; - std::string postFix = strOnchainDataAvailability + std::to_string(numElements); + std::string postFix = strOnchainDataAvailability + std::to_string(blockSize); std::cout << "Building circuit... " << std::endl; std::string baseFilename = "keys/"; @@ -300,7 +300,7 @@ int main (int argc, char **argv) case 0: { baseFilename += "trade" + postFix; - if (!trade(mode, onchainDataAvailability, numElements, input, pb)) + if (!trade(mode, onchainDataAvailability, blockSize, input, pb)) { return 1; } @@ -309,7 +309,7 @@ int main (int argc, char **argv) case 1: { baseFilename += "deposit" + postFix; - if (!deposit(mode, numElements, input, pb)) + if (!deposit(mode, blockSize, input, pb)) { return 1; } @@ -318,7 +318,7 @@ int main (int argc, char **argv) case 2: { baseFilename += "withdraw_onchain" + postFix; - if (!onchainWithdraw(mode, onchainDataAvailability, numElements, input, pb)) + if (!onchainWithdraw(mode, onchainDataAvailability, blockSize, input, pb)) { return 1; } @@ -327,7 +327,7 @@ int main (int argc, char **argv) case 3: { baseFilename += "withdraw_offchain" + postFix; - if (!offchainWithdraw(mode, onchainDataAvailability, numElements, input, pb)) + if (!offchainWithdraw(mode, onchainDataAvailability, blockSize, input, pb)) { return 1; } @@ -336,7 +336,7 @@ int main (int argc, char **argv) case 4: { baseFilename += "cancel" + postFix; - if (!cancel(mode, onchainDataAvailability, numElements, input, pb)) + if (!cancel(mode, onchainDataAvailability, blockSize, input, pb)) { return 1; } diff --git a/service.cpp b/service.cpp index edc16de..c53567f 100644 --- a/service.cpp +++ b/service.cpp @@ -209,10 +209,10 @@ bool cancel(Mode mode, bool onchainDataAvailability, unsigned int numCancels, co std::string generateBaseFileName(const json& input) { // Read meta data int blockType = input["blockType"].get(); - unsigned int numElements = input["numElements"].get(); + unsigned int blockSize = input["blockSize"].get(); bool onchainDataAvailability = input["onchainDataAvailability"].get(); std::string strOnchainDataAvailability = onchainDataAvailability ? "_DA_" : "_"; - std::string postFix = strOnchainDataAvailability + std::to_string(numElements); + std::string postFix = strOnchainDataAvailability + std::to_string(blockSize); std::string baseFilename = "keys/"; switch(blockType) { @@ -253,14 +253,14 @@ std::string generateBaseFileName(const json& input) { int runCircuit(Mode mode, const json& input, ethsnarks::ProtoboardT& pb) { // Read meta data int blockType = input["blockType"].get(); - unsigned int numElements = input["numElements"].get(); + unsigned int blockSize = input["blockSize"].get(); bool onchainDataAvailability = input["onchainDataAvailability"].get(); switch(blockType) { case 0: { - if (!trade(mode, onchainDataAvailability, numElements, input, pb)) + if (!trade(mode, onchainDataAvailability, blockSize, input, pb)) { return 1; } @@ -268,7 +268,7 @@ int runCircuit(Mode mode, const json& input, ethsnarks::ProtoboardT& pb) { } case 1: { - if (!deposit(mode, numElements, input, pb)) + if (!deposit(mode, blockSize, input, pb)) { return 1; } @@ -276,7 +276,7 @@ int runCircuit(Mode mode, const json& input, ethsnarks::ProtoboardT& pb) { } case 2: { - if (!onchainWithdraw(mode, onchainDataAvailability, numElements, input, pb)) + if (!onchainWithdraw(mode, onchainDataAvailability, blockSize, input, pb)) { return 1; } @@ -284,7 +284,7 @@ int runCircuit(Mode mode, const json& input, ethsnarks::ProtoboardT& pb) { } case 3: { - if (!offchainWithdraw(mode, onchainDataAvailability, numElements, input, pb)) + if (!offchainWithdraw(mode, onchainDataAvailability, blockSize, input, pb)) { return 1; } @@ -292,7 +292,7 @@ int runCircuit(Mode mode, const json& input, ethsnarks::ProtoboardT& pb) { } case 4: { - if (!cancel(mode, onchainDataAvailability, numElements, input, pb)) + if (!cancel(mode, onchainDataAvailability, blockSize, input, pb)) { return 1; } @@ -309,11 +309,11 @@ int runCircuit(Mode mode, const json& input, ethsnarks::ProtoboardT& pb) { bool validateBlock(char* inputJson) { ethsnarks::ppT::init_public_params(); - + json input = json::parse(inputJson); ethsnarks::ProtoboardT pb; runCircuit(Mode::Validate, input, pb); - + // Check if the inputs are valid for the circuit if (!pb.is_satisfied()) { @@ -324,7 +324,7 @@ bool validateBlock(char* inputJson) { bool createKeyPair(char* inputJson) { ethsnarks::ppT::init_public_params(); - + json input = json::parse(inputJson); std::string baseFilename = generateBaseFileName(input); ethsnarks::ProtoboardT pb; @@ -367,7 +367,7 @@ ProofResult generateProof(char* inputJson) { std::string proofJsonStr = generateProof(pb, (baseFilename + "_pk.raw").c_str()); clock_gettime(CLOCK_MONOTONIC, &time2); timespec duration = diff(time1,time2); - + if (!proofJsonStr.empty()) { res.success = false; res.errorMessage = "Failed to generate Proof:" + baseFilename + "_pk.raw";