public static Script CreateOfferScript(OfferScriptPubKeyParameters parameters) { if (parameters == null) { throw new ArgumentNullException(nameof(parameters)); } List <Op> ops = new List <Op>(); ops.Add(OpcodeType.OP_DEPTH); ops.Add(Op.GetPushOp(parameters.Hashes.Length + 1)); ops.Add(OpcodeType.OP_EQUAL); ops.Add(OpcodeType.OP_IF); foreach (var hash in parameters.Hashes) { ops.Add(OpcodeType.OP_RIPEMD160); ops.Add(Op.GetPushOp(hash.ToBytes())); ops.Add(OpcodeType.OP_EQUALVERIFY); } ops.Add(Op.GetPushOp(parameters.FulfillKey.ToBytes())); ops.Add(OpcodeType.OP_ELSE); ops.Add(Op.GetPushOp(parameters.Expiration)); ops.Add(OpcodeType.OP_CHECKLOCKTIMEVERIFY); ops.Add(OpcodeType.OP_DROP); ops.Add(Op.GetPushOp(parameters.RedeemKey.ToBytes())); ops.Add(OpcodeType.OP_ENDIF); ops.Add(OpcodeType.OP_CHECKSIG); return(new Script(ops.ToArray())); }
public TransactionSignature SignOffer(OfferInformation offerInformation) { if (offerInformation == null) { throw new ArgumentNullException(nameof(offerInformation)); } AssertState(SolverClientStates.WaitingOffer); var offerScript = new OfferScriptPubKeyParameters { Hashes = _PuzzleElements.OfType <RealPuzzle>().Select(p => p.Commitment.KeyHash).ToArray(), FulfillKey = offerInformation.FulfillKey, Expiration = EscrowScriptPubKeyParameters.GetFromCoin(InternalState.EscrowedCoin).LockTime, RedeemKey = InternalState.EscrowKey.PubKey }.ToScript(); var escrowCoin = InternalState.EscrowedCoin; var txOut = new TxOut(escrowCoin.Amount - offerInformation.Fee, offerScript.WitHash.ScriptPubKey.Hash); var offerCoin = new Coin(escrowCoin.Outpoint, txOut).ToScriptCoin(offerScript); Transaction tx = new Transaction(); tx.Inputs.Add(new TxIn(escrowCoin.Outpoint)); tx.Outputs.Add(offerCoin.TxOut); var escrow = EscrowScriptPubKeyParameters.GetFromCoin(escrowCoin); escrowCoin = escrowCoin.Clone(); escrowCoin.OverrideScriptCode(escrow.GetInitiatorScriptCode()); var signature = tx.Inputs.AsIndexedInputs().First().Sign(InternalState.EscrowKey, escrowCoin, SigHash.All); InternalState.OfferCoin = offerCoin; InternalState.Status = SolverClientStates.WaitingPuzzleSolutions; return(signature); }
public OfferInformation CheckBlindedFactors(BlindFactor[] blindFactors, FeeRate feeRate) { if (blindFactors == null) { throw new ArgumentNullException(nameof(blindFactors)); } if (blindFactors.Length != Parameters.RealPuzzleCount) { throw new ArgumentException("Expecting " + Parameters.RealPuzzleCount + " blind factors"); } AssertState(SolverServerStates.WaitingBlindFactor); Puzzle unblindedPuzzle = null; int y = 0; for (int i = 0; i < Parameters.RealPuzzleCount; i++) { var solvedPuzzle = InternalState.SolvedPuzzles[i]; var unblinded = new Puzzle(Parameters.ServerKey, solvedPuzzle.Puzzle).Unblind(blindFactors[i]); if (unblindedPuzzle == null) { unblindedPuzzle = unblinded; } else if (unblinded != unblindedPuzzle) { throw new PuzzleException("Invalid blind factor"); } y++; } InternalState.FulfillKey = new Key(); Transaction dummy = new Transaction(); dummy.AddInput(new TxIn(InternalState.EscrowedCoin.Outpoint)); dummy.Inputs[0].ScriptSig = new Script( Op.GetPushOp(TrustedBroadcastRequest.PlaceholderSignature), Op.GetPushOp(TrustedBroadcastRequest.PlaceholderSignature), Op.GetPushOp(InternalState.EscrowedCoin.Redeem.ToBytes()) ); dummy.Inputs[0].Witnessify(); dummy.AddOutput(new TxOut(InternalState.EscrowedCoin.Amount, new Key().ScriptPubKey.Hash)); var offerTransactionFee = feeRate.GetFee(dummy.GetVirtualSize()); var escrow = InternalState.EscrowedCoin; var escrowInformation = EscrowScriptPubKeyParameters.GetFromCoin(InternalState.EscrowedCoin); var redeem = new OfferScriptPubKeyParameters { Hashes = InternalState.SolvedPuzzles.Select(p => p.SolutionKey.GetHash()).ToArray(), FulfillKey = InternalState.FulfillKey.PubKey, Expiration = escrowInformation.LockTime, RedeemKey = escrowInformation.Initiator }.ToScript(); var txOut = new TxOut(escrow.Amount - offerTransactionFee, redeem.WitHash.ScriptPubKey.Hash); InternalState.OfferCoin = new Coin(escrow.Outpoint, txOut).ToScriptCoin(redeem); InternalState.Status = SolverServerStates.WaitingFulfillment; return(new OfferInformation { FulfillKey = InternalState.FulfillKey.PubKey, Fee = offerTransactionFee }); }
public static OfferScriptPubKeyParameters ExtractOfferScriptParameters(Script scriptPubKey) { if (scriptPubKey == null) { throw new ArgumentNullException(nameof(scriptPubKey)); } try { var result = new OfferScriptPubKeyParameters(); var ops = scriptPubKey.ToOps().ToArray(); int i = 0; if (ops[i++].Code != OpcodeType.OP_DEPTH) { return(null); } var pushCount = ops[i++].GetInt(); if (pushCount == null || pushCount > 500) { return(null); } result.Hashes = new uint160[pushCount.Value - 1]; CheckMinimal(ops[i - 1], pushCount.Value); if (ops[i++].Code != OpcodeType.OP_EQUAL) { return(null); } if (ops[i++].Code != OpcodeType.OP_IF) { return(null); } for (int y = 0; y < result.Hashes.Length; y++) { if (ops[i++].Code != OpcodeType.OP_RIPEMD160) { return(null); } result.Hashes[y] = new uint160(ops[i++].PushData); if (ops[i++].Code != OpcodeType.OP_EQUALVERIFY) { return(null); } } result.FulfillKey = new PubKey(ops[i++].PushData); if (ops[i++].Code != OpcodeType.OP_ELSE) { return(null); } result.Expiration = new LockTime(checked ((uint)ops[i++].GetLong())); CheckMinimal(ops[i - 1], (uint)result.Expiration); if (ops[i++].Code != OpcodeType.OP_CHECKLOCKTIMEVERIFY) { return(null); } if (ops[i++].Code != OpcodeType.OP_DROP) { return(null); } result.RedeemKey = new PubKey(ops[i++].PushData); if (ops[i++].Code != OpcodeType.OP_ENDIF) { return(null); } if (ops[i++].Code != OpcodeType.OP_CHECKSIG) { return(null); } if (i != ops.Length) { return(null); } return(result); } catch { return(null); } }