├── Circuits ├── Circuit.h ├── DepositCircuit.h ├── InternalTransferCircuit.h ├── OffchainWithdrawalCircuit.h ├── OnchainWithdrawalCircuit.h └── RingSettlementCircuit.h ├── Gadgets ├── AccountGadgets.h ├── MatchingGadgets.h ├── MathGadgets.h ├── MerkleTree.h ├── OrderGadgets.h └── TradingHistoryGadgets.h ├── LICENSE ├── README.md ├── ThirdParty ├── BigInt.hpp ├── BigIntHeader.hpp ├── catch.hpp ├── httplib.h └── json.hpp ├── Utils ├── Constants.h ├── Data.h └── Utils.h ├── cuda_prover ├── cuda_prover.cu └── multiexp │ ├── arith.cu │ ├── curves.cu │ ├── fixnum.cu │ ├── primitives.cu │ └── reduce.cu ├── docs ├── WithdrawCircuit_UnderStanding.pdf ├── WithdrawCircuit_Understanding.md ├── circuit-discussion-0722.pdf └── circuit_discussion_CN.md ├── main.cpp └── test ├── FloatTests.cpp ├── MatchingTests.cpp ├── MathTests.cpp ├── MerkleTreeTests.cpp ├── OrderTests.cpp ├── SignatureTests.cpp ├── TestUtils.h ├── TradeHistoryTests.cpp ├── data └── settlement_block.json └── main.cpp /Circuits/Circuit.h: -------------------------------------------------------------------------------- 1 | #ifndef _CIRCUIT_H_ 2 | #define _CIRCUIT_H_ 3 | 4 | #include "ethsnarks.hpp" 5 | #include "../Utils/Data.h" 6 | 7 | using namespace ethsnarks; 8 | 9 | namespace Loopring 10 | { 11 | 12 | class Circuit : public GadgetT 13 | { 14 | public: 15 | Circuit(libsnark::protoboard &pb, const std::string &annotation_prefix) : GadgetT(pb, annotation_prefix) {}; 16 | virtual ~Circuit() {}; 17 | virtual void generateConstraints(bool onchainDataAvailability, unsigned int blockSize) = 0; 18 | virtual bool generateWitness(const json& input) = 0; 19 | virtual BlockType getBlockType() = 0; 20 | virtual unsigned int getBlockSize() = 0; 21 | virtual void printInfo() = 0; 22 | 23 | libsnark::protoboard& getPb() 24 | { 25 | return pb; 26 | } 27 | }; 28 | 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /Circuits/DepositCircuit.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEPOSITCIRCUIT_H_ 2 | #define _DEPOSITCIRCUIT_H_ 3 | 4 | #include "Circuit.h" 5 | #include "../Utils/Constants.h" 6 | #include "../Utils/Data.h" 7 | 8 | #include "../ThirdParty/BigIntHeader.hpp" 9 | #include "ethsnarks.hpp" 10 | #include "utils.hpp" 11 | #include "gadgets/sha256_many.hpp" 12 | 13 | using namespace ethsnarks; 14 | 15 | namespace Loopring 16 | { 17 | 18 | class DepositGadget : public GadgetT 19 | { 20 | public: 21 | 22 | const Constants& constants; 23 | 24 | // User state 25 | BalanceGadget balanceBefore; 26 | AccountGadget accountBefore; 27 | 28 | // Inputs 29 | DualVariableGadget accountID; 30 | DualVariableGadget tokenID; 31 | DualVariableGadget amount; 32 | DualVariableGadget publicKeyX; 33 | DualVariableGadget publicKeyY; 34 | 35 | // Calculate the new balance 36 | UnsafeAddGadget uncappedBalanceAfter; 37 | MinGadget balanceAfter; 38 | 39 | // Update User 40 | UpdateBalanceGadget updateBalance; 41 | UpdateAccountGadget updateAccount; 42 | 43 | DepositGadget( 44 | ProtoboardT& pb, 45 | const Constants& _constants, 46 | const VariableT& root, 47 | const std::string& prefix 48 | ) : 49 | GadgetT(pb, prefix), 50 | 51 | constants(_constants), 52 | 53 | // User state 54 | balanceBefore(pb, FMT(prefix, ".balanceBefore")), 55 | accountBefore(pb, FMT(prefix, ".accountBefore")), 56 | 57 | // Inputs 58 | accountID(pb, NUM_BITS_ACCOUNT, FMT(prefix, ".accountID")), 59 | tokenID(pb, NUM_BITS_TOKEN, FMT(prefix, ".tokenID")), 60 | amount(pb, NUM_BITS_AMOUNT, FMT(prefix, ".amount")), 61 | publicKeyX(pb, 256, FMT(prefix, ".publicKeyX")), 62 | publicKeyY(pb, 256, FMT(prefix, ".publicKeyY")), 63 | 64 | // Calculate the new balance 65 | // We can't let the deposit fail (it's onchain so it needs to be included), 66 | // and we do want to cap the balance to NUM_BITS_AMOUNT bits max, so cap the balance even 67 | // if it means that the user loses some tokens (NUM_BITS_AMOUNT bits should be more than enough). 68 | uncappedBalanceAfter(pb, balanceBefore.balance, amount.packed, FMT(prefix, ".uncappedBalanceAfter")), 69 | balanceAfter(pb, uncappedBalanceAfter.result(), constants.maxAmount, NUM_BITS_AMOUNT + 1, FMT(prefix, ".balanceAfter")), 70 | 71 | // Update User 72 | updateBalance(pb, accountBefore.balancesRoot, tokenID.bits, 73 | {balanceBefore.balance, balanceBefore.tradingHistory}, 74 | {balanceAfter.result(), balanceBefore.tradingHistory}, 75 | FMT(prefix, ".updateBalance")), 76 | updateAccount(pb, root, accountID.bits, 77 | {accountBefore.publicKey.x, accountBefore.publicKey.y, accountBefore.nonce, accountBefore.balancesRoot}, 78 | {publicKeyX.packed, publicKeyY.packed, accountBefore.nonce, updateBalance.result()}, 79 | FMT(prefix, ".updateAccount")) 80 | { 81 | 82 | } 83 | 84 | void generate_r1cs_witness(const Deposit& deposit) 85 | { 86 | // User state 87 | balanceBefore.generate_r1cs_witness(deposit.balanceUpdate.before); 88 | accountBefore.generate_r1cs_witness(deposit.accountUpdate.before); 89 | 90 | // Inputs 91 | accountID.generate_r1cs_witness(pb, deposit.accountUpdate.accountID); 92 | tokenID.generate_r1cs_witness(pb, deposit.balanceUpdate.tokenID); 93 | amount.generate_r1cs_witness(pb, deposit.amount); 94 | publicKeyX.generate_r1cs_witness(pb, deposit.accountUpdate.after.publicKey.x); 95 | publicKeyY.generate_r1cs_witness(pb, deposit.accountUpdate.after.publicKey.y); 96 | 97 | // Calculate the new balance 98 | uncappedBalanceAfter.generate_r1cs_witness(); 99 | balanceAfter.generate_r1cs_witness(); 100 | 101 | // Update User 102 | updateBalance.generate_r1cs_witness(deposit.balanceUpdate.proof); 103 | updateAccount.generate_r1cs_witness(deposit.accountUpdate.proof); 104 | } 105 | 106 | void generate_r1cs_constraints() 107 | { 108 | // Inputs 109 | accountID.generate_r1cs_constraints(true); 110 | tokenID.generate_r1cs_constraints(true); 111 | amount.generate_r1cs_constraints(true); 112 | publicKeyX.generate_r1cs_constraints(true); 113 | publicKeyY.generate_r1cs_constraints(true); 114 | 115 | // Calculate the new balance 116 | uncappedBalanceAfter.generate_r1cs_constraints(); 117 | balanceAfter.generate_r1cs_constraints(); 118 | 119 | // Update User 120 | updateBalance.generate_r1cs_constraints(); 121 | updateAccount.generate_r1cs_constraints(); 122 | } 123 | 124 | const std::vector getOnchainData() const 125 | { 126 | return {accountID.bits, 127 | publicKeyX.bits, publicKeyY.bits, 128 | VariableArrayT(6, constants.zero), tokenID.bits, 129 | amount.bits}; 130 | } 131 | 132 | const VariableT& getNewAccountsRoot() const 133 | { 134 | return updateAccount.result(); 135 | } 136 | }; 137 | 138 | class DepositCircuit : public Circuit 139 | { 140 | public: 141 | 142 | PublicDataGadget publicData; 143 | Constants constants; 144 | 145 | // Inputs 146 | DualVariableGadget exchangeID; 147 | DualVariableGadget merkleRootBefore; 148 | DualVariableGadget merkleRootAfter; 149 | DualVariableGadget depositBlockHashStart; 150 | DualVariableGadget startIndex; 151 | DualVariableGadget count; 152 | 153 | // Deposits 154 | unsigned int numDeposits; 155 | std::vector deposits; 156 | std::vector hashers; 157 | 158 | DepositCircuit(ProtoboardT& pb, const std::string& prefix) : 159 | Circuit(pb, prefix), 160 | 161 | publicData(pb, FMT(prefix, ".publicData")), 162 | constants(pb, FMT(prefix, ".constants")), 163 | 164 | // Inputs 165 | exchangeID(pb, NUM_BITS_EXCHANGE_ID, FMT(prefix, ".exchangeID")), 166 | merkleRootBefore(pb, 256, FMT(prefix, ".merkleRootBefore")), 167 | merkleRootAfter(pb, 256, FMT(prefix, ".merkleRootAfter")), 168 | depositBlockHashStart(pb, 256, FMT(prefix, ".depositBlockHashStart")), 169 | startIndex(pb, 32, FMT(prefix, ".startIndex")), 170 | count(pb, 32, FMT(prefix, ".count")) 171 | { 172 | 173 | } 174 | 175 | void generateConstraints(bool onchainDataAvailability, unsigned int blockSize) override 176 | { 177 | this->numDeposits = blockSize; 178 | 179 | constants.generate_r1cs_constraints(); 180 | 181 | // Inputs 182 | exchangeID.generate_r1cs_constraints(true); 183 | merkleRootBefore.generate_r1cs_constraints(true); 184 | merkleRootAfter.generate_r1cs_constraints(true); 185 | depositBlockHashStart.generate_r1cs_constraints(true); 186 | startIndex.generate_r1cs_constraints(true); 187 | count.generate_r1cs_constraints(true); 188 | 189 | // Deposits 190 | deposits.reserve(numDeposits); 191 | hashers.reserve(numDeposits); 192 | for (size_t j = 0; j < numDeposits; j++) 193 | { 194 | VariableT depositAccountsRoot = (j == 0) ? merkleRootBefore.packed : deposits.back().getNewAccountsRoot(); 195 | deposits.emplace_back( 196 | pb, 197 | constants, 198 | depositAccountsRoot, 199 | std::string("deposit_") + std::to_string(j) 200 | ); 201 | deposits.back().generate_r1cs_constraints(); 202 | 203 | // Hash data from deposit 204 | std::vector depositData = deposits.back().getOnchainData(); 205 | std::vector hashBits; 206 | hashBits.push_back(reverse((j == 0) ? depositBlockHashStart.bits : hashers.back().result().bits)); 207 | hashBits.insert(hashBits.end(), depositData.begin(), depositData.end()); 208 | hashers.emplace_back(pb, flattenReverse(hashBits), std::string("hash_") + std::to_string(j)); 209 | hashers.back().generate_r1cs_constraints(); 210 | } 211 | 212 | // Public data 213 | publicData.add(exchangeID.bits); 214 | publicData.add(merkleRootBefore.bits); 215 | publicData.add(merkleRootAfter.bits); 216 | publicData.add(reverse(depositBlockHashStart.bits)); 217 | publicData.add(reverse(hashers.back().result().bits)); 218 | publicData.add(startIndex.bits); 219 | publicData.add(count.bits); 220 | publicData.generate_r1cs_constraints(); 221 | 222 | // Check the new merkle root 223 | requireEqual(pb, deposits.back().getNewAccountsRoot(), merkleRootAfter.packed, "newMerkleRoot"); 224 | } 225 | 226 | bool generateWitness(const DepositBlock& block) 227 | { 228 | constants.generate_r1cs_witness(); 229 | 230 | // Inputs 231 | exchangeID.generate_r1cs_witness(pb, block.exchangeID); 232 | merkleRootBefore.generate_r1cs_witness(pb, block.merkleRootBefore); 233 | merkleRootAfter.generate_r1cs_witness(pb, block.merkleRootAfter); 234 | depositBlockHashStart.generate_r1cs_witness(pb, block.startHash); 235 | startIndex.generate_r1cs_witness(pb, block.startIndex); 236 | count.generate_r1cs_witness(pb, block.count); 237 | // printBits("start hash input: 0x", depositBlockHashStart.get_bits(pb), true); 238 | 239 | // Deposits 240 | assert(deposits.size() == hashers.size()); 241 | #ifdef MULTICORE 242 | #pragma omp parallel for 243 | #endif 244 | for(unsigned int i = 0; i < block.deposits.size(); i++) 245 | { 246 | deposits[i].generate_r1cs_witness(block.deposits[i]); 247 | } 248 | // Cannot be done in parallel 249 | for(unsigned int i = 0; i < block.deposits.size(); i++) 250 | { 251 | hashers[i].generate_r1cs_witness(); 252 | } 253 | // printBits("DepositBlockHash: 0x", hashers.back().result().bits.get_bits(pb)); 254 | 255 | // Public data 256 | publicData.generate_r1cs_witness(); 257 | 258 | return true; 259 | } 260 | 261 | bool generateWitness(const json& input) override 262 | { 263 | return generateWitness(input.get()); 264 | } 265 | 266 | BlockType getBlockType() override 267 | { 268 | return BlockType::Deposit; 269 | } 270 | 271 | unsigned int getBlockSize() override 272 | { 273 | return numDeposits; 274 | } 275 | 276 | void printInfo() override 277 | { 278 | std::cout << pb.num_constraints() << " constraints (" << (pb.num_constraints() / numDeposits) << "/deposit)" << std::endl; 279 | } 280 | }; 281 | 282 | } 283 | 284 | #endif 285 | -------------------------------------------------------------------------------- /Circuits/OffchainWithdrawalCircuit.h: -------------------------------------------------------------------------------- 1 | #ifndef _OFFCHAINWITHDRAWALCIRCUIT_H_ 2 | #define _OFFCHAINWITHDRAWALCIRCUIT_H_ 3 | 4 | #include "Circuit.h" 5 | #include "../Utils/Constants.h" 6 | #include "../Utils/Data.h" 7 | #include "../Utils/Utils.h" 8 | #include "../Gadgets/AccountGadgets.h" 9 | 10 | #include "ethsnarks.hpp" 11 | #include "utils.hpp" 12 | #include "gadgets/subadd.hpp" 13 | 14 | using namespace ethsnarks; 15 | 16 | namespace Loopring 17 | { 18 | 19 | class OffchainWithdrawalGadget : public GadgetT 20 | { 21 | public: 22 | 23 | const Constants& constants; 24 | 25 | // User state 26 | BalanceGadget balanceFBefore; 27 | BalanceGadget balanceBefore; 28 | AccountGadget accountBefore; 29 | // Operator state 30 | BalanceGadget balanceBefore_O; 31 | 32 | // Inputs 33 | DualVariableGadget accountID; 34 | DualVariableGadget tokenID; 35 | DualVariableGadget amountRequested; 36 | DualVariableGadget feeTokenID; 37 | DualVariableGadget fee; 38 | 39 | // Fee as float 40 | FloatGadget fFee; 41 | RequireAccuracyGadget requireAccuracyFee; 42 | 43 | // Fee payment from the user to the operator 44 | subadd_gadget feePayment; 45 | 46 | // Calculate how much can be withdrawn 47 | MinGadget amountToWithdraw; 48 | FloatGadget amountWithdrawn; 49 | RequireAccuracyGadget requireAccuracyAmountWithdrawn; 50 | 51 | // Calculate the new balance 52 | UnsafeSubGadget balance_after; 53 | 54 | // Increase the nonce of the user by 1 55 | AddGadget nonce_after; 56 | 57 | // Update User 58 | UpdateBalanceGadget updateBalanceF_A; 59 | UpdateBalanceGadget updateBalance_A; 60 | UpdateAccountGadget updateAccount_A; 61 | 62 | // Update Operator 63 | UpdateBalanceGadget updateBalanceF_O; 64 | 65 | // Signature 66 | Poseidon_gadget_T<8, 1, 6, 53, 7, 1> hash; 67 | SignatureVerifier signatureVerifier; 68 | 69 | OffchainWithdrawalGadget( 70 | ProtoboardT& pb, 71 | const jubjub::Params& params, 72 | const Constants& _constants, 73 | const VariableT& accountsMerkleRoot, 74 | const VariableT& operatorBalancesRoot, 75 | const VariableT& blockExchangeID, 76 | const std::string& prefix 77 | ) : 78 | GadgetT(pb, prefix), 79 | 80 | constants(_constants), 81 | 82 | // User state 83 | balanceFBefore(pb, FMT(prefix, ".balanceFBefore")), 84 | balanceBefore(pb, FMT(prefix, ".balanceBefore")), 85 | accountBefore(pb, FMT(prefix, ".accountBefore")), 86 | // Operator state 87 | balanceBefore_O(pb, FMT(prefix, ".balanceBefore_O")), 88 | 89 | // Inputs 90 | accountID(pb, NUM_BITS_ACCOUNT, FMT(prefix, ".accountID")), 91 | tokenID(pb, NUM_BITS_TOKEN, FMT(prefix, ".tokenID")), 92 | amountRequested(pb, NUM_BITS_AMOUNT, FMT(prefix, ".amountRequested")), 93 | feeTokenID(pb, NUM_BITS_TOKEN, FMT(prefix, ".feeTokenID")), 94 | fee(pb, NUM_BITS_AMOUNT, FMT(prefix, ".fee")), 95 | 96 | // Fee as float 97 | fFee(pb, constants, Float16Encoding, FMT(prefix, ".fFee")), 98 | requireAccuracyFee(pb, fFee.value(), fee.packed, Float16Accuracy, NUM_BITS_AMOUNT, FMT(prefix, ".requireAccuracyFee")), 99 | 100 | // Fee payment from the user to the operator 101 | feePayment(pb, NUM_BITS_AMOUNT, balanceFBefore.balance, balanceBefore_O.balance, fFee.value(), FMT(prefix, ".feePayment")), 102 | 103 | // Calculate how much can be withdrawn 104 | amountToWithdraw(pb, amountRequested.packed, balanceBefore.balance, NUM_BITS_AMOUNT, FMT(prefix, ".min(amountRequested, balance)")), 105 | amountWithdrawn(pb, constants, Float24Encoding, FMT(prefix, ".amountWithdrawn")), 106 | requireAccuracyAmountWithdrawn(pb, amountWithdrawn.value(), amountToWithdraw.result(), Float24Accuracy, NUM_BITS_AMOUNT, FMT(prefix, ".requireAccuracyAmountRequested")), 107 | 108 | // Calculate the new balance 109 | balance_after(pb, balanceBefore.balance, amountWithdrawn.value(), FMT(prefix, ".balance_after")), 110 | 111 | // Increase the nonce of the user by 1 112 | nonce_after(pb, accountBefore.nonce, constants.one, NUM_BITS_NONCE, FMT(prefix, ".nonce_after")), 113 | 114 | // Update User 115 | updateBalanceF_A(pb, accountBefore.balancesRoot, feeTokenID.bits, 116 | {balanceFBefore.balance, balanceFBefore.tradingHistory}, 117 | {feePayment.X, balanceFBefore.tradingHistory}, 118 | FMT(prefix, ".updateBalanceF_A")), 119 | updateBalance_A(pb, updateBalanceF_A.result(), tokenID.bits, 120 | {balanceBefore.balance, balanceBefore.tradingHistory}, 121 | {balance_after.result(), balanceBefore.tradingHistory}, 122 | FMT(prefix, ".updateBalance_A")), 123 | updateAccount_A(pb, accountsMerkleRoot, accountID.bits, 124 | {accountBefore.publicKey.x, accountBefore.publicKey.y, accountBefore.nonce, accountBefore.balancesRoot}, 125 | {accountBefore.publicKey.x, accountBefore.publicKey.y, nonce_after.result(), updateBalance_A.result()}, 126 | FMT(prefix, ".updateAccount_A")), 127 | 128 | // Update Operator 129 | updateBalanceF_O(pb, operatorBalancesRoot, feeTokenID.bits, 130 | {balanceBefore_O.balance, balanceBefore_O.tradingHistory}, 131 | {feePayment.Y, balanceBefore_O.tradingHistory}, 132 | FMT(prefix, ".updateBalanceF_O")), 133 | 134 | // Signature 135 | hash(pb, var_array({ 136 | blockExchangeID, 137 | accountID.packed, 138 | tokenID.packed, 139 | amountRequested.packed, 140 | feeTokenID.packed, 141 | fee.packed, 142 | accountBefore.nonce 143 | }), FMT(this->annotation_prefix, ".hash")), 144 | signatureVerifier(pb, params, constants, accountBefore.publicKey, hash.result(), FMT(prefix, ".signatureVerifier")) 145 | { 146 | 147 | } 148 | 149 | void generate_r1cs_witness(const OffchainWithdrawal& withdrawal) 150 | { 151 | // User state 152 | balanceFBefore.generate_r1cs_witness(withdrawal.balanceUpdateF_A.before); 153 | balanceBefore.generate_r1cs_witness(withdrawal.balanceUpdateW_A.before); 154 | accountBefore.generate_r1cs_witness(withdrawal.accountUpdate_A.before); 155 | // Operator state 156 | balanceBefore_O.generate_r1cs_witness(withdrawal.balanceUpdateF_O.before); 157 | 158 | // Inputs 159 | accountID.generate_r1cs_witness(pb, withdrawal.accountUpdate_A.accountID); 160 | tokenID.generate_r1cs_witness(pb, withdrawal.balanceUpdateW_A.tokenID); 161 | amountRequested.generate_r1cs_witness(pb, withdrawal.amountRequested); 162 | feeTokenID.generate_r1cs_witness(pb, withdrawal.balanceUpdateF_A.tokenID); 163 | fee.generate_r1cs_witness(pb, withdrawal.fee); 164 | 165 | // Fee as float 166 | fFee.generate_r1cs_witness(toFloat(withdrawal.fee, Float16Encoding)); 167 | requireAccuracyFee.generate_r1cs_witness(); 168 | 169 | // Fee payment from the user to the operator 170 | feePayment.generate_r1cs_witness(); 171 | 172 | // Calculate how much can be withdrawn 173 | amountToWithdraw.generate_r1cs_witness(); 174 | amountWithdrawn.generate_r1cs_witness(toFloat(pb.val(amountToWithdraw.result()), Float24Encoding)); 175 | requireAccuracyAmountWithdrawn.generate_r1cs_witness(); 176 | 177 | // Calculate the new balance 178 | balance_after.generate_r1cs_witness(); 179 | 180 | // Increase the nonce of the user by 1 181 | nonce_after.generate_r1cs_witness(); 182 | 183 | // Update User 184 | updateBalanceF_A.generate_r1cs_witness(withdrawal.balanceUpdateF_A.proof); 185 | updateBalance_A.generate_r1cs_witness(withdrawal.balanceUpdateW_A.proof); 186 | updateAccount_A.generate_r1cs_witness(withdrawal.accountUpdate_A.proof); 187 | 188 | // Update Operator 189 | updateBalanceF_O.generate_r1cs_witness(withdrawal.balanceUpdateF_O.proof); 190 | 191 | // Check signature 192 | hash.generate_r1cs_witness(); 193 | signatureVerifier.generate_r1cs_witness(withdrawal.signature); 194 | } 195 | 196 | void generate_r1cs_constraints() 197 | { 198 | // Inputs 199 | accountID.generate_r1cs_constraints(true); 200 | tokenID.generate_r1cs_constraints(true); 201 | amountRequested.generate_r1cs_constraints(true); 202 | feeTokenID.generate_r1cs_constraints(true); 203 | fee.generate_r1cs_constraints(true); 204 | 205 | // Fee as float 206 | fFee.generate_r1cs_constraints(); 207 | requireAccuracyFee.generate_r1cs_constraints(); 208 | 209 | // Fee payment from the user to the operator 210 | feePayment.generate_r1cs_constraints(); 211 | 212 | // Calculate how much can be withdrawn 213 | amountToWithdraw.generate_r1cs_constraints(); 214 | amountWithdrawn.generate_r1cs_constraints(); 215 | requireAccuracyAmountWithdrawn.generate_r1cs_constraints(); 216 | 217 | // Calculate the new balance 218 | balance_after.generate_r1cs_constraints(); 219 | 220 | // Increase the nonce of the user by 1 221 | nonce_after.generate_r1cs_constraints(); 222 | 223 | // Update User 224 | updateBalanceF_A.generate_r1cs_constraints(); 225 | updateBalance_A.generate_r1cs_constraints(); 226 | updateAccount_A.generate_r1cs_constraints(); 227 | 228 | // Update Operator 229 | updateBalanceF_O.generate_r1cs_constraints(); 230 | 231 | // Check signature 232 | hash.generate_r1cs_constraints(); 233 | signatureVerifier.generate_r1cs_constraints(); 234 | } 235 | 236 | const std::vector getApprovedWithdrawalData() const 237 | { 238 | return {VariableArrayT(6, constants.zero), tokenID.bits, 239 | accountID.bits, 240 | amountWithdrawn.bits()}; 241 | } 242 | 243 | const std::vector getDataAvailabilityData() const 244 | { 245 | return {VariableArrayT(6, constants.zero), feeTokenID.bits, 246 | fFee.bits()}; 247 | } 248 | 249 | const VariableT& getNewAccountsRoot() const 250 | { 251 | return updateAccount_A.result(); 252 | } 253 | 254 | const VariableT& getNewOperatorBalancesRoot() const 255 | { 256 | return updateBalanceF_O.result(); 257 | } 258 | }; 259 | 260 | class OffchainWithdrawalCircuit : public Circuit 261 | { 262 | public: 263 | 264 | PublicDataGadget publicData; 265 | Constants constants; 266 | jubjub::Params params; 267 | 268 | // State 269 | AccountGadget accountBefore_O; 270 | 271 | // Inputs 272 | DualVariableGadget exchangeID; 273 | DualVariableGadget merkleRootBefore; 274 | DualVariableGadget merkleRootAfter; 275 | DualVariableGadget operatorAccountID; 276 | 277 | // Operator account check 278 | RequireNotZeroGadget publicKeyX_notZero; 279 | 280 | // Withdrawals 281 | bool onchainDataAvailability; 282 | unsigned int numWithdrawals; 283 | std::vector withdrawals; 284 | 285 | // Update Operator 286 | std::unique_ptr updateAccount_O; 287 | 288 | OffchainWithdrawalCircuit(ProtoboardT& pb, const std::string& prefix) : 289 | Circuit(pb, prefix), 290 | 291 | publicData(pb, FMT(prefix, ".publicData")), 292 | constants(pb, FMT(prefix, ".constants")), 293 | 294 | // State 295 | accountBefore_O(pb, FMT(prefix, ".accountBefore_O")), 296 | 297 | // Inputs 298 | exchangeID(pb, NUM_BITS_EXCHANGE_ID, FMT(prefix, ".exchangeID")), 299 | merkleRootBefore(pb, 256, FMT(prefix, ".merkleRootBefore")), 300 | merkleRootAfter(pb, 256, FMT(prefix, ".merkleRootAfter")), 301 | operatorAccountID(pb, NUM_BITS_ACCOUNT, FMT(prefix, ".operatorAccountID")), 302 | 303 | // Operator account check 304 | publicKeyX_notZero(pb, accountBefore_O.publicKey.x, FMT(prefix, ".publicKeyX_notZero")) 305 | { 306 | 307 | } 308 | 309 | void generateConstraints(bool onchainDataAvailability, unsigned int blockSize) override 310 | { 311 | this->onchainDataAvailability = onchainDataAvailability; 312 | this->numWithdrawals = blockSize; 313 | 314 | constants.generate_r1cs_constraints(); 315 | 316 | // Inputs 317 | exchangeID.generate_r1cs_constraints(true); 318 | merkleRootBefore.generate_r1cs_constraints(true); 319 | merkleRootAfter.generate_r1cs_constraints(true); 320 | operatorAccountID.generate_r1cs_constraints(true); 321 | 322 | // Operator account check 323 | publicKeyX_notZero.generate_r1cs_constraints(); 324 | 325 | // Withdrawals 326 | withdrawals.reserve(numWithdrawals); 327 | for (size_t j = 0; j < numWithdrawals; j++) 328 | { 329 | VariableT withdrawalAccountsRoot = (j == 0) ? merkleRootBefore.packed : withdrawals.back().getNewAccountsRoot(); 330 | VariableT withdrawalOperatorBalancesRoot = (j == 0) ? accountBefore_O.balancesRoot : withdrawals.back().getNewOperatorBalancesRoot(); 331 | withdrawals.emplace_back( 332 | pb, 333 | params, 334 | constants, 335 | withdrawalAccountsRoot, 336 | withdrawalOperatorBalancesRoot, 337 | exchangeID.packed, 338 | std::string("withdrawals_") + std::to_string(j) 339 | ); 340 | withdrawals.back().generate_r1cs_constraints(); 341 | } 342 | 343 | // Update Operator 344 | updateAccount_O.reset(new UpdateAccountGadget(pb, withdrawals.back().getNewAccountsRoot(), operatorAccountID.bits, 345 | {accountBefore_O.publicKey.x, accountBefore_O.publicKey.y, accountBefore_O.nonce, accountBefore_O.balancesRoot}, 346 | {accountBefore_O.publicKey.x, accountBefore_O.publicKey.y, accountBefore_O.nonce, withdrawals.back().getNewOperatorBalancesRoot()}, 347 | FMT(annotation_prefix, ".updateAccount_O"))); 348 | updateAccount_O->generate_r1cs_constraints(); 349 | 350 | // Public data 351 | publicData.add(exchangeID.bits); 352 | publicData.add(merkleRootBefore.bits); 353 | publicData.add(merkleRootAfter.bits); 354 | // Store the approved data for all withdrawals 355 | for (auto& withdrawal : withdrawals) 356 | { 357 | publicData.add(withdrawal.getApprovedWithdrawalData()); 358 | } 359 | // Data availability 360 | if (onchainDataAvailability) 361 | { 362 | publicData.add(operatorAccountID.bits); 363 | for (auto& withdrawal : withdrawals) 364 | { 365 | publicData.add(withdrawal.getDataAvailabilityData()); 366 | } 367 | } 368 | publicData.generate_r1cs_constraints(); 369 | 370 | // Check the new merkle root 371 | requireEqual(pb, updateAccount_O->result(), merkleRootAfter.packed, "newMerkleRoot"); 372 | } 373 | 374 | bool generateWitness(const OffchainWithdrawalBlock& block) 375 | { 376 | constants.generate_r1cs_witness(); 377 | 378 | // State 379 | accountBefore_O.generate_r1cs_witness(block.accountUpdate_O.before); 380 | 381 | // Inputs 382 | exchangeID.generate_r1cs_witness(pb, block.exchangeID); 383 | merkleRootBefore.generate_r1cs_witness(pb, block.merkleRootBefore); 384 | merkleRootAfter.generate_r1cs_witness(pb, block.merkleRootAfter); 385 | operatorAccountID.generate_r1cs_witness(pb, block.operatorAccountID); 386 | 387 | // Operator account check 388 | publicKeyX_notZero.generate_r1cs_witness(); 389 | 390 | // Withdrawals 391 | #ifdef MULTICORE 392 | #pragma omp parallel for 393 | #endif 394 | for(unsigned int i = 0; i < block.withdrawals.size(); i++) 395 | { 396 | withdrawals[i].generate_r1cs_witness(block.withdrawals[i]); 397 | } 398 | 399 | // Update Operator 400 | updateAccount_O->generate_r1cs_witness(block.accountUpdate_O.proof); 401 | 402 | // Public data 403 | publicData.generate_r1cs_witness(); 404 | 405 | return true; 406 | } 407 | 408 | bool generateWitness(const json& input) override 409 | { 410 | return generateWitness(input.get()); 411 | } 412 | 413 | BlockType getBlockType() override 414 | { 415 | return BlockType::OffchainWithdrawal; 416 | } 417 | 418 | unsigned int getBlockSize() override 419 | { 420 | return numWithdrawals; 421 | } 422 | 423 | void printInfo() override 424 | { 425 | std::cout << pb.num_constraints() << " constraints (" << (pb.num_constraints() / numWithdrawals) << "/offchain withdrawal)" << std::endl; 426 | } 427 | }; 428 | 429 | } 430 | 431 | #endif 432 | -------------------------------------------------------------------------------- /Circuits/OnchainWithdrawalCircuit.h: -------------------------------------------------------------------------------- 1 | #ifndef _ONCHAINWITHDRAWALCIRCUIT_H_ 2 | #define _ONCHAINWITHDRAWALCIRCUIT_H_ 3 | 4 | #include "Circuit.h" 5 | #include "../Utils/Constants.h" 6 | #include "../Utils/Data.h" 7 | #include "../Utils/Utils.h" 8 | #include "../Gadgets/AccountGadgets.h" 9 | 10 | #include "ethsnarks.hpp" 11 | #include "utils.hpp" 12 | #include "gadgets/merkle_tree.hpp" 13 | 14 | using namespace ethsnarks; 15 | 16 | namespace Loopring 17 | { 18 | 19 | class OnchainWithdrawalGadget : public GadgetT 20 | { 21 | public: 22 | 23 | const Constants& constants; 24 | 25 | // User state 26 | BalanceGadget balanceBefore; 27 | AccountGadget accountBefore; 28 | 29 | // Inputs 30 | DualVariableGadget accountID; 31 | DualVariableGadget tokenID; 32 | DualVariableGadget amountRequested; 33 | 34 | // Calculate how much can be withdrawn 35 | MinGadget amountToWithdrawMin; 36 | TernaryGadget amountToWithdraw; 37 | // Float 38 | FloatGadget amountWithdrawn; 39 | RequireAccuracyGadget requireAccuracyAmountWithdrawn; 40 | 41 | // Shutdown mode 42 | TernaryGadget amountToSubtract; 43 | TernaryGadget tradingHistoryAfter; 44 | TernaryGadget publicKeyXAfter; 45 | TernaryGadget publicKeyYAfter; 46 | TernaryGadget nonceAfter; 47 | 48 | // Calculate the new balance 49 | UnsafeSubGadget balance_after; 50 | 51 | // Update User 52 | UpdateBalanceGadget updateBalance_A; 53 | UpdateAccountGadget updateAccount_A; 54 | 55 | OnchainWithdrawalGadget( 56 | ProtoboardT& pb, 57 | const Constants& _constants, 58 | const VariableT& accountsMerkleRoot, 59 | const VariableT& bShutdownMode, 60 | const std::string& prefix 61 | ) : 62 | GadgetT(pb, prefix), 63 | 64 | constants(_constants), 65 | 66 | // User state 67 | balanceBefore(pb, FMT(prefix, ".balanceBefore")), 68 | accountBefore(pb, FMT(prefix, ".accountBefore")), 69 | 70 | // Inputs 71 | accountID(pb, NUM_BITS_ACCOUNT, FMT(prefix, ".accountID")), 72 | tokenID(pb, NUM_BITS_TOKEN, FMT(prefix, ".tokenID")), 73 | amountRequested(pb, NUM_BITS_AMOUNT, FMT(prefix, ".amountRequested")), 74 | 75 | // Calculate how much can be withdrawn 76 | // In shutdown mode always withdraw the complete balance 77 | amountToWithdrawMin(pb, amountRequested.packed, balanceBefore.balance, NUM_BITS_AMOUNT, FMT(prefix, ".min(amountRequested, balance)")), 78 | amountToWithdraw(pb, bShutdownMode, balanceBefore.balance, amountToWithdrawMin.result(), FMT(prefix, ".amountToWithdraw")), 79 | // Float 80 | amountWithdrawn(pb, constants, Float24Encoding, FMT(prefix, ".amountWithdrawn")), 81 | requireAccuracyAmountWithdrawn(pb, amountWithdrawn.value(), amountToWithdraw.result(), Float24Accuracy, NUM_BITS_AMOUNT, FMT(prefix, ".requireAccuracyAmountRequested")), 82 | 83 | // Shutdown mode - Reset values to genesis state 84 | amountToSubtract(pb, bShutdownMode, amountToWithdraw.result(), amountWithdrawn.value(), FMT(prefix, ".amountToSubtract")), 85 | tradingHistoryAfter(pb, bShutdownMode, constants.emptyTradeHistory, balanceBefore.tradingHistory, FMT(prefix, ".tradingHistoryAfter")), 86 | publicKeyXAfter(pb, bShutdownMode, constants.zero, accountBefore.publicKey.x, FMT(prefix, ".publicKeyXAfter")), 87 | publicKeyYAfter(pb, bShutdownMode, constants.zero, accountBefore.publicKey.y, FMT(prefix, ".publicKeyYAfter")), 88 | nonceAfter(pb, bShutdownMode, constants.zero, accountBefore.nonce, FMT(prefix, ".nonceAfter")), 89 | 90 | // Calculate the new balance 91 | balance_after(pb, balanceBefore.balance, amountToSubtract.result(), FMT(prefix, ".balance_after")), 92 | 93 | // Update User 94 | updateBalance_A(pb, accountBefore.balancesRoot, tokenID.bits, 95 | {balanceBefore.balance, balanceBefore.tradingHistory}, 96 | {balance_after.result(), tradingHistoryAfter.result()}, 97 | FMT(prefix, ".updateBalance_A")), 98 | updateAccount_A(pb, accountsMerkleRoot, accountID.bits, 99 | {accountBefore.publicKey.x, accountBefore.publicKey.y, accountBefore.nonce, accountBefore.balancesRoot}, 100 | {publicKeyXAfter.result(), publicKeyYAfter.result(), nonceAfter.result(), updateBalance_A.result()}, 101 | FMT(prefix, ".updateAccount_A")) 102 | { 103 | 104 | } 105 | 106 | void generate_r1cs_witness(const OnchainWithdrawal& withdrawal) 107 | { 108 | // User state 109 | balanceBefore.generate_r1cs_witness(withdrawal.balanceUpdate.before); 110 | accountBefore.generate_r1cs_witness(withdrawal.accountUpdate.before); 111 | 112 | // Inputs 113 | accountID.generate_r1cs_witness(pb, withdrawal.accountUpdate.accountID); 114 | tokenID.generate_r1cs_witness(pb, withdrawal.balanceUpdate.tokenID); 115 | amountRequested.generate_r1cs_witness(pb, withdrawal.amountRequested); 116 | 117 | // Withdrawal calculations 118 | amountToWithdrawMin.generate_r1cs_witness(); 119 | amountToWithdraw.generate_r1cs_witness(); 120 | // Float 121 | amountWithdrawn.generate_r1cs_witness(toFloat(pb.val(amountToWithdraw.result()), Float24Encoding)); 122 | requireAccuracyAmountWithdrawn.generate_r1cs_witness(); 123 | 124 | // Shutdown mode 125 | amountToSubtract.generate_r1cs_witness(); 126 | tradingHistoryAfter.generate_r1cs_witness(); 127 | publicKeyXAfter.generate_r1cs_witness(); 128 | publicKeyYAfter.generate_r1cs_witness(); 129 | nonceAfter.generate_r1cs_witness(); 130 | 131 | // Calculate the new balance 132 | balance_after.generate_r1cs_witness(); 133 | 134 | // Update User 135 | updateBalance_A.generate_r1cs_witness(withdrawal.balanceUpdate.proof); 136 | updateAccount_A.generate_r1cs_witness(withdrawal.accountUpdate.proof); 137 | } 138 | 139 | void generate_r1cs_constraints() 140 | { 141 | // Inputs 142 | accountID.generate_r1cs_constraints(true); 143 | tokenID.generate_r1cs_constraints(true); 144 | amountRequested.generate_r1cs_constraints(true); 145 | 146 | // Withdrawal calculations 147 | amountToWithdrawMin.generate_r1cs_constraints(); 148 | amountToWithdraw.generate_r1cs_constraints(); 149 | // Float 150 | amountWithdrawn.generate_r1cs_constraints(); 151 | requireAccuracyAmountWithdrawn.generate_r1cs_constraints(); 152 | 153 | // Shutdown mode 154 | amountToSubtract.generate_r1cs_constraints(); 155 | tradingHistoryAfter.generate_r1cs_constraints(); 156 | publicKeyXAfter.generate_r1cs_constraints(); 157 | publicKeyYAfter.generate_r1cs_constraints(); 158 | nonceAfter.generate_r1cs_constraints(); 159 | 160 | // Calculate the new balance 161 | balance_after.generate_r1cs_constraints(); 162 | 163 | // Update User 164 | updateBalance_A.generate_r1cs_constraints(); 165 | updateAccount_A.generate_r1cs_constraints(); 166 | } 167 | 168 | const std::vector getOnchainData() const 169 | { 170 | return {accountID.bits, 171 | VariableArrayT(6, constants.zero), tokenID.bits, 172 | amountRequested.bits}; 173 | } 174 | 175 | const std::vector getApprovedWithdrawalData() const 176 | { 177 | return {VariableArrayT(6, constants.zero), tokenID.bits, 178 | accountID.bits, 179 | amountWithdrawn.bits()}; 180 | } 181 | 182 | const VariableT& getNewAccountsRoot() const 183 | { 184 | return updateAccount_A.result(); 185 | } 186 | }; 187 | 188 | class OnchainWithdrawalCircuit : public Circuit 189 | { 190 | public: 191 | 192 | PublicDataGadget publicData; 193 | Constants constants; 194 | 195 | // Inputs 196 | DualVariableGadget exchangeID; 197 | DualVariableGadget merkleRootBefore; 198 | DualVariableGadget merkleRootAfter; 199 | DualVariableGadget withdrawalBlockHashStart; 200 | DualVariableGadget startIndex; 201 | DualVariableGadget count; 202 | 203 | // Shutdown 204 | EqualGadget bShutdownMode; 205 | 206 | // Withdrawals 207 | unsigned int numWithdrawals; 208 | std::vector withdrawals; 209 | std::vector hashers; 210 | 211 | OnchainWithdrawalCircuit(ProtoboardT& pb, const std::string& prefix) : 212 | Circuit(pb, prefix), 213 | 214 | publicData(pb, FMT(prefix, ".publicData")), 215 | constants(pb, FMT(prefix, ".constants")), 216 | 217 | // Inputs 218 | exchangeID(pb, NUM_BITS_EXCHANGE_ID, FMT(prefix, ".exchangeID")), 219 | merkleRootBefore(pb, 256, FMT(prefix, ".merkleRootBefore")), 220 | merkleRootAfter(pb, 256, FMT(prefix, ".merkleRootAfter")), 221 | withdrawalBlockHashStart(pb, 256, FMT(prefix, ".withdrawalBlockHashStart")), 222 | startIndex(pb, 32, FMT(prefix, ".startIndex")), 223 | count(pb, 32, FMT(prefix, ".count")), 224 | 225 | // Shutdown 226 | bShutdownMode(pb, count.packed, constants.zero, FMT(prefix, ".bShutdownMode")) 227 | { 228 | 229 | } 230 | 231 | void generateConstraints(bool onchainDataAvailability, unsigned int blockSize) override 232 | { 233 | this->numWithdrawals = blockSize; 234 | 235 | constants.generate_r1cs_constraints(); 236 | 237 | // Inputs 238 | exchangeID.generate_r1cs_constraints(true); 239 | merkleRootBefore.generate_r1cs_constraints(true); 240 | merkleRootAfter.generate_r1cs_constraints(true); 241 | withdrawalBlockHashStart.generate_r1cs_constraints(true); 242 | startIndex.generate_r1cs_constraints(true); 243 | count.generate_r1cs_constraints(true); 244 | 245 | // Shutdown 246 | bShutdownMode.generate_r1cs_constraints(); 247 | 248 | // Withdrawals 249 | withdrawals.reserve(numWithdrawals); 250 | hashers.reserve(numWithdrawals); 251 | for (size_t j = 0; j < numWithdrawals; j++) 252 | { 253 | VariableT withdrawalAccountsRoot = (j == 0) ? merkleRootBefore.packed : withdrawals.back().getNewAccountsRoot(); 254 | withdrawals.emplace_back( 255 | pb, 256 | constants, 257 | withdrawalAccountsRoot, 258 | bShutdownMode.result(), 259 | std::string("withdrawals_") + std::to_string(j) 260 | ); 261 | withdrawals.back().generate_r1cs_constraints(); 262 | 263 | // Hash data from withdrawal request 264 | std::vector withdrawalRequestData = withdrawals.back().getOnchainData(); 265 | std::vector hash; 266 | hash.push_back(reverse((j == 0) ? withdrawalBlockHashStart.bits : hashers.back().result().bits)); 267 | hash.insert(hash.end(), withdrawalRequestData.begin(), withdrawalRequestData.end()); 268 | hashers.emplace_back(pb, flattenReverse(hash), std::string("hash_") + std::to_string(j)); 269 | hashers.back().generate_r1cs_constraints(); 270 | } 271 | 272 | // Public data 273 | publicData.add(exchangeID.bits); 274 | publicData.add(merkleRootBefore.bits); 275 | publicData.add(merkleRootAfter.bits); 276 | publicData.add(reverse(withdrawalBlockHashStart.bits)); 277 | publicData.add(reverse(hashers.back().result().bits)); 278 | publicData.add(startIndex.bits); 279 | publicData.add(count.bits); 280 | // Store the approved data for all withdrawals 281 | for (auto& withdrawal : withdrawals) 282 | { 283 | publicData.add(withdrawal.getApprovedWithdrawalData()); 284 | } 285 | publicData.generate_r1cs_constraints(); 286 | 287 | // Check the new merkle root 288 | requireEqual(pb, withdrawals.back().getNewAccountsRoot(), merkleRootAfter.packed, "newMerkleRoot"); 289 | } 290 | 291 | bool generateWitness(const OnchainWithdrawalBlock& block) 292 | { 293 | constants.generate_r1cs_witness(); 294 | 295 | // Inputs 296 | exchangeID.generate_r1cs_witness(pb, block.exchangeID); 297 | merkleRootBefore.generate_r1cs_witness(pb, block.merkleRootBefore); 298 | merkleRootAfter.generate_r1cs_witness(pb, block.merkleRootAfter); 299 | withdrawalBlockHashStart.generate_r1cs_witness(pb, block.startHash); 300 | startIndex.generate_r1cs_witness(pb, block.startIndex); 301 | count.generate_r1cs_witness(pb, block.count); 302 | // printBits("start hash input: 0x", depositBlockHashStart.get_bits(pb), true); 303 | 304 | // Shutdown 305 | bShutdownMode.generate_r1cs_witness(); 306 | 307 | // Withdrawals 308 | assert(withdrawals.size() == hashers.size()); 309 | #ifdef MULTICORE 310 | #pragma omp parallel for 311 | #endif 312 | for(unsigned int i = 0; i < block.withdrawals.size(); i++) 313 | { 314 | withdrawals[i].generate_r1cs_witness(block.withdrawals[i]); 315 | } 316 | // Cannot be done in parallel 317 | for(unsigned int i = 0; i < block.withdrawals.size(); i++) 318 | { 319 | hashers[i].generate_r1cs_witness(); 320 | } 321 | // printBits("WithdrawBlockHash: 0x", hashers.back().result().bits.get_bits(pb)); 322 | 323 | // Public data 324 | publicData.generate_r1cs_witness(); 325 | 326 | return true; 327 | } 328 | 329 | bool generateWitness(const json& input) override 330 | { 331 | return generateWitness(input.get()); 332 | } 333 | 334 | BlockType getBlockType() override 335 | { 336 | return BlockType::OnchainWithdrawal; 337 | } 338 | 339 | unsigned int getBlockSize() override 340 | { 341 | return numWithdrawals; 342 | } 343 | 344 | void printInfo() override 345 | { 346 | std::cout << pb.num_constraints() << " constraints (" << (pb.num_constraints() / numWithdrawals) << "/onchain withdrawal)" << std::endl; 347 | } 348 | }; 349 | 350 | } 351 | 352 | #endif 353 | -------------------------------------------------------------------------------- /Gadgets/AccountGadgets.h: -------------------------------------------------------------------------------- 1 | #ifndef _ACCOUNTGADGETS_H_ 2 | #define _ACCOUNTGADGETS_H_ 3 | 4 | #include "../Utils/Constants.h" 5 | #include "../Utils/Data.h" 6 | 7 | #include "MerkleTree.h" 8 | 9 | #include "ethsnarks.hpp" 10 | #include "utils.hpp" 11 | #include "gadgets/merkle_tree.hpp" 12 | #include "gadgets/poseidon.hpp" 13 | 14 | using namespace ethsnarks; 15 | 16 | namespace Loopring 17 | { 18 | 19 | struct AccountState 20 | { 21 | VariableT publicKeyX; 22 | VariableT publicKeyY; 23 | VariableT nonce; 24 | VariableT balancesRoot; 25 | }; 26 | 27 | class AccountGadget : public GadgetT 28 | { 29 | public: 30 | const jubjub::VariablePointT publicKey; 31 | VariableT nonce; 32 | VariableT balancesRoot; 33 | 34 | AccountGadget( 35 | ProtoboardT& pb, 36 | const std::string& prefix 37 | ) : 38 | GadgetT(pb, prefix), 39 | 40 | publicKey(pb, FMT(prefix, ".publicKey")), 41 | nonce(make_variable(pb, FMT(prefix, ".nonce"))), 42 | balancesRoot(make_variable(pb, FMT(prefix, ".balancesRoot"))) 43 | { 44 | 45 | } 46 | 47 | void generate_r1cs_witness(const Account& account) 48 | { 49 | pb.val(publicKey.x) = account.publicKey.x; 50 | pb.val(publicKey.y) = account.publicKey.y; 51 | pb.val(nonce) = account.nonce; 52 | pb.val(balancesRoot) = account.balancesRoot; 53 | } 54 | }; 55 | 56 | class UpdateAccountGadget : public GadgetT 57 | { 58 | public: 59 | HashAccountLeaf leafBefore; 60 | HashAccountLeaf leafAfter; 61 | 62 | const VariableArrayT proof; 63 | MerklePathCheckT proofVerifierBefore; 64 | MerklePathT rootCalculatorAfter; 65 | 66 | UpdateAccountGadget( 67 | ProtoboardT& pb, 68 | const VariableT& merkleRoot, 69 | const VariableArrayT& address, 70 | const AccountState& before, 71 | const AccountState& after, 72 | const std::string& prefix 73 | ) : 74 | GadgetT(pb, prefix), 75 | 76 | leafBefore(pb, var_array({before.publicKeyX, before.publicKeyY, before.nonce, before.balancesRoot}), FMT(prefix, ".leafBefore")), 77 | leafAfter(pb, var_array({after.publicKeyX, after.publicKeyY, after.nonce, after.balancesRoot}), FMT(prefix, ".leafAfter")), 78 | 79 | proof(make_var_array(pb, TREE_DEPTH_ACCOUNTS * 3, FMT(prefix, ".proof"))), 80 | proofVerifierBefore(pb, TREE_DEPTH_ACCOUNTS, address, leafBefore.result(), merkleRoot, proof, FMT(prefix, ".pathBefore")), 81 | rootCalculatorAfter(pb, TREE_DEPTH_ACCOUNTS, address, leafAfter.result(), proof, FMT(prefix, ".pathAfter")) 82 | { 83 | 84 | } 85 | 86 | void generate_r1cs_witness(const Proof& _proof) 87 | { 88 | leafBefore.generate_r1cs_witness(); 89 | leafAfter.generate_r1cs_witness(); 90 | 91 | proof.fill_with_field_elements(pb, _proof.data); 92 | proofVerifierBefore.generate_r1cs_witness(); 93 | rootCalculatorAfter.generate_r1cs_witness(); 94 | } 95 | 96 | void generate_r1cs_constraints() 97 | { 98 | leafBefore.generate_r1cs_constraints(); 99 | leafAfter.generate_r1cs_constraints(); 100 | 101 | proofVerifierBefore.generate_r1cs_constraints(); 102 | rootCalculatorAfter.generate_r1cs_constraints(); 103 | } 104 | 105 | const VariableT& result() const 106 | { 107 | return rootCalculatorAfter.result(); 108 | } 109 | }; 110 | 111 | struct BalanceState 112 | { 113 | VariableT balance; 114 | VariableT tradingHistory; 115 | }; 116 | 117 | class BalanceGadget : public GadgetT 118 | { 119 | public: 120 | VariableT balance; 121 | VariableT tradingHistory; 122 | 123 | BalanceGadget( 124 | ProtoboardT& pb, 125 | const std::string& prefix 126 | ) : 127 | GadgetT(pb, prefix), 128 | 129 | balance(make_variable(pb, FMT(prefix, ".balance"))), 130 | tradingHistory(make_variable(pb, FMT(prefix, ".tradingHistory"))) 131 | { 132 | 133 | } 134 | 135 | void generate_r1cs_witness(const BalanceLeaf& balanceLeaf) 136 | { 137 | pb.val(balance) = balanceLeaf.balance; 138 | pb.val(tradingHistory) = balanceLeaf.tradingHistoryRoot; 139 | } 140 | }; 141 | 142 | class UpdateBalanceGadget : public GadgetT 143 | { 144 | public: 145 | HashBalanceLeaf leafBefore; 146 | HashBalanceLeaf leafAfter; 147 | 148 | const VariableArrayT proof; 149 | MerklePathCheckT proofVerifierBefore; 150 | MerklePathT rootCalculatorAfter; 151 | 152 | UpdateBalanceGadget( 153 | ProtoboardT& pb, 154 | const VariableT& merkleRoot, 155 | const VariableArrayT& tokenID, 156 | const BalanceState before, 157 | const BalanceState after, 158 | const std::string& prefix 159 | ) : 160 | GadgetT(pb, prefix), 161 | 162 | leafBefore(pb, var_array({before.balance, before.tradingHistory}), FMT(prefix, ".leafBefore")), 163 | leafAfter(pb, var_array({after.balance, after.tradingHistory}), FMT(prefix, ".leafAfter")), 164 | 165 | proof(make_var_array(pb, TREE_DEPTH_TOKENS * 3, FMT(prefix, ".proof"))), 166 | proofVerifierBefore(pb, TREE_DEPTH_TOKENS, tokenID, leafBefore.result(), merkleRoot, proof, FMT(prefix, ".pathBefore")), 167 | rootCalculatorAfter(pb, TREE_DEPTH_TOKENS, tokenID, leafAfter.result(), proof, FMT(prefix, ".pathAfter")) 168 | { 169 | 170 | } 171 | 172 | void generate_r1cs_witness(const Proof& _proof) 173 | { 174 | leafBefore.generate_r1cs_witness(); 175 | leafAfter.generate_r1cs_witness(); 176 | 177 | proof.fill_with_field_elements(pb, _proof.data); 178 | proofVerifierBefore.generate_r1cs_witness(); 179 | rootCalculatorAfter.generate_r1cs_witness(); 180 | } 181 | 182 | void generate_r1cs_constraints() 183 | { 184 | leafBefore.generate_r1cs_constraints(); 185 | leafAfter.generate_r1cs_constraints(); 186 | 187 | proofVerifierBefore.generate_r1cs_constraints(); 188 | rootCalculatorAfter.generate_r1cs_constraints(); 189 | } 190 | 191 | const VariableT& result() const 192 | { 193 | return rootCalculatorAfter.result(); 194 | } 195 | }; 196 | 197 | } 198 | 199 | #endif 200 | -------------------------------------------------------------------------------- /Gadgets/MatchingGadgets.h: -------------------------------------------------------------------------------- 1 | #ifndef _MATCHINGGADGETS_H_ 2 | #define _MATCHINGGADGETS_H_ 3 | 4 | #include "../Utils/Constants.h" 5 | #include "../Utils/Data.h" 6 | #include "MathGadgets.h" 7 | #include "OrderGadgets.h" 8 | #include "TradingHistoryGadgets.h" 9 | 10 | #include "ethsnarks.hpp" 11 | #include "utils.hpp" 12 | #include "gadgets/subadd.hpp" 13 | 14 | using namespace ethsnarks; 15 | 16 | namespace Loopring 17 | { 18 | 19 | // Checks if the fill rate <= 0.1% worse than the target rate 20 | // (fillAmountS/fillAmountB) * 1000 <= (amountS/amountB) * 1001 21 | // (fillAmountS * amountB * 1000) <= (fillAmountB * amountS * 1001) 22 | // Also checks that not just a single fill is non-zero. 23 | class RequireFillRateGadget : public GadgetT 24 | { 25 | public: 26 | UnsafeMulGadget fillAmountS_mul_amountB; 27 | UnsafeMulGadget fillAmountS_mul_amountB_mul_1000; 28 | UnsafeMulGadget fillAmountB_mul_amountS; 29 | UnsafeMulGadget fillAmountB_mul_amountS_mul_1001; 30 | RequireLeqGadget validRate; 31 | 32 | IsNonZero isNonZeroFillAmountS; 33 | IsNonZero isNonZeroFillAmountB; 34 | AndGadget fillsNonZero; 35 | NotGadget isZeroFillAmountS; 36 | NotGadget isZeroFillAmountB; 37 | AndGadget fillsZero; 38 | OrGadget fillsValid; 39 | RequireEqualGadget requireFillsValid; 40 | 41 | RequireFillRateGadget( 42 | ProtoboardT& pb, 43 | const Constants& constants, 44 | const VariableT& amountS, 45 | const VariableT& amountB, 46 | const VariableT& fillAmountS, 47 | const VariableT& fillAmountB, 48 | unsigned int n, 49 | const std::string& prefix 50 | ) : 51 | GadgetT(pb, prefix), 52 | 53 | fillAmountS_mul_amountB(pb, fillAmountS, amountB, FMT(prefix, ".fillAmountS_mul_amountB")), 54 | fillAmountS_mul_amountB_mul_1000(pb, fillAmountS_mul_amountB.result(), constants._1000, FMT(prefix, ".fillAmountS_mul_amountB_mul_1000")), 55 | fillAmountB_mul_amountS(pb, fillAmountB, amountS, FMT(prefix, ".fillAmountB_mul_amountS")), 56 | fillAmountB_mul_amountS_mul_1001(pb, fillAmountB_mul_amountS.result(), constants._1001, FMT(prefix, ".fillAmountB_mul_amountS_mul_1001")), 57 | validRate(pb, fillAmountS_mul_amountB_mul_1000.result(), fillAmountB_mul_amountS_mul_1001.result(), n * 2 + 10 /*=ceil(log2(1000))*/, FMT(prefix, ".validRate")), 58 | 59 | // Also enforce that either both fill amounts are zero or both are non-zero. 60 | // This check is also important to make sure no token transfers can happen to unregistered token IDs. 61 | isNonZeroFillAmountS(pb, fillAmountS, FMT(prefix, "isNonZeroFillAmountS")), 62 | isNonZeroFillAmountB(pb, fillAmountB, FMT(prefix, "isNonZeroFillAmountB")), 63 | fillsNonZero(pb, {isNonZeroFillAmountS.result(), isNonZeroFillAmountB.result()}, FMT(prefix, "fillsNonZero")), 64 | isZeroFillAmountS(pb, isNonZeroFillAmountS.result(), FMT(prefix, "isZeroFillAmountS")), 65 | isZeroFillAmountB(pb, isNonZeroFillAmountB.result(), FMT(prefix, "isZeroFillAmountB")), 66 | fillsZero(pb, {isZeroFillAmountS.result(), isZeroFillAmountB.result()}, FMT(prefix, "fillsZero")), 67 | fillsValid(pb, {fillsNonZero.result(), fillsZero.result()}, FMT(prefix, "fillsValid")), 68 | requireFillsValid(pb, fillsValid.result(), constants.one, FMT(prefix, "requireFillsValid")) 69 | { 70 | 71 | } 72 | 73 | void generate_r1cs_witness() 74 | { 75 | fillAmountS_mul_amountB.generate_r1cs_witness(); 76 | fillAmountS_mul_amountB_mul_1000.generate_r1cs_witness(); 77 | fillAmountB_mul_amountS.generate_r1cs_witness(); 78 | fillAmountB_mul_amountS_mul_1001.generate_r1cs_witness(); 79 | validRate.generate_r1cs_witness(); 80 | 81 | isNonZeroFillAmountS.generate_r1cs_witness(); 82 | isNonZeroFillAmountB.generate_r1cs_witness(); 83 | fillsNonZero.generate_r1cs_witness(); 84 | isZeroFillAmountS.generate_r1cs_witness(); 85 | isZeroFillAmountB.generate_r1cs_witness(); 86 | fillsZero.generate_r1cs_witness(); 87 | fillsValid.generate_r1cs_witness(); 88 | requireFillsValid.generate_r1cs_witness(); 89 | } 90 | 91 | void generate_r1cs_constraints() 92 | { 93 | fillAmountS_mul_amountB.generate_r1cs_constraints(); 94 | fillAmountS_mul_amountB_mul_1000.generate_r1cs_constraints(); 95 | fillAmountB_mul_amountS.generate_r1cs_constraints(); 96 | fillAmountB_mul_amountS_mul_1001.generate_r1cs_constraints(); 97 | validRate.generate_r1cs_constraints(); 98 | 99 | isNonZeroFillAmountS.generate_r1cs_constraints(); 100 | isNonZeroFillAmountB.generate_r1cs_constraints(); 101 | fillsNonZero.generate_r1cs_constraints(); 102 | isZeroFillAmountS.generate_r1cs_constraints(); 103 | isZeroFillAmountB.generate_r1cs_constraints(); 104 | fillsZero.generate_r1cs_constraints(); 105 | fillsValid.generate_r1cs_constraints(); 106 | requireFillsValid.generate_r1cs_constraints(); 107 | } 108 | }; 109 | 110 | // Check if an order is filled correctly 111 | class CheckValidGadget : public GadgetT 112 | { 113 | public: 114 | 115 | LeqGadget fillAmountS_lt_amountS; 116 | LeqGadget fillAmountB_lt_amountB; 117 | NotGadget order_sell; 118 | AndGadget notValidAllOrNoneSell; 119 | AndGadget notValidAllOrNoneBuy; 120 | 121 | LeqGadget validSince_leq_timestamp; 122 | LeqGadget timestamp_leq_validUntil; 123 | 124 | NotGadget validAllOrNoneSell; 125 | NotGadget validAllOrNoneBuy; 126 | 127 | AndGadget valid; 128 | 129 | CheckValidGadget( 130 | ProtoboardT& pb, 131 | const Constants& constants, 132 | const VariableT& timestamp, 133 | const OrderGadget& order, 134 | const VariableT& fillAmountS, 135 | const VariableT& fillAmountB, 136 | const std::string& prefix 137 | ) : 138 | GadgetT(pb, prefix), 139 | 140 | // This can be combined in a single comparison (buy/sell order) 141 | fillAmountS_lt_amountS(pb, fillAmountS, order.amountS.packed, NUM_BITS_AMOUNT, FMT(prefix, ".fillAmountS_lt_amountS")), 142 | fillAmountB_lt_amountB(pb, fillAmountB, order.amountB.packed, NUM_BITS_AMOUNT, FMT(prefix, ".fillAmountB_lt_amountB")), 143 | order_sell(pb, order.buy.packed, FMT(prefix, ".order_sell")), 144 | notValidAllOrNoneSell(pb, { order.allOrNone.packed, order_sell.result(), fillAmountS_lt_amountS.lt() }, FMT(prefix, ".notValidAllOrNoneSell")), 145 | notValidAllOrNoneBuy(pb, { order.allOrNone.packed, order.buy.packed, fillAmountB_lt_amountB.lt() }, FMT(prefix, ".notValidAllOrNoneBuy")), 146 | 147 | validSince_leq_timestamp(pb, order.validSince.packed, timestamp, NUM_BITS_TIMESTAMP, FMT(prefix, "validSince <= timestamp")), 148 | timestamp_leq_validUntil(pb, timestamp, order.validUntil.packed, NUM_BITS_TIMESTAMP, FMT(prefix, "timestamp <= validUntil")), 149 | 150 | validAllOrNoneSell(pb, notValidAllOrNoneSell.result(), FMT(prefix, "validAllOrNoneSell")), 151 | validAllOrNoneBuy(pb, notValidAllOrNoneBuy.result(), FMT(prefix, "validAllOrNoneBuy")), 152 | 153 | valid(pb, 154 | { 155 | validSince_leq_timestamp.leq(), 156 | timestamp_leq_validUntil.leq(), 157 | validAllOrNoneSell.result(), 158 | validAllOrNoneBuy.result() 159 | }, 160 | FMT(prefix, ".valid") 161 | ) 162 | { 163 | 164 | } 165 | 166 | void generate_r1cs_witness () 167 | { 168 | fillAmountS_lt_amountS.generate_r1cs_witness(); 169 | fillAmountB_lt_amountB.generate_r1cs_witness(); 170 | order_sell.generate_r1cs_witness(); 171 | notValidAllOrNoneSell.generate_r1cs_witness(); 172 | notValidAllOrNoneBuy.generate_r1cs_witness(); 173 | 174 | validSince_leq_timestamp.generate_r1cs_witness(); 175 | timestamp_leq_validUntil.generate_r1cs_witness(); 176 | 177 | validAllOrNoneSell.generate_r1cs_witness(); 178 | validAllOrNoneBuy.generate_r1cs_witness(); 179 | 180 | valid.generate_r1cs_witness(); 181 | } 182 | 183 | void generate_r1cs_constraints() 184 | { 185 | fillAmountS_lt_amountS.generate_r1cs_constraints(); 186 | fillAmountB_lt_amountB.generate_r1cs_constraints(); 187 | order_sell.generate_r1cs_constraints(); 188 | notValidAllOrNoneSell.generate_r1cs_constraints(); 189 | notValidAllOrNoneBuy.generate_r1cs_constraints(); 190 | 191 | validSince_leq_timestamp.generate_r1cs_constraints(); 192 | timestamp_leq_validUntil.generate_r1cs_constraints(); 193 | 194 | validAllOrNoneSell.generate_r1cs_constraints(); 195 | validAllOrNoneBuy.generate_r1cs_constraints(); 196 | 197 | valid.generate_r1cs_constraints(); 198 | } 199 | 200 | const VariableT& isValid() const 201 | { 202 | return valid.result(); 203 | } 204 | }; 205 | 206 | // Calculates the fees for an order 207 | class FeeCalculatorGadget : public GadgetT 208 | { 209 | public: 210 | 211 | // We could combine the fee and rebate calculations here, saving a MulDiv, but the MulDiv is cheap here, 212 | // so let's keep things simple. 213 | MulDivGadget protocolFee; 214 | MulDivGadget fee; 215 | MulDivGadget rebate; 216 | 217 | FeeCalculatorGadget( 218 | ProtoboardT& pb, 219 | const Constants& constants, 220 | const VariableT& amountB, 221 | const VariableT& protocolFeeBips, 222 | const VariableT& feeBips, 223 | const VariableT& rebateBips, 224 | const std::string& prefix 225 | ) : 226 | GadgetT(pb, prefix), 227 | 228 | protocolFee(pb, constants, amountB, protocolFeeBips, constants._100000, NUM_BITS_AMOUNT, NUM_BITS_PROTOCOL_FEE_BIPS, 17 /*=ceil(log2(100000))*/, FMT(prefix, ".protocolFee")), 229 | fee(pb, constants, amountB, feeBips, constants._10000, NUM_BITS_AMOUNT, NUM_BITS_BIPS, 14 /*=ceil(log2(10000))*/, FMT(prefix, ".fee")), 230 | rebate(pb, constants, amountB, rebateBips, constants._10000, NUM_BITS_AMOUNT, NUM_BITS_BIPS, 14 /*=ceil(log2(10000))*/, FMT(prefix, ".rebate")) 231 | { 232 | 233 | } 234 | 235 | void generate_r1cs_witness() 236 | { 237 | protocolFee.generate_r1cs_witness(); 238 | fee.generate_r1cs_witness(); 239 | rebate.generate_r1cs_witness(); 240 | } 241 | 242 | void generate_r1cs_constraints() 243 | { 244 | protocolFee.generate_r1cs_constraints(); 245 | fee.generate_r1cs_constraints(); 246 | rebate.generate_r1cs_constraints(); 247 | } 248 | 249 | const VariableT& getProtocolFee() const 250 | { 251 | return protocolFee.result(); 252 | } 253 | 254 | const VariableT& getFee() const 255 | { 256 | return fee.result(); 257 | } 258 | 259 | const VariableT& getRebate() const 260 | { 261 | return rebate.result(); 262 | } 263 | }; 264 | 265 | // Checks if the order isn't filled too much 266 | class RequireFillLimitGadget : public GadgetT 267 | { 268 | public: 269 | TernaryGadget fillAmount; 270 | TernaryGadget fillLimit; 271 | AddGadget filledAfter; 272 | RequireLeqGadget filledAfter_leq_fillLimit; 273 | 274 | RequireFillLimitGadget( 275 | ProtoboardT& pb, 276 | const Constants& constants, 277 | const OrderGadget& order, 278 | const VariableT& fillS, 279 | const VariableT& fillB, 280 | const std::string& prefix 281 | ) : 282 | GadgetT(pb, prefix), 283 | 284 | fillAmount(pb, order.buy.packed, fillB, fillS, FMT(prefix, ".fillAmount")), 285 | fillLimit(pb, order.buy.packed, order.amountB.packed, order.amountS.packed, FMT(prefix, ".fillLimit")), 286 | filledAfter(pb, order.tradeHistory.getFilled(), fillAmount.result(), NUM_BITS_AMOUNT, FMT(prefix, ".filledAfter")), 287 | filledAfter_leq_fillLimit(pb, filledAfter.result(), fillLimit.result(), NUM_BITS_AMOUNT, FMT(prefix, ".filledAfter_leq_fillLimit")) 288 | { 289 | 290 | } 291 | 292 | void generate_r1cs_witness() 293 | { 294 | fillAmount.generate_r1cs_witness(); 295 | fillLimit.generate_r1cs_witness(); 296 | filledAfter.generate_r1cs_witness(); 297 | filledAfter_leq_fillLimit.generate_r1cs_witness(); 298 | } 299 | 300 | void generate_r1cs_constraints() 301 | { 302 | fillAmount.generate_r1cs_constraints(); 303 | fillLimit.generate_r1cs_constraints(); 304 | filledAfter.generate_r1cs_constraints(); 305 | filledAfter_leq_fillLimit.generate_r1cs_constraints(); 306 | } 307 | 308 | const VariableT& getFilledAfter() const 309 | { 310 | return filledAfter.result(); 311 | } 312 | }; 313 | 314 | // Checks if the order requirements are fulfilled with the given fill amounts 315 | class RequireOrderFillsGadget : public GadgetT 316 | { 317 | public: 318 | // Check balance limit 319 | RequireLeqGadget fillS_leq_balanceS; 320 | // Check rate 321 | RequireFillRateGadget requireFillRate; 322 | // Check fill limit 323 | RequireFillLimitGadget requireFillLimit; 324 | 325 | RequireOrderFillsGadget( 326 | ProtoboardT& pb, 327 | const Constants& constants, 328 | const OrderGadget& order, 329 | const VariableT& fillS, 330 | const VariableT& fillB, 331 | const std::string& prefix 332 | ) : 333 | GadgetT(pb, prefix), 334 | 335 | // Check balance 336 | fillS_leq_balanceS(pb, fillS, order.balanceSBefore.balance, NUM_BITS_AMOUNT, FMT(prefix, ".fillS_leq_balanceS")), 337 | // Check rate 338 | requireFillRate(pb, constants, order.amountS.packed, order.amountB.packed, fillS, fillB, NUM_BITS_AMOUNT, FMT(prefix, ".requireFillRate")), 339 | // Check fill limit 340 | requireFillLimit(pb, constants, order, fillS, fillB, FMT(prefix, ".requireFillLimit")) 341 | { 342 | 343 | } 344 | 345 | void generate_r1cs_witness() 346 | { 347 | fillS_leq_balanceS.generate_r1cs_witness(); 348 | requireFillRate.generate_r1cs_witness(); 349 | requireFillLimit.generate_r1cs_witness(); 350 | } 351 | 352 | void generate_r1cs_constraints() 353 | { 354 | fillS_leq_balanceS.generate_r1cs_constraints(); 355 | requireFillRate.generate_r1cs_constraints(); 356 | requireFillLimit.generate_r1cs_constraints(); 357 | } 358 | 359 | const VariableT& getFilledAfter() const 360 | { 361 | return requireFillLimit.getFilledAfter(); 362 | } 363 | }; 364 | 365 | // Matches 2 orders 366 | class OrderMatchingGadget : public GadgetT 367 | { 368 | public: 369 | 370 | const VariableT& fillS_A; 371 | const VariableT& fillS_B; 372 | 373 | // Verify the order fills 374 | RequireOrderFillsGadget requireOrderFillsA; 375 | RequireOrderFillsGadget requireOrderFillsB; 376 | 377 | // Check if tokenS/tokenB match 378 | RequireEqualGadget orderA_tokenS_eq_orderB_tokenB; 379 | RequireEqualGadget orderA_tokenB_eq_orderB_tokenS; 380 | 381 | // Check if the orders in the settlement are correctly filled 382 | CheckValidGadget checkValidA; 383 | CheckValidGadget checkValidB; 384 | AndGadget valid; 385 | RequireEqualGadget requireValid; 386 | 387 | OrderMatchingGadget( 388 | ProtoboardT& pb, 389 | const Constants& constants, 390 | const VariableT& timestamp, 391 | const OrderGadget& orderA, 392 | const OrderGadget& orderB, 393 | const VariableT& _fillS_A, 394 | const VariableT& _fillS_B, 395 | const std::string& prefix 396 | ) : 397 | GadgetT(pb, prefix), 398 | fillS_A(_fillS_A), 399 | fillS_B(_fillS_B), 400 | 401 | // Check if the fills are valid for the orders 402 | requireOrderFillsA(pb, constants, orderA, fillS_A, fillS_B, FMT(prefix, ".requireOrderFillsA")), 403 | requireOrderFillsB(pb, constants, orderB, fillS_B, fillS_A, FMT(prefix, ".requireOrderFillsB")), 404 | 405 | // Check if tokenS/tokenB match 406 | orderA_tokenS_eq_orderB_tokenB(pb, orderA.tokenS.packed, orderB.tokenB.packed, FMT(prefix, ".orderA_tokenS_eq_orderB_tokenB")), 407 | orderA_tokenB_eq_orderB_tokenS(pb, orderA.tokenB.packed, orderB.tokenS.packed, FMT(prefix, ".orderA_tokenB_eq_orderB_tokenS")), 408 | 409 | // Check if the orders in the settlement are correctly filled 410 | checkValidA(pb, constants, timestamp, orderA, fillS_A, fillS_B, FMT(prefix, ".checkValidA")), 411 | checkValidB(pb, constants, timestamp, orderB, fillS_B, fillS_A, FMT(prefix, ".checkValidB")), 412 | valid(pb, {checkValidA.isValid(), checkValidB.isValid()}, FMT(prefix, ".valid")), 413 | requireValid(pb, valid.result(), constants.one, FMT(prefix, ".requireValid")) 414 | { 415 | 416 | } 417 | 418 | void generate_r1cs_witness() 419 | { 420 | // Check if the fills are valid for the orders 421 | requireOrderFillsA.generate_r1cs_witness(); 422 | requireOrderFillsB.generate_r1cs_witness(); 423 | 424 | // Check if tokenS/tokenB match 425 | orderA_tokenS_eq_orderB_tokenB.generate_r1cs_witness(); 426 | orderA_tokenB_eq_orderB_tokenS.generate_r1cs_witness(); 427 | 428 | // Check if the orders in the settlement are correctly filled 429 | checkValidA.generate_r1cs_witness(); 430 | checkValidB.generate_r1cs_witness(); 431 | valid.generate_r1cs_witness(); 432 | requireValid.generate_r1cs_witness(); 433 | } 434 | 435 | void generate_r1cs_constraints() 436 | { 437 | // Check if the fills are valid for the orders 438 | requireOrderFillsA.generate_r1cs_constraints(); 439 | requireOrderFillsB.generate_r1cs_constraints(); 440 | 441 | // Check if tokenS/tokenB match 442 | orderA_tokenS_eq_orderB_tokenB.generate_r1cs_constraints(); 443 | orderA_tokenB_eq_orderB_tokenS.generate_r1cs_constraints(); 444 | 445 | // Check if the orders in the settlement are correctly filled 446 | checkValidA.generate_r1cs_constraints(); 447 | checkValidB.generate_r1cs_constraints(); 448 | valid.generate_r1cs_constraints(); 449 | requireValid.generate_r1cs_constraints(); 450 | } 451 | 452 | const VariableT& getFilledAfter_A() const 453 | { 454 | return requireOrderFillsA.getFilledAfter(); 455 | } 456 | 457 | const VariableT& getFilledAfter_B() const 458 | { 459 | return requireOrderFillsB.getFilledAfter(); 460 | } 461 | }; 462 | 463 | 464 | } 465 | 466 | #endif 467 | -------------------------------------------------------------------------------- /Gadgets/MerkleTree.h: -------------------------------------------------------------------------------- 1 | #ifndef _MERKLETREE_H_ 2 | #define _MERKLETREE_H_ 3 | 4 | #include "ethsnarks.hpp" 5 | #include "gadgets/poseidon.hpp" 6 | #include "MathGadgets.h" 7 | 8 | namespace Loopring { 9 | 10 | class merkle_path_selector_4 : public GadgetT 11 | { 12 | public: 13 | OrGadget bit0_or_bit1; 14 | AndGadget bit0_and_bit1; 15 | 16 | TernaryGadget child0; 17 | TernaryGadget child1p; 18 | TernaryGadget child1; 19 | TernaryGadget child2p; 20 | TernaryGadget child2; 21 | TernaryGadget child3; 22 | 23 | // 00 x y0 y1 y2 24 | // 01 y0 x y1 y2 25 | // 10 y0 y1 x y2 26 | // 11 y0 y1 y2 x 27 | merkle_path_selector_4( 28 | ProtoboardT &pb, 29 | const VariableT& input, 30 | std::vector sideNodes, 31 | const VariableT& bit0, 32 | const VariableT& bit1, 33 | const std::string &prefix 34 | ) : 35 | GadgetT(pb, prefix), 36 | 37 | bit0_or_bit1(pb, {bit0, bit1}, FMT(prefix, ".bit0_or_bit1")), 38 | bit0_and_bit1(pb, {bit0, bit1}, FMT(prefix, ".bit0_and_bit1")), 39 | 40 | child0(pb, bit0_or_bit1.result(), sideNodes[0], input, FMT(prefix, ".child0")), 41 | child1p(pb, bit0, input, sideNodes[0], FMT(prefix, ".child1p")), 42 | child1(pb, bit1, sideNodes[1], child1p.result(), FMT(prefix, ".child1")), 43 | child2p(pb, bit0, sideNodes[2], input, FMT(prefix, ".child2p")), 44 | child2(pb, bit1, child2p.result(), sideNodes[1], FMT(prefix, ".child2")), 45 | child3(pb, bit0_and_bit1.result(), input, sideNodes[2], FMT(prefix, ".child3")) 46 | { 47 | assert(sideNodes.size() == 3); 48 | } 49 | 50 | void generate_r1cs_constraints() 51 | { 52 | bit0_or_bit1.generate_r1cs_constraints(); 53 | bit0_and_bit1.generate_r1cs_constraints(); 54 | 55 | child0.generate_r1cs_constraints(false); 56 | child1p.generate_r1cs_constraints(false); 57 | child1.generate_r1cs_constraints(false); 58 | child2p.generate_r1cs_constraints(false); 59 | child2.generate_r1cs_constraints(false); 60 | child3.generate_r1cs_constraints(false); 61 | } 62 | 63 | void generate_r1cs_witness() 64 | { 65 | bit0_or_bit1.generate_r1cs_witness(); 66 | bit0_and_bit1.generate_r1cs_witness(); 67 | 68 | child0.generate_r1cs_witness(); 69 | child1p.generate_r1cs_witness(); 70 | child1.generate_r1cs_witness(); 71 | child2p.generate_r1cs_witness(); 72 | child2.generate_r1cs_witness(); 73 | child3.generate_r1cs_witness(); 74 | } 75 | 76 | std::vector getChildren() const 77 | { 78 | return {child0.result(), child1.result(), child2.result(), child3.result()}; 79 | } 80 | }; 81 | 82 | template 83 | class merkle_path_compute_4 : public GadgetT 84 | { 85 | public: 86 | std::vector m_selectors; 87 | std::vector m_hashers; 88 | 89 | merkle_path_compute_4( 90 | ProtoboardT &in_pb, 91 | const size_t in_depth, 92 | const VariableArrayT& in_address_bits, 93 | const VariableT in_leaf, 94 | const VariableArrayT& in_path, 95 | const std::string &in_annotation_prefix 96 | ) : 97 | GadgetT(in_pb, in_annotation_prefix) 98 | { 99 | assert( in_depth > 0 ); 100 | assert( in_address_bits.size() == in_depth * 2 ); 101 | 102 | m_selectors.reserve(in_depth); 103 | m_hashers.reserve(in_depth); 104 | for( size_t i = 0; i < in_depth; i++ ) 105 | { 106 | m_selectors.push_back( 107 | merkle_path_selector_4( 108 | in_pb, (i == 0) ? in_leaf : m_hashers[i-1].result(), 109 | {in_path[i*3 + 0], in_path[i*3 + 1], in_path[i*3 + 2]}, 110 | in_address_bits[i*2 + 0], in_address_bits[i*2 + 1], 111 | FMT(this->annotation_prefix, ".selector[%zu]", i))); 112 | 113 | m_hashers.emplace_back( 114 | in_pb, 115 | var_array(m_selectors[i].getChildren()), 116 | FMT(this->annotation_prefix, ".hasher[%zu]", i) 117 | ); 118 | } 119 | } 120 | 121 | const VariableT& result() const 122 | { 123 | assert( m_hashers.size() > 0 ); 124 | return m_hashers.back().result(); 125 | } 126 | 127 | void generate_r1cs_constraints() 128 | { 129 | for (size_t i = 0; i < m_hashers.size(); i++) 130 | { 131 | m_selectors[i].generate_r1cs_constraints(); 132 | m_hashers[i].generate_r1cs_constraints(); 133 | } 134 | } 135 | 136 | void generate_r1cs_witness() 137 | { 138 | for (size_t i = 0; i < m_hashers.size(); i++) 139 | { 140 | m_selectors[i].generate_r1cs_witness(); 141 | m_hashers[i].generate_r1cs_witness(); 142 | } 143 | } 144 | }; 145 | 146 | 147 | /** 148 | * Merkle path authenticator, verifies computed root matches expected result 149 | */ 150 | template 151 | class merkle_path_authenticator_4 : public merkle_path_compute_4 152 | { 153 | public: 154 | const VariableT m_expected_root; 155 | 156 | merkle_path_authenticator_4( 157 | ProtoboardT &in_pb, 158 | const size_t in_depth, 159 | const VariableArrayT in_address_bits, 160 | const VariableT in_leaf, 161 | const VariableT in_expected_root, 162 | const VariableArrayT in_path, 163 | const std::string &in_annotation_prefix 164 | ) : 165 | merkle_path_compute_4::merkle_path_compute_4(in_pb, in_depth, in_address_bits, in_leaf, in_path, in_annotation_prefix), 166 | m_expected_root(in_expected_root) 167 | { } 168 | 169 | bool is_valid() const 170 | { 171 | return this->pb.val(this->result()) == this->pb.val(m_expected_root); 172 | } 173 | 174 | void generate_r1cs_constraints() 175 | { 176 | merkle_path_compute_4::generate_r1cs_constraints(); 177 | 178 | // Ensure root matches calculated path hash 179 | this->pb.add_r1cs_constraint( 180 | ConstraintT(this->result(), 1, m_expected_root), 181 | FMT(this->annotation_prefix, ".expected_root authenticator")); 182 | } 183 | }; 184 | 185 | // Same parameters for ease of implementation in EVM 186 | using HashMerkleTree = Poseidon_gadget_T<5, 1, 6, 52, 4, 1>; 187 | using HashAccountLeaf = Poseidon_gadget_T<5, 1, 6, 52, 4, 1>; 188 | using HashBalanceLeaf = Poseidon_gadget_T<5, 1, 6, 52, 2, 1>; 189 | using HashTradingHistoryLeaf = Poseidon_gadget_T<5, 1, 6, 52, 2, 1>; 190 | 191 | // Minimal parameters for 128bit security: 192 | //using HashMerkleTree = Poseidon_gadget_T<5, 1, 6, 52, 4, 1>; 193 | //using HashAccountLeaf = Poseidon_gadget_T<5, 1, 6, 52, 4, 1>; 194 | //using HashBalanceLeaf = Poseidon_gadget_T<3, 1, 6, 51, 2, 1>; 195 | //using HashTradingHistoryLeaf = Poseidon_gadget_T<4, 1, 6, 52, 3, 1>; 196 | 197 | using MerklePathCheckT = merkle_path_authenticator_4; 198 | using MerklePathT = merkle_path_compute_4; 199 | 200 | } 201 | 202 | #endif 203 | -------------------------------------------------------------------------------- /Gadgets/OrderGadgets.h: -------------------------------------------------------------------------------- 1 | #ifndef _ORDERGADGETS_H_ 2 | #define _ORDERGADGETS_H_ 3 | 4 | #include "../Utils/Constants.h" 5 | #include "../Utils/Data.h" 6 | #include "TradingHistoryGadgets.h" 7 | #include "AccountGadgets.h" 8 | 9 | #include "ethsnarks.hpp" 10 | #include "gadgets/poseidon.hpp" 11 | #include "utils.hpp" 12 | 13 | using namespace ethsnarks; 14 | 15 | namespace Loopring 16 | { 17 | 18 | class OrderGadget : public GadgetT 19 | { 20 | public: 21 | 22 | // State 23 | TradeHistoryGadget tradeHistoryBefore; 24 | BalanceGadget balanceSBefore; 25 | BalanceGadget balanceBBefore; 26 | AccountGadget accountBefore; 27 | 28 | // Inputs 29 | DualVariableGadget orderID; 30 | DualVariableGadget accountID; 31 | DualVariableGadget tokenS; 32 | DualVariableGadget tokenB; 33 | DualVariableGadget amountS; 34 | DualVariableGadget amountB; 35 | DualVariableGadget allOrNone; 36 | DualVariableGadget validSince; 37 | DualVariableGadget validUntil; 38 | DualVariableGadget maxFeeBips; 39 | DualVariableGadget buy; 40 | 41 | DualVariableGadget feeBips; 42 | DualVariableGadget rebateBips; 43 | 44 | // Checks 45 | RequireZeroAorBGadget feeOrRebateZero; 46 | RequireLeqGadget feeBips_leq_maxFeeBips; 47 | RequireNotEqualGadget tokenS_neq_tokenB; 48 | RequireNotZeroGadget amountS_notZero; 49 | RequireNotZeroGadget amountB_notZero; 50 | 51 | // FeeOrRebate public input 52 | IsNonZero bRebateNonZero; 53 | UnsafeAddGadget fee_plus_rebate; 54 | libsnark::dual_variable_gadget feeOrRebateBips; 55 | 56 | // Trade history 57 | TradeHistoryTrimmingGadget tradeHistory; 58 | 59 | // Signature 60 | Poseidon_gadget_T<13, 1, 6, 53, 12, 1> hash; 61 | SignatureVerifier signatureVerifier; 62 | 63 | OrderGadget( 64 | ProtoboardT& pb, 65 | const jubjub::Params& params, 66 | const Constants& constants, 67 | const VariableT& blockExchangeID, 68 | const std::string& prefix 69 | ) : 70 | GadgetT(pb, prefix), 71 | 72 | // State 73 | tradeHistoryBefore(pb, FMT(prefix, ".tradeHistoryBefore")), 74 | balanceSBefore(pb, FMT(prefix, ".balanceSBefore")), 75 | balanceBBefore(pb, FMT(prefix, ".balanceBBefore")), 76 | accountBefore(pb, FMT(prefix, ".accountBefore")), 77 | 78 | // Inputs 79 | orderID(pb, NUM_BITS_ORDERID, FMT(prefix, ".orderID")), 80 | accountID(pb, NUM_BITS_ACCOUNT, FMT(prefix, ".accountID")), 81 | tokenS(pb, NUM_BITS_TOKEN, FMT(prefix, ".tokenS")), 82 | tokenB(pb, NUM_BITS_TOKEN, FMT(prefix, ".tokenB")), 83 | amountS(pb, NUM_BITS_AMOUNT, FMT(prefix, ".amountS")), 84 | amountB(pb, NUM_BITS_AMOUNT, FMT(prefix, ".amountB")), 85 | allOrNone(pb, 1, FMT(prefix, ".allOrNone")), 86 | validSince(pb, NUM_BITS_TIMESTAMP, FMT(prefix, ".validSince")), 87 | validUntil(pb, NUM_BITS_TIMESTAMP, FMT(prefix, ".validUntil")), 88 | maxFeeBips(pb, NUM_BITS_BIPS, FMT(prefix, ".maxFeeBips")), 89 | buy(pb, 1, FMT(prefix, ".buy")), 90 | 91 | feeBips(pb, NUM_BITS_BIPS, FMT(prefix, ".feeBips")), 92 | rebateBips(pb, NUM_BITS_BIPS, FMT(prefix, ".rebateBips")), 93 | 94 | // Checks 95 | feeOrRebateZero(pb, feeBips.packed, rebateBips.packed, FMT(prefix, ".feeOrRebateZero")), 96 | feeBips_leq_maxFeeBips(pb, feeBips.packed, maxFeeBips.packed, NUM_BITS_BIPS, FMT(prefix, ".feeBips <= maxFeeBips")), 97 | tokenS_neq_tokenB(pb, tokenS.packed, tokenB.packed, FMT(prefix, ".tokenS != tokenB")), 98 | amountS_notZero(pb, amountS.packed, FMT(prefix, ".amountS != 0")), 99 | amountB_notZero(pb, amountB.packed, FMT(prefix, ".amountB != 0")), 100 | 101 | // FeeOrRebate public input 102 | fee_plus_rebate(pb, feeBips.packed, rebateBips.packed, FMT(prefix, ".fee_plus_rebate")), 103 | feeOrRebateBips(pb, fee_plus_rebate.result(), NUM_BITS_BIPS, FMT(prefix, ".feeOrRebateBips")), 104 | bRebateNonZero(pb, rebateBips.packed, FMT(prefix, ".bRebateNonZero")), 105 | 106 | // Trade history 107 | tradeHistory(pb, constants, tradeHistoryBefore, orderID, FMT(prefix, ".tradeHistory")), 108 | 109 | // Signature 110 | hash(pb, var_array({ 111 | blockExchangeID, 112 | orderID.packed, 113 | accountID.packed, 114 | tokenS.packed, 115 | tokenB.packed, 116 | amountS.packed, 117 | amountB.packed, 118 | allOrNone.packed, 119 | validSince.packed, 120 | validUntil.packed, 121 | maxFeeBips.packed, 122 | buy.packed 123 | }), FMT(this->annotation_prefix, ".hash")), 124 | signatureVerifier(pb, params, constants, accountBefore.publicKey, hash.result(), FMT(prefix, ".signatureVerifier")) 125 | { 126 | 127 | } 128 | 129 | void generate_r1cs_witness(const Order& order, const Account& account, 130 | const BalanceLeaf& balanceLeafS, const BalanceLeaf& balanceLeafB, 131 | const TradeHistoryLeaf& tradeHistoryLeaf) 132 | { 133 | // State 134 | tradeHistoryBefore.generate_r1cs_witness(tradeHistoryLeaf); 135 | balanceSBefore.generate_r1cs_witness(balanceLeafS); 136 | balanceBBefore.generate_r1cs_witness(balanceLeafB); 137 | accountBefore.generate_r1cs_witness(account); 138 | 139 | // Inputs 140 | orderID.generate_r1cs_witness(pb, order.orderID); 141 | accountID.generate_r1cs_witness(pb, order.accountID); 142 | tokenS.generate_r1cs_witness(pb, order.tokenS); 143 | tokenB.generate_r1cs_witness(pb, order.tokenB); 144 | amountS.generate_r1cs_witness(pb, order.amountS); 145 | amountB.generate_r1cs_witness(pb, order.amountB); 146 | allOrNone.generate_r1cs_witness(pb, order.allOrNone); 147 | validSince.generate_r1cs_witness(pb, order.validSince); 148 | validUntil.generate_r1cs_witness(pb, order.validUntil); 149 | maxFeeBips.generate_r1cs_witness(pb, order.maxFeeBips); 150 | buy.generate_r1cs_witness(pb, order.buy); 151 | 152 | feeBips.generate_r1cs_witness(pb, order.feeBips); 153 | rebateBips.generate_r1cs_witness(pb, order.rebateBips); 154 | 155 | // Checks 156 | feeOrRebateZero.generate_r1cs_witness(); 157 | feeBips_leq_maxFeeBips.generate_r1cs_witness(); 158 | tokenS_neq_tokenB.generate_r1cs_witness(); 159 | amountS_notZero.generate_r1cs_witness(); 160 | amountB_notZero.generate_r1cs_witness(); 161 | 162 | // FeeOrRebate public input 163 | fee_plus_rebate.generate_r1cs_witness(); 164 | feeOrRebateBips.generate_r1cs_witness_from_packed(); 165 | bRebateNonZero.generate_r1cs_witness(); 166 | 167 | // Trade history 168 | tradeHistory.generate_r1cs_witness(); 169 | 170 | // Signature 171 | hash.generate_r1cs_witness(); 172 | signatureVerifier.generate_r1cs_witness(order.signature); 173 | } 174 | 175 | void generate_r1cs_constraints(bool doSignatureCheck = true) 176 | { 177 | // Inputs 178 | orderID.generate_r1cs_constraints(true); 179 | accountID.generate_r1cs_constraints(true); 180 | tokenS.generate_r1cs_constraints(true); 181 | tokenB.generate_r1cs_constraints(true); 182 | amountS.generate_r1cs_constraints(true); 183 | amountB.generate_r1cs_constraints(true); 184 | allOrNone.generate_r1cs_constraints(true); 185 | validSince.generate_r1cs_constraints(true); 186 | validUntil.generate_r1cs_constraints(true); 187 | maxFeeBips.generate_r1cs_constraints(true); 188 | buy.generate_r1cs_constraints(true); 189 | 190 | feeBips.generate_r1cs_constraints(true); 191 | rebateBips.generate_r1cs_constraints(true); 192 | 193 | // Checks 194 | feeOrRebateZero.generate_r1cs_constraints(); 195 | feeBips_leq_maxFeeBips.generate_r1cs_constraints(); 196 | tokenS_neq_tokenB.generate_r1cs_constraints(); 197 | amountS_notZero.generate_r1cs_constraints(); 198 | amountB_notZero.generate_r1cs_constraints(); 199 | 200 | // FeeOrRebate public input 201 | fee_plus_rebate.generate_r1cs_constraints(); 202 | feeOrRebateBips.generate_r1cs_constraints(true); 203 | bRebateNonZero.generate_r1cs_constraints(); 204 | 205 | // Trade history 206 | tradeHistory.generate_r1cs_constraints(); 207 | 208 | // Signature 209 | hash.generate_r1cs_constraints(); 210 | if (doSignatureCheck) 211 | { 212 | signatureVerifier.generate_r1cs_constraints(); 213 | } 214 | } 215 | 216 | const VariableT& hasRebate() const 217 | { 218 | return bRebateNonZero.result(); 219 | } 220 | }; 221 | 222 | } 223 | 224 | #endif 225 | -------------------------------------------------------------------------------- /Gadgets/TradingHistoryGadgets.h: -------------------------------------------------------------------------------- 1 | #ifndef _TRADINGHISTORYGADGETS_H_ 2 | #define _TRADINGHISTORYGADGETS_H_ 3 | 4 | #include "../Utils/Constants.h" 5 | #include "../Utils/Data.h" 6 | 7 | #include "MerkleTree.h" 8 | 9 | #include "ethsnarks.hpp" 10 | #include "utils.hpp" 11 | 12 | using namespace ethsnarks; 13 | 14 | namespace Loopring 15 | { 16 | 17 | struct TradeHistoryState 18 | { 19 | VariableT filled; 20 | VariableT orderID; 21 | }; 22 | 23 | class TradeHistoryGadget : public GadgetT 24 | { 25 | public: 26 | VariableT filled; 27 | VariableT orderID; 28 | 29 | TradeHistoryGadget( 30 | ProtoboardT& pb, 31 | const std::string& prefix 32 | ) : 33 | GadgetT(pb, prefix), 34 | 35 | filled(make_variable(pb, FMT(prefix, ".filled"))), 36 | orderID(make_variable(pb, FMT(prefix, ".orderID"))) 37 | { 38 | 39 | } 40 | 41 | void generate_r1cs_witness(const TradeHistoryLeaf& tradeHistoryLeaf) 42 | { 43 | pb.val(filled) = tradeHistoryLeaf.filled; 44 | pb.val(orderID) = tradeHistoryLeaf.orderID; 45 | } 46 | }; 47 | 48 | class UpdateTradeHistoryGadget : public GadgetT 49 | { 50 | public: 51 | HashTradingHistoryLeaf leafBefore; 52 | HashTradingHistoryLeaf leafAfter; 53 | 54 | const VariableArrayT proof; 55 | MerklePathCheckT proofVerifierBefore; 56 | MerklePathT rootCalculatorAfter; 57 | 58 | UpdateTradeHistoryGadget( 59 | ProtoboardT& pb, 60 | const VariableT& merkleRoot, 61 | const VariableArrayT& address, 62 | const TradeHistoryState& before, 63 | const TradeHistoryState& after, 64 | const std::string& prefix 65 | ) : 66 | GadgetT(pb, prefix), 67 | 68 | leafBefore(pb, var_array({before.filled, before.orderID}), FMT(prefix, ".leafBefore")), 69 | leafAfter(pb, var_array({after.filled, after.orderID}), FMT(prefix, ".leafAfter")), 70 | 71 | proof(make_var_array(pb, TREE_DEPTH_TRADING_HISTORY * 3, FMT(prefix, ".proof"))), 72 | proofVerifierBefore(pb, TREE_DEPTH_TRADING_HISTORY, address, leafBefore.result(), merkleRoot, proof, FMT(prefix, ".pathBefore")), 73 | rootCalculatorAfter(pb, TREE_DEPTH_TRADING_HISTORY, address, leafAfter.result(), proof, FMT(prefix, ".pathAfter")) 74 | { 75 | 76 | } 77 | 78 | void generate_r1cs_witness(const Proof& _proof) 79 | { 80 | leafBefore.generate_r1cs_witness(); 81 | leafAfter.generate_r1cs_witness(); 82 | 83 | proof.fill_with_field_elements(pb, _proof.data); 84 | proofVerifierBefore.generate_r1cs_witness(); 85 | rootCalculatorAfter.generate_r1cs_witness(); 86 | } 87 | 88 | void generate_r1cs_constraints() 89 | { 90 | leafBefore.generate_r1cs_constraints(); 91 | leafAfter.generate_r1cs_constraints(); 92 | 93 | proofVerifierBefore.generate_r1cs_constraints(); 94 | rootCalculatorAfter.generate_r1cs_constraints(); 95 | } 96 | 97 | const VariableT& result() const 98 | { 99 | return rootCalculatorAfter.result(); 100 | } 101 | }; 102 | 103 | class TradeHistoryTrimmingGadget : public GadgetT 104 | { 105 | VariableT address; 106 | libsnark::packing_gadget packAddress; 107 | IsNonZero isNonZeroTradeHistoryOrderID; 108 | TernaryGadget tradeHistoryOrderID; 109 | 110 | UnsafeAddGadget nextTradeHistoryOrderID; 111 | 112 | EqualGadget orderID_eq_tradeHistoryOrderID; 113 | EqualGadget orderID_eq_nextTradeHistoryOrderID; 114 | 115 | OrGadget isValidOrderID; 116 | RequireEqualGadget requireValidOrderID; 117 | 118 | TernaryGadget filled; 119 | 120 | public: 121 | 122 | TradeHistoryTrimmingGadget( 123 | ProtoboardT& pb, 124 | const Constants& constants, 125 | const TradeHistoryGadget& tradeHistory, 126 | const DualVariableGadget& orderID, 127 | const std::string& prefix 128 | ) : 129 | GadgetT(pb, prefix), 130 | 131 | address(make_variable(pb, FMT(prefix, ".address"))), 132 | 133 | packAddress(pb, subArray(orderID.bits, 0, NUM_BITS_TRADING_HISTORY), address, FMT(prefix, ".packAddress")), 134 | isNonZeroTradeHistoryOrderID(pb, tradeHistory.orderID, FMT(prefix, ".isNonZeroTradeHistoryOrderID")), 135 | tradeHistoryOrderID(pb, isNonZeroTradeHistoryOrderID.result(), tradeHistory.orderID, address, FMT(prefix, ".tradeHistoryOrderID")), 136 | 137 | nextTradeHistoryOrderID(pb, tradeHistoryOrderID.result(), constants.maxConcurrentOrderIDs, FMT(prefix, ".nextTradeHistoryOrderID")), 138 | 139 | orderID_eq_tradeHistoryOrderID(pb, orderID.packed, tradeHistoryOrderID.result(), FMT(prefix, ".nextTradeHistoryOrderID")), 140 | orderID_eq_nextTradeHistoryOrderID(pb, orderID.packed, nextTradeHistoryOrderID.result(), FMT(prefix, ".orderID_eq_nextTradeHistoryOrderID")), 141 | isValidOrderID(pb, {orderID_eq_tradeHistoryOrderID.result(), orderID_eq_nextTradeHistoryOrderID.result()}, FMT(prefix, ".isValidOrderID")), 142 | requireValidOrderID(pb, isValidOrderID.result(), constants.one, FMT(prefix, ".requireValidOrderID")), 143 | 144 | filled(pb, orderID_eq_tradeHistoryOrderID.result(), tradeHistory.filled, constants.zero, FMT(prefix, ".filled")) 145 | { 146 | 147 | } 148 | 149 | void generate_r1cs_witness() 150 | { 151 | packAddress.generate_r1cs_witness_from_bits(); 152 | isNonZeroTradeHistoryOrderID.generate_r1cs_witness(); 153 | tradeHistoryOrderID.generate_r1cs_witness(); 154 | 155 | nextTradeHistoryOrderID.generate_r1cs_witness(); 156 | 157 | orderID_eq_tradeHistoryOrderID.generate_r1cs_witness(); 158 | orderID_eq_nextTradeHistoryOrderID.generate_r1cs_witness(); 159 | isValidOrderID.generate_r1cs_witness(); 160 | requireValidOrderID.generate_r1cs_witness(); 161 | 162 | filled.generate_r1cs_witness(); 163 | } 164 | 165 | void generate_r1cs_constraints() 166 | { 167 | packAddress.generate_r1cs_constraints(false); 168 | isNonZeroTradeHistoryOrderID.generate_r1cs_constraints(); 169 | tradeHistoryOrderID.generate_r1cs_constraints(); 170 | 171 | nextTradeHistoryOrderID.generate_r1cs_constraints(); 172 | 173 | orderID_eq_tradeHistoryOrderID.generate_r1cs_constraints(); 174 | orderID_eq_nextTradeHistoryOrderID.generate_r1cs_constraints(); 175 | isValidOrderID.generate_r1cs_constraints(); 176 | requireValidOrderID.generate_r1cs_constraints(); 177 | 178 | filled.generate_r1cs_constraints(); 179 | } 180 | 181 | const VariableT& getFilled() const 182 | { 183 | return filled.result(); 184 | } 185 | 186 | const VariableT& getOverwrite() const 187 | { 188 | return orderID_eq_nextTradeHistoryOrderID.result(); 189 | } 190 | }; 191 | 192 | } 193 | 194 | #endif 195 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # loopring3-circuits 2 | 3 | Source code of the circuits used in Loopring v3. 4 | 5 | ## Compile 6 | To compile the code, download the code and place it inside the same directory with the [protocols](https://github.com/Loopring/protocols) project, then run `npm run build` inside `protocols/packages/loopring_v3/`. 7 | -------------------------------------------------------------------------------- /ThirdParty/BigIntHeader.hpp: -------------------------------------------------------------------------------- 1 | #define BIG_INT_UTILITY_FUNCTIONS_HPP 2 | #define BIG_INT_CONSTRUCTORS_HPP 3 | #define BIG_INT_CONVERSION_FUNCTIONS_HPP 4 | #define BIG_INT_ASSIGNMENT_OPERATORS_HPP 5 | #define BIG_INT_UNARY_ARITHMETIC_OPERATORS_HPP 6 | #define BIG_INT_RELATIONAL_OPERATORS_HPP 7 | #define BIG_INT_MATH_FUNCTIONS_HPP 8 | #define BIG_INT_BINARY_ARITHMETIC_OPERATORS_HPP 9 | #define BIG_INT_ARITHMETIC_ASSIGNMENT_OPERATORS_HPP 10 | #define BIG_INT_INCREMENT_DECREMENT_OPERATORS_HPP 11 | #define BIG_INT_IO_STREAM_OPERATORS_HPP 12 | #include "BigInt.hpp" -------------------------------------------------------------------------------- /Utils/Constants.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONSTANTS_H_ 2 | #define _CONSTANTS_H_ 3 | 4 | namespace Loopring 5 | { 6 | static const unsigned int TREE_DEPTH_TRADING_HISTORY = 7; 7 | static const unsigned int TREE_DEPTH_ACCOUNTS = 12; 8 | static const unsigned int TREE_DEPTH_TOKENS = 5; 9 | 10 | static const unsigned int NUM_BITS_MAX_VALUE = 254; 11 | static const unsigned int NUM_BITS_FIELD_CAPACITY = 253; 12 | static const unsigned int NUM_BITS_AMOUNT = 96; 13 | static const unsigned int NUM_BITS_TRADING_HISTORY = TREE_DEPTH_TRADING_HISTORY * 2; 14 | static const unsigned int NUM_BITS_ACCOUNT = TREE_DEPTH_ACCOUNTS * 2; 15 | static const unsigned int NUM_BITS_TOKEN = TREE_DEPTH_TOKENS * 2; 16 | static const unsigned int NUM_BITS_ORDERID = 64; 17 | static const unsigned int NUM_BITS_TIMESTAMP = 32; 18 | static const unsigned int NUM_BITS_NONCE = 32; 19 | static const unsigned int NUM_BITS_BIPS = 6; 20 | static const unsigned int NUM_BITS_EXCHANGE_ID = 32; 21 | static const unsigned int NUM_BITS_PROTOCOL_FEE_BIPS = 8; 22 | static const unsigned int NUM_BITS_TYPE = 8; 23 | static const unsigned int MAX_CONCURRENT_ORDERIDS = 16384; // 2**NUM_BITS_TRADING_HISTORY 24 | 25 | 26 | static const char* EMPTY_TRADE_HISTORY = "6592749167578234498153410564243369229486412054742481069049239297514590357090"; 27 | static const char* MAX_AMOUNT = "79228162514264337593543950335"; // 2^96 - 1 28 | 29 | struct FloatEncoding 30 | { 31 | unsigned int numBitsExponent; 32 | unsigned int numBitsMantissa; 33 | unsigned int exponentBase; 34 | }; 35 | static const FloatEncoding Float28Encoding = {5, 23, 10}; 36 | static const FloatEncoding Float24Encoding = {5, 19, 10}; 37 | static const FloatEncoding Float16Encoding = {5, 11, 10}; 38 | 39 | struct Accuracy 40 | { 41 | unsigned int numerator; 42 | unsigned int denominator; 43 | }; 44 | static const Accuracy Float28Accuracy = {10000000 - 12, 10000000}; 45 | static const Accuracy Float24Accuracy = {100000 - 2, 100000}; 46 | static const Accuracy Float16Accuracy = {1000 - 5, 1000}; 47 | } 48 | 49 | #endif -------------------------------------------------------------------------------- /Utils/Data.h: -------------------------------------------------------------------------------- 1 | #ifndef _DATA_H_ 2 | #define _DATA_H_ 3 | 4 | #include "Constants.h" 5 | 6 | //#include "../ThirdParty/json.hpp" 7 | #include "ethsnarks.hpp" 8 | #include "jubjub/point.hpp" 9 | #include "jubjub/eddsa.hpp" 10 | 11 | using json = nlohmann::json; 12 | 13 | 14 | namespace Loopring 15 | { 16 | 17 | enum class BlockType 18 | { 19 | RingSettlement = 0, 20 | Deposit, 21 | OnchainWithdrawal, 22 | OffchainWithdrawal, 23 | OrderCancellation, 24 | InternalTransfer, 25 | 26 | COUNT 27 | }; 28 | 29 | class Proof 30 | { 31 | public: 32 | std::vector data; 33 | }; 34 | 35 | static void from_json(const json& j, Proof& proof) 36 | { 37 | for(unsigned int i = 0; i < j.size(); i++) 38 | { 39 | proof.data.push_back(ethsnarks::FieldT(j[i].get().c_str())); 40 | } 41 | } 42 | 43 | class TradeHistoryLeaf 44 | { 45 | public: 46 | ethsnarks::FieldT filled; 47 | ethsnarks::FieldT orderID; 48 | }; 49 | 50 | static void from_json(const json& j, TradeHistoryLeaf& leaf) 51 | { 52 | leaf.filled = ethsnarks::FieldT(j.at("filled").get().c_str()); 53 | leaf.orderID = ethsnarks::FieldT(j.at("orderID").get().c_str()); 54 | } 55 | 56 | class BalanceLeaf 57 | { 58 | public: 59 | ethsnarks::FieldT balance; 60 | ethsnarks::FieldT tradingHistoryRoot; 61 | }; 62 | 63 | static void from_json(const json& j, BalanceLeaf& leaf) 64 | { 65 | leaf.balance = ethsnarks::FieldT(j.at("balance").get().c_str()); 66 | leaf.tradingHistoryRoot = ethsnarks::FieldT(j.at("tradingHistoryRoot").get().c_str()); 67 | } 68 | 69 | class Account 70 | { 71 | public: 72 | ethsnarks::jubjub::EdwardsPoint publicKey; 73 | ethsnarks::FieldT nonce; 74 | ethsnarks::FieldT balancesRoot; 75 | }; 76 | 77 | static void from_json(const json& j, Account& account) 78 | { 79 | account.publicKey.x = ethsnarks::FieldT(j.at("publicKeyX").get().c_str()); 80 | account.publicKey.y = ethsnarks::FieldT(j.at("publicKeyY").get().c_str()); 81 | account.nonce = ethsnarks::FieldT(j.at("nonce")); 82 | account.balancesRoot = ethsnarks::FieldT(j.at("balancesRoot").get().c_str()); 83 | } 84 | 85 | class BalanceUpdate 86 | { 87 | public: 88 | ethsnarks::FieldT tokenID; 89 | Proof proof; 90 | ethsnarks::FieldT rootBefore; 91 | ethsnarks::FieldT rootAfter; 92 | BalanceLeaf before; 93 | BalanceLeaf after; 94 | }; 95 | 96 | static void from_json(const json& j, BalanceUpdate& balanceUpdate) 97 | { 98 | balanceUpdate.tokenID = ethsnarks::FieldT(j.at("tokenID")); 99 | balanceUpdate.proof = j.at("proof").get(); 100 | balanceUpdate.rootBefore = ethsnarks::FieldT(j.at("rootBefore").get().c_str()); 101 | balanceUpdate.rootAfter = ethsnarks::FieldT(j.at("rootAfter").get().c_str()); 102 | balanceUpdate.before = j.at("before").get(); 103 | balanceUpdate.after = j.at("after").get(); 104 | } 105 | 106 | class TradeHistoryUpdate 107 | { 108 | public: 109 | ethsnarks::FieldT orderID; 110 | Proof proof; 111 | ethsnarks::FieldT rootBefore; 112 | ethsnarks::FieldT rootAfter; 113 | TradeHistoryLeaf before; 114 | TradeHistoryLeaf after; 115 | }; 116 | 117 | static void from_json(const json& j, TradeHistoryUpdate& tradeHistoryUpdate) 118 | { 119 | tradeHistoryUpdate.orderID = ethsnarks::FieldT(j.at("orderID").get().c_str()); 120 | tradeHistoryUpdate.proof = j.at("proof").get(); 121 | tradeHistoryUpdate.rootBefore = ethsnarks::FieldT(j.at("rootBefore").get().c_str()); 122 | tradeHistoryUpdate.rootAfter = ethsnarks::FieldT(j.at("rootAfter").get().c_str()); 123 | tradeHistoryUpdate.before = j.at("before").get(); 124 | tradeHistoryUpdate.after = j.at("after").get(); 125 | } 126 | 127 | class AccountUpdate 128 | { 129 | public: 130 | ethsnarks::FieldT accountID; 131 | Proof proof; 132 | ethsnarks::FieldT rootBefore; 133 | ethsnarks::FieldT rootAfter; 134 | Account before; 135 | Account after; 136 | }; 137 | 138 | static void from_json(const json& j, AccountUpdate& accountUpdate) 139 | { 140 | accountUpdate.accountID = ethsnarks::FieldT(j.at("accountID")); 141 | accountUpdate.proof = j.at("proof").get(); 142 | accountUpdate.rootBefore = ethsnarks::FieldT(j.at("rootBefore").get().c_str()); 143 | accountUpdate.rootAfter = ethsnarks::FieldT(j.at("rootAfter").get().c_str()); 144 | accountUpdate.before = j.at("before").get(); 145 | accountUpdate.after = j.at("after").get(); 146 | } 147 | 148 | class Signature 149 | { 150 | public: 151 | 152 | Signature() 153 | { 154 | 155 | } 156 | 157 | Signature(ethsnarks::jubjub::EdwardsPoint _R, ethsnarks::FieldT _s) : R(_R), s(_s) 158 | { 159 | 160 | } 161 | 162 | ethsnarks::jubjub::EdwardsPoint R; 163 | ethsnarks::FieldT s; 164 | }; 165 | 166 | static void from_json(const json& j, Signature& signature) 167 | { 168 | signature.R.x = ethsnarks::FieldT(j.at("Rx").get().c_str()); 169 | signature.R.y = ethsnarks::FieldT(j.at("Ry").get().c_str()); 170 | signature.s = ethsnarks::FieldT(j.at("s").get().c_str()); 171 | } 172 | 173 | class Order 174 | { 175 | public: 176 | ethsnarks::FieldT exchangeID; 177 | ethsnarks::FieldT orderID; 178 | ethsnarks::FieldT accountID; 179 | ethsnarks::FieldT tokenS; 180 | ethsnarks::FieldT tokenB; 181 | ethsnarks::FieldT amountS; 182 | ethsnarks::FieldT amountB; 183 | ethsnarks::FieldT allOrNone; 184 | ethsnarks::FieldT validSince; 185 | ethsnarks::FieldT validUntil; 186 | ethsnarks::FieldT maxFeeBips; 187 | ethsnarks::FieldT buy; 188 | 189 | ethsnarks::FieldT feeBips; 190 | ethsnarks::FieldT rebateBips; 191 | 192 | Signature signature; 193 | }; 194 | 195 | static void from_json(const json& j, Order& order) 196 | { 197 | order.exchangeID = ethsnarks::FieldT(j.at("exchangeID")); 198 | order.orderID = ethsnarks::FieldT(j.at("orderID").get().c_str()); 199 | order.accountID = ethsnarks::FieldT(j.at("accountID")); 200 | order.tokenS = ethsnarks::FieldT(j.at("tokenS")); 201 | order.tokenB = ethsnarks::FieldT(j.at("tokenB")); 202 | order.amountS = ethsnarks::FieldT(j.at("amountS").get().c_str()); 203 | order.amountB = ethsnarks::FieldT(j.at("amountB").get().c_str()); 204 | order.allOrNone = ethsnarks::FieldT(j.at("allOrNone").get() ? 1 : 0); 205 | order.validSince = ethsnarks::FieldT(j.at("validSince")); 206 | order.validUntil = ethsnarks::FieldT(j.at("validUntil")); 207 | order.maxFeeBips = ethsnarks::FieldT(j.at("maxFeeBips")); 208 | order.buy = ethsnarks::FieldT(j.at("buy").get() ? 1 : 0); 209 | 210 | order.feeBips = ethsnarks::FieldT(j.at("feeBips")); 211 | order.rebateBips = ethsnarks::FieldT(j.at("rebateBips")); 212 | 213 | order.signature = j.at("signature").get(); 214 | } 215 | 216 | class Ring 217 | { 218 | public: 219 | Order orderA; 220 | Order orderB; 221 | ethsnarks::FieldT fillS_A; 222 | ethsnarks::FieldT fillS_B; 223 | }; 224 | 225 | static void from_json(const json& j, Ring& ring) 226 | { 227 | ring.orderA = j.at("orderA").get(); 228 | ring.orderB = j.at("orderB").get(); 229 | ring.fillS_A = ethsnarks::FieldT(j["fFillS_A"]); 230 | ring.fillS_B = ethsnarks::FieldT(j["fFillS_B"]); 231 | } 232 | 233 | class RingSettlement 234 | { 235 | public: 236 | Ring ring; 237 | 238 | ethsnarks::FieldT accountsMerkleRoot; 239 | 240 | TradeHistoryUpdate tradeHistoryUpdate_A; 241 | TradeHistoryUpdate tradeHistoryUpdate_B; 242 | 243 | BalanceUpdate balanceUpdateS_A; 244 | BalanceUpdate balanceUpdateB_A; 245 | AccountUpdate accountUpdate_A; 246 | 247 | BalanceUpdate balanceUpdateS_B; 248 | BalanceUpdate balanceUpdateB_B; 249 | AccountUpdate accountUpdate_B; 250 | 251 | BalanceUpdate balanceUpdateA_P; 252 | BalanceUpdate balanceUpdateB_P; 253 | 254 | BalanceUpdate balanceUpdateA_O; 255 | BalanceUpdate balanceUpdateB_O; 256 | }; 257 | 258 | static void from_json(const json& j, RingSettlement& ringSettlement) 259 | { 260 | ringSettlement.ring = j.at("ring").get(); 261 | 262 | ringSettlement.accountsMerkleRoot = ethsnarks::FieldT(j.at("accountsMerkleRoot").get().c_str()); 263 | 264 | ringSettlement.tradeHistoryUpdate_A = j.at("tradeHistoryUpdate_A").get(); 265 | ringSettlement.tradeHistoryUpdate_B = j.at("tradeHistoryUpdate_B").get(); 266 | 267 | ringSettlement.balanceUpdateS_A = j.at("balanceUpdateS_A").get(); 268 | ringSettlement.balanceUpdateB_A = j.at("balanceUpdateB_A").get(); 269 | ringSettlement.accountUpdate_A = j.at("accountUpdate_A").get(); 270 | 271 | ringSettlement.balanceUpdateS_B = j.at("balanceUpdateS_B").get(); 272 | ringSettlement.balanceUpdateB_B = j.at("balanceUpdateB_B").get(); 273 | ringSettlement.accountUpdate_B = j.at("accountUpdate_B").get(); 274 | 275 | ringSettlement.balanceUpdateA_P = j.at("balanceUpdateA_P").get(); 276 | ringSettlement.balanceUpdateB_P = j.at("balanceUpdateB_P").get(); 277 | 278 | ringSettlement.balanceUpdateA_O = j.at("balanceUpdateA_O").get(); 279 | ringSettlement.balanceUpdateB_O = j.at("balanceUpdateB_O").get(); 280 | } 281 | 282 | class RingSettlementBlock 283 | { 284 | public: 285 | 286 | ethsnarks::FieldT exchangeID; 287 | 288 | ethsnarks::FieldT merkleRootBefore; 289 | ethsnarks::FieldT merkleRootAfter; 290 | 291 | ethsnarks::FieldT timestamp; 292 | 293 | ethsnarks::FieldT protocolTakerFeeBips; 294 | ethsnarks::FieldT protocolMakerFeeBips; 295 | 296 | Signature signature; 297 | 298 | AccountUpdate accountUpdate_P; 299 | 300 | ethsnarks::FieldT operatorAccountID; 301 | AccountUpdate accountUpdate_O; 302 | 303 | std::vector ringSettlements; 304 | }; 305 | 306 | static void from_json(const json& j, RingSettlementBlock& block) 307 | { 308 | block.exchangeID = ethsnarks::FieldT(j["exchangeID"].get()); 309 | 310 | block.merkleRootBefore = ethsnarks::FieldT(j["merkleRootBefore"].get().c_str()); 311 | block.merkleRootAfter = ethsnarks::FieldT(j["merkleRootAfter"].get().c_str()); 312 | 313 | block.timestamp = ethsnarks::FieldT(j["timestamp"].get()); 314 | 315 | block.protocolTakerFeeBips = ethsnarks::FieldT(j["protocolTakerFeeBips"].get()); 316 | block.protocolMakerFeeBips = ethsnarks::FieldT(j["protocolMakerFeeBips"].get()); 317 | 318 | block.signature = j.at("signature").get(); 319 | 320 | block.accountUpdate_P = j.at("accountUpdate_P").get(); 321 | 322 | block.operatorAccountID = ethsnarks::FieldT(j.at("operatorAccountID")); 323 | block.accountUpdate_O = j.at("accountUpdate_O").get(); 324 | 325 | // Read settlements 326 | json jRingSettlements = j["ringSettlements"]; 327 | for(unsigned int i = 0; i < jRingSettlements.size(); i++) 328 | { 329 | block.ringSettlements.emplace_back(jRingSettlements[i].get()); 330 | } 331 | } 332 | 333 | class Deposit 334 | { 335 | public: 336 | ethsnarks::FieldT amount; 337 | BalanceUpdate balanceUpdate; 338 | AccountUpdate accountUpdate; 339 | }; 340 | 341 | static void from_json(const json& j, Deposit& deposit) 342 | { 343 | deposit.amount = ethsnarks::FieldT(j.at("amount").get().c_str()); 344 | deposit.balanceUpdate = j.at("balanceUpdate").get(); 345 | deposit.accountUpdate = j.at("accountUpdate").get(); 346 | } 347 | 348 | class DepositBlock 349 | { 350 | public: 351 | ethsnarks::FieldT exchangeID; 352 | 353 | ethsnarks::FieldT merkleRootBefore; 354 | ethsnarks::FieldT merkleRootAfter; 355 | 356 | ethsnarks::LimbT startHash; 357 | 358 | ethsnarks::FieldT startIndex; 359 | ethsnarks::FieldT count; 360 | 361 | std::vector deposits; 362 | }; 363 | 364 | static void from_json(const json& j, DepositBlock& block) 365 | { 366 | block.exchangeID = ethsnarks::FieldT(j["exchangeID"].get()); 367 | 368 | block.merkleRootBefore = ethsnarks::FieldT(j["merkleRootBefore"].get().c_str()); 369 | block.merkleRootAfter = ethsnarks::FieldT(j["merkleRootAfter"].get().c_str()); 370 | 371 | block.startHash = ethsnarks::LimbT(j["startHash"].get().c_str()); 372 | 373 | block.startIndex = ethsnarks::FieldT(j["startIndex"].get().c_str()); 374 | block.count = ethsnarks::FieldT(j["count"].get().c_str()); 375 | 376 | // Read deposits 377 | json jDeposits = j["deposits"]; 378 | for(unsigned int i = 0; i < jDeposits.size(); i++) 379 | { 380 | block.deposits.emplace_back(jDeposits[i].get()); 381 | } 382 | } 383 | 384 | class OnchainWithdrawal 385 | { 386 | public: 387 | ethsnarks::FieldT amountRequested; 388 | BalanceUpdate balanceUpdate; 389 | AccountUpdate accountUpdate; 390 | }; 391 | 392 | static void from_json(const json& j, OnchainWithdrawal& withdrawal) 393 | { 394 | withdrawal.amountRequested = ethsnarks::FieldT(j.at("amountRequested").get().c_str()); 395 | withdrawal.balanceUpdate = j.at("balanceUpdate").get(); 396 | withdrawal.accountUpdate = j.at("accountUpdate").get(); 397 | } 398 | 399 | class OnchainWithdrawalBlock 400 | { 401 | public: 402 | 403 | ethsnarks::FieldT exchangeID; 404 | 405 | ethsnarks::FieldT merkleRootBefore; 406 | ethsnarks::FieldT merkleRootAfter; 407 | 408 | ethsnarks::LimbT startHash; 409 | 410 | ethsnarks::FieldT startIndex; 411 | ethsnarks::FieldT count; 412 | 413 | std::vector withdrawals; 414 | }; 415 | 416 | static void from_json(const json& j, OnchainWithdrawalBlock& block) 417 | { 418 | block.exchangeID = ethsnarks::FieldT(j["exchangeID"].get()); 419 | 420 | block.merkleRootBefore = ethsnarks::FieldT(j["merkleRootBefore"].get().c_str()); 421 | block.merkleRootAfter = ethsnarks::FieldT(j["merkleRootAfter"].get().c_str()); 422 | 423 | block.startHash = ethsnarks::LimbT(j["startHash"].get().c_str()); 424 | 425 | block.startIndex = ethsnarks::FieldT(j["startIndex"].get().c_str()); 426 | block.count = ethsnarks::FieldT(j["count"].get().c_str()); 427 | 428 | // Read withdrawals 429 | json jWithdrawals = j["withdrawals"]; 430 | for(unsigned int i = 0; i < jWithdrawals.size(); i++) 431 | { 432 | block.withdrawals.emplace_back(jWithdrawals[i].get()); 433 | } 434 | } 435 | 436 | 437 | class OffchainWithdrawal 438 | { 439 | public: 440 | ethsnarks::FieldT amountRequested; 441 | ethsnarks::FieldT fee; 442 | Signature signature; 443 | 444 | BalanceUpdate balanceUpdateF_A; 445 | BalanceUpdate balanceUpdateW_A; 446 | AccountUpdate accountUpdate_A; 447 | BalanceUpdate balanceUpdateF_O; 448 | }; 449 | 450 | static void from_json(const json& j, OffchainWithdrawal& withdrawal) 451 | { 452 | withdrawal.amountRequested = ethsnarks::FieldT(j.at("amountRequested").get().c_str()); 453 | withdrawal.fee = ethsnarks::FieldT(j["fee"].get().c_str()); 454 | withdrawal.signature = j.at("signature").get(); 455 | 456 | withdrawal.balanceUpdateF_A = j.at("balanceUpdateF_A").get(); 457 | withdrawal.balanceUpdateW_A = j.at("balanceUpdateW_A").get(); 458 | withdrawal.accountUpdate_A = j.at("accountUpdate_A").get(); 459 | withdrawal.balanceUpdateF_O = j.at("balanceUpdateF_O").get(); 460 | } 461 | 462 | class OffchainWithdrawalBlock 463 | { 464 | public: 465 | 466 | ethsnarks::FieldT exchangeID; 467 | 468 | ethsnarks::FieldT merkleRootBefore; 469 | ethsnarks::FieldT merkleRootAfter; 470 | 471 | ethsnarks::LimbT startHash; 472 | 473 | ethsnarks::FieldT operatorAccountID; 474 | AccountUpdate accountUpdate_O; 475 | 476 | std::vector withdrawals; 477 | }; 478 | 479 | static void from_json(const json& j, OffchainWithdrawalBlock& block) 480 | { 481 | block.exchangeID = ethsnarks::FieldT(j["exchangeID"].get()); 482 | 483 | block.merkleRootBefore = ethsnarks::FieldT(j["merkleRootBefore"].get().c_str()); 484 | block.merkleRootAfter = ethsnarks::FieldT(j["merkleRootAfter"].get().c_str()); 485 | 486 | block.operatorAccountID = ethsnarks::FieldT(j.at("operatorAccountID")); 487 | block.accountUpdate_O = j.at("accountUpdate_O").get(); 488 | 489 | // Read withdrawals 490 | json jWithdrawals = j["withdrawals"]; 491 | for(unsigned int i = 0; i < jWithdrawals.size(); i++) 492 | { 493 | block.withdrawals.emplace_back(jWithdrawals[i].get()); 494 | } 495 | } 496 | 497 | /* 498 | * New internal transfer protocal 499 | */ 500 | class InternalTransfer 501 | { 502 | public: 503 | ethsnarks::FieldT fee; 504 | ethsnarks::FieldT amount; 505 | ethsnarks::FieldT type; 506 | Signature signature; 507 | 508 | ethsnarks::FieldT numConditionalTransfersAfter; 509 | 510 | BalanceUpdate balanceUpdateF_From; // pay fee step 511 | BalanceUpdate balanceUpdateT_From; // transfer step 512 | AccountUpdate accountUpdate_From; 513 | 514 | BalanceUpdate balanceUpdateT_To; // receive transfer 515 | AccountUpdate accountUpdate_To; 516 | 517 | BalanceUpdate balanceUpdateF_O; // receive fee 518 | }; 519 | 520 | static void from_json(const json& j, InternalTransfer& interTrans) 521 | { 522 | interTrans.fee = ethsnarks::FieldT(j["fee"].get().c_str()); 523 | interTrans.amount = ethsnarks::FieldT(j["amountRequested"].get().c_str()); 524 | interTrans.type = ethsnarks::FieldT(j.at("type")); 525 | interTrans.signature = j.at("signature").get(); 526 | 527 | interTrans.numConditionalTransfersAfter = ethsnarks::FieldT(j.at("numConditionalTransfersAfter")); 528 | 529 | interTrans.balanceUpdateF_From = j.at("balanceUpdateF_From").get(); 530 | interTrans.balanceUpdateT_From = j.at("balanceUpdateT_From").get(); 531 | interTrans.accountUpdate_From = j.at("accountUpdate_From").get(); 532 | 533 | interTrans.balanceUpdateT_To = j.at("balanceUpdateT_To").get(); 534 | interTrans.accountUpdate_To = j.at("accountUpdate_To").get(); 535 | 536 | interTrans.balanceUpdateF_O = j.at("balanceUpdateF_O").get(); 537 | } 538 | 539 | class InternalTransferBlock 540 | { 541 | public: 542 | ethsnarks::FieldT exchangeID; 543 | 544 | ethsnarks::FieldT merkleRootBefore; 545 | ethsnarks::FieldT merkleRootAfter; 546 | 547 | ethsnarks::FieldT operatorAccountID; 548 | AccountUpdate accountUpdate_O; 549 | 550 | std::vector transfers; 551 | }; 552 | 553 | static void from_json(const json& j, InternalTransferBlock& block) 554 | { 555 | block.exchangeID = ethsnarks::FieldT(j["exchangeID"].get()); 556 | 557 | block.merkleRootBefore = ethsnarks::FieldT(j["merkleRootBefore"].get().c_str()); 558 | block.merkleRootAfter = ethsnarks::FieldT(j["merkleRootAfter"].get().c_str()); 559 | 560 | block.operatorAccountID = ethsnarks::FieldT(j.at("operatorAccountID")); 561 | block.accountUpdate_O = j.at("accountUpdate_O").get(); 562 | 563 | // Read internal transfers 564 | json jTransfers = j["transfers"]; 565 | for(unsigned int i = 0; i < jTransfers.size(); i++) 566 | { 567 | block.transfers.emplace_back(jTransfers[i].get()); 568 | } 569 | } 570 | 571 | 572 | } 573 | 574 | #endif 575 | -------------------------------------------------------------------------------- /Utils/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTILS_H_ 2 | #define _UTILS_H_ 3 | 4 | #include "Constants.h" 5 | #include "Data.h" 6 | 7 | #include "../ThirdParty/BigIntHeader.hpp" 8 | #include "ethsnarks.hpp" 9 | #include "utils.hpp" 10 | #include "jubjub/point.hpp" 11 | #include "jubjub/eddsa.hpp" 12 | #include "gadgets/merkle_tree.hpp" 13 | #include "gadgets/sha256_many.hpp" 14 | #include "gadgets/subadd.hpp" 15 | 16 | using namespace ethsnarks; 17 | 18 | 19 | namespace Loopring 20 | { 21 | 22 | static void print(const char* description, const ethsnarks::FieldT& value) 23 | { 24 | std::cout << description << ": "; 25 | value.as_bigint().print(); 26 | } 27 | 28 | static void print(const ProtoboardT& pb, const char* description, const ethsnarks::VariableT& variable) 29 | { 30 | print(description, pb.val(variable)); 31 | } 32 | 33 | static void printBits(const char* name, const libff::bit_vector& _bits, bool reverse = false) 34 | { 35 | libff::bit_vector bits = _bits; 36 | if(reverse) 37 | { 38 | std::reverse(std::begin(bits), std::end(bits)); 39 | } 40 | unsigned int numBytes = (bits.size() + 7) / 8; 41 | uint8_t* full_output_bytes = new uint8_t[numBytes]; 42 | bv_to_bytes(bits, full_output_bytes); 43 | char* hexstr = new char[numBytes*2 + 1]; 44 | hexstr[numBytes*2] = '\0'; 45 | for(int i = 0; i < bits.size()/8; i++) 46 | { 47 | sprintf(hexstr + i*2, "%02x", full_output_bytes[i]); 48 | } 49 | std::cout << name << hexstr << std::endl; 50 | delete [] full_output_bytes; 51 | delete [] hexstr; 52 | } 53 | 54 | /** 55 | * Convert an array of variable arrays into a flat contiguous array of variables 56 | */ 57 | static const VariableArrayT flattenReverse( const std::vector &in_scalars ) 58 | { 59 | size_t total_sz = 0; 60 | for( const auto& scalar : in_scalars ) 61 | total_sz += scalar.size(); 62 | 63 | VariableArrayT result; 64 | result.resize(total_sz); 65 | 66 | size_t offset = 0; 67 | for( const auto& scalar : in_scalars ) 68 | { 69 | for (int i = int(scalar.size()) - 1; i >= 0; i--) 70 | { 71 | result[offset++].index = scalar[i].index; 72 | } 73 | } 74 | 75 | return result; 76 | } 77 | 78 | static const VariableArrayT reverse(const VariableArrayT& values) 79 | { 80 | return flattenReverse({values}); 81 | } 82 | 83 | static const VariableArrayT subArray(const VariableArrayT& bits, unsigned int start, unsigned int length) 84 | { 85 | VariableArrayT result; 86 | result.resize(length); 87 | 88 | unsigned int index = 0; 89 | for (int i = start; i < start + length; i++) 90 | { 91 | result[index++].index = bits[i].index; 92 | } 93 | 94 | return result; 95 | } 96 | 97 | static const VariableArrayT var_array(const std::vector& inputs) 98 | { 99 | return VariableArrayT(inputs.begin(), inputs.end()); 100 | } 101 | 102 | 103 | static BigInt toBigInt(ethsnarks::FieldT _value) 104 | { 105 | auto value = _value.as_bigint(); 106 | BigInt bi = 0; 107 | for(unsigned int i = 0; i < value.num_bits(); i++) 108 | { 109 | bi = bi * 2 + (value.test_bit(value.num_bits() - 1 - i) ? 1 : 0); 110 | } 111 | return bi; 112 | } 113 | 114 | static unsigned int toFloat(BigInt value, const FloatEncoding& encoding) 115 | { 116 | const unsigned int maxExponent = (1 << encoding.numBitsExponent) - 1; 117 | const unsigned int maxMantissa = (1 << encoding.numBitsMantissa) - 1; 118 | BigInt maxExponentValue = 1; 119 | for (unsigned int i = 0; i < maxExponent; i++) 120 | { 121 | maxExponentValue *= encoding.exponentBase; 122 | } 123 | BigInt maxValue = BigInt(maxMantissa) * BigInt(maxExponentValue); 124 | assert(value <= maxValue); 125 | 126 | unsigned int exponent = 0; 127 | BigInt r = value / BigInt(maxMantissa); 128 | BigInt d = 1; 129 | while (r >= encoding.exponentBase || d * maxMantissa < value) 130 | { 131 | r = r / encoding.exponentBase; 132 | exponent += 1; 133 | d = d * encoding.exponentBase; 134 | } 135 | BigInt mantissa = value / d; 136 | 137 | assert(exponent <= maxExponent); 138 | assert(mantissa <= maxMantissa); 139 | const unsigned int f = (exponent << encoding.numBitsMantissa) + mantissa.to_long(); 140 | return f; 141 | } 142 | 143 | static unsigned int toFloat(ethsnarks::FieldT value, const FloatEncoding& encoding) 144 | { 145 | return toFloat(toBigInt(value), encoding); 146 | } 147 | 148 | static BigInt fromFloat(unsigned int f, const FloatEncoding& encoding) 149 | { 150 | const unsigned int exponent = f >> encoding.numBitsMantissa; 151 | const unsigned int mantissa = f & ((1 << encoding.numBitsMantissa) - 1); 152 | BigInt multiplier = 1; 153 | for (unsigned int i = 0; i < exponent; i++) 154 | { 155 | multiplier *= 10; 156 | } 157 | BigInt value = BigInt(mantissa) * multiplier; 158 | return value; 159 | } 160 | 161 | static FieldT roundToFloatValue(const FieldT& value, const FloatEncoding& encoding) { 162 | auto f = toFloat(value, encoding); 163 | auto floatValue = fromFloat(f, encoding); 164 | return FieldT(floatValue.to_string().c_str()); 165 | } 166 | 167 | } 168 | 169 | #endif 170 | -------------------------------------------------------------------------------- /cuda_prover/cuda_prover.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define NDEBUG 1 5 | 6 | #include 7 | 8 | #include "multiexp/reduce.cu" 9 | 10 | extern void run_preprocess(const char *params_path, const char *preprocess_path); 11 | // This is where all the FFTs happen 12 | 13 | // template over the bundle of types and functions. 14 | // Overwrites ca! 15 | template 16 | typename B::vector_Fr *compute_H(size_t d, typename B::vector_Fr *ca, 17 | typename B::vector_Fr *cb, 18 | typename B::vector_Fr *cc) { 19 | auto domain = B::get_evaluation_domain(d + 1 + 1); 20 | 21 | B::domain_iFFT(domain, ca); 22 | B::domain_iFFT(domain, cb); 23 | 24 | size_t m = B::domain_get_m(domain); 25 | typename B::vector_Fr *coefficients_for_H = B::vector_Fr_zeros(m + 1); 26 | 27 | /* add coefficients of the polynomial (d2*A + d1*B - d3) + d1*d2*Z */ 28 | B::domain_mul_add_sub(coefficients_for_H, ca, cb, m); 29 | B::domain_add_poly_Z(domain, coefficients_for_H); 30 | 31 | B::domain_cosetFFT(domain, ca); 32 | B::domain_cosetFFT(domain, cb); 33 | 34 | // Use ca to store H 35 | auto H_tmp = ca; 36 | 37 | // for i in 0 to m: H_tmp[i] = ca[i] * cb[i] 38 | B::vector_Fr_muleq(H_tmp, ca, cb, m); 39 | 40 | B::domain_iFFT(domain, cc); 41 | B::domain_cosetFFT(domain, cc); 42 | 43 | // for i in 0 to m: H_tmp[i] -= cc[i] 44 | B::vector_Fr_subeq(H_tmp, cc, m); 45 | 46 | B::domain_divide_by_Z_on_coset(domain, H_tmp); 47 | 48 | B::domain_icosetFFT(domain, H_tmp); 49 | 50 | // coefficients_for_H[i] += H_tmp[i]; 51 | B::vector_Fr_add(coefficients_for_H, coefficients_for_H, H_tmp, m); 52 | 53 | return coefficients_for_H; 54 | } 55 | 56 | static size_t read_size_t(FILE* input) { 57 | size_t n; 58 | size_t readSize = fread((void *) &n, sizeof(size_t), 1, input); 59 | if (readSize != 1) { 60 | fprintf(stderr, "fread error"); 61 | abort(); 62 | } 63 | return n; 64 | } 65 | 66 | template< typename B > 67 | struct ec_type; 68 | 69 | template<> 70 | struct ec_type { 71 | typedef ECp_ALT_BN128 ECp; 72 | typedef ECp2_ALT_BN128 ECpe; 73 | }; 74 | 75 | 76 | void 77 | check_trailing(FILE *f, const char *name) { 78 | long bytes_remaining = 0; 79 | while (fgetc(f) != EOF) 80 | ++bytes_remaining; 81 | if (bytes_remaining > 0) 82 | fprintf(stderr, "!! Trailing characters in \"%s\": %ld\n", name, bytes_remaining); 83 | } 84 | 85 | 86 | static inline auto now() -> decltype(std::chrono::high_resolution_clock::now()) { 87 | return std::chrono::high_resolution_clock::now(); 88 | } 89 | 90 | template 91 | void 92 | print_time(T &t1, const char *str) { 93 | auto t2 = std::chrono::high_resolution_clock::now(); 94 | auto tim = std::chrono::duration_cast(t2 - t1).count(); 95 | printf("%s: %ld ms\n", str, tim); 96 | t1 = t2; 97 | } 98 | 99 | template 100 | void run_prover( 101 | const char *params_path, 102 | const char *input_path, 103 | const char *output_path, 104 | const char *preprocessed_path) 105 | { 106 | B::init_public_params(); 107 | 108 | size_t primary_input_size = 1; 109 | 110 | auto beginning = now(); 111 | auto t = beginning; 112 | 113 | FILE *params_file = fopen(params_path, "r"); 114 | size_t d = read_size_t(params_file); 115 | size_t orig_d = read_size_t(params_file); 116 | size_t m = read_size_t(params_file); 117 | rewind(params_file); 118 | 119 | printf("d = %zu, orig_d = %zu, m = %zu\n", d, orig_d, m); 120 | 121 | typedef typename ec_type::ECp ECp; 122 | typedef typename ec_type::ECpe ECpe; 123 | 124 | typedef typename B::G1 G1; 125 | typedef typename B::G2 G2; 126 | 127 | static constexpr int R = 32; 128 | static constexpr int C = 4; 129 | FILE *preprocessed_file = fopen(preprocessed_path, "r"); 130 | 131 | size_t space = ((m + 1) + R - 1) / R; 132 | size_t space_H = ((d) + R - 1) / R; 133 | 134 | auto A_mults = load_points_affine(((1U << C) - 1)*(m + 1), preprocessed_file); 135 | auto out_A = allocate_memory(space * ECp::NELTS * ELT_BYTES); 136 | 137 | auto B1_mults = load_points_affine(((1U << C) - 1)*(m + 1), preprocessed_file); 138 | auto out_B1 = allocate_memory(space * ECp::NELTS * ELT_BYTES); 139 | 140 | auto B2_mults = load_points_affine(((1U << C) - 1)*(m + 1), preprocessed_file); 141 | auto out_B2 = allocate_memory(space * ECpe::NELTS * ELT_BYTES); 142 | 143 | auto L_mults = load_points_affine(((1U << C) - 1)*(m - 1), preprocessed_file); 144 | auto out_L = allocate_memory(space * ECp::NELTS * ELT_BYTES); 145 | 146 | auto H_mults = load_points_affine(((1U << C) - 1)*(d), preprocessed_file); 147 | auto out_H = allocate_memory(space_H * ECp::NELTS * ELT_BYTES); 148 | 149 | fclose(preprocessed_file); 150 | 151 | print_time(t, "load preprocessing"); 152 | 153 | auto params = B::read_params(params_file, d, m); 154 | fclose(params_file); 155 | print_time(t, "load params"); 156 | 157 | auto t_main = t; 158 | 159 | FILE *inputs_file = fopen(input_path, "r"); 160 | auto w_ = load_scalars(m + 1, inputs_file); 161 | rewind(inputs_file); 162 | auto inputs = B::read_input(inputs_file, d, orig_d + 1, m); 163 | fclose(inputs_file); 164 | print_time(t, "load inputs"); 165 | 166 | const var *w = w_.get(); 167 | 168 | auto t_gpu = t; 169 | 170 | cudaStream_t sA, sB1, sB2, sL, sH; 171 | 172 | ec_reduce_straus(sA, out_A.get(), A_mults.get(), w, m + 1); 173 | ec_reduce_straus(sB1, out_B1.get(), B1_mults.get(), w, m + 1); 174 | ec_reduce_straus(sB2, out_B2.get(), B2_mults.get(), w, m + 1); 175 | ec_reduce_straus(sL, out_L.get(), L_mults.get(), w + (primary_input_size + 1) * ELT_LIMBS, m - 1); 176 | print_time(t, "gpu launch"); 177 | 178 | //G1 *evaluation_At = B::multiexp_G1(B::input_w(inputs), B::params_A(params), m + 1); 179 | //G1 *evaluation_Bt1 = B::multiexp_G1(B::input_w(inputs), B::params_B1(params), m + 1); 180 | //G2 *evaluation_Bt2 = B::multiexp_G2(B::input_w(inputs), B::params_B2(params), m + 1); 181 | 182 | // Do calculations relating to H on CPU after having set the GPU in 183 | // motion 184 | auto coefficients_for_H = 185 | compute_H(orig_d, B::input_ca(inputs), B::input_cb(inputs), B::input_cc(inputs)); 186 | print_time(t, "coefficients_for_H"); 187 | 188 | auto H_coeff_mem = allocate_memory(d * ELT_BYTES); 189 | B::coefficients_for_H_to_mem(coefficients_for_H, (uint8_t *)H_coeff_mem.get(), ELT_BYTES, d); 190 | print_time(t, "coefficients_H_mem"); 191 | #if 0 192 | auto H = B::params_H(params); 193 | G1 *evaluation_Ht = B::multiexp_G1(coefficients_for_H, H, d); 194 | B::delete_vector_G1(H); 195 | print_time(t, "evaluation_Ht"); 196 | #else 197 | ec_reduce_straus(sH, out_H.get(), H_mults.get(), H_coeff_mem.get(), d); 198 | #endif 199 | 200 | print_time(t, "cpu 1"); 201 | 202 | cudaDeviceSynchronize(); 203 | cudaStreamSynchronize(sA); 204 | G1 *alpha_g1 = B::alpha_g1(params); 205 | G1 *evaluation_At = B::read_pt_ECp(out_A.get()); 206 | auto final_At = B::G1_add(alpha_g1, evaluation_At); 207 | 208 | cudaStreamSynchronize(sB1); 209 | G1 *evaluation_Bt1 = B::read_pt_ECp(out_B1.get()); 210 | auto final_Bt1 = B::G1_add(B::beta_g1(params), evaluation_Bt1); 211 | 212 | cudaStreamSynchronize(sB2); 213 | G2 *evaluation_Bt2 = B::read_pt_ECpe(out_B2.get()); 214 | auto final_Bt2 = B::G2_add(B::beta_g2(params), evaluation_Bt2); 215 | 216 | cudaStreamSynchronize(sL); 217 | G1 *evaluation_Lt = B::read_pt_ECp(out_L.get()); 218 | 219 | cudaStreamSynchronize(sH); 220 | G1 *evaluation_Ht = B::read_pt_ECp(out_H.get()); 221 | 222 | print_time(t_gpu, "gpu e2e"); 223 | 224 | 225 | auto scaled_Bt1 = B::G1_scale(B::input_r(inputs), final_Bt1); 226 | auto Lt1_plus_scaled_Bt1 = B::G1_add(evaluation_Lt, scaled_Bt1); 227 | auto final_C = B::G1_add(evaluation_Ht, evaluation_Lt); 228 | 229 | print_time(t, "cpu 2"); 230 | 231 | B::groth16_output_write(final_At, final_Bt2, final_C, inputs, output_path); 232 | 233 | print_time(t, "store"); 234 | 235 | print_time(t_main, "Total time from input to output: "); 236 | 237 | cudaStreamDestroy(sA); 238 | cudaStreamDestroy(sB1); 239 | cudaStreamDestroy(sB2); 240 | cudaStreamDestroy(sL); 241 | cudaStreamDestroy(sH); 242 | 243 | B::delete_G1(evaluation_At); 244 | B::delete_G1(evaluation_Bt1); 245 | B::delete_G2(evaluation_Bt2); 246 | B::delete_G1(evaluation_Ht); 247 | B::delete_G1(evaluation_Lt); 248 | B::delete_G1(scaled_Bt1); 249 | B::delete_G1(Lt1_plus_scaled_Bt1); 250 | B::delete_vector_Fr(coefficients_for_H); 251 | B::delete_groth16_input(inputs); 252 | B::delete_groth16_params(params); 253 | 254 | print_time(t, "cleanup"); 255 | print_time(beginning, "Total runtime (incl. file reads)"); 256 | } 257 | 258 | int main(int argc, char **argv) { 259 | printf("main start\n"); 260 | setbuf(stdout, NULL); 261 | std::string mode(argv[1]); 262 | 263 | const char *params_path = argv[2]; 264 | 265 | if (mode == "compute") { 266 | const char *input_path = argv[3]; 267 | const char *preprocess_path = argv[4]; 268 | const char *output_path = argv[5]; 269 | run_prover(params_path, input_path, output_path, preprocess_path); 270 | } else if (mode == "preprocess") { 271 | const char *preprocess_path = argv[3]; 272 | run_preprocess(params_path, preprocess_path); 273 | } 274 | 275 | return 0; 276 | } 277 | -------------------------------------------------------------------------------- /cuda_prover/multiexp/arith.cu: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fixnum.cu" 4 | 5 | __device__ __constant__ 6 | const var ALPHA_VALUE[BIG_WIDTH] = { 7 | //0x3c208c16d87cfd46ULL, 0x97816a916871ca8dULL, 8 | //0xb85045b68181585dULL, 0x30644e72e131a029ULL 9 | 0x68c3488912edefaaULL, 0x8d087f6872aabf4fULL, 10 | 0x51e1a24709081231ULL, 0x2259d6b14729c0faULL 11 | }; 12 | 13 | __device__ __constant__ 14 | const var MOD_Q[BIG_WIDTH] = { 15 | 0x3c208c16d87cfd47ULL, 0x97816a916871ca8dULL, 16 | 0xb85045b68181585dULL, 0x30644e72e131a029ULL 17 | }; 18 | 19 | // -Q^{-1} (mod 2^64) 20 | static constexpr var Q_NINV_MOD = 0x87d20782e4866389ULL; 21 | 22 | // 2^256 mod Q 23 | __device__ __constant__ 24 | const var X_MOD_Q[BIG_WIDTH] = { 25 | 0xd35d438dc58f0d9dULL, 0xa78eb28f5c70b3dULL, 26 | 0x666ea36f7879462cULL, 0xe0a77c19a07df2fULL 27 | }; 28 | 29 | //template< const var *mod_, const var ninv_mod_, const var *binpow_mod_ > 30 | //struct modulus_info { 31 | struct ALT_BN128_MOD { 32 | __device__ __forceinline__ static int lane() { return fixnum::layout().thread_rank(); } 33 | __device__ __forceinline__ static var mod() { return MOD_Q[lane()]; } 34 | static constexpr var ninv_mod = Q_NINV_MOD; 35 | __device__ __forceinline__ static var monty_one() { return X_MOD_Q[lane()]; } 36 | }; 37 | 38 | __device__ __constant__ 39 | const var MOD_R[BIG_WIDTH] = { 40 | 0x43e1f593f0000001ULL, 0x2833e84879b97091ULL, 41 | 0xb85045b68181585dULL, 0x30644e72e131a029ULL 42 | }; 43 | 44 | // -R^{-1} (mod 2^64) 45 | static constexpr var R_NINV_MOD = 0xc2e1f593efffffffULL; 46 | 47 | // 2^256 mod R 48 | __device__ __constant__ 49 | const var X_MOD_R[BIG_WIDTH] = { 50 | 0xac96341c4ffffffbULL, 0x36fc76959f60cd29ULL, 51 | 0x666ea36f7879462eULL, 0xe0a77c19a07df2fULL 52 | }; 53 | 54 | //template< const var *mod_, const var ninv_mod_, const var *binpow_mod_ > 55 | //struct modulus_info { 56 | struct ALT_BN128_MOD_R { 57 | __device__ __forceinline__ static int lane() { return fixnum::layout().thread_rank(); } 58 | __device__ __forceinline__ static var mod() { return MOD_R[lane()]; } 59 | static constexpr var ninv_mod = R_NINV_MOD; 60 | __device__ __forceinline__ static var monty_one() { return X_MOD_R[lane()]; } 61 | }; 62 | // Apparently we still can't do partial specialisation of function 63 | // templates in C++, so we do it in a class instead. Woot. 64 | template< int n > 65 | struct mul_ { 66 | template< typename G > 67 | __device__ static void x(G &z, const G &x); 68 | }; 69 | 70 | template<> 71 | template< typename G > 72 | __device__ void 73 | mul_<2>::x(G &z, const G &x) { 74 | // TODO: Shift by one bit 75 | G::add(z, x, x); 76 | } 77 | 78 | template<> 79 | template< typename G > 80 | __device__ void 81 | mul_<4>::x(G &z, const G &x) { 82 | // TODO: Shift by two bits 83 | mul_<2>::x(z, x); // z = 2x 84 | mul_<2>::x(z, z); // z = 4x 85 | } 86 | 87 | template<> 88 | template< typename G > 89 | __device__ void 90 | mul_<8>::x(G &z, const G &x) { 91 | // TODO: Shift by three bits 92 | mul_<4>::x(z, x); // z = 4x 93 | mul_<2>::x(z, z); // z = 8x 94 | } 95 | 96 | template<> 97 | template< typename G > 98 | __device__ void 99 | mul_<16>::x(G &z, const G &x) { 100 | // TODO: Shift by four bits 101 | mul_<8>::x(z, x); // z = 8x 102 | mul_<2>::x(z, z); // z = 16x 103 | } 104 | 105 | template<> 106 | template< typename G > 107 | __device__ void 108 | mul_<32>::x(G &z, const G &x) { 109 | // TODO: Shift by five bits 110 | mul_<16>::x(z, x); // z = 16x 111 | mul_<2>::x(z, z); // z = 32x 112 | } 113 | 114 | template<> 115 | template< typename G > 116 | __device__ void 117 | mul_<64>::x(G &z, const G &x) { 118 | // TODO: Shift by six bits 119 | mul_<32>::x(z, x); // z = 32x 120 | mul_<2>::x(z, z); // z = 64x 121 | } 122 | 123 | template<> 124 | template< typename G > 125 | __device__ void 126 | mul_<3>::x(G &z, const G &x) { 127 | G t; 128 | mul_<2>::x(t, x); 129 | G::add(z, t, x); 130 | } 131 | 132 | template<> 133 | template< typename G > 134 | __device__ void 135 | mul_<11>::x(G &z, const G &x) { 136 | // TODO: Do this without carry/overflow checks 137 | // TODO: Check that this is indeed optimal 138 | // 11 = 8 + 2 + 1 139 | G t; 140 | mul_<2>::x(t, x); // t = 2x 141 | G::add(z, t, x); // z = 3x 142 | mul_<4>::x(t, t); // t = 8x 143 | G::add(z, z, t); // z = 11x 144 | } 145 | 146 | template<> 147 | template< typename G > 148 | __device__ void 149 | mul_<13>::x(G &z, const G &x) { 150 | // 13 = 8 + 4 + 1 151 | G t; 152 | mul_<4>::x(t, x); // t = 4x 153 | G::add(z, t, x); // z = 5x 154 | mul_<2>::x(t, t); // t = 8x 155 | G::add(z, z, t); // z = 13x 156 | } 157 | 158 | template<> 159 | template< typename G > 160 | __device__ void 161 | mul_<26>::x(G &z, const G &x) { 162 | // 26 = 16 + 8 + 2 163 | G t; 164 | mul_<2>::x(z, x); // z = 2x 165 | mul_<4>::x(t, z); // t = 8x 166 | G::add(z, z, t); // z = 10x 167 | mul_<2>::x(t, t); // t = 16x 168 | G::add(z, z, t); // z = 26x 169 | } 170 | 171 | template<> 172 | template< typename G > 173 | __device__ void 174 | mul_<121>::x(G &z, const G &x) { 175 | // 121 = 64 + 32 + 16 + 8 + 1 176 | G t; 177 | mul_<8>::x(t, x); // t = 8x 178 | G::add(z, t, x); // z = 9x 179 | mul_<2>::x(t, t); // t = 16x 180 | G::add(z, z, t); // z = 25x 181 | mul_<2>::x(t, t); // t = 32x 182 | G::add(z, z, t); // z = 57x 183 | mul_<2>::x(t, t); // t = 64x 184 | G::add(z, z, t); // z = 121x 185 | } 186 | 187 | template< typename modulus_info > 188 | struct Fp { 189 | typedef Fp PrimeField; 190 | 191 | var a; 192 | 193 | static constexpr int DEGREE = 1; 194 | 195 | __device__ 196 | static void 197 | load(Fp &x, const var *mem) { 198 | int t = fixnum::layout().thread_rank(); 199 | x.a = (t < ELT_LIMBS) ? mem[t] : 0UL; 200 | } 201 | 202 | __device__ 203 | static void 204 | load_alpha(Fp &x) { 205 | int t = fixnum::layout().thread_rank(); 206 | x.a = (t < ELT_LIMBS) ? ALPHA_VALUE[t] : 0UL; 207 | } 208 | 209 | __device__ 210 | static void 211 | store(var *mem, const Fp &x) { 212 | int t = fixnum::layout().thread_rank(); 213 | if (t < ELT_LIMBS) 214 | mem[t] = x.a; 215 | } 216 | 217 | __device__ 218 | static int 219 | are_equal(const Fp &x, const Fp &y) { return fixnum::cmp(x.a, y.a) == 0; } 220 | 221 | __device__ 222 | static void 223 | set_zero(Fp &x) { x.a = fixnum::zero(); } 224 | 225 | __device__ 226 | static int 227 | is_zero(const Fp &x) { return fixnum::is_zero(x.a); } 228 | 229 | __device__ 230 | static void 231 | set_one(Fp &x) { x.a = modulus_info::monty_one(); } 232 | 233 | __device__ 234 | static void 235 | add(Fp &zz, const Fp &xx, const Fp &yy) { 236 | int br; 237 | var x = xx.a, y = yy.a, z, r; 238 | var mod = modulus_info::mod(); 239 | fixnum::add(z, x, y); 240 | fixnum::sub_br(r, br, z, mod); 241 | zz.a = br ? z : r; 242 | } 243 | 244 | __device__ 245 | static void 246 | neg(Fp &z, const Fp &x) { 247 | var mod = modulus_info::mod(); 248 | fixnum::sub(z.a, mod, x.a); 249 | } 250 | 251 | __device__ 252 | static void 253 | sub(Fp &z, const Fp &x, const Fp &y) { 254 | int br; 255 | var r, mod = modulus_info::mod(); 256 | fixnum::sub_br(r, br, x.a, y.a); 257 | if (br) 258 | fixnum::add(r, r, mod); 259 | z.a = r; 260 | } 261 | 262 | __device__ 263 | static void 264 | mul(Fp &zz, const Fp &xx, const Fp &yy) { 265 | auto grp = fixnum::layout(); 266 | int L = grp.thread_rank(); 267 | var mod = modulus_info::mod(); 268 | 269 | var x = xx.a, y = yy.a, z = digit::zero(); 270 | var tmp; 271 | digit::mul_lo(tmp, x, modulus_info::ninv_mod); 272 | digit::mul_lo(tmp, tmp, grp.shfl(y, 0)); 273 | int cy = 0; 274 | 275 | for (int i = 0; i < ELT_LIMBS; ++i) { 276 | var u; 277 | var xi = grp.shfl(x, i); 278 | var z0 = grp.shfl(z, 0); 279 | var tmpi = grp.shfl(tmp, i); 280 | 281 | digit::mad_lo(u, z0, modulus_info::ninv_mod, tmpi); 282 | digit::mad_lo_cy(z, cy, mod, u, z); 283 | digit::mad_lo_cy(z, cy, y, xi, z); 284 | 285 | assert(L || z == 0); // z[0] must be 0 286 | z = grp.shfl_down(z, 1); // Shift right one word 287 | z = (L >= ELT_LIMBS - 1) ? 0 : z; 288 | 289 | digit::add_cy(z, cy, z, cy); 290 | digit::mad_hi_cy(z, cy, mod, u, z); 291 | digit::mad_hi_cy(z, cy, y, xi, z); 292 | } 293 | // Resolve carries 294 | int msb = grp.shfl(cy, ELT_LIMBS - 1); 295 | cy = grp.shfl_up(cy, 1); // left shift by 1 296 | cy = (L == 0) ? 0 : cy; 297 | 298 | fixnum::add_cy(z, cy, z, cy); 299 | msb += cy; 300 | assert(msb == !!msb); // msb = 0 or 1. 301 | 302 | // br = 0 ==> z >= mod 303 | var r; 304 | int br; 305 | fixnum::sub_br(r, br, z, mod); 306 | if (msb || br == 0) { 307 | // If the msb was set, then we must have had to borrow. 308 | assert(!msb || msb == br); 309 | z = r; 310 | } 311 | zz.a = z; 312 | } 313 | 314 | __device__ 315 | static void 316 | sqr(Fp &z, const Fp &x) { 317 | // TODO: Find a faster way to do this. Actually only option 318 | // might be full squaring with REDC. 319 | mul(z, x, x); 320 | } 321 | 322 | #if 0 323 | __device__ 324 | static void 325 | inv(Fp &z, const Fp &x) { 326 | // FIXME: Implement! See HEHCC Algorithm 11.12. 327 | z = x; 328 | } 329 | #endif 330 | 331 | __device__ 332 | static void 333 | from_monty(Fp &y, const Fp &x) { 334 | Fp one; 335 | one.a = fixnum::one(); 336 | mul(y, x, one); 337 | } 338 | }; 339 | 340 | 341 | 342 | // Reference for multiplication and squaring methods below: 343 | // https://pdfs.semanticscholar.org/3e01/de88d7428076b2547b60072088507d881bf1.pdf 344 | 345 | template< typename Fp, int ALPHA > 346 | struct Fp2 { 347 | typedef Fp PrimeField; 348 | 349 | // TODO: Use __builtin_align__(8) or whatever they use for the 350 | // builtin vector types. 351 | Fp a0, a1; 352 | 353 | static constexpr int DEGREE = 2; 354 | 355 | __device__ 356 | static void 357 | load(Fp2 &x, const var *mem) { 358 | Fp::load(x.a0, mem); 359 | Fp::load(x.a1, mem + ELT_LIMBS); 360 | } 361 | 362 | __device__ 363 | static void 364 | store(var *mem, const Fp2 &x) { 365 | Fp::store(mem, x.a0); 366 | Fp::store(mem + ELT_LIMBS, x.a1); 367 | } 368 | 369 | __device__ 370 | static int 371 | are_equal(const Fp2 &x, const Fp2 &y) { 372 | return Fp::are_equal(x.a0, y.a0) && Fp::are_equal(x.a1, y.a1); 373 | } 374 | 375 | __device__ 376 | static void 377 | set_zero(Fp2 &x) { Fp::set_zero(x.a0); Fp::set_zero(x.a1); } 378 | 379 | __device__ 380 | static int 381 | is_zero(const Fp2 &x) { return Fp::is_zero(x.a0) && Fp::is_zero(x.a1); } 382 | 383 | __device__ 384 | static void 385 | set_one(Fp2 &x) { Fp::set_one(x.a0); Fp::set_zero(x.a1); } 386 | 387 | __device__ 388 | static void 389 | add(Fp2 &s, const Fp2 &x, const Fp2 &y) { 390 | Fp::add(s.a0, x.a0, y.a0); 391 | Fp::add(s.a1, x.a1, y.a1); 392 | } 393 | 394 | __device__ 395 | static void 396 | sub(Fp2 &s, const Fp2 &x, const Fp2 &y) { 397 | Fp::sub(s.a0, x.a0, y.a0); 398 | Fp::sub(s.a1, x.a1, y.a1); 399 | } 400 | 401 | __device__ 402 | static void 403 | mul(Fp2 &p, const Fp2 &a, const Fp2 &b) { 404 | Fp a0_b0, a1_b1, a0_plus_a1, b0_plus_b1, c, t0, t1; 405 | Fp xx; 406 | 407 | Fp::mul(a0_b0, a.a0, b.a0); 408 | Fp::mul(a1_b1, a.a1, b.a1); 409 | 410 | Fp::add(a0_plus_a1, a.a0, a.a1); 411 | Fp::add(b0_plus_b1, b.a0, b.a1); 412 | Fp::mul(c, a0_plus_a1, b0_plus_b1); 413 | 414 | //mul_::x(t0, a1_b1); 415 | Fp::load_alpha(xx); 416 | Fp::mul(t0, xx, a1_b1); 417 | Fp::sub(t1, c, a0_b0); 418 | 419 | Fp::add(p.a0, a0_b0, t0); 420 | Fp::sub(p.a1, t1, a1_b1); 421 | } 422 | 423 | 424 | __device__ 425 | static void 426 | sqr(Fp2 &s, const Fp2 &a) { 427 | Fp a0_a1, a0_plus_a1, a0_plus_13_a1, t0, t1, t2; 428 | Fp xx; 429 | 430 | Fp::mul(a0_a1, a.a0, a.a1); 431 | Fp::add(a0_plus_a1, a.a0, a.a1); 432 | //mul_::x(t0, a.a1); 433 | Fp::load_alpha(xx); 434 | Fp::mul(t0, xx, a.a1); 435 | Fp::add(a0_plus_13_a1, a.a0, t0); 436 | Fp::mul(t0, a0_plus_a1, a0_plus_13_a1); 437 | // TODO: Could do mul_14 to save a sub? 438 | Fp::sub(t1, t0, a0_a1); 439 | //mul_::x(t2, a0_a1); 440 | Fp::load_alpha(xx); 441 | Fp::mul(t2, xx, a0_a1); 442 | Fp::sub(s.a0, t1, t2); 443 | mul_<2>::x(s.a1, a0_a1); 444 | } 445 | }; 446 | 447 | typedef Fp Fp_ALT_BN128; 448 | typedef Fp2 Fp2_ALT_BN128; 449 | 450 | typedef Fp Fp_ALT_BN128_R; 451 | typedef Fp2 Fp2_ALT_BN128_R; 452 | -------------------------------------------------------------------------------- /cuda_prover/multiexp/fixnum.cu: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "primitives.cu" 6 | 7 | /* 8 | * var is the basic register type that we deal with. The 9 | * interpretation of (one or more) such registers is determined by the 10 | * struct used, e.g. digit, fixnum, etc. 11 | */ 12 | typedef std::uint64_t var; 13 | 14 | static constexpr size_t ELT_LIMBS = 4; 15 | static constexpr size_t ELT_BYTES = ELT_LIMBS * sizeof(var); 16 | 17 | static constexpr size_t BIG_WIDTH = ELT_LIMBS; // = 4 18 | 19 | 20 | struct digit { 21 | static constexpr int BYTES = sizeof(var); 22 | static constexpr int BITS = BYTES * 8; 23 | 24 | __device__ __forceinline__ 25 | static void 26 | add(var &s, var a, var b) { 27 | s = a + b; 28 | } 29 | 30 | __device__ __forceinline__ 31 | static void 32 | add_cy(var &s, int &cy, var a, var b) { 33 | s = a + b; 34 | cy = s < a; 35 | } 36 | 37 | __device__ __forceinline__ 38 | static void 39 | sub(var &d, var a, var b) { 40 | d = a - b; 41 | } 42 | 43 | __device__ __forceinline__ 44 | static void 45 | sub_br(var &d, int &br, var a, var b) { 46 | d = a - b; 47 | br = d > a; 48 | } 49 | 50 | __device__ __forceinline__ 51 | static var 52 | zero() { return 0ULL; } 53 | 54 | __device__ __forceinline__ 55 | static int 56 | is_max(var a) { return a == ~0ULL; } 57 | 58 | __device__ __forceinline__ 59 | static int 60 | is_min(var a) { return a == 0ULL; } 61 | 62 | __device__ __forceinline__ 63 | static int 64 | is_zero(var a) { return a == zero(); } 65 | 66 | __device__ __forceinline__ 67 | static void 68 | mul_lo(var &lo, var a, var b) { 69 | lo = a * b; 70 | } 71 | 72 | // lo = a * b + c (mod 2^64) 73 | __device__ __forceinline__ 74 | static void 75 | mad_lo(var &lo, var a, var b, var c) { 76 | internal::mad_lo(lo, a, b, c); 77 | } 78 | 79 | // as above but increment cy by the mad carry 80 | __device__ __forceinline__ 81 | static void 82 | mad_lo_cy(var &lo, int &cy, var a, var b, var c) { 83 | internal::mad_lo_cc(lo, a, b, c); 84 | internal::addc(cy, cy, 0); 85 | } 86 | 87 | __device__ __forceinline__ 88 | static void 89 | mad_hi(var &hi, var a, var b, var c) { 90 | internal::mad_hi(hi, a, b, c); 91 | } 92 | 93 | // as above but increment cy by the mad carry 94 | __device__ __forceinline__ 95 | static void 96 | mad_hi_cy(var &hi, int &cy, var a, var b, var c) { 97 | internal::mad_hi_cc(hi, a, b, c); 98 | internal::addc(cy, cy, 0); 99 | } 100 | }; 101 | 102 | 103 | struct fixnum { 104 | // 4 because digit::BITS * 4 = 256 > 254 = digit::bits * 4 105 | // Must be < 32 for effective_carries to work. 106 | static constexpr unsigned WIDTH = 4; 107 | 108 | // TODO: Previous versiona allowed 'auto' return type here instead 109 | // of this mess 110 | __device__ 111 | static cooperative_groups::thread_block_tile 112 | layout() { 113 | return cooperative_groups::tiled_partition( 114 | cooperative_groups::this_thread_block()); 115 | } 116 | 117 | __device__ __forceinline__ 118 | static var 119 | zero() { return digit::zero(); } 120 | 121 | __device__ __forceinline__ 122 | static var 123 | one() { 124 | auto t = layout().thread_rank(); 125 | return (var)(t == 0); 126 | } 127 | 128 | __device__ 129 | static void 130 | add_cy(var &r, int &cy_hi, const var &a, const var &b) { 131 | int cy; 132 | digit::add_cy(r, cy, a, b); 133 | // r propagates carries iff r = FIXNUM_MAX 134 | var r_cy = effective_carries(cy_hi, digit::is_max(r), cy); 135 | digit::add(r, r, r_cy); 136 | } 137 | 138 | __device__ 139 | static void 140 | add(var &r, const var &a, const var &b) { 141 | int cy_hi; 142 | add_cy(r, cy_hi, a, b); 143 | } 144 | 145 | __device__ 146 | static void 147 | sub_br(var &r, int &br_lo, const var &a, const var &b) { 148 | int br; 149 | digit::sub_br(r, br, a, b); 150 | // r propagates borrows iff r = FIXNUM_MIN 151 | var r_br = effective_carries(br_lo, digit::is_min(r), br); 152 | digit::sub(r, r, r_br); 153 | } 154 | 155 | __device__ 156 | static void 157 | sub(var &r, const var &a, const var &b) { 158 | int br_lo; 159 | sub_br(r, br_lo, a, b); 160 | } 161 | 162 | __device__ static uint32_t nonzero_mask(var r) { 163 | return fixnum::layout().ballot( ! digit::is_zero(r)); 164 | } 165 | 166 | __device__ static int is_zero(var r) { 167 | return nonzero_mask(r) == 0U; 168 | } 169 | 170 | __device__ static int most_sig_dig(var x) { 171 | enum { UINT32_BITS = 8 * sizeof(uint32_t) }; 172 | 173 | uint32_t a = nonzero_mask(x); 174 | return UINT32_BITS - (internal::clz(a) + 1); 175 | } 176 | 177 | __device__ static int cmp(var x, var y) { 178 | var r; 179 | int br; 180 | sub_br(r, br, x, y); 181 | // r != 0 iff x != y. If x != y, then br != 0 => x < y. 182 | return nonzero_mask(r) ? (br ? -1 : 1) : 0; 183 | } 184 | 185 | __device__ 186 | static var 187 | effective_carries(int &cy_hi, int propagate, int cy) { 188 | uint32_t allcarries, p, g; 189 | auto grp = fixnum::layout(); 190 | 191 | g = grp.ballot(cy); // carry generate 192 | p = grp.ballot(propagate); // carry propagate 193 | allcarries = (p | g) + g; // propagate all carries 194 | cy_hi = (allcarries >> grp.size()) & 1; // detect hi overflow 195 | allcarries = (allcarries ^ p) | (g << 1); // get effective carries 196 | return (allcarries >> grp.thread_rank()) & 1; 197 | } 198 | }; 199 | -------------------------------------------------------------------------------- /cuda_prover/multiexp/reduce.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "curves.cu" 8 | 9 | // C is the size of the precomputation 10 | // R is the number of points we're handling per thread 11 | template< typename EC, int C = 4, int RR = 8 > 12 | __global__ void 13 | ec_multiexp_straus(var *out, const var *multiples_, const var *scalars_, size_t N) 14 | { 15 | int T = threadIdx.x, B = blockIdx.x, D = blockDim.x; 16 | int elts_per_block = D / BIG_WIDTH; 17 | int tileIdx = T / BIG_WIDTH; 18 | 19 | int idx = elts_per_block * B + tileIdx; 20 | 21 | size_t n = (N + RR - 1) / RR; 22 | if (idx < n) { 23 | // TODO: Treat remainder separately so R can remain a compile time constant 24 | size_t R = (idx < n - 1) ? RR : (N % RR); 25 | if (R == 0) R = 1; 26 | 27 | typedef typename EC::group_type Fr; 28 | static constexpr int JAC_POINT_LIMBS = 3 * EC::field_type::DEGREE * ELT_LIMBS; 29 | static constexpr int AFF_POINT_LIMBS = 2 * EC::field_type::DEGREE * ELT_LIMBS; 30 | int out_off = idx * JAC_POINT_LIMBS; 31 | int m_off = idx * RR * AFF_POINT_LIMBS; 32 | int s_off = idx * RR * ELT_LIMBS; 33 | 34 | Fr scalars[RR]; 35 | for (int j = 0; j < R; ++j) { 36 | Fr::load(scalars[j], scalars_ + s_off + j*ELT_LIMBS); 37 | Fr::from_monty(scalars[j], scalars[j]); 38 | } 39 | 40 | const var *multiples = multiples_ + m_off; 41 | // TODO: Consider loading multiples and/or scalars into shared memory 42 | 43 | // i is smallest multiple of C such that i > 254 44 | int i = C * ((254 + C - 1) / C); // C * ceiling(254/C) 45 | assert((i - C * 254) < C); 46 | static constexpr var C_MASK = (1U << C) - 1U; 47 | 48 | EC x; 49 | EC::set_zero(x); 50 | size_t set_value = 0; 51 | while (i >= C) { 52 | if (set_value) 53 | EC::mul_2exp(T, x, x); 54 | i -= C; 55 | 56 | int q = i / digit::BITS, r = i % digit::BITS; 57 | for (int j = 0; j < R; ++j) { 58 | //(scalars[j][q] >> r) & C_MASK 59 | auto g = fixnum::layout(); 60 | var s = g.shfl(scalars[j].a, q); 61 | var win = (s >> r) & C_MASK; 62 | // Handle case where C doesn't divide digit::BITS 63 | int bottom_bits = digit::BITS - r; 64 | // detect when window overlaps digit boundary 65 | if (bottom_bits < C) { 66 | s = g.shfl(scalars[j].a, q + 1); 67 | win |= (s << bottom_bits) & C_MASK; 68 | } 69 | if (win > 0) { 70 | EC m; 71 | set_value = 1; 72 | //EC::add(x, x, multiples[win - 1][j]); 73 | EC::load_affine(m, multiples + ((win-1)*N + j)*AFF_POINT_LIMBS); 74 | EC::mixed_add(T, x, x, m); 75 | } 76 | } 77 | } 78 | EC::store_jac(T, out + out_off, x); 79 | } 80 | } 81 | 82 | template< typename EC > 83 | __global__ void 84 | ec_multiexp(var *X, const var *W, size_t n) 85 | { 86 | int T = threadIdx.x, B = blockIdx.x, D = blockDim.x; 87 | int elts_per_block = D / BIG_WIDTH; 88 | int tileIdx = T / BIG_WIDTH; 89 | 90 | int idx = elts_per_block * B + tileIdx; 91 | 92 | if (idx < n) { 93 | typedef typename EC::group_type Fr; 94 | EC x; 95 | Fr w; 96 | int x_off = idx * EC::NELTS * ELT_LIMBS; 97 | int w_off = idx * ELT_LIMBS; 98 | 99 | EC::load_affine(x, X + x_off); 100 | Fr::load(w, W + w_off); 101 | 102 | // We're given W in Monty form for some reason, so undo that. 103 | Fr::from_monty(w, w); 104 | EC::mul(x, w.a, x); 105 | 106 | EC::store_jac(T, X + x_off, x); 107 | } 108 | } 109 | 110 | template< typename EC > 111 | __global__ void 112 | ec_sum_all(var *X, const var *Y, size_t n) 113 | { 114 | int T = threadIdx.x, B = blockIdx.x, D = blockDim.x; 115 | int elts_per_block = D / BIG_WIDTH; 116 | int tileIdx = T / BIG_WIDTH; 117 | 118 | int idx = elts_per_block * B + tileIdx; 119 | 120 | if (idx < n) { 121 | EC z, x, y; 122 | int off = idx * EC::NELTS * ELT_LIMBS; 123 | 124 | EC::load_jac(x, X + off); 125 | EC::load_jac(y, Y + off); 126 | 127 | EC::add(T, z, x, y); 128 | 129 | EC::store_jac(T, X + off, z); 130 | } 131 | } 132 | 133 | static constexpr size_t threads_per_block = 256; 134 | 135 | template< typename EC, int C, int R > 136 | void 137 | ec_reduce_straus(cudaStream_t &strm, var *out, const var *multiples, const var *scalars, size_t N) 138 | { 139 | cudaStreamCreate(&strm); 140 | 141 | static constexpr size_t pt_limbs = EC::NELTS * ELT_LIMBS; 142 | size_t n = (N + R - 1) / R; 143 | 144 | size_t nblocks = (n * BIG_WIDTH + threads_per_block - 1) / threads_per_block; 145 | 146 | ec_multiexp_straus<<< nblocks, threads_per_block, 0, strm>>>(out, multiples, scalars, N); 147 | 148 | size_t r = n & 1, m = n / 2; 149 | for ( ; m != 0; r = m & 1, m >>= 1) { 150 | nblocks = (m * BIG_WIDTH + threads_per_block - 1) / threads_per_block; 151 | 152 | ec_sum_all<<>>(out, out + m*pt_limbs, m); 153 | if (r) 154 | ec_sum_all<<<1, threads_per_block, 0, strm>>>(out, out + 2*m*pt_limbs, 1); 155 | } 156 | } 157 | 158 | template< typename EC > 159 | void 160 | ec_reduce(cudaStream_t &strm, var *X, const var *w, size_t n) 161 | { 162 | cudaStreamCreate(&strm); 163 | 164 | size_t nblocks = (n * BIG_WIDTH + threads_per_block - 1) / threads_per_block; 165 | 166 | // FIXME: Only works on Pascal and later. 167 | //auto grid = cg::this_grid(); 168 | ec_multiexp<<< nblocks, threads_per_block, 0, strm>>>(X, w, n); 169 | 170 | static constexpr size_t pt_limbs = EC::NELTS * ELT_LIMBS; 171 | 172 | size_t r = n & 1, m = n / 2; 173 | for ( ; m != 0; r = m & 1, m >>= 1) { 174 | nblocks = (m * BIG_WIDTH + threads_per_block - 1) / threads_per_block; 175 | 176 | ec_sum_all<<>>(X, X + m*pt_limbs, m); 177 | if (r) 178 | ec_sum_all<<<1, threads_per_block, 0, strm>>>(X, X + 2*m*pt_limbs, 1); 179 | // TODO: Not sure this is really necessary. 180 | //grid.sync(); 181 | } 182 | } 183 | 184 | static inline double as_mebibytes(size_t n) { 185 | return n / (long double)(1UL << 20); 186 | } 187 | 188 | void print_meminfo(size_t allocated) { 189 | size_t free_mem, dev_mem; 190 | cudaMemGetInfo(&free_mem, &dev_mem); 191 | fprintf(stderr, "Allocated %zu bytes; device has %.1f MiB free (%.1f%%).\n", 192 | allocated, 193 | as_mebibytes(free_mem), 194 | 100.0 * free_mem / dev_mem); 195 | } 196 | 197 | struct CudaFree { 198 | void operator()(var *mem) { cudaFree(mem); } 199 | }; 200 | typedef std::unique_ptr var_ptr; 201 | 202 | var_ptr 203 | allocate_memory(size_t nbytes, int dbg = 0) { 204 | var *mem = nullptr; 205 | cudaMallocManaged(&mem, nbytes); 206 | if (mem == nullptr) { 207 | fprintf(stderr, "Failed to allocate enough device memory\n"); 208 | abort(); 209 | } 210 | if (dbg) 211 | print_meminfo(nbytes); 212 | return var_ptr(mem); 213 | } 214 | 215 | var_ptr 216 | load_scalars(size_t n, FILE *inputs) 217 | { 218 | static constexpr size_t scalar_bytes = ELT_BYTES; 219 | size_t total_bytes = n * scalar_bytes; 220 | 221 | auto mem = allocate_memory(total_bytes); 222 | if (fread((void *)mem.get(), total_bytes, 1, inputs) < 1) { 223 | fprintf(stderr, "Failed to read scalars\n"); 224 | abort(); 225 | } 226 | return mem; 227 | } 228 | 229 | template< typename EC > 230 | var_ptr 231 | load_points(size_t n, FILE *inputs) 232 | { 233 | typedef typename EC::field_type FF; 234 | 235 | static constexpr size_t coord_bytes = FF::DEGREE * ELT_BYTES; 236 | static constexpr size_t aff_pt_bytes = 2 * coord_bytes; 237 | static constexpr size_t jac_pt_bytes = 3 * coord_bytes; 238 | 239 | size_t total_aff_bytes = n * aff_pt_bytes; 240 | size_t total_jac_bytes = n * jac_pt_bytes; 241 | 242 | auto mem = allocate_memory(total_jac_bytes); 243 | if (fread((void *)mem.get(), total_aff_bytes, 1, inputs) < 1) { 244 | fprintf(stderr, "Failed to read all curve poinst\n"); 245 | abort(); 246 | } 247 | 248 | // insert space for z-coordinates 249 | char *cmem = reinterpret_cast(mem.get()); //lazy 250 | for (size_t i = n - 1; i > 0; --i) { 251 | char tmp_pt[aff_pt_bytes]; 252 | memcpy(tmp_pt, cmem + i * aff_pt_bytes, aff_pt_bytes); 253 | memcpy(cmem + i * jac_pt_bytes, tmp_pt, aff_pt_bytes); 254 | } 255 | return mem; 256 | } 257 | 258 | template< typename EC > 259 | var_ptr 260 | load_points_affine(size_t n, FILE *inputs) 261 | { 262 | typedef typename EC::field_type FF; 263 | 264 | static constexpr size_t coord_bytes = FF::DEGREE * ELT_BYTES; 265 | static constexpr size_t aff_pt_bytes = 2 * coord_bytes; 266 | 267 | size_t total_aff_bytes = n * aff_pt_bytes; 268 | 269 | auto mem = allocate_memory(total_aff_bytes); 270 | if (fread((void *)mem.get(), total_aff_bytes, 1, inputs) < 1) { 271 | fprintf(stderr, "Failed to read all curve poinst\n"); 272 | abort(); 273 | } 274 | return mem; 275 | } 276 | -------------------------------------------------------------------------------- /docs/WithdrawCircuit_UnderStanding.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Loopring/protocol3-circuits/017410225047735b6a165d94ac0363cc752d9dae/docs/WithdrawCircuit_UnderStanding.pdf -------------------------------------------------------------------------------- /docs/WithdrawCircuit_Understanding.md: -------------------------------------------------------------------------------- 1 | # WithdrawCircuit 2 | 3 | 现在withdraw有两种方式:onchain withdraw和offchain withdraw。 4 | 5 | 两个各有优劣: 6 | 7 | 1 onchain withdraw缺点是比较慢,要先经过以太主网确认才会到中继进行处理;优点是可以确保被处理(合约设置超过15分钟就要被优先处理) 8 | 9 | 2 offchain withdraw的优点是快,直接发到中继进行处理;缺点是中继可以选择性的不处理; 10 | 11 | 12 | 13 | ## 1 OnchainWithdrawCircuit 14 | 15 | ### 1.1 流程 16 | 17 | ![OnchainWithdraw流程](https://imgur.com/7S5ESQb) 18 | 19 | 1 用户发送withdraw请求到ETH主链的Exchange合约 20 | 21 | 2 中继监听Exchange合约收到用户的withdraw请求 22 | 23 | 3 中继组装withdraw请求生成withdraw的block提交,调用Exchange的commitBlock 24 | 25 | 4 中继调用电路部分生成block的prove 26 | 27 | 5 中继调用Exchange的verifyBlock 28 | 29 | 30 | 31 | ### 1.2 Prove Input 32 | 33 | Prove的Input包含block的信息和每个withdraw的信息 34 | 35 | #### block的信息: 36 | 37 | ``` 38 | class OnchainWithdrawalBlock 39 | { 40 | public: 41 | 42 | ethsnarks::FieldT exchangeID; 43 | 44 | ethsnarks::FieldT merkleRootBefore; 45 | ethsnarks::FieldT merkleRootAfter; 46 | 47 | libff::bigint startHash; 48 | 49 | ethsnarks::FieldT startIndex; 50 | ethsnarks::FieldT count; 51 | 52 | std::vector withdrawals; 53 | }; 54 | ``` 55 | 56 | #### 各个withdrawal的信息: 57 | 58 | ``` 59 | class OnchainWithdrawal 60 | { 61 | public: 62 | ethsnarks::FieldT amountRequested; 63 | ethsnarks::FieldT fAmountWithdrawn; 64 | BalanceUpdate balanceUpdate; 65 | AccountUpdate accountUpdate; 66 | }; 67 | 68 | class AccountUpdate 69 | { 70 | public: 71 | ethsnarks::FieldT accountID; 72 | Proof proof; 73 | ethsnarks::FieldT rootBefore; 74 | ethsnarks::FieldT rootAfter; 75 | Account before; 76 | Account after; 77 | }; 78 | 79 | class Account 80 | { 81 | public: 82 | ethsnarks::jubjub::EdwardsPoint publicKey; 83 | ethsnarks::FieldT nonce; 84 | ethsnarks::FieldT balancesRoot; 85 | }; 86 | 87 | class BalanceUpdate 88 | { 89 | public: 90 | ethsnarks::FieldT tokenID; 91 | Proof proof; 92 | ethsnarks::FieldT rootBefore; 93 | ethsnarks::FieldT rootAfter; 94 | BalanceLeaf before; 95 | BalanceLeaf after; 96 | }; 97 | 98 | class BalanceLeaf 99 | { 100 | public: 101 | ethsnarks::FieldT balance; 102 | ethsnarks::FieldT tradingHistoryRoot; 103 | }; 104 | ``` 105 | 106 | 107 | 108 | ![OnChainWithdraw](https://imgur.com/HvqbRzW) 109 | 110 | ### 1.3 Circuit 111 | 112 | #### 1.3.1 计算publicDataHash 113 | 114 | 电路会计算一个sha256 hash,verifyBlock的时候输入hash与这个hash一致即表示verify成功。 115 | 116 | ![OnChainWithdrawCircuit (2)](https://imgur.com/NlhVi1H) 117 | 118 | #### 1.3.2 OnchainWithdrawalGadget 119 | 120 | 每个withdrawal的逻辑就是验证withdraw的合法性,并且加入了shutdownmode的判断,如果是shutdownmode就要withdraw用户的所有balance,然后更新balance和account。 121 | 122 | 具体步骤如下: 123 | 124 | ``` 125 | 1 取用户amountRequested和balanceBefore的最小值算出amountToWithdrawMin 126 | 2 根据shutdownmode计算amountToWithdraw,如果是shutdownmode结果为balanceBefore,否则为amountToWithdrawMin 127 | 3 设置amountWithdrawn,值是输入的fAmountWithdrawn(toFloat(min(amountRequested, balance)), 类型Float28Encoding // {5, 23, 10}; 128 | struct FloatEncoding 129 | { 130 | unsigned int numBitsExponent; 131 | unsigned int numBitsMantissa; 132 | unsigned int exponentBase; 133 | }; 134 | 4 ensureAccuracyAmountWithdrawn, 确保amountToWithdraw和fAmountWithdrawn在精度范围内相等。Float28Accuracy // {12, 10000000}; 135 | struct Accuracy 136 | { 137 | unsigned int numerator; 138 | unsigned int denominator; 139 | }; 140 | TODO: amountRequested和fAmountWithdrawn都是输入,有点不合理,Brecht said: 141 | We also don't covert to float in the circuit, this would be expensive. The float is decoded and it's checked that the float value is close enough to the actual value. 142 | 现在跟fee的处理不一样,后续会改成一致。 143 | 5 根据shutdownmode计算amountToSubtract,true则是amountToWithdraw,否则是amountWithdrawn 144 | 6 根据shutdownmode计算tradingHistoryAfter, true则是emptyTradeHistory, 否则是balanceBefore.tradingHistory 145 | 7 根据shutdownmode计算publicKeyXAfter, true则是zero,否则是accountBefore.publicKeyX 146 | 8 根据shutdownmode计算publicKeyYAfter, true则是zero,否则是accountBefore.publicKeyY 147 | 9 根据shutdownmode计算nonceAfter,true则是zero,否则是accountBefore.nonce 148 | (可以看出onchain withdraw不改变nonce,跟deposit一样) 149 | 10 确保balanceBefore.balance = balanceAfter.balance + amountToSubtract 150 | 11 updateBalance 151 | 12 updateAccount 152 | ``` 153 | 154 | withdrawCircuit中会check最后一个用户的rootCalculatorAfter等于输入的merkleRootAfter 155 | 156 | 157 | 158 | ## 2 OffchainWithdrawCircuit 159 | 160 | ### 2.1 流程 161 | 162 | ![OffchainWithdraw流程](https://imgur.com/GHnZcjF) 163 | 164 | 1 用户发送withdraw请求到中继 165 | 166 | 2 中继收到用户withdraw请求。 167 | 168 | 组装withdraw请求生成withdraw的block提交到ETH主链,调用Exchange的commitBlock 169 | 170 | 3 中继调用电路部分生成block的prove 171 | 172 | 4 中继调用Exchange合约的verifyBlock 173 | 174 | 175 | 176 | ### 2.2 Prove Input 177 | 178 | Prove的Input包含block的信息和每个withdraw的信息 179 | 180 | #### block的信息: 181 | 182 | ``` 183 | class OffchainWithdrawalBlock 184 | { 185 | public: 186 | 187 | ethsnarks::FieldT exchangeID; 188 | 189 | ethsnarks::FieldT merkleRootBefore; 190 | ethsnarks::FieldT merkleRootAfter; 191 | 192 | libff::bigint startHash; 193 | 194 | ethsnarks::FieldT startIndex; 195 | ethsnarks::FieldT count; 196 | 197 | ethsnarks::FieldT operatorAccountID; 198 | AccountUpdate accountUpdate_O; 199 | 200 | std::vector withdrawals; 201 | }; 202 | ``` 203 | 204 | #### 各个withdrawal的信息: 205 | 206 | ``` 207 | class OffchainWithdrawal 208 | { 209 | public: 210 | ethsnarks::FieldT amountRequested; 211 | ethsnarks::FieldT fee; 212 | ethsnarks::FieldT walletSplitPercentage; 213 | Signature signature; 214 | 215 | BalanceUpdate balanceUpdateF_A; 216 | BalanceUpdate balanceUpdateW_A; 217 | AccountUpdate accountUpdate_A; 218 | BalanceUpdate balanceUpdateF_W; 219 | AccountUpdate accountUpdate_W; 220 | BalanceUpdate balanceUpdateF_O; 221 | }; 222 | ``` 223 | 224 | ![OffChainWithdraw](https://imgur.com/TT6ye62) 225 | 226 | ### 2.3 Circuit 227 | 228 | #### 2.3.1 计算publicDataHash 229 | 230 | 电路会计算一个sha256 hash,verifyBlock的时候输入hash与这个hash一致即表示verify成功。 231 | 232 | ![OffChainWithdrawCircuit](https://imgur.com/Mtgghxe) 233 | 234 | #### 2.3.2 OffchainWithdrawalGadget 235 | 236 | 每个withdrawal的逻辑跟onchainwithdraw的逻辑类似,但是不需要判断shutdownmode,增加了fee的操作以及验证用户的签名等逻辑。 237 | 238 | fee需要付给wallet和operator,所以这里涉及到3个角色:用户、wallet和operator 239 | 240 | 具体步骤如下: 241 | 242 | ``` 243 | 1 计算feeToWallet = fee * walletSplitPercentage 244 | 2 计算feeToOperator = fee - feeToWallet 245 | 3 计算feePaymentWallet.X = balanceFBefore.balance - feeToWallet //用户的fee token balanceAfter_1 246 | 和feePaymentWallet.Y = balanceWalletBefore.balance + feeToWallet // wallet的balanceAfter 247 | 4 计算feePaymentOperator.X = feePaymentWallet.X - feeToOperator // 用户fee token balanceAfter final 248 | 和feePaymentOperator.Y = balanceF_O_before + feeToOperator // operator的balanceAfter 249 | 5 根据用户的amountRequested计算用户的withdraw amount: amountWithdrawn 250 | 6 计算用户的balance_after = balanceBefore.balance - amountWithdrawn 251 | 7 更新user信息: 252 | 7.1 update fee token的balance 253 | 7.2 update withdraw token的balance 254 | 7.3 updateAccount 255 | 8 更新wallet信息 256 | 8.1 update wallet balance 257 | 8.2 update wallet acount 258 | 9 更新Operator的balance 259 | (Operator的account是在withdrawCircuit中最后更新一次) 260 | 10 检查用户签名 261 | 这里用的是EdDSA_Poseidon 262 | ``` 263 | 264 | withdrawCircuit中会check operator的rootCalculatorAfter等于输入的merkleRootAfter 265 | 266 | -------------------------------------------------------------------------------- /docs/circuit-discussion-0722.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Loopring/protocol3-circuits/017410225047735b6a165d94ac0363cc752d9dae/docs/circuit-discussion-0722.pdf -------------------------------------------------------------------------------- /test/FloatTests.cpp: -------------------------------------------------------------------------------- 1 | #include "../ThirdParty/catch.hpp" 2 | #include "TestUtils.h" 3 | 4 | #include "../Gadgets/MathGadgets.h" 5 | 6 | TEST_CASE("RequireAccuracy", "[RequireAccuracyGadget]") 7 | { 8 | unsigned int maxLength = 126; 9 | unsigned int numIterations = 8; 10 | for (unsigned int n = 1; n <= maxLength; n++) { 11 | DYNAMIC_SECTION("Bit-length: " << n) 12 | { 13 | auto requireAccuracyChecked = [n](const FieldT& _A, const FieldT& _B, bool expectedSatisfied) 14 | { 15 | protoboard pb; 16 | 17 | pb_variable a = make_variable(pb, _A, ".A"); 18 | pb_variable b = make_variable(pb, _B, ".B"); 19 | 20 | Accuracy accuracy = {100 - 1, 100}; 21 | RequireAccuracyGadget requireAccuracyGadget(pb, a, b, accuracy, n, "requireAccuracyGadget"); 22 | requireAccuracyGadget.generate_r1cs_constraints(); 23 | requireAccuracyGadget.generate_r1cs_witness(); 24 | 25 | REQUIRE(pb.is_satisfied() == expectedSatisfied); 26 | }; 27 | 28 | FieldT max = getMaxFieldElement(n); 29 | 30 | SECTION("0, 0") 31 | { 32 | requireAccuracyChecked(0, 0, true); 33 | } 34 | 35 | SECTION("0, 1") 36 | { 37 | requireAccuracyChecked(0, 1, false); 38 | } 39 | 40 | SECTION("0, max") 41 | { 42 | requireAccuracyChecked(0, max, false); 43 | } 44 | 45 | SECTION("max, 0") 46 | { 47 | requireAccuracyChecked(max, 0, false); 48 | } 49 | 50 | SECTION("value > original value") 51 | { 52 | FieldT A = getRandomFieldElement(n); 53 | while (A == FieldT::zero()) 54 | { 55 | A = getRandomFieldElement(n); 56 | } 57 | FieldT B = A - 1; 58 | requireAccuracyChecked(A, B, false); 59 | } 60 | 61 | // Do some specific tests 62 | if (n == NUM_BITS_AMOUNT) 63 | { 64 | SECTION("100, 100") 65 | { 66 | requireAccuracyChecked(100, 100, true); 67 | } 68 | 69 | SECTION("101, 100") 70 | { 71 | requireAccuracyChecked(101, 100, false); 72 | } 73 | 74 | SECTION("99, 100") 75 | { 76 | requireAccuracyChecked(99, 100, true); 77 | } 78 | 79 | SECTION("max + 1, max") 80 | { 81 | requireAccuracyChecked(max + 1, max, false); 82 | } 83 | 84 | SECTION("max, 3000") 85 | { 86 | requireAccuracyChecked(max, 3000, false); 87 | } 88 | 89 | SECTION("Exhaustive checks against a single value") 90 | { 91 | unsigned int originalValue = 3000; 92 | for(unsigned int i = 0; i < originalValue * 3; i++) 93 | { 94 | bool expectedSatisfied = (i >= 2970 && i <= 3000); 95 | requireAccuracyChecked(i, 3000, expectedSatisfied); 96 | } 97 | } 98 | } 99 | }} 100 | } 101 | 102 | TEST_CASE("Float", "[FloatGadget]") 103 | { 104 | FloatEncoding encoding = Float24Encoding; 105 | unsigned int numBitsFloat = encoding.numBitsExponent + encoding.numBitsMantissa; 106 | 107 | unsigned int maxLength = NUM_BITS_AMOUNT; 108 | unsigned int numIterations = 8; 109 | for (unsigned int n = 1; n <= maxLength; n++) { 110 | DYNAMIC_SECTION("Bit-length: " << n) 111 | { 112 | auto floatChecked = [n, encoding, numBitsFloat](const FieldT& _value) 113 | { 114 | protoboard pb; 115 | 116 | Constants constants(pb, "constants"); 117 | FloatGadget floatGadget(pb, constants, encoding, "floatGadget"); 118 | floatGadget.generate_r1cs_constraints(); 119 | unsigned int f = toFloat(_value, encoding); 120 | floatGadget.generate_r1cs_witness(f); 121 | 122 | FieldT rValue = toFieldElement(fromFloat(f, encoding)); 123 | REQUIRE(pb.is_satisfied()); 124 | REQUIRE((pb.val(floatGadget.value()) == rValue)); 125 | REQUIRE(compareBits(floatGadget.bits().get_bits(pb), toBits(f, numBitsFloat))); 126 | }; 127 | 128 | SECTION("0") 129 | { 130 | floatChecked(0); 131 | } 132 | 133 | SECTION("1") 134 | { 135 | floatChecked(1); 136 | } 137 | 138 | SECTION("max") 139 | { 140 | floatChecked(getMaxFieldElement(n)); 141 | } 142 | 143 | SECTION("Random") 144 | { 145 | for (unsigned int j = 0; j < numIterations; j++) 146 | { 147 | floatChecked(getRandomFieldElement(n)); 148 | } 149 | } 150 | }} 151 | } 152 | 153 | TEST_CASE("Float+Accuracy", "[FloatGadget+RequireAccuracy]") 154 | { 155 | std::vector encodings = {Float16Encoding, Float24Encoding, Float28Encoding}; 156 | std::vector accuracies = {Float16Accuracy, Float24Accuracy, Float28Accuracy}; 157 | std::vector worstAccuracyValues = {FieldT("20499999999999999999999999999"), FieldT("52428999999999999999999999999"), FieldT("8388609999999999999999999999")}; 158 | for (unsigned int e = 0; e < encodings.size(); e++) { 159 | DYNAMIC_SECTION("Encoding: " << encodings[e].numBitsExponent + encodings[e].numBitsMantissa) 160 | { 161 | const FloatEncoding& encoding = encodings[e]; 162 | const Accuracy& accuracy = accuracies[e]; 163 | const FieldT& worstAccuracyValue = worstAccuracyValues[e]; 164 | 165 | unsigned int n = NUM_BITS_AMOUNT; 166 | unsigned int numBitsFloat = encoding.numBitsExponent + encoding.numBitsMantissa; 167 | 168 | protoboard pb; 169 | 170 | Constants constants(pb, "constants"); 171 | FloatGadget floatGadget(pb, constants, encoding, "floatGadget"); 172 | floatGadget.generate_r1cs_constraints(); 173 | 174 | pb_variable value = make_variable(pb, ".value"); 175 | pb_variable rValue = make_variable(pb, ".rValue"); 176 | RequireAccuracyGadget requireAccuracyGadget(pb, rValue, value, accuracy, n, "requireAccuracyGadget"); 177 | requireAccuracyGadget.generate_r1cs_constraints(); 178 | 179 | SECTION("Value with the worst accuracy") 180 | { 181 | unsigned int f = toFloat(worstAccuracyValue, encoding); 182 | floatGadget.generate_r1cs_witness(FieldT(f)); 183 | 184 | pb.val(value) = worstAccuracyValue; 185 | pb.val(rValue) = pb.val(floatGadget.value()); 186 | requireAccuracyGadget.generate_r1cs_witness(); 187 | 188 | REQUIRE(pb.is_satisfied()); 189 | } 190 | 191 | SECTION("Random") 192 | { 193 | unsigned int numIterations = 4 * 1024; 194 | for (unsigned int j = 0; j < numIterations; j++) 195 | { 196 | FieldT _value = getRandomFieldElement(n); 197 | unsigned int f = toFloat(_value, encoding); 198 | floatGadget.generate_r1cs_witness(FieldT(f)); 199 | 200 | pb.val(value) = _value; 201 | pb.val(rValue) = pb.val(floatGadget.value()); 202 | requireAccuracyGadget.generate_r1cs_witness(); 203 | 204 | REQUIRE(pb.is_satisfied()); 205 | } 206 | } 207 | }} 208 | } -------------------------------------------------------------------------------- /test/MerkleTreeTests.cpp: -------------------------------------------------------------------------------- 1 | #include "../ThirdParty/catch.hpp" 2 | #include "TestUtils.h" 3 | 4 | #include "../Gadgets/TradingHistoryGadgets.h" 5 | #include "../Gadgets/AccountGadgets.h" 6 | 7 | AccountState createAccountState(ProtoboardT& pb, const Account& state) 8 | { 9 | AccountState accountState; 10 | accountState.publicKeyX = make_variable(pb, state.publicKey.x, ".publicKeyX"); 11 | accountState.publicKeyY = make_variable(pb, state.publicKey.y, ".publicKeyY"); 12 | accountState.nonce = make_variable(pb, state.nonce, ".nonce"); 13 | accountState.balancesRoot = make_variable(pb, state.balancesRoot, ".balancesRoot"); 14 | return accountState; 15 | } 16 | 17 | BalanceState createBalanceState(ProtoboardT& pb, const BalanceLeaf& state) 18 | { 19 | BalanceState balanceState; 20 | balanceState.balance = make_variable(pb, state.balance, ".balance"); 21 | balanceState.tradingHistory = make_variable(pb, state.tradingHistoryRoot, ".tradingHistory"); 22 | return balanceState; 23 | } 24 | 25 | TradeHistoryState createTradeHistoryState(ProtoboardT& pb, const TradeHistoryLeaf& state) 26 | { 27 | TradeHistoryState tradeHistoryState; 28 | tradeHistoryState.filled = make_variable(pb, state.filled, ".filled"); 29 | tradeHistoryState.orderID = make_variable(pb, state.orderID, ".orderID"); 30 | return tradeHistoryState; 31 | } 32 | 33 | TEST_CASE("UpdateAccount", "[UpdateAccountGadget]") 34 | { 35 | RingSettlementBlock block = getRingSettlementBlock(); 36 | REQUIRE(block.ringSettlements.size() > 0); 37 | const RingSettlement& ringSettlement = block.ringSettlements[0]; 38 | const AccountUpdate& accountUpdate = ringSettlement.accountUpdate_B; 39 | 40 | auto updateAccountChecked = [](const AccountUpdate& accountUpdate, bool expectedSatisfied, bool expectedRootAfterCorrect = true) 41 | { 42 | protoboard pb; 43 | 44 | pb_variable rootBefore = make_variable(pb, "rootBefore"); 45 | VariableArrayT address = make_var_array(pb, NUM_BITS_ACCOUNT, ".address"); 46 | AccountState stateBefore = createAccountState(pb, accountUpdate.before); 47 | AccountState stateAfter = createAccountState(pb, accountUpdate.after); 48 | address.fill_with_bits_of_field_element(pb, accountUpdate.accountID); 49 | pb.val(rootBefore) = accountUpdate.rootBefore; 50 | 51 | UpdateAccountGadget updateAccount(pb, rootBefore, address, stateBefore, stateAfter, "updateAccount"); 52 | updateAccount.generate_r1cs_constraints(); 53 | updateAccount.generate_r1cs_witness(accountUpdate.proof); 54 | 55 | REQUIRE(pb.is_satisfied() == expectedSatisfied); 56 | if (expectedSatisfied) 57 | { 58 | REQUIRE((pb.val(updateAccount.result()) == accountUpdate.rootAfter) == expectedRootAfterCorrect); 59 | } 60 | }; 61 | 62 | SECTION("Everything correct") 63 | { 64 | updateAccountChecked(accountUpdate, true, true); 65 | } 66 | 67 | SECTION("Incorrect address") 68 | { 69 | AccountUpdate modifiedAccountUpdate = accountUpdate; 70 | modifiedAccountUpdate.accountID -= 1; 71 | updateAccountChecked(modifiedAccountUpdate, false); 72 | } 73 | 74 | SECTION("Incorrect leaf before") 75 | { 76 | AccountUpdate modifiedAccountUpdate = accountUpdate; 77 | modifiedAccountUpdate.before.nonce += 1; 78 | updateAccountChecked(modifiedAccountUpdate, false); 79 | } 80 | 81 | SECTION("Different leaf after") 82 | { 83 | AccountUpdate modifiedAccountUpdate = accountUpdate; 84 | modifiedAccountUpdate.after.nonce += 1; 85 | updateAccountChecked(modifiedAccountUpdate, true, false); 86 | } 87 | 88 | SECTION("Incorrect proof") 89 | { 90 | AccountUpdate modifiedAccountUpdate = accountUpdate; 91 | unsigned int randomIndex = rand() % modifiedAccountUpdate.proof.data.size(); 92 | modifiedAccountUpdate.proof.data[randomIndex] += 1; 93 | updateAccountChecked(modifiedAccountUpdate, false); 94 | } 95 | } 96 | 97 | TEST_CASE("UpdateBalance", "[UpdateBalanceGadget]") 98 | { 99 | RingSettlementBlock block = getRingSettlementBlock(); 100 | REQUIRE(block.ringSettlements.size() > 0); 101 | const RingSettlement& ringSettlement = block.ringSettlements[0]; 102 | const BalanceUpdate& balanceUpdate = ringSettlement.balanceUpdateB_B; 103 | 104 | auto updateBalanceChecked = [](const BalanceUpdate& balanceUpdate, bool expectedSatisfied, bool expectedRootAfterCorrect = true) 105 | { 106 | protoboard pb; 107 | 108 | pb_variable rootBefore = make_variable(pb, "rootBefore"); 109 | VariableArrayT address = make_var_array(pb, NUM_BITS_TOKEN, ".address"); 110 | BalanceState stateBefore = createBalanceState(pb, balanceUpdate.before); 111 | BalanceState stateAfter = createBalanceState(pb, balanceUpdate.after); 112 | address.fill_with_bits_of_field_element(pb, balanceUpdate.tokenID); 113 | pb.val(rootBefore) = balanceUpdate.rootBefore; 114 | 115 | UpdateBalanceGadget updateBalance(pb, rootBefore, address, stateBefore, stateAfter, "updateBalance"); 116 | updateBalance.generate_r1cs_constraints(); 117 | updateBalance.generate_r1cs_witness(balanceUpdate.proof); 118 | 119 | REQUIRE(pb.is_satisfied() == expectedSatisfied); 120 | if (expectedSatisfied) 121 | { 122 | REQUIRE((pb.val(updateBalance.result()) == balanceUpdate.rootAfter) == expectedRootAfterCorrect); 123 | } 124 | }; 125 | 126 | SECTION("Everything correct") 127 | { 128 | updateBalanceChecked(balanceUpdate, true, true); 129 | } 130 | 131 | SECTION("Incorrect address") 132 | { 133 | BalanceUpdate modifiedBalanceUpdate = balanceUpdate; 134 | modifiedBalanceUpdate.tokenID += 1; 135 | updateBalanceChecked(modifiedBalanceUpdate, true, false); 136 | } 137 | 138 | SECTION("Incorrect leaf before") 139 | { 140 | BalanceUpdate modifiedBalanceUpdate = balanceUpdate; 141 | modifiedBalanceUpdate.before.balance += 1; 142 | updateBalanceChecked(modifiedBalanceUpdate, false); 143 | } 144 | 145 | SECTION("Different leaf after") 146 | { 147 | BalanceUpdate modifiedBalanceUpdate = balanceUpdate; 148 | modifiedBalanceUpdate.after.balance += 1; 149 | updateBalanceChecked(modifiedBalanceUpdate, true, false); 150 | } 151 | 152 | SECTION("Incorrect proof") 153 | { 154 | BalanceUpdate modifiedBalanceUpdate = balanceUpdate; 155 | unsigned int randomIndex = rand() % modifiedBalanceUpdate.proof.data.size(); 156 | modifiedBalanceUpdate.proof.data[randomIndex] += 1; 157 | updateBalanceChecked(modifiedBalanceUpdate, false); 158 | } 159 | } 160 | 161 | TEST_CASE("UpdateTradeHistory", "[UpdateTradeHistoryGadget]") 162 | { 163 | RingSettlementBlock block = getRingSettlementBlock(); 164 | REQUIRE(block.ringSettlements.size() > 0); 165 | const RingSettlement& ringSettlement = block.ringSettlements[0]; 166 | const TradeHistoryUpdate& tradeHistoryUpdate = ringSettlement.tradeHistoryUpdate_A; 167 | 168 | auto updateTradeHistoryChecked = [](const TradeHistoryUpdate& tradeHistoryUpdate, bool expectedSatisfied, bool expectedRootAfterCorrect = true) 169 | { 170 | protoboard pb; 171 | 172 | pb_variable rootBefore = make_variable(pb, "rootBefore"); 173 | VariableArrayT address = make_var_array(pb, NUM_BITS_TRADING_HISTORY, ".address"); 174 | TradeHistoryState stateBefore = createTradeHistoryState(pb, tradeHistoryUpdate.before); 175 | TradeHistoryState stateAfter = createTradeHistoryState(pb, tradeHistoryUpdate.after); 176 | address.fill_with_bits_of_field_element(pb, tradeHistoryUpdate.orderID); 177 | pb.val(rootBefore) = tradeHistoryUpdate.rootBefore; 178 | 179 | UpdateTradeHistoryGadget updateTradeHistory(pb, rootBefore, subArray(address, 0, NUM_BITS_TRADING_HISTORY), stateBefore, stateAfter, "updateTradeHistory"); 180 | updateTradeHistory.generate_r1cs_constraints(); 181 | updateTradeHistory.generate_r1cs_witness(tradeHistoryUpdate.proof); 182 | 183 | REQUIRE(pb.is_satisfied() == expectedSatisfied); 184 | if (expectedSatisfied) 185 | { 186 | REQUIRE((pb.val(updateTradeHistory.result()) == tradeHistoryUpdate.rootAfter) == expectedRootAfterCorrect); 187 | } 188 | }; 189 | 190 | SECTION("Everything correct") 191 | { 192 | updateTradeHistoryChecked(tradeHistoryUpdate, true, true); 193 | } 194 | 195 | SECTION("Incorrect address") 196 | { 197 | TradeHistoryUpdate modifiedTradeHistoryUpdate = tradeHistoryUpdate; 198 | modifiedTradeHistoryUpdate.orderID += 1; 199 | updateTradeHistoryChecked(modifiedTradeHistoryUpdate, true, false); 200 | } 201 | 202 | SECTION("Incorrect leaf before") 203 | { 204 | TradeHistoryUpdate modifiedTradeHistoryUpdate = tradeHistoryUpdate; 205 | modifiedTradeHistoryUpdate.before.filled += 1; 206 | updateTradeHistoryChecked(modifiedTradeHistoryUpdate, false); 207 | } 208 | 209 | SECTION("Different leaf after") 210 | { 211 | TradeHistoryUpdate modifiedTradeHistoryUpdate = tradeHistoryUpdate; 212 | modifiedTradeHistoryUpdate.after.filled += 1; 213 | updateTradeHistoryChecked(modifiedTradeHistoryUpdate, true, false); 214 | } 215 | 216 | SECTION("Incorrect proof") 217 | { 218 | TradeHistoryUpdate modifiedTradeHistoryUpdate = tradeHistoryUpdate; 219 | unsigned int randomIndex = rand() % modifiedTradeHistoryUpdate.proof.data.size(); 220 | modifiedTradeHistoryUpdate.proof.data[randomIndex] += 1; 221 | updateTradeHistoryChecked(modifiedTradeHistoryUpdate, false); 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /test/OrderTests.cpp: -------------------------------------------------------------------------------- 1 | #include "../ThirdParty/catch.hpp" 2 | #include "TestUtils.h" 3 | 4 | #include "../Gadgets/OrderGadgets.h" 5 | 6 | 7 | TEST_CASE("Order", "[OrderGadget]") 8 | { 9 | // We can't easily create signatures here, so disable the signature check for some tests 10 | bool doSignatureCheck = true; 11 | 12 | auto orderChecked = [&doSignatureCheck](const FieldT& _exchangeID, 13 | const Order& order, const Account& account, 14 | const BalanceLeaf& balanceLeafS, const BalanceLeaf& balanceLeafB, 15 | const TradeHistoryLeaf& tradeHistoryLeaf, 16 | bool expectedSatisfied) 17 | { 18 | protoboard pb; 19 | 20 | VariableT exchangeID = make_variable(pb, _exchangeID, "exchangeID"); 21 | 22 | jubjub::Params params; 23 | Constants constants(pb, "constants"); 24 | OrderGadget orderGadget(pb, params, constants, exchangeID, ".order"); 25 | 26 | orderGadget.generate_r1cs_witness(order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf); 27 | orderGadget.generate_r1cs_constraints(doSignatureCheck); 28 | 29 | REQUIRE(pb.is_satisfied() == expectedSatisfied); 30 | 31 | return pb.val(orderGadget.hasRebate()); 32 | }; 33 | 34 | RingSettlementBlock block = getRingSettlementBlock(); 35 | REQUIRE(block.ringSettlements.size() > 0); 36 | const RingSettlement& ringSettlement = block.ringSettlements[0]; 37 | 38 | const FieldT& exchangeID = block.exchangeID; 39 | const Order& order = ringSettlement.ring.orderA; 40 | const Account& account = ringSettlement.accountUpdate_A.before; 41 | const BalanceLeaf& balanceLeafS = ringSettlement.balanceUpdateS_A.before; 42 | const BalanceLeaf& balanceLeafB = ringSettlement.balanceUpdateB_A.before; 43 | const TradeHistoryLeaf& tradeHistoryLeaf = ringSettlement.tradeHistoryUpdate_A.before; 44 | 45 | const Account& account2 = ringSettlement.accountUpdate_B.before; 46 | 47 | SECTION("Valid order") 48 | { 49 | orderChecked(exchangeID, order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, true); 50 | } 51 | 52 | SECTION("Different exchangeID") 53 | { 54 | orderChecked(exchangeID + 1, order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 55 | } 56 | 57 | SECTION("Wrong signature for the account") 58 | { 59 | orderChecked(exchangeID, order, account2, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 60 | } 61 | 62 | SECTION("Wrong order data") 63 | { 64 | SECTION("orderID") 65 | { 66 | Order _order = order; 67 | _order.orderID += 1; 68 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 69 | } 70 | SECTION("accountID") 71 | { 72 | Order _order = order; 73 | _order.accountID += 1; 74 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 75 | } 76 | SECTION("tokenS") 77 | { 78 | Order _order = order; 79 | _order.tokenS += 1; 80 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 81 | } 82 | SECTION("tokenB") 83 | { 84 | Order _order = order; 85 | _order.tokenB += 1; 86 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 87 | } 88 | SECTION("amountS") 89 | { 90 | Order _order = order; 91 | _order.amountS += 1; 92 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 93 | } 94 | SECTION("amountB") 95 | { 96 | Order _order = order; 97 | _order.amountB += 1; 98 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 99 | } 100 | SECTION("allOrNone") 101 | { 102 | Order _order = order; 103 | _order.allOrNone = (order.allOrNone == FieldT::one()) ? FieldT::zero() : FieldT::one(); 104 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 105 | } 106 | SECTION("validSince") 107 | { 108 | Order _order = order; 109 | _order.validSince += 1; 110 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 111 | } 112 | SECTION("validUntil") 113 | { 114 | Order _order = order; 115 | _order.validUntil += 1; 116 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 117 | } 118 | SECTION("maxFeeBips") 119 | { 120 | Order _order = order; 121 | _order.maxFeeBips += 1; 122 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 123 | } 124 | SECTION("buy") 125 | { 126 | Order _order = order; 127 | _order.buy = (order.buy == FieldT::one()) ? FieldT::zero() : FieldT::one(); 128 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 129 | } 130 | } 131 | 132 | doSignatureCheck = false; 133 | 134 | SECTION("order data > max allowed values") 135 | { 136 | SECTION("orderID") 137 | { 138 | Order _order = order; 139 | _order.orderID = getMaxFieldElement(NUM_BITS_ORDERID) + 1; 140 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 141 | } 142 | SECTION("accountID") 143 | { 144 | Order _order = order; 145 | _order.accountID = getMaxFieldElement(NUM_BITS_ACCOUNT) + 1; 146 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 147 | } 148 | SECTION("tokenS") 149 | { 150 | Order _order = order; 151 | _order.tokenS = getMaxFieldElement(NUM_BITS_TOKEN) + 1; 152 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 153 | } 154 | SECTION("tokenB") 155 | { 156 | Order _order = order; 157 | _order.tokenB = getMaxFieldElement(NUM_BITS_TOKEN) + 1; 158 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 159 | } 160 | SECTION("amountS") 161 | { 162 | Order _order = order; 163 | _order.amountS = getMaxFieldElement(NUM_BITS_AMOUNT) + 1; 164 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 165 | } 166 | SECTION("amountB") 167 | { 168 | Order _order = order; 169 | _order.amountB = getMaxFieldElement(NUM_BITS_AMOUNT) + 1; 170 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 171 | } 172 | SECTION("allOrNone") 173 | { 174 | Order _order = order; 175 | _order.allOrNone = 2; 176 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 177 | } 178 | SECTION("validSince") 179 | { 180 | Order _order = order; 181 | _order.validSince = getMaxFieldElement(NUM_BITS_TIMESTAMP) + 1; 182 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 183 | } 184 | SECTION("validUntil") 185 | { 186 | Order _order = order; 187 | _order.validUntil = getMaxFieldElement(NUM_BITS_TIMESTAMP) + 1; 188 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 189 | } 190 | SECTION("maxFeeBips") 191 | { 192 | Order _order = order; 193 | _order.maxFeeBips = getMaxFieldElement(NUM_BITS_BIPS) + 1; 194 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 195 | } 196 | SECTION("buy") 197 | { 198 | Order _order = order; 199 | _order.buy = 2; 200 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 201 | } 202 | SECTION("feeBips") 203 | { 204 | Order _order = order; 205 | _order.feeBips = getMaxFieldElement(NUM_BITS_BIPS) + 1; 206 | _order.rebateBips = 0; 207 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 208 | } 209 | SECTION("rebateBips") 210 | { 211 | Order _order = order; 212 | _order.feeBips = 0; 213 | _order.rebateBips = getMaxFieldElement(NUM_BITS_BIPS) + 1; 214 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 215 | } 216 | } 217 | 218 | SECTION("invalid order data") 219 | { 220 | SECTION("feeBips != 0 && rebateBips != 0") 221 | { 222 | Order _order = order; 223 | _order.feeBips = 2; 224 | _order.rebateBips = 1; 225 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 226 | } 227 | SECTION("feeBips > maxFeeBips") 228 | { 229 | Order _order = order; 230 | for (unsigned int maxFeeBips = 0; maxFeeBips < pow(2, NUM_BITS_BIPS); maxFeeBips += 3) 231 | { 232 | for (unsigned int feeBips = 0; feeBips < pow(2, NUM_BITS_BIPS); feeBips += 3) 233 | { 234 | _order.maxFeeBips = maxFeeBips; 235 | _order.feeBips = feeBips; 236 | _order.rebateBips = 0; 237 | bool expectedSatisfied = (feeBips <= maxFeeBips); 238 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, expectedSatisfied); 239 | } 240 | } 241 | } 242 | SECTION("tokenS == tokenB") 243 | { 244 | Order _order = order; 245 | for (unsigned int tokenID = 0; tokenID < pow(2, NUM_BITS_TOKEN); tokenID += 3) 246 | { 247 | _order.tokenS = tokenID; 248 | _order.tokenB = tokenID; 249 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 250 | } 251 | } 252 | SECTION("amountS == 0") 253 | { 254 | Order _order = order; 255 | _order.amountS = 0; 256 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 257 | } 258 | SECTION("amountB == 0") 259 | { 260 | Order _order = order; 261 | _order.amountB = 0; 262 | orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, false); 263 | } 264 | } 265 | 266 | SECTION("hasRebate") 267 | { 268 | Order _order = order; 269 | { 270 | _order.feeBips = 10; 271 | _order.rebateBips = 0; 272 | auto data = orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, true); 273 | REQUIRE((data == FieldT::zero())); 274 | } 275 | { 276 | _order.feeBips = 0; 277 | _order.rebateBips = 10; 278 | auto data = orderChecked(exchangeID, _order, account, balanceLeafS, balanceLeafB, tradeHistoryLeaf, true); 279 | REQUIRE((data == FieldT::one())); 280 | } 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /test/SignatureTests.cpp: -------------------------------------------------------------------------------- 1 | #include "../ThirdParty/catch.hpp" 2 | #include "TestUtils.h" 3 | 4 | #include "../Gadgets/MathGadgets.h" 5 | 6 | TEST_CASE("SignatureVerifier", "[SignatureVerifier]") 7 | { 8 | auto signatureVerifierChecked = [](const FieldT& _pubKeyX, const FieldT& _pubKeyY, const FieldT& _msg, 9 | const Loopring::Signature& signature, bool expectedSatisfied, bool checkValid = false) 10 | { 11 | for (unsigned int i = 0; i < (checkValid ? 2 : 1); i++) 12 | { 13 | bool requireValid = (i == 0); 14 | 15 | protoboard pb; 16 | 17 | Constants constants(pb, "constants"); 18 | jubjub::Params params; 19 | jubjub::VariablePointT publicKey(pb, "publicKey"); 20 | pb.val(publicKey.x) = _pubKeyX; 21 | pb.val(publicKey.y) = _pubKeyY; 22 | pb_variable message = make_variable(pb, _msg, "message"); 23 | 24 | SignatureVerifier signatureVerifier(pb, params, constants, publicKey, message, "signatureVerifier", requireValid); 25 | signatureVerifier.generate_r1cs_constraints(); 26 | signatureVerifier.generate_r1cs_witness(signature); 27 | 28 | REQUIRE(pb.is_satisfied() == (requireValid ? expectedSatisfied : true)); 29 | REQUIRE((pb.val(signatureVerifier.result()) == (expectedSatisfied ? FieldT::one() : FieldT::zero()))); 30 | } 31 | }; 32 | 33 | // Correct publicKey + message + signature 34 | FieldT pubKeyX = FieldT("21607074953141243618425427250695537464636088817373528162920186615872448542319"); 35 | FieldT pubKeyY = FieldT("3328786100751313619819855397819808730287075038642729822829479432223775713775"); 36 | FieldT msg = FieldT("18996832849579325290301086811580112302791300834635590497072390271656077158490"); 37 | FieldT Rx = FieldT("20401810397006237293387786382094924349489854205086853036638326738826249727385"); 38 | FieldT Ry = FieldT("3339178343289311394427480868578479091766919601142009911922211138735585687725"); 39 | FieldT s = FieldT("219593190015660463654216479865253652653333952251250676996482368461290160677"); 40 | 41 | // Different valid public key 42 | FieldT pubKeyX_2 = FieldT("19818098172422229289422284899436629503222263750727977198150374245991932884258"); 43 | FieldT pubKeyY_2 = FieldT("5951877988471485350710444403782724110196846988892201970720985561004735218817"); 44 | 45 | // Signature of message signed by different keypair 46 | FieldT Rx_2 = FieldT("11724635741659369482608508002194555510423986519388485904857477054244428273528"); 47 | FieldT Ry_2 = FieldT("1141584024686665974825800506178016776173372699473828623261155117285910293572"); 48 | FieldT s_2 = FieldT("48808226556453260593782345205957224790810379817643725430561166968302957481"); 49 | 50 | SECTION("All data valid") 51 | { 52 | signatureVerifierChecked(pubKeyX, pubKeyY, msg, Loopring::Signature(EdwardsPoint(Rx, Ry), s), true); 53 | } 54 | 55 | SECTION("All zeros") 56 | { 57 | signatureVerifierChecked(0, 0, 0, Loopring::Signature(EdwardsPoint(0, 0), 0), false); 58 | } 59 | 60 | SECTION("Wrong publicKey.x") 61 | { 62 | signatureVerifierChecked(pubKeyX + 1, pubKeyY, msg, Loopring::Signature(EdwardsPoint(Rx, Ry), s), false); 63 | } 64 | 65 | SECTION("Wrong publicKey.y") 66 | { 67 | signatureVerifierChecked(pubKeyX, pubKeyY + 1, msg, Loopring::Signature(EdwardsPoint(Rx, Ry), s), false); 68 | } 69 | 70 | SECTION("Different (but valid) public key") 71 | { 72 | signatureVerifierChecked(pubKeyX_2, pubKeyY_2, msg, Loopring::Signature(EdwardsPoint(Rx, Ry), s), false, true); 73 | } 74 | 75 | SECTION("Different message") 76 | { 77 | signatureVerifierChecked(pubKeyX, pubKeyY, msg + 1, Loopring::Signature(EdwardsPoint(Rx, Ry), s), false, true); 78 | } 79 | 80 | SECTION("Zero message value") 81 | { 82 | signatureVerifierChecked(pubKeyX, pubKeyY, 0, Loopring::Signature(EdwardsPoint(Rx, Ry), s), false); 83 | } 84 | 85 | SECTION("Max message value") 86 | { 87 | signatureVerifierChecked(pubKeyX, pubKeyY, getMaxFieldElement(), Loopring::Signature(EdwardsPoint(Rx, Ry), s), false); 88 | } 89 | 90 | SECTION("Different Rx") 91 | { 92 | signatureVerifierChecked(pubKeyX, pubKeyY, msg, Loopring::Signature(EdwardsPoint(Rx + 1, Ry), s), false); 93 | } 94 | 95 | SECTION("Different Ry") 96 | { 97 | signatureVerifierChecked(pubKeyX, pubKeyY, msg, Loopring::Signature(EdwardsPoint(Rx, Ry + 1), s), false); 98 | } 99 | 100 | SECTION("Different s") 101 | { 102 | signatureVerifierChecked(pubKeyX, pubKeyY, msg, Loopring::Signature(EdwardsPoint(Rx, Ry), s + 1), false); 103 | } 104 | 105 | SECTION("Signature of message of different public key") 106 | { 107 | signatureVerifierChecked(pubKeyX, pubKeyY, msg, Loopring::Signature(EdwardsPoint(Rx_2, Ry_2), s_2), false, true); 108 | } 109 | 110 | SECTION("Signature invalid but a valid point") 111 | { 112 | signatureVerifierChecked(pubKeyX, pubKeyY, msg, Loopring::Signature(EdwardsPoint(pubKeyX, pubKeyY), 0), false, true); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /test/TestUtils.h: -------------------------------------------------------------------------------- 1 | #ifndef _TEST_UTILS_H_ 2 | #define _TEST_UTILS_H_ 3 | 4 | #include "ethsnarks.hpp" 5 | #include "../Utils/Utils.h" 6 | #include "../ThirdParty/BigIntHeader.hpp" 7 | 8 | using namespace std; 9 | using namespace libff; 10 | using namespace libsnark; 11 | using namespace ethsnarks; 12 | using namespace Loopring; 13 | 14 | static const char* TEST_DATA_PATH = "../../../protocol3-circuits/test/data/"; 15 | static BigInt SNARK_SCALAR_FIELD = BigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617"); 16 | 17 | 18 | static const BigInt& validate(const BigInt& v) 19 | { 20 | // Check for overflow 21 | REQUIRE(v < SNARK_SCALAR_FIELD); 22 | // Check for underflow 23 | REQUIRE(v >= 0); 24 | return v; 25 | } 26 | 27 | static FieldT toFieldElement(const BigInt& v) 28 | { 29 | return FieldT(validate(v).to_string().c_str()); 30 | } 31 | 32 | static BigInt getRandomFieldElementAsBigInt(unsigned int numBits = 254) 33 | { 34 | BigInt v(rand()); 35 | for(unsigned int i = 0; i < 32/4; i++) 36 | { 37 | v *= 32; 38 | v += rand(); 39 | } 40 | 41 | if (numBits >= 254) 42 | { 43 | v %= SNARK_SCALAR_FIELD; 44 | } 45 | else 46 | { 47 | BigInt m(1); 48 | for (unsigned int b = 0; b < numBits; b++) 49 | { 50 | m *= 2; 51 | } 52 | v %= m; 53 | } 54 | return v; 55 | } 56 | 57 | static BigInt getMaxFieldElementAsBigInt(unsigned int numBits = 254) 58 | { 59 | if (numBits >= 254) 60 | { 61 | return SNARK_SCALAR_FIELD - 1; 62 | } 63 | else 64 | { 65 | BigInt m(1); 66 | for (unsigned int b = 0; b < numBits; b++) 67 | { 68 | m *= 2; 69 | } 70 | m -= 1; 71 | return m; 72 | } 73 | } 74 | 75 | static FieldT getRandomFieldElement(unsigned int numBits = 254) 76 | { 77 | return toFieldElement(getRandomFieldElementAsBigInt(numBits)); 78 | } 79 | 80 | static FieldT getMaxFieldElement(unsigned int numBits = 254) 81 | { 82 | if (numBits == 254) 83 | { 84 | return FieldT("21888242871839275222246405745257275088548364400416034343698204186575808495616"); 85 | } 86 | else 87 | { 88 | return (FieldT(2)^numBits) - 1; 89 | } 90 | } 91 | 92 | static libff::bit_vector toBits(const FieldT value, unsigned int numBits) 93 | { 94 | libff::bit_vector vector; 95 | const bigint rint = value.as_bigint(); 96 | for (size_t i = 0; i < numBits; ++i) 97 | { 98 | vector.push_back(rint.test_bit(i)); 99 | } 100 | return vector; 101 | } 102 | 103 | static bool compareBits(const libff::bit_vector& A, const libff::bit_vector& B) 104 | { 105 | if (A.size() != B.size()) 106 | { 107 | return false; 108 | } 109 | 110 | for (unsigned int i = 0; i < A.size(); i++) 111 | { 112 | if (A[i] != B[i]) 113 | { 114 | return false; 115 | } 116 | } 117 | return true; 118 | } 119 | 120 | static RingSettlementBlock getRingSettlementBlock() 121 | { 122 | // Read the JSON file 123 | string filename = string(TEST_DATA_PATH) + "settlement_block.json"; 124 | ifstream file(filename); 125 | if (!file.is_open()) 126 | { 127 | cerr << "Cannot open input file: " << filename << endl; 128 | REQUIRE(false); 129 | } 130 | json input; 131 | file >> input; 132 | file.close(); 133 | 134 | RingSettlementBlock block = input.get(); 135 | return block; 136 | } 137 | 138 | 139 | #endif -------------------------------------------------------------------------------- /test/TradeHistoryTests.cpp: -------------------------------------------------------------------------------- 1 | #include "../ThirdParty/catch.hpp" 2 | #include "TestUtils.h" 3 | 4 | #include "../Gadgets/TradingHistoryGadgets.h" 5 | 6 | TEST_CASE("TradeHistoryTrimming", "[TradeHistoryTrimmingGadget]") 7 | { 8 | auto tradeHistoryTrimmingChecked = [](const TradeHistoryLeaf& tradeHistoryLeaf, const FieldT& _orderID, 9 | bool expectedSatisfied, const FieldT& expectedFilled = FieldT::zero(), const FieldT& expectedOverwrite = FieldT::zero()) 10 | { 11 | protoboard pb; 12 | 13 | TradeHistoryGadget tradeHistory(pb, "tradeHistory"); 14 | tradeHistory.generate_r1cs_witness(tradeHistoryLeaf); 15 | 16 | DualVariableGadget orderID(pb, NUM_BITS_ORDERID, "orderID"); 17 | orderID.generate_r1cs_witness(pb, _orderID); 18 | 19 | jubjub::Params params; 20 | Constants constants(pb, "constants"); 21 | TradeHistoryTrimmingGadget tradeHistoryTrimmingGadget( 22 | pb, constants, tradeHistory, orderID, "tradeHistoryTrimmingGadget" 23 | ); 24 | tradeHistoryTrimmingGadget.generate_r1cs_witness(); 25 | tradeHistoryTrimmingGadget.generate_r1cs_constraints(); 26 | 27 | REQUIRE(pb.is_satisfied() == expectedSatisfied); 28 | if (expectedSatisfied) 29 | { 30 | REQUIRE((pb.val(tradeHistoryTrimmingGadget.getFilled()) == expectedFilled)); 31 | REQUIRE((pb.val(tradeHistoryTrimmingGadget.getOverwrite()) == expectedOverwrite)); 32 | } 33 | }; 34 | 35 | unsigned int delta = pow(2, NUM_BITS_TRADING_HISTORY); 36 | unsigned int orderID = rand() % delta; 37 | FieldT filled = getRandomFieldElement(NUM_BITS_AMOUNT); 38 | 39 | SECTION("orderID == tradeHistoryOrderID") 40 | { 41 | SECTION("Initial state orderID == 0") 42 | { 43 | tradeHistoryTrimmingChecked({0, 0}, 0, 44 | true, 0, 0); 45 | } 46 | SECTION("Initial state orderID > 0") 47 | { 48 | tradeHistoryTrimmingChecked({0, 0}, orderID, 49 | true, 0, 0); 50 | } 51 | SECTION("Order filled") 52 | { 53 | tradeHistoryTrimmingChecked({filled, orderID}, orderID, 54 | true, filled, 0); 55 | } 56 | SECTION("Initial state orderID == delta - 1") 57 | { 58 | tradeHistoryTrimmingChecked({0, 0}, delta - 1, 59 | true, 0, 0); 60 | } 61 | } 62 | 63 | SECTION("orderID > tradeHistoryOrderID (trimmed)") 64 | { 65 | SECTION("First overwrite") 66 | { 67 | tradeHistoryTrimmingChecked({0, 0}, delta, 68 | true, 0, 1); 69 | } 70 | SECTION("Previous order not filled") 71 | { 72 | tradeHistoryTrimmingChecked({0, orderID}, delta + orderID, 73 | true, 0, 1); 74 | } 75 | SECTION("Previous order filled") 76 | { 77 | tradeHistoryTrimmingChecked({filled, orderID}, delta + orderID, 78 | true, 0, 1); 79 | } 80 | SECTION("Max overwrite delta") 81 | { 82 | tradeHistoryTrimmingChecked({0, 0}, delta + orderID, 83 | true, 0, 1); 84 | } 85 | SECTION("orderID too big") 86 | { 87 | tradeHistoryTrimmingChecked({0, 0}, delta + delta + orderID, 88 | false); 89 | tradeHistoryTrimmingChecked({0, 0}, delta * 9 + orderID, 90 | false); 91 | tradeHistoryTrimmingChecked({0, 0}, delta * 99 + orderID, 92 | false); 93 | tradeHistoryTrimmingChecked({0, 0}, delta * 999 + orderID, 94 | false); 95 | tradeHistoryTrimmingChecked({0, 0}, delta * 9999 + orderID, 96 | false); 97 | } 98 | } 99 | 100 | SECTION("orderID < tradeHistoryOrderID (cancelled)") 101 | { 102 | SECTION("First rejection") 103 | { 104 | tradeHistoryTrimmingChecked({0, 0}, delta * 2, 105 | false); 106 | } 107 | SECTION("New order not filled") 108 | { 109 | tradeHistoryTrimmingChecked({0, delta + orderID}, orderID, 110 | false); 111 | } 112 | SECTION("New order filled") 113 | { 114 | tradeHistoryTrimmingChecked({filled, delta + orderID}, orderID, 115 | false); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file 2 | #include "../ThirdParty/catch.hpp" 3 | #include "../ThirdParty/BigInt.hpp" 4 | #include "ethsnarks.hpp" 5 | 6 | struct Initialize 7 | { 8 | Initialize() 9 | { 10 | ethsnarks::ppT::init_public_params(); 11 | srand(time(NULL)); 12 | } 13 | } initialize; --------------------------------------------------------------------------------