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
3 changes: 3 additions & 0 deletions NBitcoin.Altcoins/Litecoin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ public override void ReadWrite(BitcoinStream stream)
}
else
{
if (Inputs.Count == 0 && !stream.AllowNoInputs)
throw new InvalidOperationException("The transaction must have at least one input");

stream.ReadWrite(ref nVersion);

if (witSupported)
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
4 changes: 3 additions & 1 deletion NBitcoin.Tests/PSBTTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public static void ShouldCalculateBalanceOfHDKey(PSBTVersion version)
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 @@ -642,8 +643,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);
#if HAS_SPAN
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 @@ -988,6 +988,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 @@ -1279,6 +1280,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 @@ -1375,6 +1377,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 @@ -1762,6 +1765,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 @@ -1924,23 +1928,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 @@ -2100,6 +2087,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 @@ -2658,6 +2646,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 @@ -2852,6 +2841,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 @@ -3082,6 +3072,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
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
10 changes: 5 additions & 5 deletions NBitcoin/Transaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1574,11 +1574,8 @@ public virtual void ReadWrite(BitcoinStream stream)
{
/* The witness flag is present, and we support witnesses. */
flags ^= 1;
if (Inputs.Count != 0)
{
Witness wit = new Witness(Inputs);
wit.ReadWrite(stream);
}
Witness wit = new Witness(Inputs);
wit.ReadWrite(stream);
}
if (flags != 0)
{
Expand All @@ -1588,6 +1585,8 @@ public virtual void ReadWrite(BitcoinStream stream)
}
else
{
if (Inputs.Count == 0 && !stream.AllowNoInputs)
throw new InvalidOperationException("The transaction must have at least one input");
stream.ReadWrite(ref nVersion);

if (witSupported)
Expand Down Expand Up @@ -1637,6 +1636,7 @@ public uint256 GetHash()
{
TransactionOptions = TransactionOptions.None,
ConsensusFactory = GetConsensusFactory(),
AllowNoInputs = true
};
stream.SerializationTypeScope(SerializationType.Hash);
this.ReadWrite(stream);
Expand Down
9 changes: 3 additions & 6 deletions NBitcoin/TransactionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2443,10 +2443,9 @@ public void EstimateSizes(Transaction tx, out int witSize, out int baseSize)
if (tx == null)
throw new ArgumentNullException(nameof(tx));
var clone = tx.Clone();
clone.Inputs.Clear();
baseSize = clone.GetSerializedSize() - 1;
baseSize += new Protocol.VarInt((ulong)tx.Inputs.Count).GetSerializedSize();

clone.RemoveSignatures();
baseSize = clone.GetSerializedSize();
baseSize -= clone.Inputs.Count; // The varint to push scriptSig is accounted later
witSize = 0;
int nonWitnessCount = 0;
bool hasWitness = tx.HasWitness;
Expand All @@ -2460,10 +2459,8 @@ public void EstimateSizes(Transaction tx, out int witSize, out int baseSize)
else
nonWitnessCount++;
EstimateScriptSigSize(coin, ref witSize, ref baseSize);
baseSize += (32 + 4) + 4;
}


if (hasWitness)
{
witSize += 2; // 1 Dummy + 1 Flag
Expand Down
Loading