Skip to content

Commit

Permalink
UntypedTransaction class
Browse files Browse the repository at this point in the history
  • Loading branch information
dahlia committed May 24, 2022
1 parent 7a9734d commit 12c8763
Show file tree
Hide file tree
Showing 10 changed files with 472 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/bin/constants.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ projects=(
"Libplanet"
"Libplanet.Stun"
"Libplanet.Net"
"Libplanet.Node"
"Libplanet.RocksDBStore"
"Libplanet.Analyzers"
"Libplanet.Tools"
Expand Down
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ To be released.

### Added APIs

- Introduced *Libplanet.Node* package. [[#1974], [#1978]]
- Added `ITxMetadata` interface. [[#1164], [#1974], [#1978]]
- Added `TxMetadata` class. [[#1164], [#1974], [#1978]]
- Added `ITxExcerpt` interface. [[#1164], [#1974], [#1978]]
Expand All @@ -29,6 +30,7 @@ To be released.
- Added `Transaction<T>(ITxMetadata, IEnumerable<T>, byte[])` constructor.
[[#1164], [#1978]]
- Added `TxId.FromString()` static method. [[#1978]]
- (Libplanet.Node) Added `UntypedTransaction` class. [[#1974], [#1978]]

### Behavioral changes

Expand Down
7 changes: 7 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ on GitHub consists of several projects:
*Libplanet*. This is distributed as a distinct NuGet package:
*[Libplanet.Net]*.

- *Libplanet.Node*: User-friendly façade API for building your own
peer-to-peer network. This is distributed as a distinct NuGet package:
*[Libplanet.Node]*.

- *Libplanet.Stun*: The project dedicated to implement [TURN & STUN].
This is distributed as a distinct NuGet package: *[Libplanet.Stun]*.

Expand Down Expand Up @@ -119,6 +123,8 @@ on GitHub consists of several projects:

- *Libplanet.Net.Tests*: Unit tests of the *Libplanet.Net* project.

- *Libplanet.Node.Tests*: Unit tests of the *Libplanet.Node* project.

- *Libplanet.Stun.Tests*: Unit tests of the *Libplanet.Stun* project.

- *Libplanet.RocksDBStore.Tests*: Unit tests of the *Libplanet.RocksDBStore*
Expand All @@ -136,6 +142,7 @@ on GitHub consists of several projects:

[NuGet package]: https://www.nuget.org/packages/Libplanet/
[Libplanet.Net]: https://www.nuget.org/packages/Libplanet.Net/
[Libplanet.Node]: https://www.nuget.org/packages/Libplanet.Node/
[TURN & STUN]: https://snack.planetarium.dev/eng/2019/06/nat_traversal_2/
[RocksDB]: https://rocksdb.org/
[Libplanet.Stun]: https://www.nuget.org/packages/Libplanet.Stun/
Expand Down
1 change: 1 addition & 0 deletions Docs/docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"files": [
"Libplanet/Libplanet.csproj",
"Libplanet.Net/Libplanet.Net.csproj",
"Libplanet.Node/Libplanet.Node.csproj",
"Libplanet.RocksDBStore/Libplanet.RocksDBStore.csproj",
"Libplanet.Extensions.Cocona/Libplanet.Extensions.Cocona.csproj",
"Libplanet.Stun/Libplanet.Stun.csproj"
Expand Down
81 changes: 81 additions & 0 deletions Libplanet.Node.Tests/Libplanet.Node.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<IsTestProject>true</IsTestProject>
<LangVersion>8.0</LangVersion>
<CodeAnalysisRuleSet>..\Libplanet.Tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>

<ItemGroup>
<AdditionalFiles Include="..\Libplanet.Tests\Menees.Analyzers.Settings.xml">
<Link>Menees.Analyzers.Settings.xml</Link>
</AdditionalFiles>
<AdditionalFiles Include="..\stylecop.json" />
</ItemGroup>

<PropertyGroup Condition="
'$([System.Runtime.InteropServices.RuntimeInformation]::
OSArchitecture.ToString())' == 'Arm64' ">
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<PropertyGroup Condition=" '$(TestsTargetFramework)'!='' ">
<TargetFramework>$(TestsTargetFramework)</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="JunitXml.TestLogger" Version="3.0.98" />
<PackageReference Include="Menees.Analyzers.2017" Version="2.0.3">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.0.0" />
<PackageReference Include="Serilog.Sinks.TestCorrelator" Version="3.2.0" />
<PackageReference Include="Serilog.Sinks.XUnit" Version="1.0.7" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.205">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>
runtime; build; native; contentfiles; analyzers
</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Collections.Immutable" Version="1.7.*" />
<PackageReference Include="xRetry" Version="1.5.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="Xunit.SkippableFact" Version="1.3.12" />
</ItemGroup>

<ItemGroup Condition="'$(SkipSonar)' != 'true'">
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.12.0.21095">
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net47' ">
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Libplanet.Node\Libplanet.Node.csproj" />
</ItemGroup>

<ItemGroup Condition=" '$(MSBuildRuntimeType)'=='Mono' ">
<!--
As Mono has no proper AppDomain, we prevent it on Mono.
This works around Xunit's fatal error on Mono.
-->
<Content Include="xunit.runner.mono.json">
<Link>xunit.runner.json</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
136 changes: 136 additions & 0 deletions Libplanet.Node.Tests/UntypedTransactionTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using Bencodex;
using Bencodex.Types;
using Libplanet.Blocks;
using Libplanet.Crypto;
using Libplanet.Tx;
using Xunit;

namespace Libplanet.Node.Tests
{
public class UntypedTransactionTest
{
private readonly PrivateKey _key1;
private readonly PrivateKey _key2;
private readonly TxMetadata _meta;
private readonly IValue[] _actionValues;
private readonly ImmutableArray<byte> _sig;

public UntypedTransactionTest()
{
_key1 = new PrivateKey(new byte[]
{
0x9b, 0xf4, 0x66, 0x4b, 0xa0, 0x9a, 0x89, 0xfa, 0xeb, 0x68, 0x4b,
0x94, 0xe6, 0x9f, 0xfd, 0xe0, 0x1d, 0x26, 0xae, 0x14, 0xb5, 0x56,
0x20, 0x4d, 0x3f, 0x6a, 0xb5, 0x8f, 0x61, 0xf7, 0x84, 0x18,
});
_key2 = new PrivateKey(new byte[]
{
0xfc, 0xf3, 0x0b, 0x33, 0x3d, 0x04, 0xcc, 0xfe, 0xb5, 0x62, 0xf0,
0x00, 0xa3, 0x2d, 0xf4, 0x88, 0xe7, 0x15, 0x49, 0x49, 0xd3, 0x1d,
0xdc, 0xac, 0x3c, 0xf9, 0x27, 0x8a, 0xcb, 0x57, 0x86, 0xc7,
});
_meta = new TxMetadata(_key2.PublicKey)
{
Nonce = 0L,
UpdatedAddresses = new[]
{
_key1.ToAddress(),
_key2.ToAddress(),
}.ToImmutableHashSet(),
Timestamp = new DateTimeOffset(2022, 1, 12, 4, 56, 7, 890, default),
GenesisHash = BlockHash.FromString(
"83915317ebdbf870c567b263dd2e61ec9dca7fb381c592d80993291b6ffe5ad5"),
};
_actionValues = new IValue[] { new Integer(123), new Integer(456) };
Bencodex.Types.Dictionary unsignedDict = _meta.ToBencodex(_actionValues);
var codec = new Codec();
_sig = ImmutableArray.Create(_key2.Sign(codec.Encode(unsignedDict)));
}

[Fact]
public void Constructor()
{
var untyped = new UntypedTransaction(_meta, _actionValues, _sig);
Assert.Equal(_meta.Nonce, untyped.Nonce);
Assert.Equal(_meta.Signer, untyped.Signer);
Assert.Equal(_meta.UpdatedAddresses, untyped.UpdatedAddresses);
Assert.Equal(_meta.Timestamp, untyped.Timestamp);
Assert.Equal(_meta.PublicKey, untyped.PublicKey);
Assert.Equal(_meta.GenesisHash, untyped.GenesisHash);
Assert.Equal(_actionValues, untyped.ActionValues);
Assert.Equal(_sig, untyped.Signature);

InvalidTxSignatureException e;
e = Assert.Throws<InvalidTxSignatureException>(
() => new UntypedTransaction(_meta, _actionValues, default)
);
Assert.Equal(
TxId.FromHex("ea601351c27c3c6291c4352ec060f06650b81c02ded4a4d22858da756098fd4e"),
e.TxId
);

e = Assert.Throws<InvalidTxSignatureException>(
() => new UntypedTransaction(_meta, Enumerable.Empty<IValue>(), _sig)
);
Assert.Equal(
TxId.FromHex("f91abd37cad6962cb206a9c29faffddede8bce47751f3e5e4b0e1c8f714a4a82"),
e.TxId
);
}

[Fact]
public void Deserialize()
{
Bencodex.Types.Dictionary signedDict = _meta.ToBencodex(_actionValues, _sig);
var untyped = new UntypedTransaction(signedDict);
Assert.Equal(_meta.Nonce, untyped.Nonce);
Assert.Equal(_meta.Signer, untyped.Signer);
Assert.Equal(_meta.UpdatedAddresses, untyped.UpdatedAddresses);
Assert.Equal(_meta.Timestamp, untyped.Timestamp);
Assert.Equal(_meta.PublicKey, untyped.PublicKey);
Assert.Equal(_meta.GenesisHash, untyped.GenesisHash);
Assert.Equal(_actionValues, untyped.ActionValues);
Assert.Equal(_sig, untyped.Signature);

InvalidTxSignatureException e;
var invalidSigDict = signedDict.SetItem(TxMetadata.SignatureKey, Array.Empty<byte>());
e = Assert.Throws<InvalidTxSignatureException>(
() => new UntypedTransaction(invalidSigDict)
);
Assert.Equal(
TxId.FromHex("ea601351c27c3c6291c4352ec060f06650b81c02ded4a4d22858da756098fd4e"),
e.TxId
);

var invalidActionsDict = signedDict.SetItem(TxMetadata.ActionsKey, (IValue)new List());
e = Assert.Throws<InvalidTxSignatureException>(
() => new UntypedTransaction(invalidActionsDict)
);
Assert.Equal(
TxId.FromHex("f91abd37cad6962cb206a9c29faffddede8bce47751f3e5e4b0e1c8f714a4a82"),
e.TxId
);
}

[Fact]
public void ToBencodex()
{
Bencodex.Types.Dictionary dict =
new UntypedTransaction(_meta, _actionValues, _sig).ToBencodex();
Assert.Equal(_meta.ToBencodex(_actionValues, _sig), dict);

var deserialized = new UntypedTransaction(dict);
Assert.Equal(_meta.Nonce, deserialized.Nonce);
Assert.Equal(_meta.Signer, deserialized.Signer);
Assert.Equal(_meta.UpdatedAddresses, deserialized.UpdatedAddresses);
Assert.Equal(_meta.Timestamp, deserialized.Timestamp);
Assert.Equal(_meta.PublicKey, deserialized.PublicKey);
Assert.Equal(_meta.GenesisHash, deserialized.GenesisHash);
Assert.Equal(_actionValues, deserialized.ActionValues);
Assert.Equal(_sig, deserialized.Signature);
}
}
}
76 changes: 76 additions & 0 deletions Libplanet.Node/Libplanet.Node.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Libplanet.Node</PackageId>
<PackageProjectUrl>https://libplanet.io/</PackageProjectUrl>
<PackageIcon>icon.png</PackageIcon>
<Authors>Planetarium</Authors>
<Company>Planetarium</Company>
<PackageLicenseExpression>LGPL-2.1-or-later</PackageLicenseExpression>
<RequireLicenseAcceptance>true</RequireLicenseAcceptance>
<PackageReleaseNotes>https://github.com/planetarium/libplanet/blob/main/CHANGES.md</PackageReleaseNotes>
<PackageTags>multiplayer online game;game;blockchain</PackageTags>
<RepositoryUrl>https://github.com/planetarium/libplanet.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>

<PropertyGroup>
<LangVersion>8.0</LangVersion>
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
<Nullable>enable</Nullable>
<RootNamespace>Libplanet.Node</RootNamespace>
<AssemblyName>Libplanet.Node</AssemblyName>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
<Nullable>enable</Nullable>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>$(NoWarn);NU5104;MEN001</NoWarn>
<IsTestProject>false</IsTestProject>
<CodeAnalysisRuleSet>..\Libplanet.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>

<ItemGroup>
<None Include="..\LICENSE" Pack="true" PackagePath="LICENSE.txt" />
<None Include="..\README.md" Pack="true" PackagePath="README.md" />
<None Include="..\icon.png" Pack="true" PackagePath="icon.png" />
<AdditionalFiles Include="..\Menees.Analyzers.Settings.xml">
<Link>Menees.Analyzers.Settings.xml</Link>
</AdditionalFiles>
<AdditionalFiles Include="..\stylecop.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Menees.Analyzers.2017" Version="2.0.3">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.DotNet.Analyzers.Compatibility" Version="0.2.12-alpha">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>
runtime; build; native; contentfiles; analyzers; buildtransitive
</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.205">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>
runtime; build; native; contentfiles; analyzers
</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup Condition="'$(SkipSonar)' != 'true'">
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.12.0.21095">
<IncludeAssets>
runtime; build; native; contentfiles; analyzers
</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Libplanet.Net\Libplanet.Net.csproj" />
<ProjectReference Include="..\Libplanet.RocksDBStore\Libplanet.RocksDBStore.csproj" />
<ProjectReference Include="..\Libplanet.Stun\Libplanet.Stun.csproj" />
<ProjectReference Include="..\Libplanet\Libplanet.csproj" />
</ItemGroup>
</Project>
Loading

0 comments on commit 12c8763

Please sign in to comment.