public SolverServerSession ConfirmClientEscrow(Transaction transaction, out PuzzleSolution solvedVoucher)
        {
            AssertState(AliceServerChannelNegotiationStates.WaitingClientEscrow);
            solvedVoucher = null;
            var escrow = CreateEscrowScript();
            var coin   = transaction.Outputs.AsCoins().FirstOrDefault(txout => txout.ScriptPubKey == escrow.Hash.ScriptPubKey);

            if (coin == null)
            {
                throw new PuzzleException("No output containing the escrowed coin");
            }
            if (coin.Amount != Parameters.Denomination + Parameters.Fee)
            {
                throw new PuzzleException("Incorrect amount");
            }
            var voucher      = InternalState.UnsignedVoucher;
            var escrowedCoin = coin.ToScriptCoin(escrow);

            var session = new SolverServerSession(TumblerKey, Parameters.CreateSolverParamaters());

            session.ConfigureEscrowedCoin(escrowedCoin, InternalState.EscrowKey);
            InternalState.UnsignedVoucher = null;
            InternalState.OtherEscrowKey  = null;
            InternalState.RedeemKey       = null;
            InternalState.EscrowKey       = null;
            solvedVoucher        = voucher.WithRsaKey(VoucherKey.PubKey).Solve(VoucherKey);
            InternalState.Status = AliceServerChannelNegotiationStates.Completed;
            return(session);
        }
 public void Save(int cycleId, SolverServerSession session)
 {
     Repository.UpdateOrInsert(GetCyclePartition(cycleId), session.Id.ToString(), session.GetInternalState(), (o, n) =>
     {
         if (o.ETag != n.ETag)
         {
             throw new InvalidOperationException("Optimistic concurrency failure");
         }
         n.ETag++;
         return(n);
     });
 }
Esempio n. 3
0
        private void RoundTrip(ref SolverServerSession server, SolverParameters parameters, RsaKey key)
        {
            var clone = Serializer.Clone(server.GetInternalState());

            server = new SolverServerSession(key, parameters, clone);
        }
Esempio n. 4
0
        public void TestPuzzleSolver()
        {
            RsaKey         key = TestKeys.Default;
            PuzzleSolution expectedSolution = null;
            Puzzle         puzzle           = key.PubKey.GeneratePuzzle(ref expectedSolution);

            var parameters = new SolverParameters
            {
                FakePuzzleCount = 50,
                RealPuzzleCount = 10,
                ServerKey       = key.PubKey
            };
            SolverClientSession client = new SolverClientSession(parameters);
            SolverServerSession server = new SolverServerSession(key, parameters);

            var clientEscrow = new Key();
            var serverEscrow = new Key();

            var escrow            = CreateEscrowCoin(clientEscrow.PubKey, serverEscrow.PubKey);
            var redeemDestination = new Key().ScriptPubKey;

            client.ConfigureEscrowedCoin(escrow, clientEscrow, redeemDestination);
            client.AcceptPuzzle(puzzle.PuzzleValue);
            RoundTrip(ref client, parameters);
            Assert.True(client.GetInternalState().RedeemDestination == redeemDestination);
            PuzzleValue[] puzzles = client.GeneratePuzzles();
            RoundTrip(ref client, parameters);
            RoundTrip(ref puzzles);

            server.ConfigureEscrowedCoin(escrow, serverEscrow);
            var commitments = server.SolvePuzzles(puzzles);

            RoundTrip(ref server, parameters, key);
            RoundTrip(ref commitments);

            var revelation = client.Reveal(commitments);

            RoundTrip(ref client, parameters);
            RoundTrip(ref revelation);

            SolutionKey[] fakePuzzleKeys = server.CheckRevelation(revelation);
            RoundTrip(ref server, parameters, key);
            RoundTrip(ref fakePuzzleKeys);


            BlindFactor[] blindFactors = client.GetBlindFactors(fakePuzzleKeys);
            RoundTrip(ref client, parameters);
            RoundTrip(ref blindFactors);

            var offerInformation = server.CheckBlindedFactors(blindFactors, FeeRate);

            RoundTrip(ref server, parameters, key);

            var clientOfferSig = client.SignOffer(offerInformation);


            //Verify if the scripts are correctly created
            var fulfill     = server.FulfillOffer(clientOfferSig, new Key().ScriptPubKey, FeeRate);
            var offerRedeem = client.CreateOfferRedeemTransaction(FeeRate);

            var offerTransaction = server.GetSignedOfferTransaction();
            var offerCoin        = offerTransaction.Transaction.Outputs.AsCoins().First();
            var resigned         = offerTransaction.ReSign(client.EscrowedCoin);

            TransactionBuilder txBuilder = new TransactionBuilder();

            txBuilder.AddCoins(client.EscrowedCoin);
            Assert.True(txBuilder.Verify(resigned));

            resigned  = fulfill.ReSign(offerCoin);
            txBuilder = new TransactionBuilder();
            txBuilder.AddCoins(offerCoin);
            Assert.True(txBuilder.Verify(resigned));

            var offerRedeemTx = offerRedeem.ReSign(offerCoin);

            txBuilder = new TransactionBuilder();
            txBuilder.AddCoins(offerCoin);
            Assert.True(txBuilder.Verify(offerRedeemTx));


            client.CheckSolutions(fulfill.Transaction);
            RoundTrip(ref client, parameters);

            var clientEscapeSignature = client.SignEscape();
            var escapeTransaction     = server.GetSignedEscapeTransaction(clientEscapeSignature, FeeRate, new Key().ScriptPubKey);

            txBuilder = new TransactionBuilder();
            txBuilder.AddCoins(client.EscrowedCoin);
            Assert.True(txBuilder.Verify(escapeTransaction));

            var solution = client.GetSolution();

            RoundTrip(ref client, parameters);
            Assert.True(solution == expectedSolution);
        }
Esempio n. 5
0
 private static CorrelationId GetCorrelation(SolverServerSession session)
 {
     return(new CorrelationId(session.Id));
 }
Esempio n. 6
0
        public async Task <PuzzleSolution> SignVoucher(
            [ModelBinder(BinderType = typeof(TumblerParametersModelBinder))]
            ClassicTumblerParameters tumblerId,
            [FromBody] SignVoucherRequest request)
        {
            if (tumblerId == null)
            {
                throw new ArgumentNullException("tumblerId");
            }
            if (request.UnsignedVoucher == null)
            {
                throw new ActionResultException(BadRequest("Missing UnsignedVoucher"));
            }
            if (request.MerkleProof == null)
            {
                throw new ActionResultException(BadRequest("Missing MerkleProof"));
            }
            if (request.Transaction == null)
            {
                throw new ActionResultException(BadRequest("Missing Transaction"));
            }
            if (request.ClientEscrowKey == null)
            {
                throw new ActionResultException(BadRequest("Missing ClientEscrowKey"));
            }
            if (request.ChannelId == null)
            {
                throw new ActionResultException(BadRequest("Missing ChannelId"));
            }

            var cycle  = GetCycle(request.Cycle);
            var height = Services.BlockExplorerService.GetCurrentHeight();

            if (!cycle.IsInPhase(CyclePhase.ClientChannelEstablishment, height))
            {
                throw new ActionResultException(BadRequest("invalid-phase"));
            }

            if (request.MerkleProof.PartialMerkleTree
                .GetMatchedTransactions()
                .FirstOrDefault() != request.Transaction.GetHash() || !request.MerkleProof.Header.CheckProofOfWork())
            {
                Logs.Tumbler.LogDebug("Invalid transaction merkle proof");
                throw new ActionResultException(BadRequest("invalid-merkleproof"));
            }

            var confirmations = Services.BlockExplorerService.GetBlockConfirmations(request.MerkleProof.Header.GetHash());

            if ((confirmations < Parameters.CycleGenerator.FirstCycle.SafetyPeriodDuration))
            {
                Logs.Tumbler.LogDebug("Not enough confirmations");
                throw new ActionResultException(BadRequest("not-enough-confirmation"));
            }

            var transaction = request.Transaction;

            if (transaction.Outputs.Count > 2)
            {
                Logs.Tumbler.LogDebug("Incorrect number of outputs");
                throw new ActionResultException(BadRequest("invalid-transaction"));
            }

            var key = Repository.GetKey(cycle.Start, request.KeyReference);

            if (Repository.IsUsed(cycle.Start, request.ChannelId))
            {
                throw new ActionResultException(BadRequest("duplicate-query"));
            }

            var expectedEscrow = new EscrowScriptPubKeyParameters(request.ClientEscrowKey, key.PubKey, cycle.GetClientLockTime());

            var expectedTxOut = new TxOut(Parameters.Denomination + Parameters.Fee, expectedEscrow.ToScript().WitHash.ScriptPubKey.Hash);
            var escrowedCoin  =
                transaction
                .Outputs
                .AsCoins()
                .Where(c => c.TxOut.Value == expectedTxOut.Value &&
                       c.TxOut.ScriptPubKey == expectedTxOut.ScriptPubKey)
                .Select(c => c.ToScriptCoin(expectedEscrow.ToScript()))
                .FirstOrDefault();

            if (escrowedCoin == null)
            {
                Logs.Tumbler.LogDebug("Could not find escrowed coin");
                throw new ActionResultException(BadRequest("invalid-transaction"));
            }

            var solverServerSession = new SolverServerSession(Runtime.TumblerKey, Parameters.CreateSolverParamaters());

            solverServerSession.SetChannelId(request.ChannelId);
            solverServerSession.ConfigureEscrowedCoin(escrowedCoin, key);
            await Services.BlockExplorerService.TrackAsync(escrowedCoin.ScriptPubKey);

            if (!await Services.BlockExplorerService.TrackPrunedTransactionAsync(request.Transaction, request.MerkleProof))
            {
                throw new ActionResultException(BadRequest("invalid-merkleproof"));
            }

            //Without this one, someone could spam the nonce db by replaying this request with different channelId
            if (!Repository.MarkUsedNonce(cycle.Start, Hashes.Hash160(escrowedCoin.Outpoint.ToBytes())))
            {
                throw new ActionResultException(BadRequest("duplicate-query"));
            }

            AssertNotDuplicateQuery(cycle.Start, request.ChannelId);

            Repository.Save(cycle.Start, solverServerSession);
            Logs.Tumbler.LogInformation($"Cycle {cycle.Start} Proof of Escrow signed for " + transaction.GetHash());

            var correlation = GetCorrelation(solverServerSession);

            Tracker.AddressCreated(cycle.Start, TransactionType.ClientEscrow, escrowedCoin.ScriptPubKey, correlation);
            Tracker.TransactionCreated(cycle.Start, TransactionType.ClientEscrow, request.Transaction.GetHash(), correlation);
            var solution = request.UnsignedVoucher.WithRsaKey(Runtime.VoucherKey.PubKey).Solve(Runtime.VoucherKey);

            return(solution);
        }
 private static CorrelationId GetCorrelation(SolverServerSession session)
 {
     return(EscrowScriptPubKeyParameters.GetFromCoin(session.EscrowedCoin).GetCorrelation());
 }
        public PuzzleSolution SignVoucher(
            [ModelBinder(BinderType = typeof(TumblerParametersModelBinder))]
            ClassicTumblerParameters tumblerId,
            [FromBody] SignVoucherRequest request)
        {
            if (tumblerId == null)
            {
                throw new ArgumentNullException("tumblerId");
            }
            if (request.UnsignedVoucher == null)
            {
                throw new ActionResultException(BadRequest("Missing UnsignedVoucher"));
            }
            if (request.MerkleProof == null)
            {
                throw new ActionResultException(BadRequest("Missing MerkleProof"));
            }
            if (request.Transaction == null)
            {
                throw new ActionResultException(BadRequest("Missing Transaction"));
            }
            if (request.ClientEscrowKey == null)
            {
                throw new ActionResultException(BadRequest("Missing ClientEscrowKey"));
            }

            if (request.MerkleProof.PartialMerkleTree
                .GetMatchedTransactions()
                .FirstOrDefault() != request.Transaction.GetHash() || !request.MerkleProof.Header.CheckProofOfWork())
            {
                throw new ActionResultException(BadRequest("invalid-merkleproof"));
            }

            var confirmations = Services.BlockExplorerService.GetBlockConfirmations(request.MerkleProof.Header.GetHash());

            if ((confirmations < Parameters.CycleGenerator.FirstCycle.SafetyPeriodDuration))
            {
                throw new ActionResultException(BadRequest("not-enough-confirmation"));
            }

            var transaction = request.Transaction;

            if (transaction.Outputs.Count > 2)
            {
                throw new ActionResultException(BadRequest("invalid-transaction"));
            }

            var cycle  = GetCycle(request.Cycle);
            var height = Services.BlockExplorerService.GetCurrentHeight();

            if (!cycle.IsInPhase(CyclePhase.ClientChannelEstablishment, height))
            {
                throw new ActionResultException(BadRequest("invalid-phase"));
            }


            var key = Repository.GetKey(cycle.Start, request.KeyReference);

            var expectedEscrow = new EscrowScriptPubKeyParameters(request.ClientEscrowKey, key.PubKey, cycle.GetClientLockTime());

            var expectedTxOut = new TxOut(Parameters.Denomination + Parameters.Fee, expectedEscrow.ToScript().Hash);
            var escrowedCoin  =
                transaction
                .Outputs
                .AsCoins()
                .Where(c => c.TxOut.Value == expectedTxOut.Value &&
                       c.TxOut.ScriptPubKey == expectedTxOut.ScriptPubKey)
                .Select(c => c.ToScriptCoin(expectedEscrow.ToScript()))
                .FirstOrDefault();

            if (escrowedCoin == null)
            {
                throw new ActionResultException(BadRequest("invalid-transaction"));
            }

            try
            {
                var solverServerSession = new SolverServerSession(Runtime.TumblerKey, Parameters.CreateSolverParamaters());
                solverServerSession.ConfigureEscrowedCoin(escrowedCoin, key);

                Services.BlockExplorerService.Track(escrowedCoin.ScriptPubKey);
                if (!Services.BlockExplorerService.TrackPrunedTransaction(request.Transaction, request.MerkleProof))
                {
                    throw new ActionResultException(BadRequest("invalid-merkleproof"));
                }

                if (!Repository.MarkUsedNonce(cycle.Start, new uint160(key.PubKey.Hash.ToBytes())))
                {
                    throw new ActionResultException(BadRequest("invalid-transaction"));
                }
                Repository.Save(cycle.Start, solverServerSession);
                Logs.Tumbler.LogInformation($"Cycle {cycle.Start} Proof of Escrow signed for " + transaction.GetHash());

                var correlation = GetCorrelation(solverServerSession);
                Tracker.AddressCreated(cycle.Start, TransactionType.ClientEscrow, escrowedCoin.ScriptPubKey, correlation);
                Tracker.TransactionCreated(cycle.Start, TransactionType.ClientEscrow, request.Transaction.GetHash(), correlation);
                var solution = request.UnsignedVoucher.WithRsaKey(Runtime.VoucherKey.PubKey).Solve(Runtime.VoucherKey);
                return(solution);
            }
            catch (PuzzleException)
            {
                throw new ActionResultException(BadRequest("invalid-transaction"));
            }
        }
Esempio n. 9
0
        public void TestPuzzleSolver()
        {
            RsaKey         key = TestKeys.Default;
            PuzzleSolution expectedSolution = null;
            Puzzle         puzzle           = key.PubKey.GeneratePuzzle(ref expectedSolution);

            var parameters = new SolverParameters
            {
                FakePuzzleCount = 50,
                RealPuzzleCount = 10,
                ServerKey       = key.PubKey
            };
            SolverClientSession client = new SolverClientSession(parameters);
            SolverServerSession server = new SolverServerSession(key, parameters);

            var clientEscrow = new Key();
            var serverEscrow = new Key();
            var clientRedeem = new Key();

            var escrow = CreateEscrowCoin(clientEscrow.PubKey, serverEscrow.PubKey, clientRedeem.PubKey);

            client.ConfigureEscrowedCoin(escrow, clientEscrow, clientRedeem);
            client.AcceptPuzzle(puzzle.PuzzleValue);
            RoundTrip(ref client, parameters);
            PuzzleValue[] puzzles = client.GeneratePuzzles();
            RoundTrip(ref client, parameters);
            RoundTrip(ref puzzles);

            server.ConfigureEscrowedCoin(escrow, serverEscrow);
            var commitments = server.SolvePuzzles(puzzles);

            RoundTrip(ref server, parameters, key);
            RoundTrip(ref commitments);

            var revelation = client.Reveal(commitments);

            RoundTrip(ref client, parameters);
            RoundTrip(ref revelation);

            SolutionKey[] fakePuzzleKeys = server.CheckRevelation(revelation);
            RoundTrip(ref server, parameters, key);
            RoundTrip(ref fakePuzzleKeys);


            BlindFactor[] blindFactors = client.GetBlindFactors(fakePuzzleKeys);
            RoundTrip(ref client, parameters);
            RoundTrip(ref blindFactors);

            var offerInformation = server.CheckBlindedFactors(blindFactors, FeeRate);

            RoundTrip(ref server, parameters, key);

            var clientOfferSig = client.SignOffer(offerInformation);

            //Verify if the scripts are correctly created
            var fulfill = server.FulfillOffer(clientOfferSig, new Key().ScriptPubKey, FeeRate);

            var offerTransaction         = server.GetSignedOfferTransaction();
            TransactionBuilder txBuilder = new TransactionBuilder();

            txBuilder.AddCoins(client.EscrowedCoin);
            Assert.True(txBuilder.Verify(offerTransaction.Transaction));

            txBuilder = new TransactionBuilder();
            txBuilder.AddCoins(offerTransaction.Transaction.Outputs.AsCoins().ToArray());
            Assert.True(txBuilder.Verify(fulfill.Transaction));

            //Check if can resign fulfill in case offer get malleated
            offerTransaction.Transaction.LockTime = new LockTime(1);
            fulfill.Transaction.Inputs[0].PrevOut = offerTransaction.Transaction.Outputs.AsCoins().First().Outpoint;
            txBuilder = new TransactionBuilder();
            txBuilder.Extensions.Add(new OfferBuilderExtension());
            txBuilder.AddKeys(server.GetInternalState().FulfillKey);
            txBuilder.AddCoins(offerTransaction.Transaction.Outputs.AsCoins().ToArray());
            txBuilder.SignTransactionInPlace(fulfill.Transaction);
            Assert.True(txBuilder.Verify(fulfill.Transaction));
            ////////////////////////////////////////////////

            client.CheckSolutions(fulfill.Transaction);
            RoundTrip(ref client, parameters);
            var solution = client.GetSolution();

            RoundTrip(ref client, parameters);
            Assert.True(solution == expectedSolution);
        }