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 }); }
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 }); }