Skip to content

Commit c298c3e

Browse files
dangershonyzeptin
andauthored
Adding support for Strax (#355)
* Initial STRAX support * update checkpoints Co-authored-by: Kevin Loubser <zeptin@gmail.com>
1 parent 7447b31 commit c298c3e

24 files changed

Lines changed: 1812 additions & 2 deletions

src/Blockcore.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blockcore.Networks.Impleum"
133133
EndProject
134134
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blockcore.Networks.BCP", "Networks\Blockcore.Networks.BCP\Blockcore.Networks.BCP.csproj", "{120500DF-C04F-43CC-9A1E-523A6429301F}"
135135
EndProject
136+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blockcore.Networks.Strax", "Networks\Blockcore.Networks.Strax\Blockcore.Networks.Strax.csproj", "{17330538-D54C-44B0-85AD-D652453FAD65}"
137+
EndProject
136138
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blockcore.Networks.X1", "Networks\Blockcore.Networks.X1\Blockcore.Networks.X1.csproj", "{9A2BA15A-C316-42B4-8E4D-E01B4873190C}"
137139
EndProject
138140
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blockcore.Networks.XRC", "Networks\Blockcore.Networks.XRC\Blockcore.Networks.XRC.csproj", "{4615D1C6-14CD-47CA-8B78-A462A37057F6}"
@@ -364,6 +366,10 @@ Global
364366
{120500DF-C04F-43CC-9A1E-523A6429301F}.Debug|Any CPU.Build.0 = Debug|Any CPU
365367
{120500DF-C04F-43CC-9A1E-523A6429301F}.Release|Any CPU.ActiveCfg = Release|Any CPU
366368
{120500DF-C04F-43CC-9A1E-523A6429301F}.Release|Any CPU.Build.0 = Release|Any CPU
369+
{17330538-D54C-44B0-85AD-D652453FAD65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
370+
{17330538-D54C-44B0-85AD-D652453FAD65}.Debug|Any CPU.Build.0 = Debug|Any CPU
371+
{17330538-D54C-44B0-85AD-D652453FAD65}.Release|Any CPU.ActiveCfg = Release|Any CPU
372+
{17330538-D54C-44B0-85AD-D652453FAD65}.Release|Any CPU.Build.0 = Release|Any CPU
367373
{9A2BA15A-C316-42B4-8E4D-E01B4873190C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
368374
{9A2BA15A-C316-42B4-8E4D-E01B4873190C}.Debug|Any CPU.Build.0 = Debug|Any CPU
369375
{9A2BA15A-C316-42B4-8E4D-E01B4873190C}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -440,6 +446,7 @@ Global
440446
{4275AF0C-587B-4C9D-A100-0F2DD1702674} = {64694A14-97E0-4CBC-8032-754F9353B2DD}
441447
{64E9C309-867E-45F6-A88E-7BC061305D0B} = {3B56C02B-4468-4268-B797-851562789FCC}
442448
{120500DF-C04F-43CC-9A1E-523A6429301F} = {3B56C02B-4468-4268-B797-851562789FCC}
449+
{17330538-D54C-44B0-85AD-D652453FAD65} = {3B56C02B-4468-4268-B797-851562789FCC}
443450
{9A2BA15A-C316-42B4-8E4D-E01B4873190C} = {3B56C02B-4468-4268-B797-851562789FCC}
444451
{4615D1C6-14CD-47CA-8B78-A462A37057F6} = {3B56C02B-4468-4268-B797-851562789FCC}
445452
{074057D3-A1BE-409D-B4B4-F01CFA434D58} = {3B56C02B-4468-4268-B797-851562789FCC}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<AssemblyTitle>Blockcore.Networks.Strax</AssemblyTitle>
5+
<AssemblyName>Blockcore.Networks.Strax</AssemblyName>
6+
<PackageId>Blockcore.Networks.Strax</PackageId>
7+
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
8+
<IsPackable>true</IsPackable>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<EmbeddedResource Include="icon.png" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\..\Features\Blockcore.Features.Consensus\Blockcore.Features.Consensus.csproj" />
17+
<ProjectReference Include="..\..\Features\Blockcore.Features.MemoryPool\Blockcore.Features.MemoryPool.csproj" />
18+
</ItemGroup>
19+
</Project>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using Blockcore.Base.Deployments;
2+
using Blockcore.Consensus.ScriptInfo;
3+
using Blockcore.Consensus.TransactionInfo;
4+
5+
namespace Blockcore.Networks.Strax.Deployments
6+
{
7+
/// <summary>
8+
/// BIP9 deployments for the Strax network.
9+
/// </summary>
10+
public class StraxBIP9Deployments : BIP9DeploymentsArray
11+
{
12+
// The position of each deployment in the deployments array. Note that this is decoupled from the actual position of the flag bit for the deployment in the block version.
13+
public const int CSV = 0;
14+
public const int Segwit = 1;
15+
public const int ColdStaking = 2;
16+
17+
// The number of deployments.
18+
public const int NumberOfDeployments = 3;
19+
20+
/// <summary>
21+
/// Constructs the BIP9 deployments array.
22+
/// </summary>
23+
public StraxBIP9Deployments() : base(NumberOfDeployments)
24+
{
25+
}
26+
27+
/// <summary>
28+
/// Gets the deployment flags to set when the deployment activates.
29+
/// </summary>
30+
/// <param name="deployment">The deployment number.</param>
31+
/// <returns>The deployment flags.</returns>
32+
public override BIP9DeploymentFlags GetFlags(int deployment)
33+
{
34+
// The flags get combined in the caller, so it is ok to make a fresh object here.
35+
var flags = new BIP9DeploymentFlags();
36+
37+
switch (deployment)
38+
{
39+
case ColdStaking:
40+
flags.ScriptFlags = ScriptVerify.CheckColdStakeVerify;
41+
break;
42+
43+
case CSV:
44+
// Start enforcing BIP68 (sequence locks), BIP112 (CHECKSEQUENCEVERIFY) and BIP113 (Median Time Past) using versionbits logic.
45+
flags.ScriptFlags = ScriptVerify.CheckSequenceVerify;
46+
flags.LockTimeFlags = Transaction.LockTimeFlags.VerifySequence | Transaction.LockTimeFlags.MedianTimePast;
47+
break;
48+
49+
case Segwit:
50+
// Start enforcing WITNESS rules using versionbits logic.
51+
flags.ScriptFlags = ScriptVerify.Witness;
52+
break;
53+
}
54+
55+
return flags;
56+
}
57+
}
58+
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Blockcore.Consensus.ScriptInfo;
5+
using Blockcore.Networks.Strax.ScriptTemplates;
6+
using NBitcoin;
7+
8+
namespace Blockcore.Networks.Strax.Federation
9+
{
10+
public interface IFederation
11+
{
12+
Script MultisigScript { get; }
13+
FederationId Id { get; }
14+
15+
(PubKey[] transactionSigningKeys, int signaturesRequired) GetFederationDetails();
16+
}
17+
18+
/// <summary>
19+
/// Compares two byte arrays for equality.
20+
/// </summary>
21+
public sealed class ByteArrayComparer : IEqualityComparer<byte[]>, IComparer<byte[]>
22+
{
23+
public int Compare(byte[] first, byte[] second)
24+
{
25+
int firstLen = first?.Length ?? -1;
26+
int secondLen = second?.Length ?? -1;
27+
int commonLen = Math.Min(firstLen, secondLen);
28+
29+
for (int i = 0; i < commonLen; i++)
30+
{
31+
if (first[i] == second[i])
32+
continue;
33+
34+
return (first[i] < second[i]) ? -1 : 1;
35+
}
36+
37+
return firstLen.CompareTo(secondLen);
38+
}
39+
40+
public bool Equals(byte[] first, byte[] second)
41+
{
42+
return this.Compare(first, second) == 0;
43+
}
44+
45+
public int GetHashCode(byte[] obj)
46+
{
47+
ulong hash = 17;
48+
49+
foreach (byte objByte in obj)
50+
{
51+
hash = (hash << 5) - hash + objByte;
52+
}
53+
54+
return (int)hash;
55+
}
56+
}
57+
58+
public class FederationId : IBitcoinSerializable
59+
{
60+
byte[] federationId;
61+
ByteArrayComparer comparer;
62+
63+
public FederationId()
64+
{
65+
}
66+
67+
public FederationId(byte[] value)
68+
{
69+
this.federationId = value;
70+
this.comparer = new ByteArrayComparer();
71+
}
72+
73+
public void ReadWrite(BitcoinStream s)
74+
{
75+
s.ReadWrite(ref this.federationId);
76+
}
77+
78+
public override bool Equals(object obj)
79+
{
80+
return this.comparer.Equals(((FederationId)obj).federationId, this.federationId);
81+
}
82+
83+
public override int GetHashCode()
84+
{
85+
return this.comparer.GetHashCode(this.federationId);
86+
}
87+
}
88+
89+
public class Federation : IFederation
90+
{
91+
private PubKey[] transactionSigningKeys;
92+
93+
private int signaturesRequired;
94+
95+
public Script MultisigScript { get; private set; }
96+
97+
public FederationId Id { get; private set; }
98+
99+
/// <summary>
100+
/// Creates a new federation from a set of transaction signing keys.
101+
/// </summary>
102+
/// <param name="transactionSigningPubKeys">A list of transaction signing PubKeys.</param>
103+
/// <param name="signaturesRequired">The amount of signatures required to ensure that the transaction is fully signed.</param>
104+
public Federation(IEnumerable<PubKey> transactionSigningPubKeys, int? signaturesRequired = null)
105+
{
106+
// Ensures that the federation id will always map to the same members in the same order.
107+
this.transactionSigningKeys = transactionSigningPubKeys.OrderBy(k => k.ToHex()).ToArray();
108+
this.signaturesRequired = signaturesRequired ?? (this.transactionSigningKeys.Length + 1) / 2;
109+
110+
// The federationId is derived by XOR'ing all the genesis federation members.
111+
byte[] federationId = this.transactionSigningKeys.First().ToBytes();
112+
foreach (PubKey pubKey in this.transactionSigningKeys.Skip(1))
113+
{
114+
byte[] pubKeyBytes = pubKey.ToBytes();
115+
for (int i = 0; i < federationId.Length; i++)
116+
federationId[i] ^= pubKeyBytes[i];
117+
}
118+
119+
this.Id = new FederationId(federationId);
120+
this.MultisigScript = PayToFederationTemplate.Instance.GenerateScriptPubKey(this.Id);
121+
}
122+
123+
public (PubKey[] transactionSigningKeys, int signaturesRequired) GetFederationDetails()
124+
{
125+
// Until dynamic membership is implemented we just return the genesis members.
126+
return (this.transactionSigningKeys, this.signaturesRequired);
127+
}
128+
}
129+
130+
public interface IFederations
131+
{
132+
/// <summary>
133+
/// Registers a new federation with transaction signing keys.
134+
/// </summary>
135+
/// <param name="federation">The federation to be registered.</param>
136+
void RegisterFederation(IFederation federation);
137+
138+
IFederation GetFederation(FederationId federationId);
139+
140+
IFederation GetFederation(byte[] federationId);
141+
142+
IFederation GetOnlyFederation();
143+
}
144+
145+
public class Federations : IFederations
146+
{
147+
private readonly Dictionary<FederationId, IFederation> federations;
148+
149+
public Federations()
150+
{
151+
this.federations = new Dictionary<FederationId, IFederation>();
152+
}
153+
154+
public IFederation GetFederation(FederationId federationId)
155+
{
156+
return this.federations.TryGetValue(federationId, out IFederation federation) ? federation : null;
157+
}
158+
159+
public IFederation GetFederation(byte[] federationId)
160+
{
161+
return this.federations.TryGetValue(new FederationId(federationId), out IFederation federation) ? federation : null;
162+
}
163+
164+
// TODO: Deprectate this method when multiple federations are supported.
165+
public IFederation GetOnlyFederation()
166+
{
167+
return this.federations.First().Value;
168+
}
169+
170+
public void RegisterFederation(IFederation federation)
171+
{
172+
// TODO: Remove this when multiple federations are supported.
173+
this.federations.Clear();
174+
175+
this.federations[federation.Id] = federation;
176+
}
177+
}
178+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace Blockcore.Networks.Strax
2+
{
3+
public static class Networks
4+
{
5+
public static NetworksSelector Strax
6+
{
7+
get
8+
{
9+
return new NetworksSelector(() => new StraxMain(), () => new StraxTest(), () => new StraxRegTest());
10+
}
11+
}
12+
}
13+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System.Collections.Generic;
2+
using Blockcore.Consensus.ScriptInfo;
3+
using Blockcore.Networks.Strax.ScriptTemplates;
4+
5+
namespace Blockcore.Networks.Strax.Policies
6+
{
7+
/// <summary>
8+
/// Strax-specific standard transaction definitions.
9+
/// </summary>
10+
public class StraxStandardScriptsRegistry : StandardScriptsRegistry
11+
{
12+
public const int MaxOpReturnRelay = 83;
13+
14+
// Need a network-specific version of the template list
15+
private static readonly List<ScriptTemplate> standardTemplates = new List<ScriptTemplate>
16+
{
17+
new PayToPubkeyHashTemplate(),
18+
new PayToPubkeyTemplate(),
19+
new PayToScriptHashTemplate(),
20+
new PayToMultiSigTemplate(),
21+
new PayToFederationTemplate(),
22+
new TxNullDataTemplate(MaxOpReturnRelay),
23+
new PayToWitTemplate()
24+
};
25+
26+
public override List<ScriptTemplate> GetScriptTemplates => standardTemplates;
27+
}
28+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using Blockcore.Consensus;
2+
using Blockcore.Consensus.Chain;
3+
using Blockcore.Consensus.Rules;
4+
using Blockcore.Features.Consensus.Rules.CommonRules;
5+
using Blockcore.Utilities;
6+
using Microsoft.Extensions.Logging;
7+
8+
namespace Blockcore.Networks.Strax.Rules
9+
{
10+
/// <summary>
11+
/// Checks if <see cref="StraxMain"/> network block's header has a valid block version.
12+
/// </summary>
13+
public class StratisHeaderVersionRule : HeaderVersionRule
14+
{
15+
/// <inheritdoc />
16+
/// <exception cref="ConsensusErrors.BadVersion">Thrown if block's version is outdated or otherwise invalid.</exception>
17+
public override void Run(RuleContext context)
18+
{
19+
Guard.NotNull(context.ValidationContext.ChainedHeaderToValidate, nameof(context.ValidationContext.ChainedHeaderToValidate));
20+
21+
ChainedHeader chainedHeader = context.ValidationContext.ChainedHeaderToValidate;
22+
23+
// A version of precisely 7 is what was generated by the legacy stratisX C++ nodes.
24+
25+
// The stratisX block validation rules mandate if (!IsProtocolV3(nTime)) && (nVersion > 7), then reject block.
26+
// Further, if (IsProtocolV2(nHeight) && nVersion < 7), then reject block.
27+
// And lastly, if (!IsProtocolV2(nHeight) && nVersion > 6), then reject block.
28+
29+
// Protocol version determination is based on either the block height or timestamp as shown:
30+
// IsProtocolV2(nHeight) { return TestNet() || nHeight > 0; }
31+
// IsProtocolV3(nTime) { return TestNet() || nTime > 1470467000; }
32+
33+
// The Stratis mainnet genesis block has nTime = 1470713393, so V3 is applied immediately and this supersedes V2.
34+
// The block versions have therefore been version 7 since genesis on Stratis mainnet.
35+
36+
// Whereas BIP9 mandates that the top bits of version be 001. So a standard node should never generate
37+
// block versions above 7 and below 0x20000000.
38+
39+
// The acceptable common subset of the rules is therefore that the block version must be >= 7.
40+
41+
if (chainedHeader.Header.Version < 7)
42+
{
43+
this.Logger.LogTrace("(-)[BAD_VERSION]");
44+
ConsensusErrors.BadVersion.Throw();
45+
}
46+
}
47+
}
48+
}

0 commit comments

Comments
 (0)