public void CheckSolutions(Transaction fulfillTx)
        {
            if (fulfillTx == null)
            {
                throw new ArgumentNullException(nameof(fulfillTx));
            }

            AssertState(SolverClientStates.WaitingPuzzleSolutions);
            foreach (var input in fulfillTx.Inputs)
            {
                var solutions = SolverScriptBuilder.ExtractSolutions(input.ScriptSig, Parameters.RealPuzzleCount);
                if (solutions == null)
                {
                    continue;
                }
                try
                {
                    CheckSolutions(solutions);
                    return;
                }
                catch (PuzzleException)
                {
                }
            }
            throw new PuzzleException("Impossible to find solution to the puzzle");
        }
        public TrustedBroadcastRequest FulfillOffer(
            TransactionSignature clientSignature,
            Script cashout,
            FeeRate feeRate)
        {
            if (clientSignature == null)
            {
                throw new ArgumentNullException(nameof(clientSignature));
            }
            if (feeRate == null)
            {
                throw new ArgumentNullException(nameof(feeRate));
            }
            AssertState(SolverServerStates.WaitingFulfillment);
            Script offerScript = GetOfferScript();

            var offer = GetUnsignedOfferTransaction();

            PubKey clientKey = AssertValidSignature(clientSignature, offer);

            TransactionBuilder builder = new TransactionBuilder();

            builder.StandardTransactionPolicy.CheckFee = false;
            builder.Extensions.Add(new EscrowBuilderExtension());
            builder.AddCoins(InternalState.EscrowedCoin);
            builder.AddKeys(InternalState.EscrowKey);
            builder.AddKnownSignature(clientKey, clientSignature);
            builder.SignTransactionInPlace(offer);
            if (!builder.Verify(offer))
            {
                throw new PuzzleException("invalid-tumbler-signature");
            }
            var offerCoin = offer.Outputs.AsCoins().First().ToScriptCoin(offerScript);

            var         solutions = InternalState.SolvedPuzzles.Select(s => s.SolutionKey).ToArray();
            Transaction fulfill   = new Transaction();

            fulfill.Inputs.Add(new TxIn(offerCoin.Outpoint));
            fulfill.Outputs.Add(new TxOut(offerCoin.Amount, cashout));

            var fulfillScript = SolverScriptBuilder.CreateFulfillScript(NBitcoin.BuilderExtensions.BuilderExtension.DummySignature, solutions);

            fulfill.Inputs[0].ScriptSig = fulfillScript + Op.GetPushOp(offerCoin.Redeem.ToBytes());

            fulfill.Outputs[0].Value -= feeRate.GetFee(fulfill.GetVirtualSize());

            var signature = fulfill.Inputs.AsIndexedInputs().First().Sign(InternalState.FulfillKey, offerCoin, SigHash.All);

            fulfillScript = SolverScriptBuilder.CreateFulfillScript(signature, solutions);
            fulfill.Inputs[0].ScriptSig = fulfillScript + Op.GetPushOp(offerCoin.Redeem.ToBytes());

            InternalState.OfferClientSignature = clientSignature;
            InternalState.Status = SolverServerStates.WaitingEscape;
            return(new TrustedBroadcastRequest
            {
                Key = InternalState.FulfillKey,
                PreviousScriptPubKey = offerCoin.ScriptPubKey,
                Transaction = fulfill
            });
        }
Exemple #3
0
        public TrustedBroadcastRequest FulfillOffer(
            TransactionSignature clientSignature,
            Script cashout,
            FeeRate feeRate)
        {
            if (clientSignature == null)
            {
                throw new ArgumentNullException(nameof(clientSignature));
            }
            if (feeRate == null)
            {
                throw new ArgumentNullException(nameof(feeRate));
            }
            AssertState(SolverServerStates.WaitingFulfillment);

            var    offer     = GetUnsignedOfferTransaction();
            PubKey clientKey = AssertValidSignature(clientSignature, offer);

            offer.Inputs[0].ScriptSig = new Script(
                Op.GetPushOp(clientSignature.ToBytes()),
                Op.GetPushOp(CreateOfferSignature().ToBytes()),
                Op.GetPushOp(InternalState.EscrowedCoin.Redeem.ToBytes())
                );
            offer.Inputs[0].Witnessify();

            if (!offer.Inputs.AsIndexedInputs().First().VerifyScript(_Network, InternalState.EscrowedCoin))
            {
                throw new PuzzleException("invalid-tumbler-signature");
            }


            var         solutions = InternalState.SolvedPuzzles.Select(s => s.SolutionKey).ToArray();
            Transaction fulfill   = new Transaction();

            fulfill.Inputs.Add(new TxIn());
            fulfill.Outputs.Add(new TxOut(InternalState.OfferCoin.Amount, cashout));

            var fulfillScript = SolverScriptBuilder.CreateFulfillScript(null, solutions);

            fulfill.Inputs[0].ScriptSig = fulfillScript + Op.GetPushOp(InternalState.OfferCoin.Redeem.ToBytes());
            fulfill.Inputs[0].Witnessify();
            var virtualSize = fulfill.HasWitness ? fulfill.GetVirtualSize(_Network.Consensus.Options.WitnessScaleFactor) : fulfill.GetSerializedSize();

            fulfill.Outputs[0].Value -= feeRate.GetFee(virtualSize);

            InternalState.OfferClientSignature = clientSignature;
            InternalState.Status = SolverServerStates.WaitingEscape;
            return(new TrustedBroadcastRequest
            {
                Key = InternalState.FulfillKey,
                PreviousScriptPubKey = InternalState.OfferCoin.ScriptPubKey,
                Transaction = fulfill
            });
        }
        private Script GetOfferScript()
        {
            var escrow = EscrowScriptBuilder.ExtractEscrowScriptPubKeyParameters(InternalState.EscrowedCoin.Redeem);

            return(SolverScriptBuilder.CreateOfferScript(new OfferScriptPubKeyParameters
            {
                Hashes = InternalState.SolvedPuzzles.Select(p => p.SolutionKey.GetHash()).ToArray(),
                FulfillKey = InternalState.FulfillKey.PubKey,
                RedeemKey = escrow.RedeemKey,
                Expiration = escrow.LockTime
            }));
        }
        public override Script GenerateScriptSig(Script scriptPubKey, IKeyRepository keyRepo, ISigner signer)
        {
            var offer = SolverScriptBuilder.ExtractOfferScriptParameters(scriptPubKey);
            var key   = keyRepo.FindKey(offer.FulfillKey.ScriptPubKey) ?? keyRepo.FindKey(offer.RedeemKey.ScriptPubKey);

            if (key == null)
            {
                return(null);
            }
            var sig = signer.Sign(key);

            return(new Script(Op.GetPushOp(sig.ToBytes())));
        }
        public void CheckSolutions(Script scriptSig)
        {
            if (scriptSig == null)
            {
                throw new ArgumentNullException(nameof(scriptSig));
            }
            AssertState(SolverClientStates.WaitingPuzzleSolutions);
            var solutions = SolverScriptBuilder.ExtractSolutions(scriptSig, Parameters.RealPuzzleCount);

            if (solutions == null)
            {
                throw new PuzzleException("Impossible to find solution to the puzzle");
            }
            CheckSolutions(solutions);
        }
 private Script CreateOfferScript()
 {
     return(SolverScriptBuilder.CreateOfferScript(CreateOfferScriptParameters()));
 }
        public override int EstimateScriptSigSize(Script scriptPubKey)
        {
            var offer = SolverScriptBuilder.ExtractOfferScriptParameters(scriptPubKey);

            return(DummySignature.ToBytes().Length + offer.Hashes.Length * (int)SolutionKey.KeySize);
        }
 public override bool CanGenerateScriptSig(Script scriptPubKey)
 {
     return(SolverScriptBuilder.ExtractOfferScriptParameters(scriptPubKey) != null);
 }