diff --git a/NBitcoin.Altcoins/Dogecoin.cs b/NBitcoin.Altcoins/Dogecoin.cs index 279bc9f05..b13e9a1ff 100644 --- a/NBitcoin.Altcoins/Dogecoin.cs +++ b/NBitcoin.Altcoins/Dogecoin.cs @@ -30,6 +30,11 @@ private DogeConsensusFactory() } public static DogeConsensusFactory Instance { get; } = new DogeConsensusFactory(); + public override Payload CreatePayload(string command) + { + return command == "headers" ? new DogecoinHeadersPayload() : base.CreatePayload(command); + } + public override Block CreateBlock() { return new DogecoinBlock(this.CreateBlockHeader()); @@ -231,6 +236,27 @@ public override ConsensusFactory GetConsensusFactory() return DogeConsensusFactory.Instance; } } + class DogecoinHeadersPayload : HeadersPayload + { + List blocks = 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); + else + base.ReadWriteCore(stream); + return; + } + + // Dogecoin serializes headers as blocks with empty transaction vectors. + stream.ReadWrite(ref blocks); + Headers.Clear(); + Headers.AddRange(blocks.Select(b => b.Header)); + } + } public class DogecoinTestnetAddressStringParser : NetworkStringParser { diff --git a/NBitcoin.Tests/AltcoinTests.cs b/NBitcoin.Tests/AltcoinTests.cs index f78bbd37c..c69697c7d 100644 --- a/NBitcoin.Tests/AltcoinTests.cs +++ b/NBitcoin.Tests/AltcoinTests.cs @@ -29,15 +29,16 @@ public void CanParseDashBlock() Assert.Contains(b.Transactions, b => b.GetHash().ToString() == "6f0151cae1d5e65305fe2620a4f387108b054874a4ed9a23d4b004d5b08fbf79"); } + private const string DogecoinAuxPowBlockHex = + "040162007ce673a4b3de481e2032b637a5b17830e394ca8b398bb43a14ae85b476ae6df7fa189fdc785290db11ef8212d0735f6463b67151341c582a68d71595458bf4db7c64d76904737b190000000001000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5f03e2192f182f5669614254432f4d696e6564206279206476647331362f2cfabe6d6df959eb1ebd67503fd83e29448af13a9b7d4b5e3c132771692f2f182443f40769200000000000000010abc2940185c81b09e8a5d8498503000000000000ffffffff023a404325000000001976a91493e278b485679cfd2d21441a94bb29dd7c99d72088ac0000000000000000266a24aa21a9eda791454574eb7d95558a799ad6bbe7e14e6454b36e2da97453f423515d5d3db3000000008224660c3778e681ad6174e36be14a4a1ad8ceb40f6c67082242ee3a05d628b607e502ae7142ae0a26b07ff28ffbff1f3d601dc6378b31b3683f0273813ae75110e363e1294277c19e5f702285e709680d279c9ed3a0ebab400df0ab261bfe31a31becdc9cfe819bb0530df4f5ee39614ed5a184c8d693e387fa7dc8490066943a74c054bc16a29779aa2789d066bb30e13685e2067f7616062011cc967eca379191655d30596fb46bc44ee305c62656bdc65d1b3b75c6d359b70dd09466c7c7c0ca2dea37693a2966d2c3bbebd7d7d0595f353a29a321c4a90b07a7ff3d1ad2357e76f4cb9d082102875aba4558c9745a56da75cdf2f5cf6a0c53897cc1208de900000000050000000000000000000000000000000000000000000000000000000000000000e2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf96f40916a101bf9876381a3683a2f4df5dcd88fffca18a73bac836571814b7248968a851f207b2dfed8afc0c57d5641055af5f22721b83b4f0ae77bf846210283364e7522d83e751596a2de6ca156b3dd2d6b0696c1f99cf50758849a207ff7f11800000000000020af4a9a0be5289704f1a406ff4d3cc2719a7863febb5a388794b0d8e5301cf5900a8232c00ae7372bff48fef9749b9bddb87377adc98c5b7fd2010d0b25651cb25264d769ebb32d19691aa6360301000000010000000000000000000000000000000000000000000000000000000000000000ffffffff060330f85d0101ffffffff01007b54e2e8000000232102b178dbea168d92b2be696f8a44157ce1ee54fd3e65f7456d98281c5f99536a31ac000000000100000001364bec604560558fa25e4f56733616f0604e8a9f53abd38423767298142b1d9a010000006b483045022100f177c0a5d7a679732f0dbd41c44ed23678d5042c4e2ed696e8401f22637e6a920220097c46a0845b909063f2e77838e28a641a08e4c10dbd4063f03d896e8640cb5401210265a3c00a588c899676681e166c916ed030385c9da5a4c8d4b9029eed88a56d44ffffffff02fc7e32c3020000001976a914b9e2a1914f3cfde6d3fca7ae1818429bf379da4488ac8a596931bb0000001976a9140be4c25349ee33a3f3d9674fdd31618918cacd4588ac000000000100000001ea837334c70bcbd8044304e0edb36d178e856cdc3f888250dac9c56f345def5901000000d90047304402203690d2688f99ef59cc0c74a0be4a49ec7095b8595d16aae53f2e5ca804d88f0c022030579cd4d317f5bd13675bc780c29a559db019d1d15ff18c2a2544b7875d92f701473044022008bf677592bbf5d45b5dac1a9d67b39d447545e8be35391a4c8e4ec60cb86fb90220601da76799fab2da3ef52965d659889eba8aa52f05c4a17e2137aac63127f7560147522103aefb71198b8d2416692aa9e9137f99089973b2ca98e764a51d3b94a55efabca421025832ebd5560733f3ee3c1712d49709211f27a8e1bffaeab1915ac16d0febb77452ae0000000002281304a7050000001976a914d9f0ebfd04d7d4a610983746543396d948c4d34c88acb1989e9e4c00000017a914649405cec18dabffaafc8f76f4765d6177b261308700000000"; + [Fact] public void CanRoundtripAuxPowOnDoge() { - var hex = - "040162007ce673a4b3de481e2032b637a5b17830e394ca8b398bb43a14ae85b476ae6df7fa189fdc785290db11ef8212d0735f6463b67151341c582a68d71595458bf4db7c64d76904737b190000000001000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5f03e2192f182f5669614254432f4d696e6564206279206476647331362f2cfabe6d6df959eb1ebd67503fd83e29448af13a9b7d4b5e3c132771692f2f182443f40769200000000000000010abc2940185c81b09e8a5d8498503000000000000ffffffff023a404325000000001976a91493e278b485679cfd2d21441a94bb29dd7c99d72088ac0000000000000000266a24aa21a9eda791454574eb7d95558a799ad6bbe7e14e6454b36e2da97453f423515d5d3db3000000008224660c3778e681ad6174e36be14a4a1ad8ceb40f6c67082242ee3a05d628b607e502ae7142ae0a26b07ff28ffbff1f3d601dc6378b31b3683f0273813ae75110e363e1294277c19e5f702285e709680d279c9ed3a0ebab400df0ab261bfe31a31becdc9cfe819bb0530df4f5ee39614ed5a184c8d693e387fa7dc8490066943a74c054bc16a29779aa2789d066bb30e13685e2067f7616062011cc967eca379191655d30596fb46bc44ee305c62656bdc65d1b3b75c6d359b70dd09466c7c7c0ca2dea37693a2966d2c3bbebd7d7d0595f353a29a321c4a90b07a7ff3d1ad2357e76f4cb9d082102875aba4558c9745a56da75cdf2f5cf6a0c53897cc1208de900000000050000000000000000000000000000000000000000000000000000000000000000e2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf96f40916a101bf9876381a3683a2f4df5dcd88fffca18a73bac836571814b7248968a851f207b2dfed8afc0c57d5641055af5f22721b83b4f0ae77bf846210283364e7522d83e751596a2de6ca156b3dd2d6b0696c1f99cf50758849a207ff7f11800000000000020af4a9a0be5289704f1a406ff4d3cc2719a7863febb5a388794b0d8e5301cf5900a8232c00ae7372bff48fef9749b9bddb87377adc98c5b7fd2010d0b25651cb25264d769ebb32d19691aa6360301000000010000000000000000000000000000000000000000000000000000000000000000ffffffff060330f85d0101ffffffff01007b54e2e8000000232102b178dbea168d92b2be696f8a44157ce1ee54fd3e65f7456d98281c5f99536a31ac000000000100000001364bec604560558fa25e4f56733616f0604e8a9f53abd38423767298142b1d9a010000006b483045022100f177c0a5d7a679732f0dbd41c44ed23678d5042c4e2ed696e8401f22637e6a920220097c46a0845b909063f2e77838e28a641a08e4c10dbd4063f03d896e8640cb5401210265a3c00a588c899676681e166c916ed030385c9da5a4c8d4b9029eed88a56d44ffffffff02fc7e32c3020000001976a914b9e2a1914f3cfde6d3fca7ae1818429bf379da4488ac8a596931bb0000001976a9140be4c25349ee33a3f3d9674fdd31618918cacd4588ac000000000100000001ea837334c70bcbd8044304e0edb36d178e856cdc3f888250dac9c56f345def5901000000d90047304402203690d2688f99ef59cc0c74a0be4a49ec7095b8595d16aae53f2e5ca804d88f0c022030579cd4d317f5bd13675bc780c29a559db019d1d15ff18c2a2544b7875d92f701473044022008bf677592bbf5d45b5dac1a9d67b39d447545e8be35391a4c8e4ec60cb86fb90220601da76799fab2da3ef52965d659889eba8aa52f05c4a17e2137aac63127f7560147522103aefb71198b8d2416692aa9e9137f99089973b2ca98e764a51d3b94a55efabca421025832ebd5560733f3ee3c1712d49709211f27a8e1bffaeab1915ac16d0febb77452ae0000000002281304a7050000001976a914d9f0ebfd04d7d4a610983746543396d948c4d34c88acb1989e9e4c00000017a914649405cec18dabffaafc8f76f4765d6177b261308700000000"; var b = Block.Parse( - hex, + DogecoinAuxPowBlockHex, Dogecoin.Instance.Mainnet); - Assert.Equal(hex, Encoders.Hex.EncodeData(b.ToBytes())); + Assert.Equal(DogecoinAuxPowBlockHex, Encoders.Hex.EncodeData(b.ToBytes())); Assert.Equal("07a956c15d4086cde65441890983f985919c6b616c9de9a2d11735d60511ad22", b.GetHash().ToString()); var headerBytes = b.Header.ToBytes(); @@ -45,6 +46,25 @@ public void CanRoundtripAuxPowOnDoge() Assert.Equal("07a956c15d4086cde65441890983f985919c6b616c9de9a2d11735d60511ad22", h2.GetHash().ToString()); } + [Fact] + public void CanParseAuxPowDogeHeaders() + { + var network = Dogecoin.Instance.Mainnet; + const string headersHex = + "02040162007ce673a4b3de481e2032b637a5b17830e394ca8b398bb43a14ae85b476ae6df7fa189fdc785290db11ef8212d0735f6463b67151341c582a68d71595458bf4db7c64d76904737b190000000001000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5f03e2192f182f5669614254432f4d696e6564206279206476647331362f2cfabe6d6df959eb1ebd67503fd83e29448af13a9b7d4b5e3c132771692f2f182443f40769200000000000000010abc2940185c81b09e8a5d8498503000000000000ffffffff023a404325000000001976a91493e278b485679cfd2d21441a94bb29dd7c99d72088ac0000000000000000266a24aa21a9eda791454574eb7d95558a799ad6bbe7e14e6454b36e2da97453f423515d5d3db3000000008224660c3778e681ad6174e36be14a4a1ad8ceb40f6c67082242ee3a05d628b607e502ae7142ae0a26b07ff28ffbff1f3d601dc6378b31b3683f0273813ae75110e363e1294277c19e5f702285e709680d279c9ed3a0ebab400df0ab261bfe31a31becdc9cfe819bb0530df4f5ee39614ed5a184c8d693e387fa7dc8490066943a74c054bc16a29779aa2789d066bb30e13685e2067f7616062011cc967eca379191655d30596fb46bc44ee305c62656bdc65d1b3b75c6d359b70dd09466c7c7c0ca2dea37693a2966d2c3bbebd7d7d0595f353a29a321c4a90b07a7ff3d1ad2357e76f4cb9d082102875aba4558c9745a56da75cdf2f5cf6a0c53897cc1208de900000000050000000000000000000000000000000000000000000000000000000000000000e2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf96f40916a101bf9876381a3683a2f4df5dcd88fffca18a73bac836571814b7248968a851f207b2dfed8afc0c57d5641055af5f22721b83b4f0ae77bf846210283364e7522d83e751596a2de6ca156b3dd2d6b0696c1f99cf50758849a207ff7f11800000000000020af4a9a0be5289704f1a406ff4d3cc2719a7863febb5a388794b0d8e5301cf5900a8232c00ae7372bff48fef9749b9bddb87377adc98c5b7fd2010d0b25651cb25264d769ebb32d19691aa63600040062007ce673a4b3de481e2032b637a5b17830e394ca8b398bb43a14ae85b476ae6df7fa189fdc785290db11ef8212d0735f6463b67151341c582a68d71595458bf4db7c64d76904737b190000000000"; + + var payload = network.Consensus.ConsensusFactory.CreatePayload("headers"); + payload.ReadWrite(Encoders.Hex.DecodeData(headersHex), network); + var headers = Assert.IsAssignableFrom(payload); + + Assert.Equal(new[] + { + uint256.Parse("07a956c15d4086cde65441890983f985919c6b616c9de9a2d11735d60511ad22"), + uint256.Parse("81ac7632ae8e7bd5517456d8b9ae9574cee2e1b4dcdfa0938a78fc695bfdb2d6") + }, headers.Headers.Select(h => h.GetHash())); + Assert.Equal(headersHex, Encoders.Hex.EncodeData(payload.ToBytes())); + } + [Fact] public void NoCrashQuickTest() {