public PuzzleValue[] CheckCommitmentProof(ServerCommitmentsProof proof) { // steps 8, 10, 12 if (proof == null) { throw new ArgumentNullException(nameof(proof)); } var FakeSolutionsCount = proof.FakeSolutions.Select(a => a.Length).Sum(); // sums the number of FakeSolutions. if (FakeSolutionsCount != Parameters.GetTotalFakeTransactionsCount()) { throw new ArgumentException($"Expecting {Parameters.GetTotalFakeTransactionsCount()} solutions"); } var QuotientsCount = proof.Quotients.Select(a => a.Length).Sum(); // sums the number of Quotients. if (QuotientsCount != (Parameters.GetTotalRealTransactionsCount() - _Parameters.PaymentsCount)) // this is Q * (mu - 1) { throw new ArgumentException($"Expecting {(Parameters.GetTotalRealTransactionsCount() - _Parameters.PaymentsCount)} quotients"); } AssertState(PromiseClientStates.WaitingCommitmentsProof); var previousSolutions = new byte[Parameters.FakeTransactionCountPerLevel][]; previousSolutions = previousSolutions.Select(a => new byte[0]).ToArray(); // Initialize to empty for (int i = 0; i < _Hashes.Length; i++) { var fakeHashes = _Hashes[i].OfType <FakeHash>().ToArray(); for (int j = 0; j < fakeHashes.Length; j++) { // TODO: prove that the solutions are lined up in same order as the hashes. var fakeHash = fakeHashes[j]; var solution = proof.FakeSolutions[i][j]; if (solution._Value.CompareTo(Parameters.ServerKey._Key.Modulus) >= 0) { throw new PuzzleException("Solution bigger than modulus"); } if (!new Puzzle(Parameters.ServerKey, fakeHash.Commitment.Puzzle).Verify(solution)) { throw new PuzzleException("Invalid puzzle solution"); } previousSolutions[j] = Utils.Combine(solution.ToBytes(), previousSolutions[j]); var paddedSolution = new PuzzleSolution(Utils.Combine(NBitcoin.Utils.ToBytes((uint)i, true), NBitcoin.Utils.ToBytes((uint)fakeHash.Index, true), previousSolutions[j])); if (!IsValidSignature(paddedSolution, fakeHash, out ECDSASignature sig)) { throw new PuzzleException("Invalid ECDSA signature"); } } } // Step 10 for (int i = 0; i < _Hashes.Length; i++) { var realHashes = _Hashes[i].OfType <RealHash>().ToArray(); for (int j = 1; j < realHashes.Length; j++) { var q = proof.Quotients[i][j - 1]._Value; var p1 = realHashes[j - 1].Commitment.Puzzle._Value; var p2 = realHashes[j].Commitment.Puzzle._Value; var p22 = p1.Multiply(Parameters.ServerKey.Encrypt(q)).Mod(Parameters.ServerKey._Key.Modulus); if (!p2.Equals(p22)) { throw new PuzzleException("Invalid quotient"); } } } _Hashes = _Hashes.Select(a => a.OfType <RealHash>().ToArray()).ToArray(); // we do not need the fake one anymore InternalState.FakeColumns = null; InternalState.Quotients = proof.Quotients; // Step 12 // Maybe move this step outside such that we can blind and send puzzles one by one. BlindFactor[] blindFactors = new BlindFactor[_Hashes.Length]; PuzzleValue[] blindedPuzzles = new PuzzleValue[_Hashes.Length]; for (int i = 0; i < _Hashes.Length; i++) { var puzzleToSolve = _Hashes[i].OfType <RealHash>().First().Commitment.Puzzle; blindedPuzzles[i] = new Puzzle(Parameters.ServerKey, puzzleToSolve).Blind(ref blindFactors[i]).PuzzleValue; } InternalState.BlindFactors = blindFactors; InternalState.Status = PromiseClientStates.Completed; return(blindedPuzzles); }
public ServerCommitmentsProof CheckRevelation(ClientRevelation revelation, Script cashoutDestination, FeeRate feeRate) { /* * Steps 7, 9 * Almost ready, just need to figure out: * - The CashOutFormat for the validation of RealSet. * - How to get the cashoutDestination and the feeRate, * for now I pass them in like in "CreateSignatureRequest" * from ClientSession. */ if (revelation == null) { throw new ArgumentNullException(nameof(revelation)); } var saltCount = revelation.Salts.Select(a => a.Length).Sum(); if (saltCount != Parameters.GetTotalFakeTransactionsCount() || revelation.FakeIndexes.Length != Parameters.FakeTransactionCountPerLevel) { throw new ArgumentNullException($"The revelation should contains {Parameters.GetTotalFakeTransactionsCount()} salts and {Parameters.FakeTransactionCountPerLevel} indices"); } var variationCount = revelation.FeeVariations.Select(a => a.Length).Sum(); if (variationCount != Parameters.GetTotalRealTransactionsCount()) { throw new ArgumentNullException($"The revelation should contains {Parameters.GetTotalRealTransactionsCount()} fee variations"); } AssertState(PromiseServerStates.WaitingRevelation); var indexSalt = revelation.IndexesSalt; if (InternalState.FakeIndexesHash != PromiseUtils.HashIndexes(ref indexSalt, revelation.FakeIndexes)) { throw new PuzzleException("Invalid index salt"); } Transaction cashout = new Transaction(); // TODO: Figure out the cashout format for j Bitcoins cashout.AddInput(new TxIn(InternalState.EscrowedCoin.Outpoint)); cashout.Inputs[0].ScriptSig = new Script( Op.GetPushOp(TrustedBroadcastRequest.PlaceholderSignature), Op.GetPushOp(TrustedBroadcastRequest.PlaceholderSignature), Op.GetPushOp(InternalState.EscrowedCoin.Redeem.ToBytes()) ); cashout.AddOutput(new TxOut(InternalState.EscrowedCoin.Amount, cashoutDestination)); cashout.Outputs[0].Value -= feeRate.GetFee(cashout.GetVirtualSize()); var solutions = new PuzzleSolution[Parameters.PaymentsCount][]; var RealIndexes = Enumerable.Range(0, Parameters.GetTotalTransactionsCountPerLevel()).Where(a => !revelation.FakeIndexes.Contains(a)).ToArray(); for (int i = 0; i < solutions.Length; i++) { // Checking valid Transactions for (int j = 0; j < Parameters.RealTransactionCountPerLevel; j++) { var feeVariation = revelation.FeeVariations[i][j]; var encrypted = InternalState.EncryptedSignatures[i][RealIndexes[j]]; // Check if this function approved! var actualSignedHash = Parameters.CreateRealHash(cashout, InternalState.EscrowedCoin, feeVariation); if (actualSignedHash != encrypted.SignedHash) { throw new PuzzleException("Incorrect feeVariation provided"); } } // Checking Fake Transactions solutions[i] = new PuzzleSolution[Parameters.FakeTransactionCountPerLevel]; // Initialization for (int j = 0; j < solutions[i].Length; j++) { var salt = revelation.Salts[i][j]; var encrypted = InternalState.EncryptedSignatures[i][revelation.FakeIndexes[j]]; var actualSignedHash = Parameters.CreateFakeHash(salt); if (actualSignedHash != encrypted.SignedHash) { throw new PuzzleException("Incorrect salt provided"); } solutions[i][j] = encrypted.PuzzleSolution; } } // We can throw away the fake puzzles InternalState.EncryptedSignatures = InternalState.EncryptedSignatures.Select(a => a.Where((e, i) => !revelation.FakeIndexes.Contains(i)).ToArray()).ToArray(); // Step 9 var quotients = new Quotient[Parameters.PaymentsCount][]; for (int i = 0; i < quotients.Length; i++) { quotients[i] = new Quotient[Parameters.RealTransactionCountPerLevel - 1]; for (int j = 0; j < quotients[i].Length; j++) { var a = InternalState.EncryptedSignatures[i][j].PuzzleSolution._Value; var b = InternalState.EncryptedSignatures[i][j + 1].PuzzleSolution._Value; quotients[i][j] = new Quotient(b.Multiply(a.ModInverse(Parameters.ServerKey._Key.Modulus)).Mod(Parameters.ServerKey._Key.Modulus)); } } InternalState.FakeIndexesHash = null; InternalState.Status = PromiseServerStates.Completed; return(new ServerCommitmentsProof(solutions.ToArray(), quotients)); }