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