Skip to content

Commit 232af8d

Browse files
committed
Adds the ability to register custom Bitcoin signets.
Signed-off-by: Aaron Clauson <[email protected]>
1 parent 52fb9fb commit 232af8d

File tree

2 files changed

+78
-12
lines changed

2 files changed

+78
-12
lines changed

NBitcoin.Tests/NetworkTests.cs

100644100755
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using NBitcoin;
55
using Xunit;
66
using System;
7+
using NBitcoin.Crypto;
78

89
namespace NBitcoin.Tests
910
{
@@ -64,5 +65,41 @@ public void ReadMagicByteWithFirstByteDuplicated()
6465
Assert.True(found);
6566
}
6667
}
68+
69+
[Fact]
70+
[Trait("UnitTest", "UnitTest")]
71+
public void CanGetDefaultSignet()
72+
{
73+
var signet = Network.GetNetwork("signet");
74+
75+
Assert.NotNull(signet);
76+
77+
var consensusFactory = new ConsensusFactory();
78+
var block = consensusFactory.CreateBlock();
79+
block.ReadWrite(DataEncoders.Encoders.Hex.DecodeData(Bitcoin.DEFAULT_SIGNET_GENESIS_BLOCK), consensusFactory);
80+
81+
Assert.Equal(block.GetHash(), signet.GenesisHash);
82+
}
83+
84+
[Fact]
85+
[Trait("UnitTest", "UnitTest")]
86+
public void CanGetCustomSignet()
87+
{
88+
var customSignetName = "signet-custom";
89+
var customSignetChallenge = "5121033da06bd7068e9859ee902a0608df9b948829718c60c587f2e497ad4d7420e43151AE";
90+
var customSignetGenesisBlock = "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a008f4d5fae77031e8ad222030101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000";
91+
92+
var initSignet = Bitcoin.Instance.InitCustomSignet(customSignetName, customSignetChallenge, customSignetGenesisBlock);
93+
94+
Assert.NotNull(initSignet);
95+
96+
var customSignet = Network.GetNetwork(customSignetName);
97+
98+
var consensusFactory = new ConsensusFactory();
99+
var block = consensusFactory.CreateBlock();
100+
block.ReadWrite(DataEncoders.Encoders.Hex.DecodeData(customSignetGenesisBlock), consensusFactory);
101+
102+
Assert.Equal(block.GetHash(), customSignet.GenesisHash);
103+
}
67104
}
68105
}

NBitcoin/Bitcoin.cs

100644100755
Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,23 @@ namespace NBitcoin
1010
{
1111
public class Bitcoin : INetworkSet
1212
{
13+
public const string DEFAULT_SIGNET_NAME = "signet";
14+
15+
public const string DEFAULT_SIGNET_CHALLENGE = "512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae";
16+
17+
public const string DEFAULT_SIGNET_GENESIS_BLOCK = "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a008f4d5fae77031e8ad222030101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000";
18+
1319
private Bitcoin()
1420
{
1521
}
1622

17-
private Network CreateSignet()
23+
private Network CreateSignet(
24+
string name = DEFAULT_SIGNET_NAME,
25+
string challenge = DEFAULT_SIGNET_CHALLENGE,
26+
string genesisBlock = DEFAULT_SIGNET_GENESIS_BLOCK)
1827
{
1928
NetworkBuilder builder = new NetworkBuilder();
20-
builder.SetChainName(SignetName);
29+
builder.SetChainName(name == DEFAULT_SIGNET_NAME ? SignetName : new ChainName(name));
2130
builder.SetNetworkSet(this);
2231
builder.SetConsensus(new Consensus()
2332
{
@@ -32,7 +41,7 @@ private Network CreateSignet()
3241
PowAllowMinDifficultyBlocks = false,
3342
PowNoRetargeting = false,
3443
RuleChangeActivationThreshold = 1916,
35-
MinerConfirmationWindow = 2016,
44+
MinerConfirmationWindow = 2016,
3645
CoinbaseMaturity = 100,
3746
SupportSegwit = true,
3847
SupportTaproot = true
@@ -45,35 +54,40 @@ private Network CreateSignet()
4554
.SetBech32(Bech32Type.WITNESS_PUBKEY_ADDRESS, "tb")
4655
.SetBech32(Bech32Type.WITNESS_SCRIPT_ADDRESS, "tb")
4756
.SetBech32(Bech32Type.TAPROOT_ADDRESS, "tb")
48-
.SetMagic(GetSignetMagic())
57+
.SetMagic(GetSignetMagic(challenge))
4958
.SetPort(38333)
5059
.SetRPCPort(38332)
51-
.SetName("signet")
52-
.AddAlias("bitcoin-signet")
53-
.AddAlias("btc-signet")
60+
.SetName(name)
5461
#if !NOSOCKET
5562
.AddSeeds(new[]
5663
{
5764
"178.128.221.177",
5865
"2a01:7c8:d005:390::5"
5966
}.Select(o => new Protocol.NetworkAddress(System.Net.IPAddress.Parse(o))))
6067
#endif
61-
.SetGenesis("0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a008f4d5fae77031e8ad222030101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000");
68+
.SetGenesis(genesisBlock);
69+
70+
if (name == DEFAULT_SIGNET_NAME)
71+
{
72+
builder.AddAlias("bitcoin-signet");
73+
builder.AddAlias("btc-signet");
74+
}
75+
6276
var network = builder.BuildAndRegister();
6377
#if !NOFILEIO
6478
var data = Network.GetDefaultDataFolder("bitcoin");
6579
if (data != null)
6680
{
67-
var signetCookie = Path.Combine(data, "signet", ".cookie");
81+
var signetCookie = Path.Combine(data, DEFAULT_SIGNET_NAME, ".cookie");
6882
RPC.RPCClient.RegisterDefaultCookiePath(network, signetCookie);
6983
}
7084
#endif
7185
return network;
7286
}
7387

74-
private static uint GetSignetMagic()
88+
private static uint GetSignetMagic(string challengeHex)
7589
{
76-
var challengeBytes = DataEncoders.Encoders.Hex.DecodeData("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae");
90+
var challengeBytes = DataEncoders.Encoders.Hex.DecodeData(challengeHex);
7791
var challenge = new Script(challengeBytes);
7892
MemoryStream ms = new MemoryStream();
7993
BitcoinStream bitcoinStream = new BitcoinStream(ms, true);
@@ -99,9 +113,12 @@ public Network Signet
99113
{
100114
get
101115
{
102-
return _Signet ??= Network.GetNetwork("signet");
116+
return _Signet ??= Network.GetNetwork(DEFAULT_SIGNET_NAME);
103117
}
104118
}
119+
120+
static Dictionary<ChainName, Network> _CustomSignets = new Dictionary<ChainName, Network>();
121+
105122
public Network GetNetwork(ChainName chainName)
106123
{
107124
if (chainName == null)
@@ -114,12 +131,24 @@ public Network GetNetwork(ChainName chainName)
114131
return Regtest;
115132
if (chainName == SignetName)
116133
return Signet;
134+
if (_CustomSignets.TryGetValue(chainName, out var network))
135+
return network;
117136
return null;
118137
}
119138

120139
internal Network InitSignet()
121140
{
122141
return _Signet = CreateSignet();
123142
}
143+
144+
public Network InitCustomSignet(string name, string challenge, string genesisBlock)
145+
{
146+
if (_CustomSignets.TryGetValue(new ChainName(name), out var network))
147+
return network;
148+
149+
var customSignet = CreateSignet(name, challenge ?? DEFAULT_SIGNET_CHALLENGE, genesisBlock ?? DEFAULT_SIGNET_GENESIS_BLOCK);
150+
_CustomSignets.Add(customSignet.ChainName, customSignet);
151+
return customSignet;
152+
}
124153
}
125154
}

0 commit comments

Comments
 (0)