Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions NBitcoin.Altcoins/Bitcoinplus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -338,11 +338,7 @@ private void DeserializeTxn(BitcoinStream stream, bool witSupported)
stream.ReadWrite(ref vinTemp);
vinTemp.Transaction = this;

var hasNoDummy = (nVersionTemp & NoDummyInput) != 0 && vinTemp.Count == 0;
if (witSupported && hasNoDummy)
nVersionTemp = nVersionTemp & ~NoDummyInput;

if (vinTemp.Count == 0 && witSupported && !hasNoDummy)
if (vinTemp.Count == 0 && witSupported)
{
/* We read a dummy or an empty vin. */
stream.ReadWrite(ref flags);
Expand Down Expand Up @@ -392,8 +388,7 @@ private void DeserializeTxn(BitcoinStream stream, bool witSupported)
private void SerializeTxn(BitcoinStream stream, bool witSupported)
{
byte flags = 0;
var version = (witSupported && (this.Inputs.Count == 0 && this.Outputs.Count > 0)) ? this.Version | NoDummyInput : this.Version;
stream.ReadWrite(ref version);
stream.ReadWrite(ref nVersion);

// POS Timestamp
var time = this.Time;
Expand Down
19 changes: 8 additions & 11 deletions NBitcoin.Altcoins/Litecoin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,7 @@ public override ConsensusFactory GetConsensusFactory()
public override void ReadWrite(BitcoinStream stream)
{
var witSupported = (((uint)stream.TransactionOptions & (uint)TransactionOptions.Witness) != 0) &&
stream.ProtocolCapabilities.SupportWitness;

//var mwebSupported = false; //when mweb is supported in nbitcoin this is to be fixed
stream.ProtocolCapabilities.SupportWitness;

byte flags = 0;
if (!stream.Serializing)
Expand All @@ -172,11 +170,8 @@ public override void ReadWrite(BitcoinStream stream)
/* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */
stream.ReadWrite(ref vin);
vin.Transaction = this;
var hasNoDummy = (nVersion & NoDummyInput) != 0 && vin.Count == 0;
if (witSupported && hasNoDummy)
nVersion = nVersion & ~NoDummyInput;

if (vin.Count == 0 && witSupported && !hasNoDummy)
if (vin.Count == 0 && witSupported)
{
/* We read a dummy or an empty vin. */
stream.ReadWrite(ref flags);
Expand Down Expand Up @@ -224,8 +219,10 @@ public override void ReadWrite(BitcoinStream stream)
}
else
{
var version = (witSupported && (vin.Count == 0 && vout.Count > 0)) ? nVersion | NoDummyInput : nVersion;
stream.ReadWrite(ref version);
if (Inputs.Count == 0 && !stream.AllowNoInputs)
throw new InvalidOperationException("The transaction must have at least one input");

stream.ReadWrite(ref nVersion);

if (witSupported)
{
Expand All @@ -238,8 +235,8 @@ public override void ReadWrite(BitcoinStream stream)
if (flags != 0)
{
/* Use extended format in case witnesses are to be serialized. */
TxInList vinDummy = new TxInList();
stream.ReadWrite(ref vinDummy);
byte marker = 0;
stream.ReadWrite(ref marker);
stream.ReadWrite(ref flags);
}
stream.ReadWrite(ref vin);
Expand Down
2 changes: 1 addition & 1 deletion NBitcoin.Altcoins/NBitcoin.Altcoins.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<TargetFrameworks Condition="'$(TargetFrameworkOverride)' != ''">$(TargetFrameworkOverride)</TargetFrameworks>
<NoWarn>1591;1573;1572;1584;1570;3021</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Version>3.0.28</Version>
<Version>3.0.28.1</Version>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\NBitcoin\NBitcoin.csproj" />
Expand Down
8 changes: 2 additions & 6 deletions NBitcoin.Altcoins/Stratis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -360,11 +360,8 @@ private void DeserializeTxn(BitcoinStream stream, bool witSupported)
// Try to read the vin. In case the dummy is there, this will be read as an empty vector.
stream.ReadWrite(ref vinTemp);
vinTemp.Transaction = this;
var hasNoDummy = (nVersionTemp & NoDummyInput) != 0 && vinTemp.Count == 0;
if (witSupported && hasNoDummy)
nVersionTemp = nVersionTemp & ~NoDummyInput;

if (vinTemp.Count == 0 && witSupported && !hasNoDummy)
if (vinTemp.Count == 0 && witSupported)
{
// We read a dummy or an empty vin.
stream.ReadWrite(ref flags);
Expand Down Expand Up @@ -414,8 +411,7 @@ private void DeserializeTxn(BitcoinStream stream, bool witSupported)
private void SerializeTxn(BitcoinStream stream, bool witSupported)
{
byte flags = 0;
var version = (witSupported && (this.Inputs.Count == 0 && this.Outputs.Count > 0)) ? this.Version | NoDummyInput : this.Version;
stream.ReadWrite(ref version);
stream.ReadWrite(ref nVersion);

// POS Timestamp
var time = this.Time;
Expand Down
2 changes: 1 addition & 1 deletion NBitcoin.TestFramework/NBitcoin.TestFramework.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<VersionPrefix>3.0.28</VersionPrefix>
<VersionPrefix>3.0.28.1</VersionPrefix>
<LangVersion>9.0</LangVersion>
<TargetFrameworks>netstandard1.6;net472;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition="'$(BuildingInsideVisualStudio)' == 'true'">netstandard2.1</TargetFrameworks>
Expand Down
4 changes: 3 additions & 1 deletion NBitcoin.Tests/ChainTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,9 @@ public ChainedBlock AppendBlock(ChainedBlock previous, params ConcurrentChain[]
var nonce = RandomUtils.GetUInt32();
foreach (var chain in chains)
{
var block = TestUtils.CreateFakeBlock(Network.Main.CreateTransaction());
var tx = Network.Main.CreateTransaction();
tx.Inputs.Add();
var block = TestUtils.CreateFakeBlock(tx);
block.Header.HashPrevBlock = previous == null ? chain.Tip.HashBlock : previous.HashBlock;
block.Header.Nonce = nonce;
if (!chain.TrySetTip(block.Header, out last))
Expand Down
1 change: 1 addition & 0 deletions NBitcoin.Tests/ColoredCoinsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ public void CanColorizeSpecScenario()
Assert.True(destroyed[0].Id == a2.Id);

var prior = Network.Main.CreateTransaction();
prior.Inputs.Add();
prior.Outputs.Add(new TxOut(dust, a1.ScriptPubKey));
prior.Outputs.Add(new TxOut(dust, a2.ScriptPubKey));
prior.Outputs.Add(new TxOut(dust, h.ScriptPubKey));
Expand Down
2 changes: 1 addition & 1 deletion NBitcoin.Tests/Generators/PSBTGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ from psbt in SanePSBT(network)
/// <param name="network"></param>
/// <returns></returns>
public static Gen<PSBT> SanePSBT(Network network) =>
from inputN in Gen.Choose(0, 8)
from inputN in Gen.Choose(1, 8)
from scripts in Gen.ListOf(inputN, ScriptGenerator.RandomScriptSig())
from txOuts in Gen.Sequence(scripts.Select(sc => OutputFromRedeem(sc)))
from prevN in Gen.Choose(0, 5)
Expand Down
15 changes: 14 additions & 1 deletion NBitcoin.Tests/PSBTTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using System.Linq;
using static NBitcoin.Tests.Comparer;
using Xunit.Abstractions;
using System.Net.Http;
using System.Threading.Tasks;

namespace NBitcoin.Tests
{
Expand Down Expand Up @@ -52,6 +54,7 @@ public static void ShouldCalculateBalanceOfHDKey()
var bob = bobMaster.Derive(new KeyPath("4/5/6"));

var funding = network.CreateTransaction();
funding.Inputs.Add();
funding.Outputs.Add(Money.Coins(1.0m), alice);
funding.Outputs.Add(Money.Coins(1.5m), bob);

Expand Down Expand Up @@ -153,6 +156,15 @@ public static void ShouldParseValidDataDeterministically()
Assert.Equal(psbt, psbt2, ComparerInstance);
}
}

[Fact]
[Trait("UnitTest", "UnitTest")]
public void FixVersionParsingBug()
{
var tx = Transaction.Parse("39a75f190001010000000000000000000000000000000000000000000000000000000000000000ffffffff4903d7ae0d04970a256861627a637862fabe6d6d86846e235af48afb776d0a32cb278930b7dd601fb0e05011789ef233a3e0e7d1010000000000000012b299f4e40000000000ffffffffffffffff02ec12bb1200000000160014b6f3cfc20084e3b9f0d12b0e6f9da8fcbcf5a2d90000000000000000266a24aa21a9edc8a9c6157fb538507480083f8f5144e2f73d1bd9b6b5fabde17bd08ff524b7100120000000000000000000000000000000000000000000000000000000000000000000000000", Network.Main);
Assert.Equal("889d4fed1ec4a775d02082b5ff727f48d93013469db709d8d435e15b173118f3", tx.GetHash().ToString());
}

[Fact]
[Trait("UnitTest", "UnitTest")]
public void AddCoinsShouldNotRemoveInfoFromPSBT()
Expand Down Expand Up @@ -594,8 +606,9 @@ public void CanRebaseKeypathInPSBT()
var accountExtKey = masterExtkey.Derive(new KeyPath("0'/0'/0'"));
var accountRootedKeyPath = new KeyPath("0'/0'/0'").ToRootedKeyPath(masterExtkey);
uint hardenedFlag = 0x80000000U;
retry:
retry:
Transaction funding = masterExtkey.Network.CreateTransaction();
funding.Inputs.Add();
funding.Outputs.Add(Money.Coins(2.0m), accountExtKey.Derive(0 | hardenedFlag).ScriptPubKey);
funding.Outputs.Add(Money.Coins(2.0m), accountExtKey.Derive(1 | hardenedFlag).ScriptPubKey);

Expand Down
4 changes: 3 additions & 1 deletion NBitcoin.Tests/ProtocolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1102,7 +1102,9 @@ public void CanConnectMultipleTimeToServer()
public void CanRoundtripCmpctBlock()
{
Block block = Network.Main.Consensus.ConsensusFactory.CreateBlock();
block.Transactions.Add(Network.Main.Consensus.ConsensusFactory.CreateTransaction());
var tx = Network.Main.Consensus.ConsensusFactory.CreateTransaction();
tx.Inputs.Add();
block.Transactions.Add(tx);
var cmpct = new CmpctBlockPayload(block);
cmpct.Clone();
}
Expand Down
4 changes: 3 additions & 1 deletion NBitcoin.Tests/TestUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ public static Block CreateFakeBlock(Transaction tx)

public static Block CreateFakeBlock()
{
var block = TestUtils.CreateFakeBlock(Network.Main.CreateTransaction());
var tx = Network.Main.CreateTransaction();
tx.Inputs.Add();
var block = TestUtils.CreateFakeBlock(tx);
block.Header.HashPrevBlock = new uint256(RandomUtils.GetBytes(32));
block.Header.Nonce = RandomUtils.GetUInt32();
return block;
Expand Down
3 changes: 3 additions & 0 deletions NBitcoin.Tests/script_tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ public void BIP65_tests()
private void BIP65_testsCore(LockTime target, LockTime now, bool expectedResult)
{
Transaction tx = Network.CreateTransaction();
tx.Inputs.Add();
tx.Outputs.Add(new TxOut()
{
ScriptPubKey = new Script(Op.GetPushOp(target.Value), OpcodeType.OP_CHECKLOCKTIMEVERIFY)
Expand Down Expand Up @@ -736,6 +737,7 @@ public void script_CHECKMULTISIG12()
);

Transaction txFrom12 = Network.CreateTransaction();
txFrom12.Inputs.Add();
txFrom12.Outputs.Add(new TxOut());
txFrom12.Outputs[0].ScriptPubKey = scriptPubKey12;

Expand Down Expand Up @@ -780,6 +782,7 @@ public void script_CHECKMULTISIG23()


var txFrom23 = Network.CreateTransaction();
txFrom23.Inputs.Add();
txFrom23.Outputs.Add(new TxOut());
txFrom23.Outputs[0].ScriptPubKey = scriptPubKey23;

Expand Down
25 changes: 8 additions & 17 deletions NBitcoin.Tests/transaction_tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,7 @@ public void CanBuildShuffleColoredTransaction()
var repo = new NoSqlColoredTransactionRepository(new NoSqlTransactionRepository(), new InMemoryNoSqlRepository());

var init = Network.CreateTransaction();
init.Inputs.Add();
init.Outputs.Add("1.0", gold.PubKey);
init.Outputs.Add("1.0", silver.PubKey);
init.Outputs.Add("1.0", satoshi.PubKey);
Expand Down Expand Up @@ -1278,6 +1279,7 @@ public void CanBuildColoredTransaction()
var repo = new NoSqlColoredTransactionRepository();

var init = Network.CreateTransaction();
init.Inputs.Add();
init.Outputs.Add("1.0", gold.PubKey);
init.Outputs.Add("1.0", silver.PubKey);
init.Outputs.Add("1.0", satoshi.PubKey);
Expand Down Expand Up @@ -1374,6 +1376,7 @@ public void CanBuildColoredTransaction()

//Gold receive 2.5 BTC
tx = txBuilder.Network.Consensus.ConsensusFactory.CreateTransaction();
tx.Inputs.Add();
tx.Outputs.Add("2.5", gold.PubKey);
repo.Transactions.Put(tx.GetHash(), tx);

Expand Down Expand Up @@ -1761,6 +1764,7 @@ public void CanEstimateFees()
builder.SendEstimatedFees(rate);
signed = builder.BuildTransaction(true);
Assert.True(builder.Verify(signed, estimatedFees));
Assert.Equal(1174, builder.EstimateSize(signed));
}

private Coin RandomCoin(Money amount, IDestination dest, bool p2sh)
Expand Down Expand Up @@ -1919,23 +1923,6 @@ void BitcoinStreamCoverageCore<TItem>(TItem[] input, BitcoinStreamCoverageCoreDe
AssertEx.CollectionEquals(before, input);
}

[Fact]
[Trait("UnitTest", "UnitTest")]
public void CanSerializeInvalidTransactionsBackAndForth()
{
Transaction before = Network.CreateTransaction();
var versionBefore = before.Version;
before.Outputs.Add(new TxOut());
Transaction after = AssertClone(before);
Assert.Equal(before.Version, after.Version);
Assert.Equal(versionBefore, after.Version);
Assert.True(after.Outputs.Count == 1);

before = Network.CreateTransaction();
after = AssertClone(before);
Assert.Equal(before.Version, versionBefore);
}

private Transaction AssertClone(Transaction before)
{
Transaction after = before.Clone();
Expand Down Expand Up @@ -2095,6 +2082,7 @@ public void CanFilterUneconomicalCoins()
var bob = new Key();
//P2SH(P2WSH)
var previousTx = Network.CreateTransaction();
previousTx.Inputs.Add();
previousTx.Outputs.Add(new TxOut(Money.Coins(1.0m), alice.PubKey.ScriptPubKey.WitHash.ScriptPubKey.Hash));
var previousCoin = previousTx.Outputs.AsCoins().First();

Expand Down Expand Up @@ -2643,6 +2631,7 @@ public void CanBuildTransactionWithDustPrevention()
var bob = new Key();
var alice = new Key();
var tx = Network.CreateTransaction();
tx.Inputs.Add();
tx.Outputs.Add(Money.Coins(1.0m), bob);
var coins = tx.Outputs.AsCoins().ToArray();

Expand Down Expand Up @@ -2837,6 +2826,7 @@ public void CanMutateSignature()
public void CanUseLockTime()
{
var tx = Network.CreateTransaction();
tx.Inputs.Add();
tx.LockTime = new LockTime(4);
var clone = tx.Clone();
Assert.Equal(tx.LockTime, clone.LockTime);
Expand Down Expand Up @@ -3067,6 +3057,7 @@ public void witnessHasPushSizeLimit()
{
var bob = new Key().GetWif(Network.RegTest);
Transaction tx = Network.CreateTransaction();
tx.Inputs.Add();
tx.Outputs.Add(new TxOut(Money.Coins(1.0m), bob.PubKey.ScriptPubKey.WitHash));
ScriptCoin coin = new ScriptCoin(tx.Outputs.AsCoins().First(), bob.PubKey.ScriptPubKey);

Expand Down
8 changes: 8 additions & 0 deletions NBitcoin/BitcoinStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ public void CopyParameters(BitcoinStream from)
IsBigEndian = from.IsBigEndian;
MaxArraySize = from.MaxArraySize;
Type = from.Type;
AllowNoInputs = from.AllowNoInputs;
}

public SerializationType Type
Expand Down Expand Up @@ -630,6 +631,13 @@ public System.Threading.CancellationToken ReadCancellationToken
set;
}

/// <summary>
/// Allows serialization of transactions with no inputs.
/// Such transactions are not valid for deserialization, but may still be useful,
/// for example, when computing a transaction hash or estimating size.
/// </summary>
public bool AllowNoInputs { get; set; }

public void ReadWriteAsVarInt(ref uint val)
{
if (Serializing)
Expand Down
2 changes: 2 additions & 0 deletions NBitcoin/IBitcoinSerializable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public static void ReadWrite(this IBitcoinSerializable serializable, Stream stre
public static int GetSerializedSize(this IBitcoinSerializable serializable, uint? version, SerializationType serializationType)
{
BitcoinStream s = new BitcoinStream(Stream.Null, true);
s.AllowNoInputs = true;
s.Type = serializationType;
s.ProtocolVersion = version;
s.ReadWrite(serializable);
Expand All @@ -40,6 +41,7 @@ public static int GetSerializedSize(this IBitcoinSerializable serializable, uint
public static int GetSerializedSize(this IBitcoinSerializable serializable, TransactionOptions options)
{
var bms = new BitcoinStream(Stream.Null, true);
bms.AllowNoInputs = true;
bms.TransactionOptions = options;
serializable.ReadWrite(bms);
return (int)bms.Counter.WrittenBytes;
Expand Down
2 changes: 1 addition & 1 deletion NBitcoin/NBitcoin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<PropertyGroup>
<Version Condition=" '$(Version)' == '' ">7.0.42.1</Version>
<Version Condition=" '$(Version)' == '' ">7.0.42.2</Version>
<LangVersion>9.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
Expand Down
8 changes: 6 additions & 2 deletions NBitcoin/RPC/RPCClient.Wallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,12 @@ private string ToHex(Transaction tx)
// if there is inputs, then it can't be confusing
if (tx.Inputs.Count > 0)
return tx.ToHex();
// if there is, do this ACK so that NBitcoin does not change the version number
return Encoders.Hex.EncodeData(tx.ToBytes(70012 - 1));

var ms = new MemoryStream();
BitcoinStream bs = new BitcoinStream(ms, true);
bs.AllowNoInputs = true;
tx.ReadWrite(bs);
return Encoders.Hex.EncodeData(ms.ToArrayEfficient());
}


Expand Down
Loading
Loading