Skip to content

Commit

Permalink
Add psbt version flag in txbuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolasDorier committed Feb 28, 2025
1 parent 29fb55c commit 7bbadd3
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 57 deletions.
2 changes: 1 addition & 1 deletion NBitcoin.Tests/Generators/PSBTGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ from locktime in PrimitiveGenerator.UInt32()
from TxsToAdd in Gen.SubListOf(prevTxs)
from CoinsToAdd in Gen.SubListOf(prevTxs.SelectMany(tx => tx.Outputs.AsCoins()))
from scriptsToAdd in Gen.SubListOf<Script>(scripts)
let psbt = tx.CreatePSBT(network)
let psbt = tx.CreatePSBT(network, PSBTVersion.PSBTv0)
.AddTransactions(prevTxs.ToArray())
.AddCoins(CoinsToAdd.ToArray())
.AddScripts(scriptsToAdd.ToArray())
Expand Down
2 changes: 1 addition & 1 deletion NBitcoin.Tests/PSBT2Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public void PSBT2_CreateAndSerialize()
tx.Outputs.Add(new TxOut(Money.Coins(1.0m), new Script()));

// Create a new PSBT2 from the unsinged transaction.
var psbt = Assert.IsType<PSBT2>(PSBT.FromTransaction(tx, Network.Main, true));
var psbt = Assert.IsType<PSBT2>(PSBT.FromTransaction(tx, Network.Main, PSBTVersion.PSBTv2));

// Act
string hex = psbt.ToHex();
Expand Down
10 changes: 6 additions & 4 deletions NBitcoin.Tests/PSBTTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,11 @@ public void CalculateFinalizedHashCorrectly()
Assert.False(psbt.TryGetFinalizedHash(out actualHash));
}

[Fact]
[Theory]
[InlineData(PSBTVersion.PSBTv2)]
[InlineData(PSBTVersion.PSBTv0)]
[Trait("UnitTest", "UnitTest")]
public void ShouldPreserveOriginalTxPropertyAsPossible()
public void ShouldPreserveOriginalTxPropertyAsPossible(PSBTVersion version)
{
var keys = new Key[] { new Key(), new Key(), new Key() }.Select(k => k.GetWif(Network.RegTest)).ToArray();
var redeem = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(3, keys.Select(k => k.PubKey).ToArray());
Expand All @@ -190,7 +192,7 @@ public void ShouldPreserveOriginalTxPropertyAsPossible()

// 2. with (unsigned) scriptSig and witness.
tx = CreateTxToSpendFunds(funds, keys, redeem, true, false);
var psbt = PSBT.FromTransaction(tx, Network.Main).AddCoins(funds);
var psbt = PSBT.FromTransaction(tx, Network.Main, version).AddCoins(funds);
Assert.Null(psbt.Inputs[0].FinalScriptSig); // it is not finalized since it is not signed
Assert.Null(psbt.Inputs[1].FinalScriptWitness); // This too
Assert.NotNull(psbt.Inputs[2].RedeemScript); // But it holds redeem script.
Expand All @@ -200,7 +202,7 @@ public void ShouldPreserveOriginalTxPropertyAsPossible()

// 3. with finalized scriptSig and witness
tx = CreateTxToSpendFunds(funds, keys, redeem, true, true);
psbt = PSBT.FromTransaction(tx, Network.Main)
psbt = PSBT.FromTransaction(tx, Network.Main, version)
.AddTransactions(funds)
.Finalize();

Expand Down
8 changes: 5 additions & 3 deletions NBitcoin.Tests/transaction_tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2274,9 +2274,11 @@ public void DoNotBuildTooBigTransaction()
Assert.Equal(new FeeRate(1.0m).SatoshiPerByte, rate.SatoshiPerByte, 1);
}

[Fact]
[Theory]
[InlineData(PSBTVersion.PSBTv0)]
[InlineData(PSBTVersion.PSBTv2)]
[Trait("UnitTest", "UnitTest")]
public void CanBuildTransaction()
public void CanBuildTransaction(PSBTVersion version)
{
var keys = Enumerable.Range(0, 5).Select(i => new Key()).ToArray();

Expand Down Expand Up @@ -2416,7 +2418,7 @@ public void CanBuildTransaction()
.SignTransaction(tx);
Assert.True(txBuilder.Verify(partiallySigned));

var partiallySignedPSBT = partiallySigned.CreatePSBT(txBuilder.Network);
var partiallySignedPSBT = partiallySigned.CreatePSBT(txBuilder.Network, version);
txBuilder.ExtractSignatures(partiallySignedPSBT, partiallySigned);
partiallySignedPSBT.AddCoins(allCoins);
partiallySignedPSBT.AddTransactions(fundingTx);
Expand Down
14 changes: 3 additions & 11 deletions NBitcoin/BIP174/PSBT0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Map = System.Collections.Generic.SortedDictionary<byte[], byte[]>;
Expand All @@ -22,9 +23,9 @@ internal PSBT0(Transaction transaction, Network network) : base(network, PSBTVer
Inputs = new PSBTInputList();
Outputs = new PSBTOutputList();
for (var i = 0; i < tx.Inputs.Count; i++)
Inputs.Add(CreatePSBTInput((uint)i, tx.Inputs[i]));
Inputs.Add(new PSBT0Input(this, (uint)i));
for (var i = 0; i < tx.Outputs.Count; i++)
Outputs.Add(CreatePSBTOutput((uint)i, tx.Outputs[i]));
Outputs.Add(new PSBTOutput(this, (uint)i, tx.Outputs[i]));
foreach (var input in tx.Inputs)
{
input.ScriptSig = Script.Empty;
Expand Down Expand Up @@ -164,13 +165,4 @@ protected override void SetSequenceCore(Sequence sequence)
txIn.Sequence = sequence;
}
}
protected override PSBTInput CreatePSBTInput(uint index, TxIn txIn)
{
return new PSBT0Input(this, index);
}

protected override PSBTOutput CreatePSBTOutput(uint index, TxOut txOut)
{
return new PSBTOutput(this, index, txOut);
}
}
16 changes: 8 additions & 8 deletions NBitcoin/BIP174/PartiallySignedTransaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,6 @@ internal ConsensusFactory GetConsensusFactory()

public Network Network { get; }

protected abstract PSBTInput CreatePSBTInput(uint index, TxIn txIn);

protected abstract PSBTOutput CreatePSBTOutput(uint index, TxOut txOut);

protected PSBT(Network network, PSBTVersion version)
{
if (network == null)
Expand Down Expand Up @@ -991,15 +987,19 @@ public bool Equals(PSBT? b)
}
public override int GetHashCode() => Utils.GetHashCode(this.ToBytes());

public static PSBT FromTransaction(Transaction transaction, Network network, bool v2 = false)
public static PSBT FromTransaction(Transaction transaction, Network network) => FromTransaction(transaction, network, PSBTVersion.PSBTv0);
public static PSBT FromTransaction(Transaction transaction, Network network, PSBTVersion version)
{
if (transaction == null)
throw new ArgumentNullException(nameof(transaction));
if (network == null)
throw new ArgumentNullException(nameof(network));
if (v2)
return new PSBT2(transaction, network);
return new PSBT0(transaction, network);
return version switch
{
PSBTVersion.PSBTv0 => new PSBT0(transaction, network),
PSBTVersion.PSBTv2 => new PSBT2(transaction, network),
_ => throw new NotSupportedException("Unsupported PSBT version")
};
}

public PSBT AddScripts(params Script[] redeems)
Expand Down
22 changes: 10 additions & 12 deletions NBitcoin/BIP370/PSBT2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Map = System.Collections.Generic.SortedDictionary<byte[], byte[]>;
namespace NBitcoin.BIP370;

public class PSBT2 : PSBT
{
internal PSBT2(Transaction transaction, Network network):base(network, PSBTVersion.PSBTv2)
internal PSBT2(Transaction tx, Network network):base(network, PSBTVersion.PSBTv2)
{
TransactionVersion = tx.Version;
Inputs = new PSBTInputList();
Outputs = new PSBTOutputList();
for (var i = 0; i < tx.Inputs.Count; i++)
Inputs.Add(new PSBT2Input(new(), this, (uint)i, tx.Inputs[i]));
for (var i = 0; i < tx.Outputs.Count; i++)
Outputs.Add(new PSBT2Output(new(), this, (uint)i, tx.Outputs[i]));
}

internal PSBT2(List<Map> maps, Network network) : base(network, PSBTVersion.PSBTv2)
Expand Down Expand Up @@ -107,7 +115,7 @@ internal PSBT2(List<Map> maps, Network network) : base(network, PSBTVersion.PSBT
sequence = seq;
}

var input = new PSBT2Input(map, this, (uint)(mapIndex - 1), outpoint)
var input = new PSBT2Input(map, this, (uint)(mapIndex - 1), new TxIn(outpoint))
{
Sequence = sequence
};
Expand Down Expand Up @@ -139,16 +147,6 @@ internal PSBT2(List<Map> maps, Network network) : base(network, PSBTVersion.PSBT
}
}

protected override PSBTInput CreatePSBTInput(uint index, TxIn txIn)
{
return new PSBT2Input(new (), this, index, txIn.PrevOut);
}

protected override PSBTOutput CreatePSBTOutput(uint index, TxOut txOut)
{
return new PSBT2Output(new(), this, index, txOut);
}

internal override Transaction GetGlobalTransaction(bool @unsafe)
{
var tx = Network.CreateTransaction();
Expand Down
7 changes: 5 additions & 2 deletions NBitcoin/BIP370/PSBT2Input.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ public LockTime? UnifiedTimeLock
}
}

internal PSBT2Input(SortedDictionary<byte[], byte[]> map, PSBT parent, uint index, OutPoint outPoint) : base(map, parent, index)
internal PSBT2Input(SortedDictionary<byte[], byte[]> map, PSBT parent, uint index, TxIn txIn) : base(map, parent, index)
{
this.PrevOut = outPoint;
this.PrevOut = txIn.PrevOut;
originalScriptSig = txIn.ScriptSig ?? Script.Empty;
originalWitScript = txIn.WitScript ?? WitScript.Empty;
}

public override OutPoint PrevOut { get; }
Expand Down Expand Up @@ -180,6 +182,7 @@ internal TxIn CreateTxIn()
{
var txin = Parent.Network.Consensus.ConsensusFactory.CreateTxIn();
txin.Sequence = Sequence ?? NBitcoin.Sequence.Final;
txin.PrevOut = PrevOut;
return txin;
}
}
5 changes: 3 additions & 2 deletions NBitcoin/Transaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1789,11 +1789,12 @@ public PrecomputedTransactionData PrecomputeTransactionData()
return new PrecomputedTransactionData(this);
}

public virtual PSBT CreatePSBT(Network network)
public PSBT CreatePSBT(Network network) => CreatePSBT(network, PSBTVersion.PSBTv0);
public virtual PSBT CreatePSBT(Network network, PSBTVersion version)
{
if (network == null)
throw new ArgumentNullException(nameof(network));
var psbt = PSBT.FromTransaction(this, network);
var psbt = PSBT.FromTransaction(this, network, version);
return psbt;
}

Expand Down
32 changes: 19 additions & 13 deletions NBitcoin/TransactionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,8 @@ public TransactionSigningContext(TransactionBuilder builder, PSBT psbt, SigningO
: this(builder, psbt, null, psbt.GetSigningOptions(signingOptions))
{
}
public TransactionSigningContext(TransactionBuilder builder, Transaction transaction, SigningOptions signingOptions)
: this(builder, transaction.CreatePSBT(builder.Network), transaction, signingOptions)
public TransactionSigningContext(TransactionBuilder builder, Transaction transaction, PSBTVersion psbtVersion, SigningOptions signingOptions)
: this(builder, transaction.CreatePSBT(builder.Network, psbtVersion), transaction, signingOptions)
{
}
public TransactionSigningContext(TransactionBuilder builder, PSBT psbt, Transaction? transaction, SigningOptions signingOptions)
Expand Down Expand Up @@ -1455,7 +1455,7 @@ public bool TrySignInput(Transaction transaction, uint index, [MaybeNullWhen(fal
{
if (transaction == null)
throw new ArgumentNullException(nameof(transaction));
var ctx = new TransactionSigningContext(this, transaction.Clone(), signingOptions);
var ctx = new TransactionSigningContext(this, transaction.Clone(), PSBTVersion.PSBTv0, signingOptions);
ctx.SignOnlyInputIndex = index;
this.SignTransactionContext(ctx);
var input = ctx.PSBT.Inputs[(int)index];
Expand Down Expand Up @@ -1592,31 +1592,37 @@ public TransactionBuilder SetCoinSelector(ICoinSelector selector)
/// Build a PSBT (Partially signed bitcoin transaction)
/// </summary>
/// <param name="sign">True if signs all inputs with the available keys</param>
/// <param name="sigHash">The sighash for signing (ignored if sign is false)</param>
/// <param name="version">The version of the PSBT to create</param>
/// <returns>A PSBT</returns>
/// <exception cref="NBitcoin.NotEnoughFundsException">Not enough funds are available</exception>
public PSBT BuildPSBT(bool sign)
public PSBT BuildPSBT(bool sign, PSBTVersion version)
{
var tx = BuildTransaction(false);
return CreatePSBTFromCore(tx, sign);
return CreatePSBTFromCore(tx, version, sign);
}
/// <inheritdoc cref="BuildPSBT(bool, PSBTVersion)"/>
public PSBT BuildPSBT(bool sign) => BuildPSBT(sign, PSBTVersion.PSBTv0);

/// <summary>
/// Create a PSBT from a transaction
/// </summary>
/// <param name="tx">The transaction</param>
/// <param name="version">The version of the PSBT to create</param>
/// <param name="sign">If true, the transaction builder will sign this transaction</param>
/// <returns></returns>
public PSBT CreatePSBTFrom(Transaction tx, bool sign)
public PSBT CreatePSBTFrom(Transaction tx, PSBTVersion version, bool sign)
{
if (tx == null)
throw new ArgumentNullException(nameof(tx));
return CreatePSBTFromCore(tx.Clone(), sign);
return CreatePSBTFromCore(tx.Clone(), version, sign);
}

PSBT CreatePSBTFromCore(Transaction tx, bool sign)
/// <inheritdoc cref="CreatePSBTFrom(Transaction, PSBTVersion, bool)"/>
public PSBT CreatePSBTFrom(Transaction tx, bool sign) => CreatePSBTFrom(tx, PSBTVersion.PSBTv0, sign);

PSBT CreatePSBTFromCore(Transaction tx, PSBTVersion version, bool sign)
{
TransactionSigningContext signingContext = new TransactionSigningContext(this, tx, signingOptions);
TransactionSigningContext signingContext = new TransactionSigningContext(this, tx, version, signingOptions);
if (sign)
SignTransactionContext(signingContext);
var psbt = signingContext.PSBT;
Expand Down Expand Up @@ -1684,7 +1690,7 @@ public TransactionBuilder ExtractSignatures(PSBT psbt, Transaction transaction)
throw new ArgumentNullException(nameof(psbt));
if (transaction is null)
throw new ArgumentNullException(nameof(transaction));
var signingContext = new TransactionSigningContext(this, transaction, signingOptions);
var signingContext = new TransactionSigningContext(this, transaction, psbt.Version, signingOptions);
ExtractExistingSignatures(signingContext);
psbt.Combine(signingContext.PSBT);
return this;
Expand Down Expand Up @@ -1978,7 +1984,7 @@ public Transaction SignTransaction(Transaction transaction)
/// <returns>The transaction object as the one passed as parameter</returns>
public Transaction SignTransactionInPlace(Transaction transaction)
{
var ctx = new TransactionSigningContext(this, transaction, signingOptions);
var ctx = new TransactionSigningContext(this, transaction, PSBTVersion.PSBTv0, signingOptions);
ExtractExistingSignatures(ctx);
SignTransactionContext(ctx);
FinalizeTransactionContext(ctx);
Expand Down Expand Up @@ -2657,7 +2663,7 @@ public TransactionBuilder AddKnownRedeems(params Script[] knownRedeems)

for (int i = 0; i < psbts.Length; i++)
{
var ctx = new TransactionSigningContext(this, transactions[i], signingOptions);
var ctx = new TransactionSigningContext(this, transactions[i], PSBTVersion.PSBTv0, signingOptions);
ExtractExistingSignatures(ctx);
psbts[i] = ctx.PSBT;
}
Expand Down

0 comments on commit 7bbadd3

Please sign in to comment.