private long CountWitnessSigOps(Script scriptSig, Script scriptPubKey, WitScript witness, DeploymentFlags flags) { witness = witness ?? WitScript.Empty; if (!flags.ScriptFlags.HasFlag(ScriptVerify.Witness)) { return(0); } WitProgramParameters witParams = PayToWitTemplate.Instance.ExtractScriptPubKeyParameters2(scriptPubKey); if (witParams != null) { return(this.WitnessSigOps(witParams, witness, flags)); } if (scriptPubKey.IsPayToScriptHash && scriptSig.IsPushOnly) { byte[] data = scriptSig.ToOps().Select(o => o.PushData).LastOrDefault() ?? new byte[0]; Script subScript = Script.FromBytesUnsafe(data); witParams = PayToWitTemplate.Instance.ExtractScriptPubKeyParameters2(scriptPubKey); if (witParams != null) { return(this.WitnessSigOps(witParams, witness, flags)); } } return(0); }
/// <summary> /// Calculates signature operation cost for single transaction input. /// </summary> /// <param name="scriptPubKey">Script public key.</param> /// <param name="witness">Witness script.</param> /// <param name="flags">Script verification flags.</param> /// <returns>Signature operation cost for single transaction input.</returns> private long CountWitnessSignatureOperation(Script scriptPubKey, WitScript witness, DeploymentFlags flags) { witness = witness ?? WitScript.Empty; if (!flags.ScriptFlags.HasFlag(ScriptVerify.Witness)) { return(0); } WitProgramParameters witParams = PayToWitTemplate.Instance.ExtractScriptPubKeyParameters2(this.Parent.Network, scriptPubKey); if (witParams?.Version == 0) { if (witParams.Program.Length == 20) { return(1); } if (witParams.Program.Length == 32 && witness.PushCount > 0) { Script subscript = Script.FromBytesUnsafe(witness.GetUnsafePush(witness.PushCount - 1)); return(subscript.GetSigOpCount(true, this.Parent.Network)); } } return(0); }
internal TxOut orphanTxOut = null; // When this input is not segwit, but we don't have the previous tx // internal PSBTInput(PSBT parent, uint index, TxIn input) : base(parent) { TxIn = input; Index = index; originalScriptSig = TxIn.ScriptSig ?? Script.Empty; originalWitScript = TxIn.WitScript ?? WitScript.Empty; }
/// <summary> /// Adds a witness stack to the list of elements that will be used for building the filter. /// </summary> /// <param name="witScript">The witScript.</param> /// <returns>The updated filter builder instance.</returns> public void AddWitness(WitScript witScript) { if (witScript == null) { throw new ArgumentNullException(nameof(witScript)); } AddEntries(witScript.Pushes); }
public void ReadWrite(BitcoinStream bitcoinStream) { bitcoinStream.ReadWrite(ref _scriptSig); if (bitcoinStream.Serializing) { Witness.WriteToStream(bitcoinStream); } else { Witness = WitScript.Load(bitcoinStream); } }
public static TxDestination GetSigner(WitScript witScript) { if (witScript == WitScript.Empty) { return(null); } var parameters = PayToWitPubKeyHashTemplate.Instance.ExtractWitScriptParameters(witScript); if (parameters != null) { return(parameters.PublicKey.WitHash); } return(Script.FromBytesUnsafe(witScript.GetUnsafePush(witScript.PushCount - 1)).WitHash); }
private void UnSerialize(BitcoinStream stream) { var fAllowWitness = stream.TransactionOptions.HasFlag(TransactionOptions.Witness) && stream.ProtocolCapabilities.SupportWitness; // Detect dynamic federation block serialization using "HF bit", // or the signed bit which is invalid in Bitcoin var isDynamic = false; int version = 0; stream.ReadWrite(ref version); isDynamic = version < 0; this.nVersion = ~DYNAFED_HF_MASK & version; if (isDynamic) { stream.ReadWrite(ref hashPrevBlock); stream.ReadWrite(ref hashMerkleRoot); stream.ReadWrite(ref nTime); stream.ReadWrite(ref _nHeight); stream.ReadWrite(ref DynaFedParams); // We do not serialize witness for hashes, or weight calculation if (stream.Type != SerializationType.Hash && fAllowWitness) { SignBlockWitness = WitScript.Load(stream); } } else { stream.ReadWrite(ref hashPrevBlock); stream.ReadWrite(ref hashMerkleRoot); stream.ReadWrite(ref nTime); if (ElementsParams <TNetwork> .BlockHeightInHeader) { stream.ReadWrite(ref _nHeight); } if (ElementsParams <TNetwork> .SignedBlocks) { stream.ReadWrite(ref _Proof); } else { stream.ReadWrite(ref nBits); stream.ReadWrite(ref nNonce); } } }
/// <summary> /// Extract witness redeem from WitScript /// </summary> /// <param name="witScript">Witscript to extract information from</param> /// <param name="expectedScriptId">Expected redeem hash</param> /// <returns>The witness redeem</returns> public Script ExtractWitScriptParameters(WitScript witScript, WitScriptId expectedScriptId = null) { if (witScript.PushCount == 0) { return(null); } var last = witScript.GetUnsafePush(witScript.PushCount - 1); Script redeem = new Script(last); if (expectedScriptId != null) { if (expectedScriptId != redeem.WitHash) { return(null); } } return(redeem); }
private long WitnessSigOps(WitProgramParameters witParams, WitScript witScript, ConsensusFlags flags) { if (witParams.Version == 0) { if (witParams.Program.Length == 20) { return(1); } if (witParams.Program.Length == 32 && witScript.PushCount > 0) { Script subscript = Script.FromBytesUnsafe(witScript.GetUnsafePush(witScript.PushCount - 1)); return(subscript.GetSigOpCount(true)); } } // Future flags may be implemented here. return(0); }
public PayToWitPubkeyHashScriptSigParameters ExtractWitScriptParameters(WitScript witScript) { if (!CheckWitScriptCore(witScript)) { return(null); } try { return(new PayToWitPubkeyHashScriptSigParameters() { TransactionSignature = (witScript[0].Length == 1 && witScript[0][0] == 0) ? null : new TransactionSignature(witScript[0]), PublicKey = new PubKey(witScript[1], true), }); } catch (FormatException) { return(null); } }
internal void ReadWrite(BitcoinStream stream) { for (int i = 0; i < _Inputs.Count; i++) { if (stream.Serializing) { var bytes = (_Inputs[i].WitScript ?? WitScript.Empty).ToBytes(); stream.ReadWrite(ref bytes); } else { _Inputs[i].WitScript = WitScript.Load(stream); } } if (IsNull()) { throw new FormatException("Superfluous witness record"); } }
protected virtual byte[] SerializeOutputTransaction(Transaction tx) { using (var stream = new MemoryStream()) { var bs = new BitcoinStream(stream, true); // serialize outputs var vout = tx.Outputs; bs.ReadWrite <TxOutList, TxOut>(ref vout); // serialize witness (segwit) if (!string.IsNullOrEmpty(BlockTemplate.DefaultWitnessCommitment)) { var witScript = new WitScript(BlockTemplate.DefaultWitnessCommitment); var raw = witScript.ToBytes(); bs.ReadWrite(ref raw); } return(stream.ToArray()); } }
public Transaction ReSign(Coin coin, out bool cached) { var transaction = Transaction.Clone(); if (coin.Outpoint == SignedOutpoint) { transaction.Inputs[0].WitScript = Signature; transaction.Inputs[0].PrevOut = SignedOutpoint; cached = true; return(transaction); } transaction.Inputs[0].PrevOut = coin.Outpoint; //TODO: REMOVE THIS LATER hack so old tx do not crash client if (transaction.Inputs[0].WitScript.PushCount == 0) { transaction.Inputs[0].WitScript = Signature; transaction.Inputs[0].PrevOut = SignedOutpoint; cached = true; return(transaction); } var redeem = new Script(transaction.Inputs[0].WitScript.Pushes.Last()); var scriptCoin = coin.ToScriptCoin(redeem); byte[] signature = transaction.SignInput(Key, scriptCoin).ToBytes(); List <byte[]> resignedScriptSig = new List <byte[]>(); foreach (var op in transaction.Inputs[0].WitScript.Pushes) { resignedScriptSig.Add(IsPlaceholder(op) ? signature : op); } Signature = new WitScript(resignedScriptSig.ToArray()); SignedOutpoint = coin.Outpoint; transaction.Inputs[0].WitScript = Signature; cached = false; return(transaction); }
private bool CheckWitScriptCore(WitScript witScript) { return witScript.PushCount == 2 && ((witScript[0].Length == 1 && witScript[0][0] == 0) || (TransactionSignature.IsValid(witScript[0], ScriptVerify.None))) && PubKey.Check(witScript[1], false); }
public async Task <IActionResult> PostSignaturesAsync([FromQuery, Required] string uniqueId, [FromQuery, Required] long roundId, [FromBody, Required] IDictionary <int, string> signatures) { if (roundId < 0 || !signatures.Any() || signatures.Any(x => x.Key < 0 || string.IsNullOrWhiteSpace(x.Value)) || !ModelState.IsValid) { return(BadRequest()); } (CoordinatorRound round, Alice alice) = GetRunningRoundAndAliceOrFailureResponse(roundId, uniqueId, RoundPhase.Signing, out IActionResult returnFailureResponse); if (returnFailureResponse != null) { return(returnFailureResponse); } // Check if Alice provided signature to all her inputs. if (signatures.Count != alice.Inputs.Count()) { return(BadRequest("Alice did not provide enough witnesses.")); } RoundPhase phase = round.Phase; switch (phase) { case RoundPhase.Signing: { using (await SigningLock.LockAsync()) { foreach (var signaturePair in signatures) { int index = signaturePair.Key; WitScript witness = null; try { witness = new WitScript(signaturePair.Value); } catch (Exception ex) { return(BadRequest($"Malformed witness is provided. Details: {ex.Message}")); } int maxIndex = round.UnsignedCoinJoin.Inputs.Count - 1; if (maxIndex < index) { return(BadRequest($"Index out of range. Maximum value: {maxIndex}. Provided value: {index}")); } // Check duplicates. if (round.SignedCoinJoin.Inputs[index].HasWitScript()) { return(BadRequest("Input is already signed.")); } // Verify witness. // 1. Copy UnsignedCoinJoin. Transaction cjCopy = Transaction.Parse(round.UnsignedCoinJoin.ToHex(), Network); // 2. Sign the copy. cjCopy.Inputs[index].WitScript = witness; // 3. Convert the current input to IndexedTxIn. IndexedTxIn currentIndexedInput = cjCopy.Inputs.AsIndexedInputs().Skip(index).First(); // 4. Find the corresponding registered input. Coin registeredCoin = alice.Inputs.Single(x => x.Outpoint == cjCopy.Inputs[index].PrevOut); // 5. Verify if currentIndexedInput is correctly signed, if not, return the specific error. if (!currentIndexedInput.VerifyScript(registeredCoin, out ScriptError error)) { return(BadRequest($"Invalid witness is provided. {nameof(ScriptError)}: {error}.")); } // Finally add it to our CJ. round.SignedCoinJoin.Inputs[index].WitScript = witness; } alice.State = AliceState.SignedCoinJoin; await round.BroadcastCoinJoinIfFullySignedAsync(); } return(NoContent()); } default: { TryLogLateRequest(roundId, RoundPhase.Signing); return(Conflict($"CoinJoin can only be requested from Signing phase. Current phase: {phase}.")); } } }
/// <inheritdoc /> public virtual void ContextualCheckBlock(RuleContext context) { this.logger.LogTrace("()"); Block block = context.BlockValidationContext.Block; DeploymentFlags deploymentFlags = context.Flags; int height = context.BestBlock == null ? 0 : context.BestBlock.Height + 1; // Start enforcing BIP113 (Median Time Past) using versionbits logic. DateTimeOffset lockTimeCutoff = deploymentFlags.LockTimeFlags.HasFlag(Transaction.LockTimeFlags.MedianTimePast) ? context.BestBlock.MedianTimePast : block.Header.BlockTime; // Check that all transactions are finalized. foreach (Transaction transaction in block.Transactions) { if (!transaction.IsFinal(lockTimeCutoff, height)) { this.logger.LogTrace("(-)[TX_NON_FINAL]"); ConsensusErrors.BadTransactionNonFinal.Throw(); } } // Enforce rule that the coinbase starts with serialized block height. if (deploymentFlags.EnforceBIP34) { var expect = new Script(Op.GetPushOp(height)); Script actual = block.Transactions[0].Inputs[0].ScriptSig; if (!this.StartWith(actual.ToBytes(true), expect.ToBytes(true))) { this.logger.LogTrace("(-)[BAD_COINBASE_HEIGHT]"); ConsensusErrors.BadCoinbaseHeight.Throw(); } } // Validation for witness commitments. // * We compute the witness hash (which is the hash including witnesses) of all the block's transactions, except the // coinbase (where 0x0000....0000 is used instead). // * The coinbase scriptWitness is a stack of a single 32-byte vector, containing a witness nonce (unconstrained). // * We build a merkle tree with all those witness hashes as leaves (similar to the hashMerkleRoot in the block header). // * There must be at least one output whose scriptPubKey is a single 36-byte push, the first 4 bytes of which are // {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness nonce). In case there are // multiple, the last one is used. bool haveWitness = false; if (deploymentFlags.ScriptFlags.HasFlag(ScriptVerify.Witness)) { int commitpos = this.GetWitnessCommitmentIndex(block); if (commitpos != -1) { uint256 hashWitness = this.BlockWitnessMerkleRoot(block, out bool unused); // The malleation check is ignored; as the transaction tree itself // already does not permit it, it is impossible to trigger in the // witness tree. WitScript witness = block.Transactions[0].Inputs[0].WitScript; if ((witness.PushCount != 1) || (witness.Pushes.First().Length != 32)) { this.logger.LogTrace("(-)[BAD_WITNESS_NONCE_SIZE]"); ConsensusErrors.BadWitnessNonceSize.Throw(); } var hashed = new byte[64]; Buffer.BlockCopy(hashWitness.ToBytes(), 0, hashed, 0, 32); Buffer.BlockCopy(witness.Pushes.First(), 0, hashed, 32, 32); hashWitness = Hashes.Hash256(hashed); if (!this.EqualsArray(hashWitness.ToBytes(), block.Transactions[0].Outputs[commitpos].ScriptPubKey.ToBytes(true).Skip(6).ToArray(), 32)) { this.logger.LogTrace("(-)[WITNESS_MERKLE_MISMATCH]"); ConsensusErrors.BadWitnessMerkleMatch.Throw(); } haveWitness = true; } } if (!haveWitness) { for (int i = 0; i < block.Transactions.Count; i++) { if (block.Transactions[i].HasWitness) { this.logger.LogTrace("(-)[UNEXPECTED_WITNESS]"); ConsensusErrors.UnexpectedWitness.Throw(); } } } // After the coinbase witness nonce and commitment are verified, // we can check if the block weight passes (before we've checked the // coinbase witness, it would be possible for the weight to be too // large by filling up the coinbase witness, which doesn't change // the block hash, so we couldn't mark the block as permanently // failed). if (this.GetBlockWeight(block) > this.ConsensusOptions.MaxBlockWeight) { this.logger.LogTrace("(-)[BAD_BLOCK_WEIGHT]"); ConsensusErrors.BadBlockWeight.Throw(); } this.logger.LogTrace("(-)[OK]"); }
public Bip322Signature(Script scriptSig, WitScript witness) { _scriptSig = scriptSig; Witness = witness; }
internal PSBTInput(BitcoinStream stream, PSBT parent, uint index, TxIn input) : base(parent) { TxIn = input; Index = index; originalScriptSig = TxIn.ScriptSig ?? Script.Empty; originalWitScript = TxIn.WitScript ?? WitScript.Empty; byte[] k = new byte[0]; byte[] v = new byte[0]; try { stream.ReadWriteAsVarString(ref k); } catch (EndOfStreamException e) { throw new FormatException("Invalid PSBTInput. Failed to Parse key.", e); } while (k.Length != 0) { try { stream.ReadWriteAsVarString(ref v); } catch (EndOfStreamException e) { throw new FormatException("Invalid PSBTInput. Failed to parse key.", e); } switch (k.First()) { case PSBTConstants.PSBT_IN_NON_WITNESS_UTXO: if (k.Length != 1) { throw new FormatException("Invalid PSBTInput. Contains illegal value in key for NonWitnessUTXO"); } if (non_witness_utxo != null) { throw new FormatException("Invalid PSBTInput. Duplicate non_witness_utxo"); } non_witness_utxo = this.GetConsensusFactory().CreateTransaction(); non_witness_utxo.FromBytes(v); break; case PSBTConstants.PSBT_IN_WITNESS_UTXO: if (k.Length != 1) { throw new FormatException("Invalid PSBTInput. Contains illegal value in key for WitnessUTXO"); } if (witness_utxo != null) { throw new FormatException("Invalid PSBTInput. Duplicate witness_utxo"); } if (this.GetConsensusFactory().TryCreateNew <TxOut>(out var txout)) { witness_utxo = txout; } else { witness_utxo = new TxOut(); } witness_utxo.FromBytes(v); break; case PSBTConstants.PSBT_IN_PARTIAL_SIG: var pubkey = new PubKey(k.Skip(1).ToArray()); if (partial_sigs.ContainsKey(pubkey)) { throw new FormatException("Invalid PSBTInput. Duplicate key for partial_sigs"); } partial_sigs.Add(pubkey, new TransactionSignature(v)); break; case PSBTConstants.PSBT_IN_SIGHASH: if (k.Length != 1) { throw new FormatException("Invalid PSBTInput. Contains illegal value in key for SigHash type"); } if (!(sighash_type is null)) { throw new FormatException("Invalid PSBTInput. Duplicate key for sighash_type"); } if (v.Length != 4) { throw new FormatException("Invalid PSBTInput. SigHash Type is not 4 byte"); } var value = Utils.ToUInt32(v, 0, true); if (!Enum.IsDefined(typeof(SigHash), value)) { throw new FormatException($"Invalid PSBTInput Unknown SigHash Type {value}"); } sighash_type = (SigHash)value; break; case PSBTConstants.PSBT_IN_REDEEMSCRIPT: if (k.Length != 1) { throw new FormatException("Invalid PSBTInput. Contains illegal value in key for redeem script"); } if (redeem_script != null) { throw new FormatException("Invalid PSBTInput. Duplicate key for redeem_script"); } redeem_script = Script.FromBytesUnsafe(v); break; case PSBTConstants.PSBT_IN_WITNESSSCRIPT: if (k.Length != 1) { throw new FormatException("Invalid PSBTInput. Contains illegal value in key for witness script"); } if (witness_script != null) { throw new FormatException("Invalid PSBTInput. Duplicate key for redeem_script"); } witness_script = Script.FromBytesUnsafe(v); break; case PSBTConstants.PSBT_IN_BIP32_DERIVATION: var pubkey2 = new PubKey(k.Skip(1).ToArray()); if (hd_keypaths.ContainsKey(pubkey2)) { throw new FormatException("Invalid PSBTInput. Duplicate key for hd_keypaths"); } var masterFingerPrint = new HDFingerprint(v.Take(4).ToArray()); KeyPath path = KeyPath.FromBytes(v.Skip(4).ToArray()); hd_keypaths.Add(pubkey2, new RootedKeyPath(masterFingerPrint, path)); break; case PSBTConstants.PSBT_IN_SCRIPTSIG: if (k.Length != 1) { throw new FormatException("Invalid PSBTInput. Contains illegal value in key for final scriptsig"); } if (final_script_sig != null) { throw new FormatException("Invalid PSBTInput. Duplicate key for final_script_sig"); } final_script_sig = Script.FromBytesUnsafe(v); break; case PSBTConstants.PSBT_IN_SCRIPTWITNESS: if (k.Length != 1) { throw new FormatException("Invalid PSBTInput. Contains illegal value in key for final script witness"); } if (final_script_witness != null) { throw new FormatException("Invalid PSBTInput. Duplicate key for final_script_witness"); } final_script_witness = new WitScript(v); break; default: if (unknown.ContainsKey(k)) { throw new FormatException("Invalid PSBTInput. Duplicate key for unknown value"); } unknown.Add(k, v); break; } stream.ReadWriteAsVarString(ref k); } }
public async Task CanBuildTaprootSingleSigTransactions() { using (var nodeBuilder = NodeBuilderEx.Create()) { var rpc = nodeBuilder.CreateNode().CreateRPCClient(); nodeBuilder.StartAll(); rpc.Generate(102); var change = new Key(); var rootKey = new ExtKey(); var accountKeyPath = new KeyPath("86'/0'/0'"); var accountRootKeyPath = new RootedKeyPath(rootKey.GetPublicKey().GetHDFingerPrint(), accountKeyPath); var accountKey = rootKey.Derive(accountKeyPath); var key = accountKey.Derive(new KeyPath("0/0")).PrivateKey; var address = key.PubKey.GetAddress(ScriptPubKeyType.TaprootBIP86, nodeBuilder.Network); var destination = new Key(); var amount = new Money(1, MoneyUnit.BTC); uint256 id = null; Transaction tx = null; ICoin coin = null; TransactionBuilder builder = null; var rate = new FeeRate(Money.Satoshis(1), 1); async Task RefreshCoin() { id = await rpc.SendToAddressAsync(address, Money.Coins(1)); tx = await rpc.GetRawTransactionAsync(id); coin = tx.Outputs.AsCoins().Where(o => o.ScriptPubKey == address.ScriptPubKey).Single(); builder = Network.Main.CreateTransactionBuilder(0); } await RefreshCoin(); var signedTx = builder .AddCoins(coin) .AddKeys(key) .Send(destination, amount) .SubtractFees() .SetChange(change) .SendEstimatedFees(rate) .BuildTransaction(true); rpc.SendRawTransaction(signedTx); await RefreshCoin(); // Let's try again, but this time with PSBT var psbt = builder .AddCoins(coin) .Send(destination, amount) .SubtractFees() .SetChange(change) .SendEstimatedFees(rate) .BuildPSBT(false); var tk = key.PubKey.GetTaprootFullPubKey(); psbt.Inputs[0].HDTaprootKeyPaths.Add(tk.OutputKey, new TaprootKeyPath(accountRootKeyPath.Derive(KeyPath.Parse("0/0")))); psbt.SignAll(ScriptPubKeyType.TaprootBIP86, accountKey, accountRootKeyPath); // Check if we can roundtrip psbt = CanRoundtripPSBT(psbt); psbt.Finalize(); rpc.SendRawTransaction(psbt.ExtractTransaction()); // Let's try again, but this time with BuildPSBT(true) await RefreshCoin(); psbt = builder .AddCoins(coin) .AddKeys(key) .Send(destination, amount) .SubtractFees() .SetChange(change) .SendEstimatedFees(rate) .BuildPSBT(true); psbt.Finalize(); rpc.SendRawTransaction(psbt.ExtractTransaction()); // Let's try again, this time with a merkle root var merkleRoot = RandomUtils.GetUInt256(); address = key.PubKey.GetTaprootFullPubKey(merkleRoot).GetAddress(nodeBuilder.Network); await RefreshCoin(); psbt = builder .AddCoins(coin) .AddKeys(key.CreateTaprootKeyPair(merkleRoot)) .Send(destination, amount) .SubtractFees() .SetChange(change) .SendEstimatedFees(rate) .BuildPSBT(true); Assert.NotNull(psbt.Inputs[0].TaprootMerkleRoot); Assert.NotNull(psbt.Inputs[0].TaprootInternalKey); Assert.NotNull(psbt.Inputs[0].TaprootKeySignature); psbt = CanRoundtripPSBT(psbt); psbt.Finalize(); rpc.SendRawTransaction(psbt.ExtractTransaction()); // Can we sign the PSBT separately? await RefreshCoin(); psbt = builder .AddCoins(coin) .Send(destination, amount) .SubtractFees() .SetChange(change) .SendEstimatedFees(rate) .BuildPSBT(false); var taprootKeyPair = key.CreateTaprootKeyPair(merkleRoot); psbt.Inputs[0].Sign(taprootKeyPair); Assert.NotNull(psbt.Inputs[0].TaprootMerkleRoot); Assert.NotNull(psbt.Inputs[0].TaprootInternalKey); Assert.NotNull(psbt.Inputs[0].TaprootKeySignature); // This line is useless, we just use it to test the PSBT roundtrip psbt.Inputs[0].HDTaprootKeyPaths.Add(taprootKeyPair.PubKey, new TaprootKeyPath(RootedKeyPath.Parse("12345678/86'/0'/0'/0/0"), new uint256[] { RandomUtils.GetUInt256() })); psbt = CanRoundtripPSBT(psbt); psbt.Finalize(); rpc.SendRawTransaction(psbt.ExtractTransaction()); // Can we sign the transaction separately? await RefreshCoin(); var coin1 = coin; await RefreshCoin(); var coin2 = coin; builder = Network.Main.CreateTransactionBuilder(0); signedTx = builder .AddCoins(coin1, coin2) .Send(destination, amount) .SubtractFees() .SetChange(change) .SendEstimatedFees(rate) .BuildTransaction(false); var unsignedTx = signedTx.Clone(); builder = Network.Main.CreateTransactionBuilder(0); builder.AddKeys(key.CreateTaprootKeyPair(merkleRoot)); builder.AddCoins(coin1); var ex = Assert.Throws <InvalidOperationException>(() => builder.SignTransactionInPlace(signedTx)); Assert.Contains("taproot", ex.Message); builder.AddCoin(coin2); builder.SignTransactionInPlace(signedTx); Assert.True(!WitScript.IsNullOrEmpty(signedTx.Inputs.FindIndexedInput(coin2.Outpoint).WitScript)); // Another solution is to set the precomputed transaction data. signedTx = unsignedTx; builder = Network.Main.CreateTransactionBuilder(0); builder.AddKeys(key.CreateTaprootKeyPair(merkleRoot)); builder.AddCoins(coin2); builder.SetSigningOptions(new SigningOptions() { PrecomputedTransactionData = signedTx.PrecomputeTransactionData(new ICoin[] { coin1, coin2 }) }); builder.SignTransactionInPlace(signedTx); Assert.True(!WitScript.IsNullOrEmpty(signedTx.Inputs.FindIndexedInput(coin2.Outpoint).WitScript)); // Let's check if we estimate precisely the size of a taproot transaction. await RefreshCoin(); signedTx = builder .AddCoins(coin) .AddKeys(key.CreateTaprootKeyPair(merkleRoot)) .Send(destination, amount) .SubtractFees() .SetChange(change) .SendEstimatedFees(rate) .BuildTransaction(false); var actualvsize = builder.EstimateSize(signedTx, true); builder.SignTransactionInPlace(signedTx); var expectedvsize = signedTx.GetVirtualSize(); // The estimator can't assume the sighash to be default // for all inputs, so we likely overestimate 1 bytes per input Assert.Equal(expectedvsize, actualvsize - 1); } }
public async Task <IActionResult> PostSignaturesAsync([FromQuery] string uniqueId, [FromQuery] long roundId, [FromBody] IDictionary <int, string> signatures) { if (roundId <= 0 || signatures == null || !signatures.Any() || signatures.Any(x => x.Key < 0 || string.IsNullOrWhiteSpace(x.Value)) || !ModelState.IsValid) { return(BadRequest()); } (CcjRound round, Alice alice) = GetRunningRoundAndAliceOrFailureResponse(roundId, uniqueId, out IActionResult returnFailureResponse); if (returnFailureResponse != null) { return(returnFailureResponse); } // Check if Alice provided signature to all her inputs. if (signatures.Count != alice.Inputs.Count()) { return(BadRequest("Alice did not provide enough witnesses.")); } CcjRoundPhase phase = round.Phase; switch (phase) { case CcjRoundPhase.Signing: { using (await SigningLock.LockAsync()) { foreach (var signaturePair in signatures) { int index = signaturePair.Key; WitScript witness = null; try { witness = new WitScript(signaturePair.Value); } catch (Exception ex) { return(BadRequest($"Malformed witness is provided. Details: {ex.Message}")); } int maxIndex = round.UnsignedCoinJoin.Inputs.Count - 1; if (maxIndex < index) { return(BadRequest($"Index out of range. Maximum value: {maxIndex}. Provided value: {index}")); } // Check duplicates. if (round.SignedCoinJoin.Inputs[index].HasWitness()) { return(BadRequest($"Input is already signed.")); } // Verify witness. var cjCopy = Transaction.Parse(round.UnsignedCoinJoin.ToHex(), Network); cjCopy.Inputs[index].WitScript = witness; TxOut output = alice.Inputs.Single(x => x.OutPoint == cjCopy.Inputs[index].PrevOut).Output; if (!Script.VerifyScript(output.ScriptPubKey, cjCopy, index, output.Value, ScriptVerify.Standard, SigHash.All)) { return(BadRequest($"Invalid witness is provided.")); } // Finally add it to our CJ. round.SignedCoinJoin.Inputs[index].WitScript = witness; } alice.State = AliceState.SignedCoinJoin; await round.BroadcastCoinJoinIfFullySignedAsync(); } return(NoContent()); } default: { return(Conflict($"CoinJoin can only be requested from Signing phase. Current phase: {phase}.")); } } }
/// <inheritdoc /> /// <exception cref="ConsensusErrors.BadWitnessNonceSize">The witness nonce size is invalid.</exception> /// <exception cref="ConsensusErrors.BadWitnessMerkleMatch">The witness merkle commitment does not match the computed commitment.</exception> /// <exception cref="ConsensusErrors.UnexpectedWitness">The block does not expect witness transactions but contains a witness transaction.</exception> public override Task RunAsync(RuleContext context) { DeploymentFlags deploymentFlags = context.Flags; Block block = context.BlockValidationContext.Block; // Validation for witness commitments. // * We compute the witness hash (which is the hash including witnesses) of all the block's transactions, except the // coinbase (where 0x0000....0000 is used instead). // * The coinbase scriptWitness is a stack of a single 32-byte vector, containing a witness nonce (unconstrained). // * We build a merkle tree with all those witness hashes as leaves (similar to the hashMerkleRoot in the block header). // * There must be at least one output whose scriptPubKey is a single 36-byte push, the first 4 bytes of which are // {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness nonce). In case there are // multiple, the last one is used. bool fHaveWitness = false; if (deploymentFlags.ScriptFlags.HasFlag(ScriptVerify.Witness)) { int commitpos = this.GetWitnessCommitmentIndex(block); if (commitpos != -1) { uint256 hashWitness = BlockWitnessMerkleRoot(block, out bool malleated); // The malleation check is ignored; as the transaction tree itself // already does not permit it, it is impossible to trigger in the // witness tree. WitScript witness = block.Transactions[0].Inputs[0].WitScript; if ((witness.PushCount != 1) || (witness.Pushes.First().Length != 32)) { this.Logger.LogTrace("(-)[BAD_WITNESS_NONCE_SIZE]"); ConsensusErrors.BadWitnessNonceSize.Throw(); } byte[] hashed = new byte[64]; Buffer.BlockCopy(hashWitness.ToBytes(), 0, hashed, 0, 32); Buffer.BlockCopy(witness.Pushes.First(), 0, hashed, 32, 32); hashWitness = Hashes.Hash256(hashed); if (!this.EqualsArray(hashWitness.ToBytes(), block.Transactions[0].Outputs[commitpos].ScriptPubKey.ToBytes(true).Skip(6).ToArray(), 32)) { this.Logger.LogTrace("(-)[WITNESS_MERKLE_MISMATCH]"); ConsensusErrors.BadWitnessMerkleMatch.Throw(); } fHaveWitness = true; } } if (!fHaveWitness) { for (int i = 0; i < block.Transactions.Count; i++) { if (block.Transactions[i].HasWitness) { this.Logger.LogTrace("(-)[UNEXPECTED_WITNESS]"); ConsensusErrors.UnexpectedWitness.Throw(); } } } return(Task.CompletedTask); }
public static TxDestination GetSigner(WitScript witScript) { if(witScript == WitScript.Empty) return null; var parameters = PayToWitPubKeyHashTemplate.Instance.ExtractWitScriptParameters(witScript); if(parameters != null) return parameters.PublicKey.WitHash; return Script.FromBytesUnsafe(witScript.GetUnsafePush(witScript.PushCount - 1)).WitHash; }
private static TxIn AttachWitScript(TxIn txIn, WitScript wit) { txIn.WitScript = wit; return(txIn); }
public InputWitnessPair(OutPoint input, WitScript witness) { Input = input; Witness = witness; }
internal void ReadWrite(BitcoinStream stream) { for (int i = 0; i < _Inputs.Count; i++) { if (stream.Serializing) { var bytes = ((ElementsTxIn)_Inputs[i]).IssuanceAmountRangeProof; stream.ReadWriteAsVarString(ref bytes); bytes = ((ElementsTxIn)_Inputs[i]).InflationKeysRangeProof; stream.ReadWriteAsVarString(ref bytes); bytes = (_Inputs[i].WitScript ?? WitScript.Empty).ToBytes(); stream.ReadWrite(ref bytes); bytes = (((ElementsTxIn)_Inputs[i]).PeginWitScript ?? WitScript.Empty).ToBytes(); stream.ReadWrite(ref bytes); } else { byte[] bytes = null; stream.ReadWriteAsVarString(ref bytes); ((ElementsTxIn)_Inputs[i]).IssuanceAmountRangeProof = bytes; bytes = null; stream.ReadWriteAsVarString(ref bytes); ((ElementsTxIn)_Inputs[i]).InflationKeysRangeProof = bytes; ((ElementsTxIn)_Inputs[i]).WitScript = WitScript.Load(stream); ((ElementsTxIn)_Inputs[i]).PeginWitScript = WitScript.Load(stream); } } for (int i = 0; i < _Outputs.Count; i++) { if (stream.Serializing) { var bytes = ((ElementsTxOut)_Outputs[i]).SurjectionProof; stream.ReadWriteAsVarString(ref bytes); bytes = ((ElementsTxOut)_Outputs[i]).RangeProof; stream.ReadWriteAsVarString(ref bytes); } else { byte[] bytes = null; stream.ReadWriteAsVarString(ref bytes); ((ElementsTxOut)_Outputs[i]).SurjectionProof = bytes; bytes = null; stream.ReadWriteAsVarString(ref bytes); ((ElementsTxOut)_Outputs[i]).RangeProof = bytes; } } if (IsNull()) { throw new FormatException("Superfluous witness record"); } }
internal void Combine(PSBTInput other) { if (this.IsFinalized()) { return; } foreach (var uk in other.unknown) { unknown.TryAdd(uk.Key, uk.Value); } if (other.final_script_sig != null) { final_script_sig = other.final_script_sig; } if (other.final_script_witness != null) { final_script_witness = other.final_script_witness; } if (IsFinalized()) { ClearForFinalize(); return; } if (non_witness_utxo == null && other.non_witness_utxo != null) { non_witness_utxo = other.non_witness_utxo; } if (witness_utxo == null && other.witness_utxo != null) { non_witness_utxo = other.non_witness_utxo; } if (sighash_type == 0 && other.sighash_type > 0) { sighash_type = other.sighash_type; } if (redeem_script == null && other.redeem_script != null) { redeem_script = other.redeem_script; } if (witness_script == null && other.witness_script != null) { witness_script = other.witness_script; } foreach (var sig in other.partial_sigs) { partial_sigs.TryAdd(sig.Key, sig.Value); } foreach (var keyPath in other.hd_keypaths) { hd_keypaths.TryAdd(keyPath.Key, keyPath.Value); } }
private bool CheckWitScriptCore(WitScript witScript) { return(witScript.PushCount == 2 && ((witScript[0].Length == 1 && witScript[0][0] == 0) || (TransactionSignature.IsValid(witScript[0], ScriptVerify.None))) && PubKey.Check(witScript[1], false)); }
public override void ExtractExistingSignatures(InputSigningContext inputSigningContext) { if (inputSigningContext.OriginalTxIn is null || inputSigningContext.TransactionContext.Transaction is null) { return; } var scriptSig = inputSigningContext.OriginalTxIn.ScriptSig; var witScript = inputSigningContext.OriginalTxIn.WitScript; var multiSigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(inputSigningContext.Coin.GetScriptCode()); var txIn = inputSigningContext.Input; var scriptPubKey = inputSigningContext.Coin.GetScriptCode(); bool hasRedeem; List <byte[]> sigs; if (inputSigningContext.Coin is ScriptCoin scriptCoin) { hasRedeem = true; if (scriptCoin.RedeemType == RedeemType.P2SH) { if (Script.IsNullOrEmpty(inputSigningContext.OriginalTxIn.ScriptSig)) { return; } sigs = scriptSig.ToOps().Select(s => s.PushData).ToList(); } else // if (scriptCoin.RedeemType == RedeemType.WitnessV0) { if (WitScript.IsNullOrEmpty(inputSigningContext.OriginalTxIn.WitScript)) { return; } sigs = witScript.Pushes.ToList(); } } else { hasRedeem = false; sigs = scriptSig.ToOps().Select(s => s.PushData).ToList(); } // At least leading 0, pk count and the redeem if (sigs.Count < 2 + (hasRedeem ? 1 : 0)) { return; } if (!(sigs[0]?.Length is 0)) { return; } sigs.RemoveAt(0); // Remove leading 0 if (hasRedeem) { sigs.RemoveAt(sigs.Count - 1); // Remove the redeem } int pkIndex = 0; for (int i = 0; i < sigs.Count && pkIndex < multiSigParams.PubKeys.Length; i++) { var sig = sigs[i]; var pk = multiSigParams.PubKeys[pkIndex]; if (sig.Length is 0) { pkIndex++; } else { try { var txsig = new TransactionSignature(sig); var hash = inputSigningContext.TransactionContext .Transaction .Inputs.FindIndexedInput(inputSigningContext.Coin.Outpoint) .GetSignatureHash(inputSigningContext.Coin, txsig.SigHash, inputSigningContext.TransactionContext.SigningOptions.PrecomputedTransactionData); while (!pk.Verify(hash, txsig.Signature)) { pkIndex++; if (pkIndex >= multiSigParams.PubKeys.Length) { goto end; } pk = multiSigParams.PubKeys[pkIndex]; } txIn.PartialSigs.TryAdd(pk, txsig); pkIndex++; } catch { } } } end:; }
public PayToWitPubkeyHashScriptSigParameters ExtractWitScriptParameters(WitScript witScript) { if(!CheckWitScriptCore(witScript)) return null; try { return new PayToWitPubkeyHashScriptSigParameters() { TransactionSignature = (witScript[0].Length == 1 && witScript[0][0] == 0) ? null : new TransactionSignature(witScript[0]), PublicKey = new PubKey(witScript[1], true), }; } catch(FormatException) { return null; } }
public async Task <IActionResult> PostSignaturesAsync([FromQuery] string uniqueId, [FromQuery] long roundId, [FromBody] IDictionary <int, string> signatures) { if (roundId <= 0 || signatures == null || signatures.Count() <= 0 || signatures.Any(x => x.Key < 0 || string.IsNullOrWhiteSpace(x.Value)) || !ModelState.IsValid) { return(BadRequest()); } Guid uniqueIdGuid = CheckUniqueId(uniqueId, out IActionResult returnFailureResponse); if (returnFailureResponse != null) { return(returnFailureResponse); } CcjRound round = Coordinator.TryGetRound(roundId); if (round == null) { return(NotFound("Round not found.")); } Alice alice = round.TryGetAliceBy(uniqueIdGuid); if (alice == null) { return(NotFound("Alice not found.")); } // Check if Alice provided signature to all her inputs. if (signatures.Count != alice.Inputs.Count()) { return(BadRequest("Alice did not provide enough witnesses.")); } if (round.Status != CcjRoundStatus.Running) { return(Gone("Round is not running.")); } CcjRoundPhase phase = round.Phase; switch (phase) { case CcjRoundPhase.Signing: { using (await SigningLock.LockAsync()) { foreach (var signaturePair in signatures) { int index = signaturePair.Key; WitScript witness = null; try { witness = new WitScript(signaturePair.Value); } catch (Exception ex) { return(BadRequest($"Malformed witness is provided. Details: {ex.Message}")); } int maxIndex = round.UnsignedCoinJoin.Inputs.Count - 1; if (maxIndex < index) { return(BadRequest($"Index out of range. Maximum value: {maxIndex}. Provided value: {index}")); } // Check duplicates. if (!string.IsNullOrWhiteSpace(round.SignedCoinJoin.Inputs[index].WitScript?.ToString())) // Not sure why WitScript?.ToString() is needed, there was something wrong in previous HiddenWallet version if I didn't do this. { return(BadRequest($"Input is already signed.")); } // Verify witness. var cjCopy = new Transaction(round.UnsignedCoinJoin.ToHex()); cjCopy.Inputs[index].WitScript = witness; TxOut output = alice.Inputs.Single(x => x.OutPoint == cjCopy.Inputs[index].PrevOut).Output; if (!Script.VerifyScript(output.ScriptPubKey, cjCopy, index, output.Value, ScriptVerify.Standard, SigHash.All)) { return(BadRequest($"Invalid witness is provided.")); } // Finally add it to our CJ. round.SignedCoinJoin.Inputs[index].WitScript = witness; } alice.State = AliceState.SignedCoinJoin; await round.BroadcastCoinJoinIfFullySignedAsync(); } return(NoContent()); } default: { return(Conflict($"CoinJoin can only be requested from Signing phase. Current phase: {phase}.")); } } }
/// <summary> /// Extract witness redeem from WitScript /// </summary> /// <param name="witScript">Witscript to extract information from</param> /// <param name="expectedScriptId">Expected redeem hash</param> /// <returns>The witness redeem</returns> public Script ExtractWitScriptParameters(WitScript witScript, WitScriptId expectedScriptId = null) { if(witScript.PushCount == 0) return null; var last = witScript.GetUnsafePush(witScript.PushCount - 1); Script redeem = new Script(last); if(expectedScriptId != null) { if(expectedScriptId != redeem.WitHash) return null; } return redeem; }
public override void ReadWrite(BitcoinStream stream) { var fAllowWitness = stream.TransactionOptions.HasFlag(TransactionOptions.Witness) && stream.ProtocolCapabilities.SupportWitness; // Detect dynamic federation block serialization using "HF bit", // or the signed bit which is invalid in Bitcoin var isDynamic = false; int version = 0; if (!stream.Serializing) { stream.ReadWrite(ref version); isDynamic = version < 0; nVersion = ~DYNAFED_HF_MASK & version; } else { version = nVersion; if (!DynaFedParams.IsNull) { version |= DYNAFED_HF_MASK; isDynamic = true; } stream.ReadWrite(ref version); } if (isDynamic) { stream.ReadWrite(ref hashPrevBlock); stream.ReadWrite(ref hashMerkleRoot); stream.ReadWrite(ref nTime); stream.ReadWrite(ref _nHeight); stream.ReadWrite(ref DynaFedParams); if (stream.Type != SerializationType.Hash && fAllowWitness) { if (stream.Serializing) { SignBlockWitness.WriteToStream(stream); } else { SignBlockWitness = WitScript.Load(stream); } } stream.ReadWrite(ref hashMerkleRoot); stream.ReadWrite(ref nTime); if (ElementsParams <TNetwork> .BlockHeightInHeader) { stream.ReadWrite(ref _nHeight); } if (ElementsParams <TNetwork> .SignedBlocks) { stream.ReadWrite(ref _Proof); } else { stream.ReadWrite(ref nBits); stream.ReadWrite(ref nNonce); } } else { stream.ReadWrite(ref hashPrevBlock); stream.ReadWrite(ref hashMerkleRoot); stream.ReadWrite(ref nTime); stream.ReadWrite(ref _nHeight); stream.ReadWrite(ref _Proof); } }
/// <summary> /// Whether transaction is witness standard. /// <seealso cref="https://github.com/bitcoin/bitcoin/blob/aa624b61c928295c27ffbb4d27be582f5aa31b56/src/policy/policy.cpp#L196"/> /// </summary> /// <param name="tx">Transaction to verify.</param> /// <param name="mapInputs">Map of previous transactions that have outputs we're spending.</param> /// <returns>Whether transaction is witness standard.</returns> private bool IsWitnessStandard(Transaction tx, MempoolCoinView mapInputs) { if (tx.IsCoinBase) { this.logger.LogTrace("(-)[IS_COINBASE]:true"); return(true); // Coinbases are skipped. } foreach (TxIn input in tx.Inputs) { // We don't care if witness for this input is empty, since it must not be bloated. // If the script is invalid without witness, it would be caught sooner or later during validation. if (input.WitScriptEmpty) { continue; } TxOut prev = mapInputs.GetOutputFor(input); // Get the scriptPubKey corresponding to this input. Script prevScript = prev.ScriptPubKey; if (prevScript.IsPayToScriptHash(this.network)) { // If the scriptPubKey is P2SH, we try to extract the redeemScript casually by converting the scriptSig // into a stack. We do not check IsPushOnly nor compare the hash as these will be done later anyway. // If the check fails at this stage, we know that this txid must be a bad one. PayToScriptHashSigParameters sigParams = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(this.network, input.ScriptSig); if (sigParams == null || sigParams.RedeemScript == null) { this.logger.LogTrace("(-)[BAD_TXID]:false"); return(false); } prevScript = sigParams.RedeemScript; } // Non-witness program must not be associated with any witness. if (!prevScript.IsWitness(this.network)) { this.logger.LogTrace("(-)[WITNESS_MISMATCH]:false"); return(false); } // Check P2WSH standard limits. WitProgramParameters wit = PayToWitTemplate.Instance.ExtractScriptPubKeyParameters2(this.chainIndexer.Network, prevScript); if (wit == null) { this.logger.LogTrace("(-)[BAD_WITNESS_PARAMS]:false"); return(false); } // Version 0 segregated witness program validation. if (wit.Version == 0 && wit.Program.Length == WitnessV0ScriptHashSize) { WitScript witness = input.WitScript; // Get P2WSH script from top of stack. Script scriptPubKey = Script.FromBytesUnsafe(witness.GetUnsafePush(witness.PushCount - 1)); // Stack items are remainder of stack. int sizeWitnessStack = witness.PushCount - 1; // Get the witness stack items. var stack = new List <byte[]>(); for (int i = 0; i < sizeWitnessStack; i++) { stack.Add(witness.GetUnsafePush(i)); } // Validate P2WSH script isn't larger than max length. if (scriptPubKey.ToBytes(true).Length > MaxStandardP2wshScriptSize) { this.logger.LogTrace("(-)[P2WSH_SCRIPT_SIZE]:false"); return(false); } // Validate number items in witness stack isn't larger than max. if (sizeWitnessStack > MaxStandardP2wshStackItems) { this.logger.LogTrace("(-)[P2WSH_STACK_ITEMS]:false"); return(false); } // Validate size of each of the witness stack items. for (int j = 0; j < sizeWitnessStack; j++) { if (stack[j].Length > MaxStandardP2wshStackItemSize) { this.logger.LogTrace("(-)[P2WSH_STACK_ITEM_SIZE]:false"); return(false); } } } } return(true); }
public InputWitnessPair(uint inputIndex, WitScript witness) { InputIndex = inputIndex; Witness = witness; }