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);
        }
Exemple #2
0
        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));
        }