Skip to content

Commit

Permalink
chore(net): implement block hash calculation (#625)
Browse files Browse the repository at this point in the history
  • Loading branch information
DennisInSky authored Nov 1, 2024
1 parent 6fe755a commit 9d3e18d
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 10 deletions.
10 changes: 8 additions & 2 deletions Sails.Net.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Substrate.Gear.Api", "net\s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sails.Remoting.Abstractions", "net\src\Sails.Remoting.Abstractions\Sails.Remoting.Abstractions.csproj", "{0442F9DE-9775-4787-866B-22869C681995}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sails.Remoting", "net\src\Sails.Remoting\Sails.Remoting.csproj", "{4C15C311-F328-480C-A102-3DCDC2C0AF11}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sails.Remoting", "net\src\Sails.Remoting\Sails.Remoting.csproj", "{4C15C311-F328-480C-A102-3DCDC2C0AF11}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{17A264B0-419F-46D2-8ACE-662FB478E570}"
ProjectSection(SolutionItems) = preProject
net\Directory.Build.props = net\Directory.Build.props
net\Directory.Packages.props = net\Directory.Packages.props
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Substrate.Gear.Client", "net\src\Substrate.Gear.Client\Substrate.Gear.Client.csproj", "{1589D5A4-0CC4-4855-89E0-2E61BBC5E0B0}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Substrate.Gear.Client", "net\src\Substrate.Gear.Client\Substrate.Gear.Client.csproj", "{1589D5A4-0CC4-4855-89E0-2E61BBC5E0B0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Substrate.Gear.Client.Tests", "net\tests\Substrate.Gear.Client.Tests\Substrate.Gear.Client.Tests.csproj", "{42B621CE-C2B4-4911-961C-5B087A514AF5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -45,6 +47,10 @@ Global
{1589D5A4-0CC4-4855-89E0-2E61BBC5E0B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1589D5A4-0CC4-4855-89E0-2E61BBC5E0B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1589D5A4-0CC4-4855-89E0-2E61BBC5E0B0}.Release|Any CPU.Build.0 = Release|Any CPU
{42B621CE-C2B4-4911-961C-5B087A514AF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42B621CE-C2B4-4911-961C-5B087A514AF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42B621CE-C2B4-4911-961C-5B087A514AF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42B621CE-C2B4-4911-961C-5B087A514AF5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
14 changes: 9 additions & 5 deletions net/Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
<Project>

<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>

<ItemGroup>
<PackageVersion Include="Ensure.That" Version="10.1.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
<PackageVersion Include="PolySharp" Version="1.14.1" />
<PackageVersion Include="Roslynator.Analyzers" Version="4.12.9" />
<PackageVersion Include="Roslynator.Formatting.Analyzers" Version="4.12.9" />
<PackageVersion Include="Substrate.NET.API" Version="0.9.24-rc6" />
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
<PackageVersion Include="System.Threading.Channels" Version="8.0.0" />
<!-- For Tests -->
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="FluentAssertions" Version="6.12.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
<!-- For Code Analysis -->
<PackageVersion Include="Roslynator.Analyzers" Version="4.12.9" />
<PackageVersion Include="Roslynator.Formatting.Analyzers" Version="4.12.9" />
</ItemGroup>

</Project>
3 changes: 2 additions & 1 deletion net/src/Sails.Remoting/Core/RemotingReplyViaNodeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Substrate.Gear.Api.Generated.Model.gprimitives;
using Substrate.Gear.Api.Generated.Model.vara_runtime;
using Substrate.Gear.Client;
using Substrate.Gear.Client.Model.Rpc;
using Substrate.Gear.Client.Model.Types.Base;
using Substrate.NetApi.Model.Types.Primitive;
using EnumGearEvent = Substrate.Gear.Api.Generated.Model.pallet_gear.pallet.EnumEvent;
Expand Down Expand Up @@ -127,7 +128,7 @@ public override async Task<T> ReadAsync(CancellationToken cancellationToken)
this.replyMessage = await this.blocksStream.ReadAllHeadersAsync(cancellationToken)
.SelectAwait(
async blockHeader =>
await this.nodeClient.ListBlockEventsAsync(blockHeader.Number, cancellationToken) // TODO: It is weird block header doesn't contain hash.
await this.nodeClient.ListBlockEventsAsync(blockHeader.GetBlockHash(), cancellationToken)
.ConfigureAwait(false))
.SelectMany(
eventRecords => eventRecords.AsAsyncEnumerable())
Expand Down
65 changes: 65 additions & 0 deletions net/src/Substrate.Gear.Client/Model/Rpc/HeaderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Linq;
using EnsureThat;
using Substrate.Gear.Client.Model.Types.Base;
using Substrate.NetApi;
using Substrate.NetApi.Model.Rpc;
using Substrate.NetApi.Model.Types.Base;

namespace Substrate.Gear.Client.Model.Rpc;

public static class HeaderExtensions
{
public static Hash GetBlockHash(this Header header)
{
EnsureArg.IsNotNull(header, nameof(header));

var parentHashBytes = header.ParentHash.AsBytesSpan();
var numberBytes = new CompactInteger(header.Number).Encode();
var stateRootBytes = header.StateRoot.AsBytesSpan();
var extrinsicsRootBytes = header.ExtrinsicsRoot.AsBytesSpan();
var logsCountBytes = new CompactInteger(header.Digest.Logs.Count).Encode();
var logsBytesLength = 0;
var logsBytes = header.Digest.Logs
.Select(log =>
{
var logBytes = Utils.HexToByteArray(log);
logsBytesLength += logBytes.Length;
return logBytes;
})
.ToArray();

var bytesToHash = new byte[
parentHashBytes.Length
+ numberBytes.Length
+ stateRootBytes.Length
+ extrinsicsRootBytes.Length
+ logsCountBytes.Length
+ logsBytesLength];

var copyAt = 0;

parentHashBytes.CopyTo(bytesToHash.AsSpan(copyAt));
copyAt += parentHashBytes.Length;

numberBytes.CopyTo(bytesToHash.AsSpan(copyAt));
copyAt += numberBytes.Length;

stateRootBytes.CopyTo(bytesToHash.AsSpan(copyAt));
copyAt += stateRootBytes.Length;

extrinsicsRootBytes.CopyTo(bytesToHash.AsSpan(copyAt));
copyAt += extrinsicsRootBytes.Length;

logsCountBytes.CopyTo(bytesToHash.AsSpan(copyAt));
copyAt += logsCountBytes.Length;

foreach (var logBytes in logsBytes)
{
logBytes.CopyTo(bytesToHash.AsSpan(copyAt));
copyAt += logBytes.Length;
}

return new Hash(HashExtension.Blake2(bytesToHash, 256));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,21 @@ public static bool IsEqualTo<T>(this T left, T right)
EnsureArg.IsTrue(left.GetType() == right.GetType(), "left/right");
EnsureArg.Is(left.TypeSize, right.TypeSize, "typeSize");

return left.Bytes.AsSpan(0, left.TypeSize)
.SequenceEqual(right.Bytes.AsSpan(0, right.TypeSize));
return left.AsBytesSpan()
.SequenceEqual(right.AsBytesSpan());
}

/// <summary>
/// Returns the exact span of bytes that represents the primitive type.
/// It is requied because the BaseType.Bytes property returns the whole array
/// which can be larger than the actual size of the primitive type.
/// </summary>
/// <param name="baseType"></param>
/// <returns></returns>
public static Span<byte> AsBytesSpan(this BaseType baseType)
{
EnsureArg.IsNotNull(baseType, nameof(baseType));

return baseType.Bytes.AsSpan(0, baseType.TypeSize);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using FluentAssertions;
using Substrate.Gear.Client.Model.Rpc;
using Substrate.Gear.Client.Model.Types.Base;
using Substrate.NetApi.Model.Rpc;
using Substrate.NetApi.Model.Types.Base;
using Substrate.NetApi.Model.Types.Primitive;
using Xunit;

namespace Substrate.Gear.Client.Tests.Model.Rpc;

public sealed class HeaderExtensionsTests
{
[Theory]
[MemberData(nameof(Block_Hash_Is_Calculated_Correctly), MemberType = typeof(TheoryData))]
public void Block_Hash_Is_Calculated_Correctly((string ExpectedBlockHash, Header BlockHeader) data)
{
var blockHash = data.BlockHeader.GetBlockHash();
var expectedBlockHash = new Hash(data.ExpectedBlockHash);

blockHash.Should().Match<Hash>((hash) => hash.IsEqualTo(expectedBlockHash));
}

private static class TheoryData
{
// Taken from Vara Network
public static readonly TheoryData<(string ExpectedBlockHash, Header BlockHeader)> Block_Hash_Is_Calculated_Correctly =
new(
(
ExpectedBlockHash: "0x0A7CA5B5B7A7B4C186D9B9355B5864DB74DD71DEAB700DEDA11BC852EA710E4A",
new Header
{
ExtrinsicsRoot = new Hash("0xd5c7e252243071f25d8013aa60e87e1650b4f069983eeafcecec10c0a03619ae"),
Number = (U64)16940619,
ParentHash = new Hash("0x9060eda7f8699cfcfa69c26640473bf5a322274eb391d066ec758c8f990c1eab"),
StateRoot = new Hash("0x7db88586b63b1ef556c13bec7a42040ebacacb2a1a8ad8517824674acf4f0106"),
Digest = new Digest
{
Logs = [
"0x0642414245340213000000ef9e612200000000",
"0x054241424501015832548cc0447f21a474529d904a7681a0e2e67baf90ef6d9f208b8e5dd4a22940e5e5b99c4e8a62e6f3a0b543883b07503cdcb40fe2c0c40a24df45dcd44f8e"
]
}
}
),
(
ExpectedBlockHash: "0x79F23DDD3EF8B1F7A9BA7ABED94574B779B73CE4E9D955657ABF53C38551F8BF",
new Header
{
ExtrinsicsRoot = new Hash("0x246c1d21868a9f8476e74d06a7b2cc0077703862b1cbeca546e25de9f203a1c6"),
Number = (U64)16940905,
ParentHash = new Hash("0x302955194b49e227fa40b111718a3449b763323e778aa89ca96ae9eb8ded3f64"),
StateRoot = new Hash("0xadac86b1731b2ebfb50a0c75f13ccfc884e21cab010cf511d2149836a61e9d6f"),
Digest = new Digest
{
Logs = [
"0x0642414245b501013100000011a0612200000000e4653ae16d22ee4766b802cae76c58273d793e59c70ddb173c2a63b452ec4820435f5d32fc12fcdbba1bd2b6a3f3349fd0356bbb003bbc3da1009cc5879c0d0d88baee2605129bd5cfb0e050df2c38aeaa927c548028a352dca52b954f375607",
"0x05424142450101e8dd5abf4f1b1f600f1257110b51bdfdf6b7bf5462c618c0b22ad39e979eb32b25e70cbc7ca4d1d229f25b839c0e01c94270e7332d6b7b73bf9a86bc09262b8f"
]
}
}
)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Substrate.Gear.Client\Substrate.Gear.Client.csproj" />
</ItemGroup>

</Project>

0 comments on commit 9d3e18d

Please sign in to comment.