Skip to content

Commit

Permalink
Do not crash on default sighash for taproot
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolasDorier committed May 20, 2024
1 parent afefcfc commit c713e79
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 34 deletions.
17 changes: 17 additions & 0 deletions NBitcoin.Tests/PSBTTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public static void ShouldCalculateBalanceOfHDKey()
}

[Fact]
[Trait("UnitTest", "UnitTest")]
public void PSBTParsingShouldUseTheRightConsensusFactory()
{
var psbt = PSBT.Parse("cHNidP8BAKABAAAAAjzOZOqTmzoqy7rQ1KHnIkaak0mSVzPv5DtvPTvCjAzKAAAAAAD9////RNxEkYoZ/DN8b5DqBHJyUlqw+YOPV2y//wY3S8Z/Ig4BAAAAAP3///8C3JQwAQAAAAAZdqkUwH39zhYQLTpA+yZ9SSHkq9y1rEGIrADh9QUAAAAAGXapFBv+ozFlGBoA7+st28C45qrqFdp8iKwAAAAAAAEA/XQBAgAAAAL+IKvJIVWjWY0mFK0l1+bcEV3NJ1DR9a6BVgxPopR1AwEAAABqRzBEAiBKMOFJ7341UHGSq2mMsu7Tuup/WrWcG3opc4qCizsZEQIgWfbIISu7W1iRKlXgAODKCKxu2pGuGcwsl2Ke2tXExJoBIQOZcYGogH0HL1t2R5WnhUBgEYChDJRerjr6LsZmZy7t0/7///+tNbQ5jJd/4ZXJszKPaLa/RRRoLxbfTFiO6UKdTyiVdAAAAABqRzBEAiBYvo6rVsaNEqtJcXttj0rg+hTjxGkAUlATmqr57SrWlwIgTaYBzvfyPooGjY3LN0NX71cAgZL+jBmAa6nOsrlUpbkBIQOZcYGogH0HL1t2R5WnhUBgEYChDJRerjr6LsZmZy7t0/7///8CcN/1BQAAAAAZdqkUsOguNJiSLxXVqfzkyYiFcJv0vgiIrADh9QUAAAAAGXapFK831Px/KVRlHDKICTeChruGMg/JiKzG0B8AIgICFN+otSuSoj/5zbp0H1MtD+edEx7WwLsvnrLEfbCjErRIMEUCIQD2yd+PvhvjpxmFcFDFv3owFleCr4IzRwHwTovk/S7y7wIgCWgmH6WHRuf0++LouAJuWKJ7pjPD4W53UXkz7WmtfvQBIgYCFN+otSuSoj/5zbp0H1MtD+edEx7WwLsvnrLEfbCjErQYChHi6CwAAIABAACAAAAAgAEAAAAKAAAAAAEA/XUBAQAAAALcjOdC7uRb/bqGnrU6Il6jcdZFeePGFZmkB4UNnwrfGgEAAABqRzBEAiB4ubLFsMIdUKl2SmGhNKdT1fTc46Ir4m2cFM9D8dtB7AIgL4cD2sSqPAdqPldtviV8dHqkjjrdXNrDkAbxjAZQzgABIQONBtYWZOSeDXT/eNAQoIcQYSwtwvkfse9m5wEgxkz4n/3////cjOdC7uRb/bqGnrU6Il6jcdZFeePGFZmkB4UNnwrfGgAAAABrSDBFAiEAndyxtsqQ+aB6s5FaGBhmQhOwhm35TOImMBEDF9jjV5oCIBg+0GQYIGvUWqXaGGxCDsngiWy5P0Tk+ngtuDCWzREpASEC55mD+vA5xSJmYvfcsYH5sykhFnsJdujFPhn2Fg+5HL39////AgDh9QUAAAAAGXapFKBSbBywL1pp4x1JqqX4jFKm2ZTdiKyEKDEBAAAAABl2qRS+dL66EpzsCxfpNVycmI5NNtKDWIisAAAAACICAkOieg7z1fAIG2cfcLj+ZFJ3L3L+yVk1tRApOHz8UwOQSDBFAiEA1bv9YiUDip8YfrBZjv76N783CQSzhj8ykdOQvpALOsECIHKAkrHCNhkF7hN6Eng11IJeqDgxEtZpFt0mGvP5xokKASIGAkOieg7z1fAIG2cfcLj+ZFJ3L3L+yVk1tRApOHz8UwOQGAoR4ugsAACAAQAAgAAAAIABAAAABwAAAAAiAgJbvS/OS/2Jnwd/aGbOJmqXrgL9YcYFarUm+ahIBAuwQBgKEeLoLAAAgAEAAIAAAACAAQAAAAsAAAAAIgID37dgjfw4pjjnV4nSdpZ4XTGqMYRLYeNuQaCD0YMtOOIYChHi6CwAAIABAACAAAAAgAAAAAAQAAAAAA==",
Expand All @@ -123,6 +124,22 @@ public void PSBTParsingShouldUseTheRightConsensusFactory()
psbt.ExtractTransaction();
}

[Fact]
[Trait("UnitTest", "UnitTest")]
public void CanParsePSBT()
{
// Taproot with sigHash 0
var psbt = PSBT.Parse("cHNidP8BAF4CAAAAAe3shDVZIXqXL+46CRSjwA9SVo3iBRYFn0x9SArAoHNfAAAAAAD9////AfpzDQAAAAAAIlEg0pT76srN3aUCY8U90B16YgiXAtrGt3bsYAMPxw0HEX4B9ioATwEENYfPA3kchbmAAAAAtS9Kup2TsHwvTkC0i1JPnc9axhJCYGtDFaWXp7cvjykCvmq8nXhJGL60ul0zP/G8YxcLCvtysjJ4UnYpgypWJ7oQTMf/ZVYAAIABAACAAAAAgAABAStAQg8AAAAAACJRIPO/0nlvtp0M9Skzx4npnrp/Gsl7hiT+N8Og6Hhp2VI9AQMEAAAAACEW8ODTatWV7S7MO5eVQ+CRiVSNjNl8r+GnnPRade0s738ZAEzH/2VWAACAAQAAgAAAAIAAAAAAAQAAAAEXIPDg02rVle0uzDuXlUPgkYlUjYzZfK/hp5z0WnXtLO9/ACEH9/OoRyEEtWoOFqufEuYWnZMt0wN1J6043WnlDj+/qTIZAEzH/2VWAACAAQAAgAAAAIAAAAAAAwAAAAEFIPfzqEchBLVqDharnxLmFp2TLdMDdSetON1p5Q4/v6kyAA==", Network.RegTest);
Assert.Equal(TaprootSigHash.Default, psbt.Inputs[0].TaprootSighashType);

// Signed with Taproot sighash 0 (keypath)
psbt = PSBT.Parse("cHNidP8BAF4CAAAAAe3shDVZIXqXL+46CRSjwA9SVo3iBRYFn0x9SArAoHNfAAAAAAD9////AfpzDQAAAAAAIlEg0pT76srN3aUCY8U90B16YgiXAtrGt3bsYAMPxw0HEX4B9ioATwEENYfPA3kchbmAAAAAtS9Kup2TsHwvTkC0i1JPnc9axhJCYGtDFaWXp7cvjykCvmq8nXhJGL60ul0zP/G8YxcLCvtysjJ4UnYpgypWJ7oQTMf/ZVYAAIABAACAAAAAgAABAStAQg8AAAAAACJRIPO/0nlvtp0M9Skzx4npnrp/Gsl7hiT+N8Og6Hhp2VI9AQMEAAAAAAETQO3wI7BGFR3JXKOKh48CyH7A27LwAG6xql4YOH4y04wbtCiqSyg8l5qJD4Op++BSa3xbq9YBzXvy3Prf00/KCOkhFvDg02rVle0uzDuXlUPgkYlUjYzZfK/hp5z0WnXtLO9/GQBMx/9lVgAAgAEAAIAAAACAAAAAAAEAAAABFyDw4NNq1ZXtLsw7l5VD4JGJVI2M2Xyv4aec9Fp17SzvfwABBSD386hHIQS1ag4Wq58S5hadky3TA3UnrTjdaeUOP7+pMiEH9/OoRyEEtWoOFqufEuYWnZMt0wN1J6043WnlDj+/qTIZAEzH/2VWAACAAQAAgAAAAIAAAAAAAwAAAAA=", Network.RegTest);
Assert.NotNull(psbt.Inputs[0].TaprootKeySignature);
#if HAS_SPAN
psbt.Finalize();
#endif
}

[Fact]
[Trait("UnitTest", "UnitTest")]
public static void ShouldParseValidDataDeterministically()
Expand Down
80 changes: 54 additions & 26 deletions NBitcoin/BIP174/PSBTInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using Newtonsoft.Json;
using System.IO;
using NBitcoin.DataEncoders;
using PartialSigKVMap = System.Collections.Generic.SortedDictionary<NBitcoin.PubKey, NBitcoin.TransactionSignature>;
using PartialSigKVMap = System.Collections.Generic.SortedDictionary<NBitcoin.PubKey, NBitcoin.ITransactionSignature>;
using System.Diagnostics.CodeAnalysis;
using NBitcoin.Crypto;
using System.Text;
Expand Down Expand Up @@ -98,9 +98,9 @@ internal PSBTInput(BitcoinStream stream, PSBT parent, uint index, TxIn input) :
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))
if (value is not (1 or 2 or 3 or 0 or 1 | 0x80 or 2 | 0x80 or 3 | 0x80 or 0 | 0x80))
throw new FormatException($"Invalid PSBTInput Unknown SigHash Type {value}");
sighash_type = (SigHash)value;
sighash_type = value;
break;
case PSBTConstants.PSBT_IN_REDEEMSCRIPT:
if (k.Length != 1)
Expand Down Expand Up @@ -216,7 +216,7 @@ public void SetSequence(ushort sequence)
private WitScript? final_script_witness;
private PartialSigKVMap partial_sigs = new PartialSigKVMap(PubKeyComparer.Instance);

SigHash? sighash_type;
uint? sighash_type;

public Transaction? NonWitnessUtxo
{
Expand Down Expand Up @@ -246,11 +246,25 @@ public SigHash? SighashType
{
get
{
return sighash_type;
if (sighash_type is null or 0)
return null;
return (SigHash)sighash_type;
}
set
{
sighash_type = value is null ? null : ((uint)value.Value);
}
}

public TaprootSigHash? TaprootSighashType
{
get
{
return sighash_type is null ? null : (TaprootSigHash)sighash_type;
}
set
{
sighash_type = value;
sighash_type = value is null ? null : ((uint)value.Value);
}
}

Expand Down Expand Up @@ -383,7 +397,7 @@ public void UpdateFrom(PSBTInput other)
if (witness_utxo == null && other.witness_utxo != null)
witness_utxo = other.witness_utxo;

if (sighash_type == 0 && other.sighash_type > 0)
if (sighash_type is null && other.sighash_type is not null)
sighash_type = other.sighash_type;

if (redeem_script == null && other.redeem_script != null)
Expand Down Expand Up @@ -675,12 +689,12 @@ public void Serialize(BitcoinStream stream)
}

// Write the sighash type
if (sighash_type > 0)
if (sighash_type is not null)
{
stream.ReadWriteAsVarInt(ref defaultKeyLen);
var key = PSBTConstants.PSBT_IN_SIGHASH;
stream.ReadWrite(ref key);
var tmp = Utils.ToBytes((uint)sighash_type, true);
var tmp = Utils.ToBytes(sighash_type.Value, true);
stream.ReadWriteAsVarString(ref tmp);
}

Expand Down Expand Up @@ -847,7 +861,7 @@ internal void Write(JsonTextWriter jsonWriter)
jsonWriter.WritePropertyValue(sig.Key.ToString(), Encoders.Hex.EncodeData(sig.Value.ToBytes()));
}
jsonWriter.WriteEndObject();
if (SighashType is SigHash s)
if (sighash_type is uint s)
jsonWriter.WritePropertyValue("sighash", GetName(s));
if (this.FinalScriptSig != null)
{
Expand Down Expand Up @@ -887,21 +901,25 @@ internal void Write(JsonTextWriter jsonWriter)
jsonWriter.WriteEndObject();
}

private string GetName(SigHash sighashType)
private string GetName(uint sighashType)
{
switch (sighashType)
{
case SigHash.All:
case 0:
return "DEFAULT";
case 1:
return "ALL";
case SigHash.None:
case 2:
return "NONE";
case SigHash.Single:
case 3:
return "SINGLE";
case SigHash.All | SigHash.AnyoneCanPay:
case 0x80:
return "DEFAULT|ANYONECANPAY";
case 1 | 0x80:
return "ALL|ANYONECANPAY";
case SigHash.None | SigHash.AnyoneCanPay:
case 2 | 0x80:
return "NONE|ANYONECANPAY";
case SigHash.Single | SigHash.AnyoneCanPay:
case 3 | 0x80:
return "SINGLE|ANYONECANPAY";
default:
return sighashType.ToString();
Expand Down Expand Up @@ -1069,12 +1087,22 @@ internal void Sign(KeyPair keyPair, SigningOptions? signingOptions)
signingOptions = Parent.GetSigningOptions(signingOptions);
if (keyPair.PubKey is PubKey ecdsapk && PartialSigs.TryGetValue(ecdsapk, out var existingSig))
{
CheckCompatibleSigHash(signingOptions.SigHash);
#if HAS_SPAN
var sigHash = keyPair is TaprootKeyPair ? (uint)signingOptions.TaprootSigHash : (uint)signingOptions.SigHash;
#else
var sigHash = (uint)signingOptions.SigHash;
#endif
CheckCompatibleSigHash(sigHash);
var signature = PartialSigs[ecdsapk];
var signatureSigHash = existingSig.SigHash;
#if HAS_SPAN
var existingSigHash = existingSig is TaprootSignature ts ? (uint)ts.SigHash :
(uint)((TransactionSignature)existingSig).SigHash;
#else
var existingSigHash = (uint)((TransactionSignature)existingSig).SigHash;
#endif
if (Transaction is IHasForkId)
signatureSigHash = (SigHash)((uint)existingSig.SigHash & ~(0x40u));
if (!SameSigHash(signatureSigHash, signingOptions.SigHash))
existingSigHash = existingSigHash & ~(0x40u);
if (!SameSigHash(existingSigHash, sigHash))
throw new InvalidOperationException("A signature with a different sighash is already in the partial sigs");
return;
}
Expand Down Expand Up @@ -1115,19 +1143,19 @@ internal void Sign(Key key, SigningOptions? signingOptions)
}
}

bool SameSigHash(SigHash a, SigHash b)
bool SameSigHash(uint a, uint b)
{
if (a == b)
return true;
if (Transaction is not IHasForkId)
return false;
a = (SigHash)((uint)a & ~(0x40u));
b = (SigHash)((uint)a & ~(0x40u));
a = ((uint)a & ~(0x40u));
b = ((uint)a & ~(0x40u));
return a == b;
}
private void CheckCompatibleSigHash(SigHash sigHash)
private void CheckCompatibleSigHash(uint sigHash)
{
if (SighashType is SigHash s && !SameSigHash(s,sigHash))
if (this.sighash_type is uint s && !SameSigHash(s,sigHash))
throw new InvalidOperationException($"The input assert the use of sighash {GetName(s)}");
}

Expand Down
6 changes: 3 additions & 3 deletions NBitcoin/BuilderExtensions/P2MultiSigBuilderExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public override void Finalize(InputSigningContext inputSigningContext)
var multiSigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey)!;
if (multiSigParams.SignatureCount > txIn.PartialSigs.Count)
return;
List<TransactionSignature> sigs = new List<TransactionSignature>();
List<ITransactionSignature> sigs = new List<ITransactionSignature>();
int sigcount = 0;
foreach (var pk in multiSigParams.PubKeys)
{
Expand Down Expand Up @@ -185,7 +185,7 @@ public override void MergePartialSignatures(InputSigningContext inputSigningCont
var txIn = inputSigningContext.Input;
var scriptPubKey = inputSigningContext.Coin.GetScriptCode();

var sigs = new TransactionSignature?[multiSigParams.PubKeys.Length];
var sigs = new ITransactionSignature?[multiSigParams.PubKeys.Length];
int sigCount = 0;
for (int i = 0; i < multiSigParams.PubKeys.Length; i++)
{
Expand All @@ -198,7 +198,7 @@ public override void MergePartialSignatures(InputSigningContext inputSigningCont
ops.Add(OpcodeType.OP_0);
for (int i = 0; i < multiSigParams.PubKeys.Length; i++)
{
if (sigs[i] is TransactionSignature sig)
if (sigs[i] is ITransactionSignature sig)
ops.Add(Op.GetPushOp(sig.ToBytes()));
else
ops.Add(OpcodeType.OP_0);
Expand Down
10 changes: 5 additions & 5 deletions NBitcoin/StandardScriptTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -380,12 +380,12 @@ protected override bool CheckScriptSigCore(Script scriptSig, Op[] scriptSigOps,
}
}

public Script GenerateScriptSig(TransactionSignature[] signatures)
public Script GenerateScriptSig(ITransactionSignature[] signatures)
{
return GenerateScriptSig((IEnumerable<TransactionSignature>)signatures);
return GenerateScriptSig((IEnumerable<ITransactionSignature>)signatures);
}

public Script GenerateScriptSig(IEnumerable<TransactionSignature> signatures)
public Script GenerateScriptSig(IEnumerable<ITransactionSignature> signatures)
{
List<Op> ops = new List<Op>();
ops.Add(OpcodeType.OP_0);
Expand Down Expand Up @@ -604,7 +604,7 @@ public Script GenerateScriptSig(ECDSASignature signature)
{
return GenerateScriptSig(new TransactionSignature(signature, SigHash.All));
}
public Script GenerateScriptSig(TransactionSignature signature)
public Script GenerateScriptSig(ITransactionSignature signature)
{
return new Script(
Op.GetPushOp(signature.ToBytes())
Expand Down Expand Up @@ -803,7 +803,7 @@ public Script GenerateScriptPubKey(KeyId pubkeyHash)
);
}

public Script GenerateScriptSig(TransactionSignature? signature, PubKey publicKey)
public Script GenerateScriptSig(ITransactionSignature? signature, PubKey publicKey)
{
if (publicKey == null)
throw new ArgumentNullException(nameof(publicKey));
Expand Down

0 comments on commit c713e79

Please sign in to comment.