diff --git a/NBitcoin.Altcoins/Dogecoin.cs b/NBitcoin.Altcoins/Dogecoin.cs index b13e9a1ff..0887f48b3 100644 --- a/NBitcoin.Altcoins/Dogecoin.cs +++ b/NBitcoin.Altcoins/Dogecoin.cs @@ -218,15 +218,19 @@ public AuxPow AuxPow } } const int VERSION_AUXPOW = (1 << 8); + internal void ReadWriteHeader(BitcoinStream stream) + { + stream.ReadWrite(ref header); + if((header.Version & VERSION_AUXPOW) != 0) + { + stream.ReadWrite(ref auxPow); + } + } public override void ReadWrite(BitcoinStream stream) { using (stream.ConsensusFactoryScope(GetConsensusFactory())) { - stream.ReadWrite(ref header); - if((header.Version & VERSION_AUXPOW) != 0) - { - stream.ReadWrite(ref auxPow); - } + ReadWriteHeader(stream); stream.ReadWrite(ref vtx); } } @@ -238,23 +242,40 @@ public override ConsensusFactory GetConsensusFactory() } class DogecoinHeadersPayload : HeadersPayload { - List blocks = new List(); + class DogecoinHeaderWithTxCount : IBitcoinSerializable + { + internal DogecoinBlock Block = (DogecoinBlock)DogeConsensusFactory.Instance.CreateBlock(); + + public void ReadWrite(BitcoinStream stream) + { + using (stream.ConsensusFactoryScope(DogeConsensusFactory.Instance)) + { + Block.ReadWriteHeader(stream); + if (stream.Serializing) + VarInt.StaticWrite(stream, 0); + else + VarInt.StaticRead(stream); + } + } + } + + List headers = new List(); public override void ReadWriteCore(BitcoinStream stream) { if (stream.Serializing) { - if (blocks.Count == Headers.Count && blocks.Select(b => b.Header).SequenceEqual(Headers)) - stream.ReadWrite(ref blocks); + if (headers.Count == Headers.Count && headers.Select(h => h.Block.Header).SequenceEqual(Headers)) + stream.ReadWrite(ref headers); else base.ReadWriteCore(stream); return; } - // Dogecoin serializes headers as blocks with empty transaction vectors. - stream.ReadWrite(ref blocks); + // Dogecoin Core parses each header and ignores its assumed-zero transaction count. + stream.ReadWrite(ref headers); Headers.Clear(); - Headers.AddRange(blocks.Select(b => b.Header)); + Headers.AddRange(headers.Select(h => h.Block.Header)); } } diff --git a/NBitcoin.Tests/AltcoinTests.cs b/NBitcoin.Tests/AltcoinTests.cs index c69697c7d..591aafe4b 100644 --- a/NBitcoin.Tests/AltcoinTests.cs +++ b/NBitcoin.Tests/AltcoinTests.cs @@ -65,6 +65,27 @@ public void CanParseAuxPowDogeHeaders() Assert.Equal(headersHex, Encoders.Hex.EncodeData(payload.ToBytes())); } + [Fact] + public void DogeHeadersIgnoreTransactionCount() + { + var network = Dogecoin.Instance.Mainnet; + var block = Block.Parse(DogecoinAuxPowBlockHex, network); + block.Transactions.Clear(); + var header = block.ToBytes(); + header[header.Length - 1] = 1; + var headersBytes = new byte[] { 2 } + .Concat(header) + .Concat(block.ToBytes()) + .ToArray(); + + var payload = network.Consensus.ConsensusFactory.CreatePayload("headers"); + payload.ReadWrite(headersBytes, network); + var headers = Assert.IsAssignableFrom(payload); + + Assert.Equal(2, headers.Headers.Count); + Assert.All(headers.Headers, h => Assert.Equal(block.GetHash(), h.GetHash())); + } + [Fact] public void NoCrashQuickTest() {