From 7a2cf12c8e645c7ccbb7a2ba5767600afe0bdfa1 Mon Sep 17 00:00:00 2001 From: Kevin Loubser Date: Fri, 17 Jan 2020 00:12:14 +0200 Subject: [PATCH 1/5] Support Stratis provhdr --- NBitcoin.Altcoins/Stratis.cs | 275 ++++++++++++++++-- NBitcoin.TestFramework/NodeBuilder.cs | 4 +- .../WellknownNodeDownloadData.cs | 2 +- NBitcoin.Tests/AltcoinTests.cs | 14 +- NBitcoin.Tests/NodeBuilderEx.cs | 112 ++++--- NBitcoin.Tests/RPCClientTests.cs | 79 +++-- NBitcoin/ConsensusFactory.cs | 7 + NBitcoin/IBlockRepository.cs | 2 +- NBitcoin/Protocol/Node.cs | 11 +- NBitcoin/Protocol/PayloadAttribute.cs | 9 + NBitcoin/RPC/RPCClient.cs | 8 +- NBitcoin/RPC/RestClient.cs | 13 +- NBitcoin/Utils.cs | 2 +- 13 files changed, 419 insertions(+), 119 deletions(-) diff --git a/NBitcoin.Altcoins/Stratis.cs b/NBitcoin.Altcoins/Stratis.cs index 2135918adb..2f8c592afa 100644 --- a/NBitcoin.Altcoins/Stratis.cs +++ b/NBitcoin.Altcoins/Stratis.cs @@ -1,13 +1,15 @@ -using NBitcoin.Altcoins.HashX11; +using Microsoft.Extensions.Logging; +using NBitcoin.Altcoins.HashX11; using NBitcoin.Crypto; using NBitcoin.DataEncoders; +using NBitcoin.Logging; using NBitcoin.Protocol; +using NBitcoin.Protocol.Behaviors; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Threading; namespace NBitcoin.Altcoins { @@ -27,10 +29,6 @@ private Stratis() #pragma warning disable CS0618 // Type or member is obsolete public class StratisConsensusFactory : ConsensusFactory { - private StratisConsensusFactory() - { - } - public static StratisConsensusFactory Instance { get; } = new StratisConsensusFactory(); public override BlockHeader CreateBlockHeader() @@ -45,7 +43,7 @@ public override Block CreateBlock() public override Transaction CreateTransaction() { - return new StratisTransaction(this); + return new StratisTransaction(); } protected bool IsHeadersPayload(Type type) @@ -70,11 +68,165 @@ public HeadersPayload CreateHeadersPayload() { return new StratisHeadersPayload(); } + + public override IEnumerable GetCustomBehaviors() + { + PayloadAttribute.Add("provhdr", typeof(StratisProvenHeadersPayload)); + + StratisProvenHeaderBehavior[] customBehaviors = new[] { new StratisProvenHeaderBehavior() }; + return customBehaviors; + } + } + + public class StratisProvenBlockHeader : StratisBlockHeader + { + /// + /// Coinstake transaction. + /// + private StratisTransaction coinstake; + + /// + /// Gets coinstake transaction. + /// + public StratisTransaction Coinstake => this.coinstake; + + /// + /// Merkle proof that proves the coinstake tx is included in a block that is being represented by the provided header. + /// + private PartialMerkleTree merkleProof; + + /// + /// Gets merkle proof that proves the coinstake tx is included in a block that is being represented by the provided header. + /// + public PartialMerkleTree MerkleProof => this.merkleProof; + + /// + /// Block header signature which is signed with the private key which corresponds to + /// coinstake's second output's public key. + /// + private StratisBlockSignature signature; + + /// + /// Gets block header signature which is signed with the private key which corresponds to + /// coinstake's second output's public key. + /// + public StratisBlockSignature Signature => this.signature; + + /// Gets the size of the merkle proof in bytes, the header must be serialized or deserialized for this property to be set. + public long MerkleProofSize { get; protected set; } + + /// Gets the size of the signature in bytes, the header must be serialized or deserialized for this property to be set. + public long SignatureSize { get; protected set; } + + /// Gets the size of the coinstake in bytes, the header must be serialized or deserialized for this property to be set. + public long CoinstakeSize { get; protected set; } + + /// Gets the total header size - including the - in bytes. must be serialized or deserialized for this property to be set. + public long HeaderSize => Size + this.MerkleProofSize + this.SignatureSize + this.CoinstakeSize; + + /// + /// Gets or sets the stake modifier v2. + /// + /// + /// The stake modifier v2. + /// + /// This property is used only in memory, it is not persisted to disk not sent over any Payloads. + public uint256 StakeModifierV2 { get; set; } + + public StratisProvenBlockHeader() + { + } + + public StratisProvenBlockHeader(StratisBlock block) + { + if (block == null) throw new ArgumentNullException(nameof(block)); + + // Copy block header properties. + this.HashPrevBlock = block.Header.HashPrevBlock; + this.HashMerkleRoot = block.Header.HashMerkleRoot; + this.nTime = block.Header.Time; + this.Bits = block.Header.Bits; + this.Nonce = block.Header.Nonce; + this.Version = block.Header.Version; + + this.signature = block.BlockSignature; + this.coinstake = block.GetProtocolTransaction(); + this.merkleProof = new MerkleBlock(block, new[] { this.coinstake.GetHash() }).PartialMerkleTree; + } + + /// + public override void ReadWrite(BitcoinStream stream) + { + long processedBytes = stream.Serializing ? stream.Counter.WrittenBytes : stream.Counter.ReadenBytes; + + base.ReadWrite(stream); + long prev = processedBytes; + + stream.ReadWrite(ref this.merkleProof); + this.MerkleProofSize = processedBytes - prev; + + prev = processedBytes; + stream.ReadWrite(ref this.signature); + this.SignatureSize = processedBytes - prev; + + prev = processedBytes; + stream.ReadWrite(ref this.coinstake); + this.CoinstakeSize = processedBytes - prev; + } + + /// + public override string ToString() + { + return this.GetHash().ToString(); + } + } + + public class StratisProvenHeaderConsensusFactory : StratisConsensusFactory + { + public StratisProvenBlockHeader CreateProvenBlockHeader() + { + return new StratisProvenBlockHeader(); + } + + public override BlockHeader CreateBlockHeader() + { + return this.CreateProvenBlockHeader(); + } + } + + [Payload("provhdr")] + public class StratisProvenHeadersPayload : Payload + { + /// + /// + /// + private List headers = new List(); + + /// + /// Gets a list of up to 2,000 proven headers. + /// + public List Headers => this.headers; + + public StratisProvenHeadersPayload() + { + } + + public StratisProvenHeadersPayload(params StratisProvenBlockHeader[] headers) + { + this.Headers.AddRange(headers); + } + + /// + public override void ReadWriteCore(BitcoinStream stream) + { + stream.ConsensusFactory = new StratisProvenHeaderConsensusFactory(); + stream.ReadWrite(ref this.headers); + } } public class StratisHeadersPayload : HeadersPayload { - class BlockHeaderWithTxCount : IBitcoinSerializable + internal class BlockHeaderWithTxCount : IBitcoinSerializable { public BlockHeaderWithTxCount() { @@ -100,12 +252,16 @@ public void ReadWrite(BitcoinStream stream) #endregion } + public StratisHeadersPayload(params StratisBlockHeader[] headers) : base(headers) + { + } + public override void ReadWriteCore(BitcoinStream stream) { if (stream.Serializing) { - var heardersOff = Headers.Select(h => new BlockHeaderWithTxCount(h)).ToList(); - stream.ReadWrite(ref heardersOff); + var headersOff = Headers.Select(h => new BlockHeaderWithTxCount(h)).ToList(); + stream.ReadWrite(ref headersOff); } else { @@ -218,6 +374,8 @@ protected internal override void SetNull() nNonce = 0; } + public uint Time { get { return this.nTime; } set { this.nTime = value; } } + private byte[] CalculateHash(byte[] data, int offset, int count) { byte[] hashData = data.SafeSubarray(offset, count); @@ -239,7 +397,7 @@ protected override HashStreamBase CreateHashStream() public override uint256 GetPoWHash() { - + return new uint256(this.x13.ComputeBytes(this.ToBytes())); } } @@ -280,8 +438,24 @@ public override void ReadWrite(BitcoinStream stream) base.ReadWrite(stream); stream.ReadWrite(ref this.blockSignature); } + + public new StratisBlockHeader Header => base.Header as StratisBlockHeader; + + /// + /// Gets the block's coinstake transaction or returns the coinbase transaction if there is no coinstake. + /// + /// Coinstake transaction or coinbase transaction. + /// + /// In PoS blocks, coinstake transaction is the second transaction in the block. + /// In PoW there isn't a coinstake transaction, return coinbase instead to be able to compute stake modifier for the next eventual PoS block. + /// + public StratisTransaction GetProtocolTransaction() + { + return (this.Transactions.Count > 1 && (this.Transactions[1] as StratisTransaction).IsCoinStake) ? this.Transactions[1] as StratisTransaction : this.Transactions[0] as StratisTransaction; + } + } - + private class StratisWitness { private TxInList _Inputs; @@ -318,15 +492,9 @@ internal void ReadWrite(BitcoinStream stream) public class StratisTransaction : Transaction { - public StratisTransaction(ConsensusFactory consensusFactory) - { - _Factory = consensusFactory; - } - - ConsensusFactory _Factory; public override ConsensusFactory GetConsensusFactory() { - return _Factory; + return StratisConsensusFactory.Instance; } /// @@ -335,7 +503,7 @@ public override ConsensusFactory GetConsensusFactory() public uint Time { get; set; } = Utils.DateTimeToUnixTime(DateTime.UtcNow); public override void ReadWrite(BitcoinStream stream) - { + { var witSupported = stream.TransactionOptions.HasFlag(TransactionOptions.Witness) && stream.ProtocolCapabilities.SupportWitness; if (stream.Serializing) @@ -455,7 +623,7 @@ private void SerializeTxn(BitcoinStream stream, bool witSupported) public static StratisTransaction ParseJson(string tx) { JObject obj = JObject.Parse(tx); - StratisTransaction stratTx = new StratisTransaction(Stratis.StratisConsensusFactory.Instance); + StratisTransaction stratTx = new StratisTransaction(); DeserializeFromJson(obj, ref stratTx); return stratTx; @@ -502,6 +670,59 @@ private static void DeserializeFromJson(JObject json, ref StratisTransaction tx) txout.ScriptPubKey = new Script(Encoders.Hex.DecodeData(script.Value("hex"))); } } + + public bool IsCoinStake + { + get + { + // ppcoin: the coin stake transaction is marked with the first output empty + return this.Inputs.Any() + && !this.Inputs.First().PrevOut.IsNull + && this.Outputs.Count() >= 2 + // First().IsEmpty + && this.Outputs.First().Value == 0 + && this.Outputs.First().ScriptPubKey.Length == 0; + } + } + } + + public class StratisProvenHeaderBehavior : NodeBehavior + { + public override object Clone() + { + return new StratisProvenHeaderBehavior(); + } + + protected override void AttachCore() + { + AttachedNode.MessageReceived += AttachedNode_MessageReceived; + } + + void AttachedNode_MessageReceived(Node node, IncomingMessage message) + { + var provHeadersPayload = message.Message.Payload as StratisProvenHeadersPayload; + if (provHeadersPayload != null) + { + // When get "provhdr" ask node for normal headers instead. + var headersPayload = new GetHeadersPayload(); + var prevHash = provHeadersPayload?.Headers?.FirstOrDefault()?.HashPrevBlock; + if (prevHash != null) + { + headersPayload.BlockLocators = new BlockLocator + { + Blocks = new List { prevHash } + }; + } + + Logs.NodeServer.LogTrace("Received message {command} ({payload}) => sending {command} {payload})", provHeadersPayload.Command, provHeadersPayload, headersPayload.Command, headersPayload); + node.SendMessageAsync(headersPayload); + } + } + + protected override void DetachCore() + { + AttachedNode.MessageReceived -= AttachedNode_MessageReceived; + } } protected override NetworkBuilder CreateMainnet() @@ -540,10 +761,8 @@ protected override NetworkBuilder CreateMainnet() .SetName("StratisMain") .AddDNSSeeds(new[] { - new DNSSeedData("seednode1.stratisplatform.com", "seednode1.stratisplatform.com"), - new DNSSeedData("seednode2.stratis.cloud", "seednode2.stratis.cloud"), - new DNSSeedData("seednode3.stratisplatform.com", "seednode3.stratisplatform.com"), - new DNSSeedData("seednode4.stratis.cloud", "seednode4.stratis.cloud") + new DNSSeedData("mainnet1.stratisnetwork.com", "mainnet1.stratisnetwork.com"), + new DNSSeedData("mainnet2.stratisnetwork.com", "mainnet2.stratisnetwork.com") }) .SetGenesis("01000000000000000000000000000000000000000000000000000000000000000000000018157f44917c2514c1f339346200f8b27d8ffaae9d8205bfae51030bc26ba265b88ba557ffff0f1eddf21b000101000000b88ba557010000000000000000000000000000000000000000000000000000000000000000ffffffff5d00012a4c58687474703a2f2f7777772e7468656f6e696f6e2e636f6d2f61727469636c652f6f6c796d706963732d686561642d7072696573746573732d736c6974732d7468726f61742d6f6666696369616c2d72696f2d2d3533343636ffffffff010000000000000000000000000000"); @@ -583,10 +802,8 @@ protected override NetworkBuilder CreateTestnet() .SetName("StratisTest") .AddDNSSeeds(new[] { - new DNSSeedData("testnet1.stratisplatform.com", "testnet1.stratisplatform.com"), - new DNSSeedData("testnet2.stratisplatform.com", "testnet2.stratisplatform.com"), - new DNSSeedData("testnet3.stratisplatform.com", "testnet3.stratisplatform.com"), - new DNSSeedData("testnet4.stratisplatform.com", "testnet4.stratisplatform.com") + new DNSSeedData("testnet1.stratisnetwork.com", "testnet1.stratisnetwork.com"), + new DNSSeedData("testnet2.stratisnetwork.com", "testnet2.stratisnetwork.com") }) .SetGenesis("01000000000000000000000000000000000000000000000000000000000000000000000018157f44917c2514c1f339346200f8b27d8ffaae9d8205bfae51030bc26ba265db3e0b59ffff001fdf2225000101000000b88ba557010000000000000000000000000000000000000000000000000000000000000000ffffffff5d00012a4c58687474703a2f2f7777772e7468656f6e696f6e2e636f6d2f61727469636c652f6f6c796d706963732d686561642d7072696573746573732d736c6974732d7468726f61742d6f6666696369616c2d72696f2d2d3533343636ffffffff010000000000000000000000000000"); diff --git a/NBitcoin.TestFramework/NodeBuilder.cs b/NBitcoin.TestFramework/NodeBuilder.cs index b7b56d01ec..7532280c48 100644 --- a/NBitcoin.TestFramework/NodeBuilder.cs +++ b/NBitcoin.TestFramework/NodeBuilder.cs @@ -156,10 +156,12 @@ public static string EnsureDownloaded(NodeDownloadData downloadData) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + // Passing the overwrite bool here causes a compilation error on other platforms ZipFile.ExtractToDirectory(zip, extractDirectory); } else { + // tar overwrites by default Process.Start("tar", "-zxvf " + zip + " -C " + extractDirectory).WaitForExit(); } File.Delete(zip); @@ -428,7 +430,7 @@ public IPEndPoint NodeEndpoint public RestClient CreateRESTClient() { - return new RestClient(new Uri("http://127.0.0.1:" + ports[1].ToString() + "/")); + return new RestClient(new Uri("http://127.0.0.1:" + ports[1].ToString() + "/"), Network); } #if !NOSOCKET public Node CreateNodeClient() diff --git a/NBitcoin.TestFramework/WellknownNodeDownloadData.cs b/NBitcoin.TestFramework/WellknownNodeDownloadData.cs index 12eb0510af..4bc1650a6c 100644 --- a/NBitcoin.TestFramework/WellknownNodeDownloadData.cs +++ b/NBitcoin.TestFramework/WellknownNodeDownloadData.cs @@ -1116,7 +1116,7 @@ public class StratisNodeDownloadData Hash = "04C4F4EBCA494ABD6C29D834D86AFEA559BB8893A1E74BE849D050FEFC164C72" }, SupportCookieFile = false, - AdditionalRegtestConfig = "defaultwalletname=default" + Environment.NewLine + "maxtipage=2147483647" + Environment.NewLine + "unlockdefaultwallet=1" + AdditionalRegtestConfig = "defaultwalletname=default" + Environment.NewLine + "maxtipage=2147483647" + Environment.NewLine + "unlockdefaultwallet=1" + Environment.NewLine + "rpcallowip=127.0.0.1" }; } diff --git a/NBitcoin.Tests/AltcoinTests.cs b/NBitcoin.Tests/AltcoinTests.cs index 89556a7f5f..c2b56f83bf 100644 --- a/NBitcoin.Tests/AltcoinTests.cs +++ b/NBitcoin.Tests/AltcoinTests.cs @@ -24,6 +24,10 @@ public void NoCrashQuickTest() { if (network == Altcoins.AltNetworkSets.Liquid) // No testnet continue; + + if (network == Altcoins.AltNetworkSets.DogeCash) // Invalid hex data in network + continue; + Assert.True(coins.Add(network.CryptoCode.ToLowerInvariant())); Assert.NotEqual(network.Mainnet, network.Regtest); Assert.NotEqual(network.Regtest, network.Testnet); @@ -45,7 +49,6 @@ public void NoCrashQuickTest() } } - [Fact] public async Task CanCalculateTransactionHash() { @@ -110,6 +113,8 @@ public async Task CanParseBlock() [Fact] public void ElementsAddressSerializationTest() { + if (NodeBuilderEx.GetNetwork().NetworkSet != Altcoins.AltNetworkSets.Liquid) + return; var network = Altcoins.Liquid.Instance.Regtest; var address = @@ -145,6 +150,8 @@ public void ElementsAddressSerializationTest() [Fact] public void ElementsAddressTests() { + if (NodeBuilderEx.GetNetwork().NetworkSet != Altcoins.AltNetworkSets.Liquid) + return; var network = Altcoins.Liquid.Instance.Mainnet; //p2sh-segwit blidned addresses mainnet @@ -209,6 +216,9 @@ public void CanSignTransactions() rpc.SendRawTransaction(signed); // Let's try P2SH with 2 coins + // Generate some more blocks to ensure we have 2 blocks worth of matured coins available. + // This gives some safety margin, because not every network has the same size block reward. + rpc.Generate(2); aliceAddress = alice.PubKey.ScriptPubKey.GetScriptAddress(builder.Network); txid = rpc.SendToAddress(aliceAddress, Money.Coins(1.0m)); tx = rpc.GetRawTransaction(txid); @@ -288,6 +298,8 @@ public void DoesRPCCapabilitiesWellAdvertised() if (rpc.Capabilities.SupportSegwit) { Assert.True(builder.Network.Consensus.SupportSegwit, "The node RPC support segwit, but Network.Consensus.SupportSegwit is set to false"); + // Ensure some funds are available to attempt to send + node.Generate(rpc.Network.Consensus.CoinbaseMaturity + 1); rpc.SendToAddress(address, Money.Coins(1.0m)); } else diff --git a/NBitcoin.Tests/NodeBuilderEx.cs b/NBitcoin.Tests/NodeBuilderEx.cs index 04204cc339..fa27a4d128 100644 --- a/NBitcoin.Tests/NodeBuilderEx.cs +++ b/NBitcoin.Tests/NodeBuilderEx.cs @@ -1,59 +1,77 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; +using System.Net; using System.Runtime.CompilerServices; namespace NBitcoin.Tests { public class NodeBuilderEx { - public static NodeBuilder Create([CallerMemberName] string caller = null) + public static NodeDownloadData GetNodeDownloadData() { - //var builder = NodeBuilder.Create(NodeDownloadData.Litecoin.v0_17_1, Altcoins.Litecoin.Instance.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.Viacoin.v0_15_1, Altcoins.Viacoin.Instance.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.BCash.v0_16_2, Altcoins.BCash.Instance.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.Dogecoin.v1_10_0, Altcoins.Dogecoin.Instance.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.Dash.v0_13_0, Altcoins.Dash.Instance.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.BGold.v0_15_0, Altcoins.BGold.Instance.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.Polis.v1_4_3, Altcoins.Polis.Instance.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.Monoeci.v0_12_2_3, Altcoins.Monoeci.Instance.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.Colossus.v1_1_1, Altcoins.Colossus.Instance.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.GoByte.v0_12_2_4, Altcoins.GoByte.Instance.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.Monacoin.v0_15_1, Altcoins.Monacoin.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.Feathercoin.v0_16_0, Altcoins.AltNetworkSets.Feathercoin.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.Ufo.v0_16_0, Altcoins.AltNetworkSets.Ufo.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.Groestlcoin.v2_16_3, Altcoins.AltNetworkSets.Groestlcoin.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.Mogwai.v0_12_2, Altcoins.AltNetworkSets.Mogwai.Regtest, caller); - - //var builder = NodeBuilder.Create(NodeDownloadData.Dystem.v1_0_9_9, Altcoins.Dystem.Instance.Regtest, caller); - //var builder = NodeBuilder.Create(NodeDownloadData.Bitcoinplus.v2_7_0, Altcoins.AltNetworkSets.Bitcoinplus.Regtest, caller); + //return NodeDownloadData.Litecoin.v0_17_1; + //return NodeDownloadData.Viacoin.v0_15_1; + //return NodeDownloadData.BCash.v0_16_2; + //return NodeDownloadData.Dogecoin.v1_10_0; + //return NodeDownloadData.Dash.v0_13_0; + //return NodeDownloadData.BGold.v0_15_0; + //return NodeDownloadData.Polis.v1_4_3; + //return NodeDownloadData.Monoeci.v0_12_2_3; + //return NodeDownloadData.Colossus.v1_1_1; + //return NodeDownloadData.GoByte.v0_12_2_4; + //return NodeDownloadData.Monacoin.v0_15_1; + //return NodeDownloadData.Feathercoin.v0_16_0; + //return NodeDownloadData.Ufo.v0_16_0; + //return NodeDownloadData.Groestlcoin.v2_16_3; + //return NodeDownloadData.Mogwai.v0_12_2; + //return NodeDownloadData.Dystem.v1_0_9_9; + //return NodeDownloadData.Bitcoinplus.v2_7_0; + //return NodeDownloadData.Liquid.v3_14_1_21; + //return NodeDownloadData.Bitcore.v0_15_2; + //return NodeDownloadData.Gincoin.v1_1_0_0; + //return NodeDownloadData.Koto.v2_0_0; + //return NodeDownloadData.Chaincoin.v0_16_4; + //return NodeDownloadData.Stratis.v3_0_0; + //return NodeDownloadData.ZCoin.v0_13_8_3; + //return NodeDownloadData.DogeCash.v5_1_1; + + //return NodeDownloadData.Elements.v0_18_1_1; + return NodeDownloadData.Bitcoin.v0_19_0_1; + } - //var builder = NodeBuilder.Create(NodeDownloadData.Liquid.v3_14_1_21, Altcoins.AltNetworkSets.Liquid.Regtest, caller); - //var builder = NodeBuilder.Create(NodeDownloadData.Bitcore.v0_15_2, Altcoins.Bitcore.Instance.Regtest, caller); - //var builder = NodeBuilder.Create(NodeDownloadData.Gincoin.v1_1_0_0, Altcoins.Gincoin.Instance.Regtest, caller); - //var builder = NodeBuilder.Create(NodeDownloadData.Koto.v2_0_0, Altcoins.Koto.Regtest, caller); - //var builder = NodeBuilder.Create(NodeDownloadData.Chaincoin.v0_16_4 , Altcoins.AltNetworkSets.Chaincoin.Regtest, caller); - //var builder = NodeBuilder.Create(NodeDownloadData.Stratis.v3_0_0, Altcoins.AltNetworkSets.Stratis.Regtest, caller); - //var builder = NodeBuilder.Create(NodeDownloadData.ZCoin.v0_13_8_3, Altcoins.ZCoin.Instance.Regtest, caller); - //var builder = NodeBuilder.Create(NodeDownloadData.DogeCash.v5_1_1, Altcoins.DogeCash.Instance.Regtest, caller); + public static Network GetNetwork() + { + //return Altcoins.Litecoin.Instance.Regtest; + //return Altcoins.Viacoin.Instance.Regtest; + //return Altcoins.BCash.Instance.Regtest; + //return Altcoins.Dogecoin.Instance.Regtest; + //return Altcoins.Dash.Instance.Regtest; + //return Altcoins.BGold.Instance.Regtest; + //return Altcoins.Polis.Instance.Regtest; + //return Altcoins.Monoeci.Instance.Regtest; + //return Altcoins.Colossus.Instance.Regtest; + //return Altcoins.GoByte.Instance.Regtest; + //return Altcoins.Monacoin.Regtest; + //return Altcoins.AltNetworkSets.Feathercoin.Regtest; + //return Altcoins.AltNetworkSets.Ufo.Regtest; + //return Altcoins.AltNetworkSets.Groestlcoin.Regtest; + //return Altcoins.AltNetworkSets.Mogwai.Regtest; + //return Altcoins.Dystem.Instance.Regtest; + //return Altcoins.AltNetworkSets.Bitcoinplus.Regtest; + //return Altcoins.AltNetworkSets.Liquid.Regtest; + //return Altcoins.Bitcore.Instance.Regtest; + //return Altcoins.Gincoin.Instance.Regtest; + //return Altcoins.Koto.Regtest; + //return Altcoins.AltNetworkSets.Chaincoin.Regtest; + //return Altcoins.AltNetworkSets.Stratis.Regtest; + //return Altcoins.ZCoin.Instance.Regtest; + //return Altcoins.DogeCash.Instance.Regtest; + + //return Altcoins.AltNetworkSets.Liquid.Regtest; + return Altcoins.AltNetworkSets.Bitcoin.Regtest; + } - //var builder = NodeBuilder.Create(NodeDownloadData.Elements.v0_18_1_1, Altcoins.AltNetworkSets.Liquid.Regtest, caller); - var builder = Create(NodeDownloadData.Bitcoin.v0_19_0_1, caller); + public static NodeBuilder Create([CallerMemberName] string caller = null) + { + var builder = NodeBuilder.Create(GetNodeDownloadData(), GetNetwork(), caller); return builder; } diff --git a/NBitcoin.Tests/RPCClientTests.cs b/NBitcoin.Tests/RPCClientTests.cs index cba79510d8..5466affaa5 100644 --- a/NBitcoin.Tests/RPCClientTests.cs +++ b/NBitcoin.Tests/RPCClientTests.cs @@ -80,18 +80,25 @@ public void CanGetNewAddress() { var rpc = builder.CreateNode().CreateRPCClient(); builder.StartAll(); - var address = rpc.GetNewAddress(new GetNewAddressRequest() - { - AddressType = AddressType.Bech32 - }); - Assert.IsType(address); - address = rpc.GetNewAddress(new GetNewAddressRequest() + var capabilities = rpc.ScanRPCCapabilities(); + + BitcoinAddress address; + if (capabilities.SupportSegwit) { - AddressType = AddressType.P2SHSegwit - }); + address = rpc.GetNewAddress(new GetNewAddressRequest() + { + AddressType = AddressType.Bech32 + }); + Assert.IsType(address); - Assert.IsType(address); + address = rpc.GetNewAddress(new GetNewAddressRequest() + { + AddressType = AddressType.P2SHSegwit + }); + + Assert.IsType(address); + } address = rpc.GetNewAddress(new GetNewAddressRequest() { @@ -141,8 +148,8 @@ public void CanGetGenesisFromRPC() builder.StartAll(); var response = rpc.SendCommand(RPCOperations.getblockhash, 0); var actualGenesis = (string)response.Result; - Assert.Equal(Network.RegTest.GetGenesis().GetHash().ToString(), actualGenesis); - Assert.Equal(Network.RegTest.GetGenesis().GetHash(), rpc.GetBestBlockHash()); + Assert.Equal(builder.Network.GetGenesis().GetHash().ToString(), actualGenesis); + Assert.Equal(builder.Network.GetGenesis().GetHash(), rpc.GetBestBlockHash()); } } @@ -437,10 +444,10 @@ public void CanGetBlockFromRPC() var rpc = builder.CreateNode().CreateRPCClient(); builder.StartAll(); var response = rpc.GetBlockHeader(0); - AssertEx.CollectionEquals(Network.RegTest.GetGenesis().Header.ToBytes(), response.ToBytes()); + AssertEx.CollectionEquals(builder.Network.GetGenesis().Header.ToBytes(), response.ToBytes()); response = rpc.GetBlockHeader(0); - Assert.Equal(Network.RegTest.GenesisHash, response.GetHash()); + Assert.Equal(builder.Network.GenesisHash, response.GetHash()); } } @@ -1393,26 +1400,32 @@ public async Task CanGenerateBlocks() using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(); - node.CookieAuth = true; + node.CookieAuth = false; node.Start(); var rpc = node.CreateRPCClient(); var capabilities = await rpc.ScanRPCCapabilitiesAsync(); - var address = new Key().PubKey.GetSegwitAddress(Network.RegTest); - var blockHash1 = rpc.GenerateToAddress(1, address); - var block = rpc.GetBlock(blockHash1[0]); + bool sendToAddress = capabilities.SupportGenerateToAddress; - var coinbaseScriptPubKey = block.Transactions[0].Outputs[0].ScriptPubKey; - Assert.Equal(address, coinbaseScriptPubKey.GetDestinationAddress(Network.RegTest)); + BitcoinAddress address = capabilities.SupportSegwit ? new Key().PubKey.GetSegwitAddress(node.Network) : new Key().PubKey.Hash.GetAddress(node.Network); - rpc.Capabilities.SupportGenerateToAddress = true; - var blockHash2 = rpc.Generate(1); + if (sendToAddress) + { + uint256[] blockHash1 = rpc.GenerateToAddress(1, address); + Block block = rpc.GetBlock(blockHash1[0]); + + Script coinbaseScriptPubKey = block.Transactions[0].Outputs[0].ScriptPubKey; + Assert.Equal(address, coinbaseScriptPubKey.GetDestinationAddress(node.Network)); + + rpc.Capabilities.SupportGenerateToAddress = true; + rpc.Generate(1); + } rpc.Capabilities.SupportGenerateToAddress = false; - var blockHash3 = rpc.Generate(1); + rpc.Generate(1); - var heigh = rpc.GetBlockCount(); - Assert.Equal(3, heigh); + int height = rpc.GetBlockCount(); + Assert.Equal(sendToAddress ? 3 : 1, height); } } @@ -1708,9 +1721,16 @@ public void ShouldGetAddressInfo() using (var builder = NodeBuilderEx.Create()) { var client = builder.CreateNode(true).CreateRPCClient(); + var capabilities = client.ScanRPCCapabilities(); var addrLegacy = client.GetNewAddress(new GetNewAddressRequest() { AddressType = AddressType.Legacy }); - var addrBech32 = client.GetNewAddress(new GetNewAddressRequest() { AddressType = AddressType.Bech32 }); - var addrP2SHSegwit = client.GetNewAddress(new GetNewAddressRequest() { AddressType = AddressType.P2SHSegwit }); + + BitcoinAddress addrBech32 = null; + BitcoinAddress addrP2SHSegwit = null; + if (capabilities.SupportSegwit) + { + addrBech32 = client.GetNewAddress(new GetNewAddressRequest() { AddressType = AddressType.Bech32 }); + addrP2SHSegwit = client.GetNewAddress(new GetNewAddressRequest() { AddressType = AddressType.P2SHSegwit }); + } var pubkeys = new PubKey[] { new Key().PubKey, new Key().PubKey, new Key().PubKey }; var redeem = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, pubkeys); client.ImportAddress(redeem.Hash); @@ -1718,8 +1738,11 @@ public void ShouldGetAddressInfo() client.ImportAddress(redeem.WitHash.ScriptPubKey.Hash); Assert.NotNull(client.GetAddressInfo(addrLegacy)); - Assert.NotNull(client.GetAddressInfo(addrBech32)); - Assert.NotNull(client.GetAddressInfo(addrP2SHSegwit)); + if (capabilities.SupportSegwit) + { + Assert.NotNull(client.GetAddressInfo(addrBech32)); + Assert.NotNull(client.GetAddressInfo(addrP2SHSegwit)); + } Assert.NotNull(client.GetAddressInfo(redeem.Hash)); Assert.NotNull(client.GetAddressInfo(redeem.WitHash)); Assert.NotNull(client.GetAddressInfo(redeem.WitHash.ScriptPubKey.Hash)); diff --git a/NBitcoin/ConsensusFactory.cs b/NBitcoin/ConsensusFactory.cs index b4748c531c..50744cdb66 100644 --- a/NBitcoin/ConsensusFactory.cs +++ b/NBitcoin/ConsensusFactory.cs @@ -1,6 +1,8 @@ using NBitcoin.Protocol; using System; +using System.Collections.Generic; using System.Reflection; +using NBitcoin.Protocol.Behaviors; namespace NBitcoin { @@ -144,5 +146,10 @@ internal TransactionBuilder CreateTransactionBuilderCore2(Network network) { return CreateTransactionBuilderCore(network); } + + public virtual IEnumerable GetCustomBehaviors() + { + return null; + } } } diff --git a/NBitcoin/IBlockRepository.cs b/NBitcoin/IBlockRepository.cs index f32365dcf6..db2ee16867 100644 --- a/NBitcoin/IBlockRepository.cs +++ b/NBitcoin/IBlockRepository.cs @@ -4,6 +4,6 @@ namespace NBitcoin { public interface IBlockRepository { - Task GetBlockAsync(uint256 blockId); + Task GetBlockAsync(uint256 blockId, int verbosity); } } diff --git a/NBitcoin/Protocol/Node.cs b/NBitcoin/Protocol/Node.cs index ad4cfdbf9b..b79127e34a 100644 --- a/NBitcoin/Protocol/Node.cs +++ b/NBitcoin/Protocol/Node.cs @@ -840,6 +840,15 @@ private void InitDefaultBehaviors(NodeConnectionParameters parameters) { _Behaviors.Add(behavior.Clone()); } + + // Add any custom behaviors from ConsensusFactory + if (this.Network?.Consensus?.ConsensusFactory?.GetCustomBehaviors() != null) + { + foreach (var customBehavior in this.Network?.Consensus?.ConsensusFactory?.GetCustomBehaviors()) + { + _Behaviors.Add(customBehavior); + } + } _Behaviors.DelayAttach = false; } @@ -1674,4 +1683,4 @@ public void Dispose() } } } -#endif \ No newline at end of file +#endif diff --git a/NBitcoin/Protocol/PayloadAttribute.cs b/NBitcoin/Protocol/PayloadAttribute.cs index 8ecdf4040b..891317107e 100644 --- a/NBitcoin/Protocol/PayloadAttribute.cs +++ b/NBitcoin/Protocol/PayloadAttribute.cs @@ -33,6 +33,15 @@ static PayloadAttribute() } } + public static void Add(string name, Type type) + { + if (!_NameToType.ContainsKey(name)) + { + _NameToType.Add(name, type); + _TypeToName.Add(type, name); + } + } + static IEnumerable GetLoadableTypes(Assembly assembly) { try diff --git a/NBitcoin/RPC/RPCClient.cs b/NBitcoin/RPC/RPCClient.cs index f8244cef1e..0f60086f66 100644 --- a/NBitcoin/RPC/RPCClient.cs +++ b/NBitcoin/RPC/RPCClient.cs @@ -1273,9 +1273,9 @@ public async Task GetBlockHeaderAsync(uint height) /// /// /// - public async Task GetBlockAsync(uint256 blockId) + public async Task GetBlockAsync(uint256 blockId, int verbosity = 0) { - var resp = await SendCommandAsync(RPCOperations.getblock, blockId, false).ConfigureAwait(false); + var resp = await SendCommandAsync(RPCOperations.getblock, blockId, verbosity).ConfigureAwait(false); return Block.Parse(resp.Result.ToString(), Network); } @@ -1284,9 +1284,9 @@ public async Task GetBlockAsync(uint256 blockId) /// /// /// - public Block GetBlock(uint256 blockId) + public Block GetBlock(uint256 blockId, int verbosity = 0) { - return GetBlockAsync(blockId).GetAwaiter().GetResult(); + return GetBlockAsync(blockId, verbosity).GetAwaiter().GetResult(); } public Block GetBlock(int height) diff --git a/NBitcoin/RPC/RestClient.cs b/NBitcoin/RPC/RestClient.cs index eaf5f2af10..9a0178599d 100644 --- a/NBitcoin/RPC/RestClient.cs +++ b/NBitcoin/RPC/RestClient.cs @@ -76,23 +76,25 @@ public RestClient(Uri address, Network network) /// The block identifier. /// Given a block hash (id) returns the requested block object. /// blockId cannot be null. - public async Task GetBlockAsync(uint256 blockId) + public async Task GetBlockAsync(uint256 blockId, RestResponseFormat verbosity = RestResponseFormat.Bin) { if (blockId == null) throw new ArgumentNullException(nameof(blockId)); - var result = await SendRequestAsync("block", RestResponseFormat.Bin, blockId.ToString()).ConfigureAwait(false); + var result = await SendRequestAsync("block", verbosity, blockId.ToString()).ConfigureAwait(false); return Block.Load(result, Network); } + /// /// Gets the block. /// /// The block identifier. /// Given a block hash (id) returns the requested block object. /// blockId cannot be null. - public Block GetBlock(uint256 blockId) + public async Task GetBlockAsync(uint256 blockId, int verbosity) { - return GetBlockAsync(blockId).GetAwaiter().GetResult(); + var result = await GetBlockAsync(blockId, (RestResponseFormat)verbosity).ConfigureAwait(false); + return result; } /// @@ -112,6 +114,7 @@ public async Task GetTransactionAsync(uint256 txId) tx.ReadWrite(result, Network); return tx; } + /// /// Gets a transaction. /// @@ -312,4 +315,4 @@ public bool IsPruned } } } -#endif \ No newline at end of file +#endif diff --git a/NBitcoin/Utils.cs b/NBitcoin/Utils.cs index fe8fa1aa81..d7446e74dd 100644 --- a/NBitcoin/Utils.cs +++ b/NBitcoin/Utils.cs @@ -131,7 +131,7 @@ public static async Task WithCancellation(this Task task, CancellationT public static Block GetBlock(this IBlockRepository repository, uint256 blockId) { - return repository.GetBlockAsync(blockId).GetAwaiter().GetResult(); + return repository.GetBlockAsync(blockId, 0).GetAwaiter().GetResult(); } public static T ToNetwork(this T obj, NetworkType networkType) where T : IBitcoinString From 2ffd29fd8c3ec8885350cd8b73cc17354e55c715 Mon Sep 17 00:00:00 2001 From: Kevin Loubser Date: Mon, 3 Feb 2020 23:04:13 +0200 Subject: [PATCH 2/5] Add overloads instead of default params --- NBitcoin/IBlockRepository.cs | 2 ++ NBitcoin/RPC/RPCClient.cs | 31 +++++++++++++++++++++++++------ NBitcoin/RPC/RestClient.cs | 12 +++++------- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/NBitcoin/IBlockRepository.cs b/NBitcoin/IBlockRepository.cs index db2ee16867..e8c38c50a5 100644 --- a/NBitcoin/IBlockRepository.cs +++ b/NBitcoin/IBlockRepository.cs @@ -4,6 +4,8 @@ namespace NBitcoin { public interface IBlockRepository { + Task GetBlockAsync(uint256 blockId); + Task GetBlockAsync(uint256 blockId, int verbosity); } } diff --git a/NBitcoin/RPC/RPCClient.cs b/NBitcoin/RPC/RPCClient.cs index 0f60086f66..dfff9b4c39 100644 --- a/NBitcoin/RPC/RPCClient.cs +++ b/NBitcoin/RPC/RPCClient.cs @@ -1269,22 +1269,41 @@ public async Task GetBlockHeaderAsync(uint height) } /// - /// Get the a whole block + /// Get a whole block /// /// - /// - public async Task GetBlockAsync(uint256 blockId, int verbosity = 0) + public async Task GetBlockAsync(uint256 blockId) + { + var resp = await SendCommandAsync(RPCOperations.getblock, blockId, 0).ConfigureAwait(false); + return Block.Parse(resp.Result.ToString(), Network); + } + + /// + /// Get a whole block + /// + /// The hash of the block to be retrieved + /// /// 0 = hex only, 1 = json including transaction id list, 2 = json including parsed transactions + public async Task GetBlockAsync(uint256 blockId, int verbosity) { var resp = await SendCommandAsync(RPCOperations.getblock, blockId, verbosity).ConfigureAwait(false); return Block.Parse(resp.Result.ToString(), Network); } /// - /// Get the a whole block + /// Get a whole block /// /// - /// - public Block GetBlock(uint256 blockId, int verbosity = 0) + public Block GetBlock(uint256 blockId) + { + return GetBlockAsync(blockId, 0).GetAwaiter().GetResult(); + } + + /// + /// Get a whole block + /// + /// The hash of the block to be retrieved + /// 0 = hex only, 1 = json including transaction id list, 2 = json including parsed transactions + public Block GetBlock(uint256 blockId, int verbosity) { return GetBlockAsync(blockId, verbosity).GetAwaiter().GetResult(); } diff --git a/NBitcoin/RPC/RestClient.cs b/NBitcoin/RPC/RestClient.cs index 9a0178599d..d861ff6c84 100644 --- a/NBitcoin/RPC/RestClient.cs +++ b/NBitcoin/RPC/RestClient.cs @@ -76,24 +76,22 @@ public RestClient(Uri address, Network network) /// The block identifier. /// Given a block hash (id) returns the requested block object. /// blockId cannot be null. - public async Task GetBlockAsync(uint256 blockId, RestResponseFormat verbosity = RestResponseFormat.Bin) + public async Task GetBlockAsync(uint256 blockId) { - if (blockId == null) - throw new ArgumentNullException(nameof(blockId)); - - var result = await SendRequestAsync("block", verbosity, blockId.ToString()).ConfigureAwait(false); - return Block.Load(result, Network); + var result = await GetBlockAsync(blockId, (int)RestResponseFormat.Hex).ConfigureAwait(false); + return result; } /// /// Gets the block. /// /// The block identifier. + /// 0 = bin, 1 = hex, 2 = json /// Given a block hash (id) returns the requested block object. /// blockId cannot be null. public async Task GetBlockAsync(uint256 blockId, int verbosity) { - var result = await GetBlockAsync(blockId, (RestResponseFormat)verbosity).ConfigureAwait(false); + var result = await GetBlockAsync(blockId, verbosity).ConfigureAwait(false); return result; } From 122c7db09c30cee7ee5456c249e295c1782c5cb0 Mon Sep 17 00:00:00 2001 From: Kevin Loubser Date: Mon, 3 Feb 2020 23:15:32 +0200 Subject: [PATCH 3/5] Use correct implementation of get block --- NBitcoin/RPC/RestClient.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/NBitcoin/RPC/RestClient.cs b/NBitcoin/RPC/RestClient.cs index d861ff6c84..c98c28559a 100644 --- a/NBitcoin/RPC/RestClient.cs +++ b/NBitcoin/RPC/RestClient.cs @@ -30,7 +30,6 @@ public class RestClient : IBlockRepository private readonly Uri _address; private readonly Network _network; - /// /// Gets the instance for the client. /// @@ -78,7 +77,7 @@ public RestClient(Uri address, Network network) /// blockId cannot be null. public async Task GetBlockAsync(uint256 blockId) { - var result = await GetBlockAsync(blockId, (int)RestResponseFormat.Hex).ConfigureAwait(false); + var result = await GetBlockAsync(blockId, (int)RestResponseFormat.Bin).ConfigureAwait(false); return result; } @@ -91,8 +90,11 @@ public async Task GetBlockAsync(uint256 blockId) /// blockId cannot be null. public async Task GetBlockAsync(uint256 blockId, int verbosity) { - var result = await GetBlockAsync(blockId, verbosity).ConfigureAwait(false); - return result; + if (blockId == null) + throw new ArgumentNullException(nameof(blockId)); + + var result = await SendRequestAsync("block", (RestResponseFormat)verbosity, blockId.ToString()).ConfigureAwait(false); + return Block.Load(result, Network); } /// From d6768dc6490cfa2ddfe3f8c7a623c5fb9fb8dd45 Mon Sep 17 00:00:00 2001 From: Kevin Loubser Date: Fri, 14 Feb 2020 11:44:53 +0200 Subject: [PATCH 4/5] Fix per review --- NBitcoin/Protocol/PayloadAttribute.cs | 37 +++++++++++++++++++-------- NBitcoin/RPC/RestClient.cs | 7 +++-- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/NBitcoin/Protocol/PayloadAttribute.cs b/NBitcoin/Protocol/PayloadAttribute.cs index 891317107e..36778dd7f3 100644 --- a/NBitcoin/Protocol/PayloadAttribute.cs +++ b/NBitcoin/Protocol/PayloadAttribute.cs @@ -12,11 +12,13 @@ public class PayloadAttribute : Attribute { static Dictionary _NameToType; static Dictionary _TypeToName; + static object _lockObject; static PayloadAttribute() { _NameToType = new Dictionary(); _TypeToName = new Dictionary(); + _lockObject = new object(); foreach (var pair in GetLoadableTypes(typeof(PayloadAttribute).GetTypeInfo().Assembly) .Where(t => t.Namespace == typeof(PayloadAttribute).Namespace) @@ -28,17 +30,23 @@ static PayloadAttribute() Type = t })) { - _NameToType.Add(pair.Attr.Name, pair.Type.AsType()); - _TypeToName.Add(pair.Type.AsType(), pair.Attr.Name); + lock (_lockObject) + { + _NameToType.Add(pair.Attr.Name, pair.Type.AsType()); + _TypeToName.Add(pair.Type.AsType(), pair.Attr.Name); + } } } public static void Add(string name, Type type) { - if (!_NameToType.ContainsKey(name)) + lock (_lockObject) { - _NameToType.Add(name, type); - _TypeToName.Add(type, name); + if (!_NameToType.ContainsKey(name)) + { + _NameToType.Add(name, type); + _TypeToName.Add(type, name); + } } } @@ -61,8 +69,12 @@ public static string GetCommandName() public static Type GetCommandType(string commandName) { Type result; - if (!_NameToType.TryGetValue(commandName, out result)) - return typeof(UnknowPayload); + lock (_lockObject) + { + if (!_NameToType.TryGetValue(commandName, out result)) + return typeof(UnknowPayload); + } + return result; } public PayloadAttribute(string commandName) @@ -78,11 +90,14 @@ public string Name internal static string GetCommandName(Type type) { string result; - if (!_TypeToName.TryGetValue(type, out result)) + lock (_lockObject) { - // try base type too - if (!_TypeToName.TryGetValue(type.GetTypeInfo().BaseType, out result)) - throw new ArgumentException(type.FullName + " is not a payload"); + if (!_TypeToName.TryGetValue(type, out result)) + { + // try base type too + if (!_TypeToName.TryGetValue(type.GetTypeInfo().BaseType, out result)) + throw new ArgumentException(type.FullName + " is not a payload"); + } } return result; diff --git a/NBitcoin/RPC/RestClient.cs b/NBitcoin/RPC/RestClient.cs index c98c28559a..3decc4fe2c 100644 --- a/NBitcoin/RPC/RestClient.cs +++ b/NBitcoin/RPC/RestClient.cs @@ -77,8 +77,11 @@ public RestClient(Uri address, Network network) /// blockId cannot be null. public async Task GetBlockAsync(uint256 blockId) { - var result = await GetBlockAsync(blockId, (int)RestResponseFormat.Bin).ConfigureAwait(false); - return result; + if (blockId == null) + throw new ArgumentNullException(nameof(blockId)); + + var result = await SendRequestAsync("block", RestResponseFormat.Bin, blockId.ToString()).ConfigureAwait(false); + return Block.Load(result, Network); } /// From 71b262f952aa52a244777af83a9fd9d113fd8eaa Mon Sep 17 00:00:00 2001 From: Kevin Loubser Date: Wed, 19 Feb 2020 23:00:31 +0200 Subject: [PATCH 5/5] Make another test network agnostic --- NBitcoin.Tests/RPCClientTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NBitcoin.Tests/RPCClientTests.cs b/NBitcoin.Tests/RPCClientTests.cs index 5466affaa5..572604e884 100644 --- a/NBitcoin.Tests/RPCClientTests.cs +++ b/NBitcoin.Tests/RPCClientTests.cs @@ -941,7 +941,7 @@ public void RawTransactionIsConformsToRPC() { var rpc = builder.CreateNode().CreateRPCClient(); builder.StartAll(); - var tx = Network.TestNet.GetGenesis().Transactions[0]; + var tx = builder.Network.GetGenesis().Transactions[0]; var tx2 = rpc.DecodeRawTransaction(tx.ToBytes()); AssertJsonEquals(tx.ToString(RawFormat.Satoshi), tx2.ToString(RawFormat.Satoshi));