Skip to content

Commit

Permalink
Merge pull request #3328 from greymistcube/refactor/keybytes-usage
Browse files Browse the repository at this point in the history
🧹 Refactor to use `KeyBytes` instead of `string`s in general
  • Loading branch information
greymistcube authored Jul 21, 2023
2 parents abb2e39 + 80d80b0 commit 1d75fde
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 118 deletions.
10 changes: 10 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ To be released.
`IImmutableDictionary<string, IValue>`. [[#3321]]
- Removed `EnumerableMeasurement` class. [[#3325]]
- Removed `KeyValueExtensions` class. [[#3325]]
- Removed `StateStoreExtensions.EncodeKey()` and
`StateStoreExtensions.DecodeKey()` methods. [[#3328]]
- Removed `StateStoreExtensions.GetStates(IStateStore, HashDigest<SHA256>?,
IReadOnlyList<string>)` method. [[#3328]]
- Removed `TrieExtensions.Set(ITrie,
IEnumerable<KeyValuePair<string, IValue?>)` method. [[#3328]]
- Removed `KeyBytes(string, Encoding)` constructor. [[#3328]]

### Backward-incompatible network protocol changes

Expand All @@ -30,6 +37,8 @@ To be released.

- Added `StateStoreExtensions.GetStates(IStateStore, HashDigest<SHA256>,
IReadOnlyList<KeyBytes>)` method. [[#3321]]
- Added `KeyBytes.Encoding` static property. [[#3328]]
- Added `KeyBytes(string)` constructor. [[#3328]]

### Behavioral changes

Expand All @@ -44,6 +53,7 @@ To be released.

[#3321]: https://github.com/planetarium/libplanet/pull/3321
[#3325]: https://github.com/planetarium/libplanet/pull/3325
[#3328]: https://github.com/planetarium/libplanet/pull/3328


Version 3.0.1
Expand Down
10 changes: 4 additions & 6 deletions Libplanet.Action.Tests/State/KeyConvertersTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Text;
using Libplanet.Common;
using Libplanet.Crypto;
using Libplanet.Store.Trie;
Expand All @@ -20,21 +19,20 @@ public void ToKeysSpec()
var currency = Currency.Uncapped("Foo", 2, new PrivateKey().ToAddress());

Assert.Equal(
new KeyBytes(ByteUtil.Hex(address.ByteArray), Encoding.UTF8),
new KeyBytes(ByteUtil.Hex(address.ByteArray)),
KeyConverters.ToStateKey(address));

Assert.Equal(
new KeyBytes(
$"_{ByteUtil.Hex(address.ByteArray)}_{ByteUtil.Hex(currency.Hash.ByteArray)}",
Encoding.UTF8),
$"_{ByteUtil.Hex(address.ByteArray)}_{ByteUtil.Hex(currency.Hash.ByteArray)}"),
KeyConverters.ToFungibleAssetKey(address, currency));

Assert.Equal(
new KeyBytes($"__{ByteUtil.Hex(currency.Hash.ByteArray)}", Encoding.UTF8),
new KeyBytes($"__{ByteUtil.Hex(currency.Hash.ByteArray)}"),
KeyConverters.ToTotalSupplyKey(currency));

Assert.Equal(
new KeyBytes("___", Encoding.UTF8),
new KeyBytes("___"),
KeyConverters.ValidatorSetKey);
}
}
Expand Down
13 changes: 7 additions & 6 deletions Libplanet.Extensions.Cocona.Tests/Commands/MptCommandTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Libplanet.Common;
using Libplanet.Extensions.Cocona.Commands;
using Libplanet.Extensions.Cocona.Configuration;
using Libplanet.Store;
using Libplanet.Store.Trie;
using Libplanet.Tools.Tests.Services;
using Xunit;
Expand All @@ -30,13 +31,13 @@ public MptCommandTest()
using var stateKeyValueStoreA = new DefaultKeyValueStore(_pathA);
using var stateKeyValueStoreB = new DefaultKeyValueStore(_pathB);
_trieA = new MerkleTrie(stateKeyValueStoreA).Set(
ImmutableDictionary<string, IValue>.Empty
.Add("deleted", Null.Value)
.Add("common", (Text)"before")).Commit();
ImmutableDictionary<KeyBytes, IValue>.Empty
.Add(new KeyBytes("deleted"), Null.Value)
.Add(new KeyBytes("common"), (Text)"before")).Commit();
_trieB = new MerkleTrie(stateKeyValueStoreB).Set(
ImmutableDictionary<string, IValue>.Empty
.Add("created", Null.Value)
.Add("common", (Text)"after")).Commit();
ImmutableDictionary<KeyBytes, IValue>.Empty
.Add(new KeyBytes("created"), Null.Value)
.Add(new KeyBytes("common"), (Text)"after")).Commit();
}

[Fact]
Expand Down
6 changes: 4 additions & 2 deletions Libplanet.Extensions.Cocona/Commands/MptCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,11 @@ public void Export(
keyValueStore,
HashDigest<SHA256>.FromString(stateRootHashHex));
var codec = new Codec();

// This assumes the original key was encoded from a sensible string.
ImmutableDictionary<string, byte[]> decoratedStates = trie.ListAllStates()
.ToImmutableDictionary(
pair => StateStoreExtensions.DecodeKey(pair.Key),
pair => KeyBytes.Encoding.GetString(pair.Key.ToByteArray()),
pair => codec.Encode(pair.Value));

Console.WriteLine(JsonSerializer.Serialize(decoratedStates));
Expand Down Expand Up @@ -227,7 +229,7 @@ public void Query(
var trie = new MerkleTrie(
keyValueStore,
HashDigest<SHA256>.FromString(stateRootHashHex));
KeyBytes stateKeyBytes = StateStoreExtensions.EncodeKey(stateKey);
KeyBytes stateKeyBytes = new KeyBytes(stateKey);
IReadOnlyList<IValue?> values = trie.Get(new[] { stateKeyBytes });
if (values.Count > 0 && values[0] is { } value)
{
Expand Down
53 changes: 0 additions & 53 deletions Libplanet.Store/StateStoreExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Collections.Immutable;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Bencodex.Types;
using Libplanet.Common;
using Libplanet.Store.Trie;
Expand All @@ -14,36 +13,6 @@ namespace Libplanet.Store
/// </summary>
public static class StateStoreExtensions
{
/// <summary>
/// The internal bytes encoding of raw state keys.
/// </summary>
internal static readonly Encoding KeyEncoding = Encoding.UTF8;

/// <summary>
/// Encodes a raw state key string to internal bytes representation.
/// </summary>
/// <param name="key">The raw state key to encode.</param>
/// <returns>An encoded key bytes.</returns>
public static KeyBytes EncodeKey(string key) =>
new KeyBytes(key, KeyEncoding);

/// <summary>
/// Decodes internal <paramref name="keyBytes"/> into a raw state key string.
/// </summary>
/// <param name="keyBytes">The key bytes to decode.</param>
/// <returns>A decoded raw state key string.</returns>
public static string DecodeKey(in KeyBytes keyBytes)
{
ImmutableArray<byte> immutableBytes = keyBytes.ByteArray;
#if NETSTANDARD2_0
byte[] neverChangedBytes = System.Runtime.CompilerServices.Unsafe
.As<ImmutableArray<byte>, byte[]>(ref immutableBytes);
return KeyEncoding.GetString(neverChangedBytes);
#else
return KeyEncoding.GetString(immutableBytes.AsSpan());
#endif
}

/// <summary>
/// Records <paramref name="rawStatesDelta"/> which is based on the previous state
/// root, and returns the new state root.
Expand All @@ -70,28 +39,6 @@ IImmutableDictionary<KeyBytes, IValue> rawStatesDelta
return stateStore.GetStateRoot(stage.Hash);
}

/// <summary>
/// Gets multiple states at once.
/// </summary>
/// <param name="stateStore">The <see cref="IStateStore"/> to get states.</param>
/// <param name="stateRootHash">The root hash of the state trie to look up states from.
/// </param>
/// <param name="rawStateKeys">State keys to get.</param>
/// <returns>The state values associated to the specified <paramref name="rawStateKeys"/>.
/// The associated values are ordered in the same way to the corresponding
/// <paramref name="rawStateKeys"/>. Absent values are represented as
/// <see langword="null"/>.</returns>
public static IReadOnlyList<IValue?> GetStates(
this IStateStore stateStore,
HashDigest<SHA256>? stateRootHash,
IReadOnlyList<string> rawStateKeys
)
{
ITrie trie = stateStore.GetStateRoot(stateRootHash);
KeyBytes[] keys = rawStateKeys.Select(EncodeKey).ToArray();
return trie.Get(keys);
}

/// <summary>
/// Gets multiple states at once.
/// </summary>
Expand Down
29 changes: 23 additions & 6 deletions Libplanet.Store/Trie/KeyBytes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ namespace Libplanet.Store.Trie
public readonly struct KeyBytes
: IEquatable<KeyBytes>, IEquatable<ImmutableArray<byte>>, IEquatable<byte[]>
{
/// <summary>
/// The default <see cref="System.Text.Encoding"/>, which is <see cref="Encoding.UTF8"/>,
/// to use when creating an instance from a <see langword="string"/>.
/// </summary>
public static readonly Encoding Encoding = Encoding.UTF8;

private readonly ImmutableArray<byte> _byteArray;

/// <summary>
Expand All @@ -34,14 +40,25 @@ public KeyBytes(in ImmutableArray<byte> bytes)
}

/// <summary>
/// Creates a new <seealso cref="KeyBytes"/> instance from the given <paramref
/// name="string"/>.
/// Creates a new <seealso cref="KeyBytes"/> instance from given
/// <paramref name="str"/> with <see cref="Encoding"/>.
/// </summary>
/// <param name="str">The key <see langword="string"/> to encode into bytes.</param>
public KeyBytes(string str)
: this(str, Encoding)
{
}

/// <summary>
/// Creates a new <seealso cref="KeyBytes"/> instance from given <paramref name="str"/>
/// with <paramref name="encoding"/>.
/// </summary>
/// <param name="string">A key string. This is encoded to bytes.</param>
/// <param name="encoding">The text encoding used for the key string.</param>
public KeyBytes(string @string, Encoding encoding)
/// <param name="str">The key <see langword="string"/> to encode into bytes.</param>
/// <param name="encoding">The <see cref="System.Text.Encoding"/> to be used for
/// <paramref name="str">.</param>
private KeyBytes(string str, Encoding encoding)
{
byte[] neverReusedBuffer = encoding.GetBytes(@string);
byte[] neverReusedBuffer = encoding.GetBytes(str);
ImmutableArray<byte> movedImmutable =
Unsafe.As<byte[], ImmutableArray<byte>>(ref neverReusedBuffer);
_byteArray = movedImmutable;
Expand Down
10 changes: 0 additions & 10 deletions Libplanet.Store/Trie/TrieExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,5 @@ public static ITrie Set(this ITrie trie, IEnumerable<KeyValuePair<KeyBytes, IVal

return trie;
}

public static ITrie Set(this ITrie trie, IEnumerable<KeyValuePair<string, IValue?>> pairs)
=> trie.Set(
pairs.Select(pair =>
new KeyValuePair<KeyBytes, IValue?>(
StateStoreExtensions.EncodeKey(pair.Key),
pair.Value
)
)
);
}
}
24 changes: 15 additions & 9 deletions Libplanet.Tests/Store/StateStoreExtensionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ public class StateStoreExtensionsTest
public static IImmutableDictionary<KeyBytes, IValue> ZeroDelta =>
ImmutableDictionary<KeyBytes, IValue>.Empty;

public static KeyBytes KeyFoo => StateStoreExtensions.EncodeKey("foo");
public static KeyBytes KeyFoo => new KeyBytes("foo");

public static KeyBytes KeyBar => StateStoreExtensions.EncodeKey("bar");
public static KeyBytes KeyBar => new KeyBytes("bar");

public static KeyBytes KeyBaz => StateStoreExtensions.EncodeKey("baz");
public static KeyBytes KeyBaz => new KeyBytes("baz");

public static IImmutableDictionary<KeyBytes, IValue> DeltaA => ZeroDelta
.Add(KeyFoo, (Text)"abc")
Expand Down Expand Up @@ -78,13 +78,19 @@ public void GetState(string label, IStateStore stateStore)
ITrie a = stateStore.Commit(null, DeltaA);
ITrie b = stateStore.Commit(a.Hash, DeltaB);

AssertBencodexEqual((Text)"abc", stateStore.GetStates(a.Hash, new[] { "foo" })[0]);
AssertBencodexEqual((Text)"def", stateStore.GetStates(a.Hash, new[] { "bar" })[0]);
AssertBencodexEqual(null, stateStore.GetStates(a.Hash, new[] { "baz" })[0]);
AssertBencodexEqual(
(Text)"abc", stateStore.GetStates(a.Hash, new[] { new KeyBytes("foo") })[0]);
AssertBencodexEqual(
(Text)"def", stateStore.GetStates(a.Hash, new[] { new KeyBytes("bar") })[0]);
AssertBencodexEqual(
null, stateStore.GetStates(a.Hash, new[] { new KeyBytes("baz") })[0]);

AssertBencodexEqual((Text)"ABC", stateStore.GetStates(b.Hash, new[] { "foo" })[0]);
AssertBencodexEqual((Text)"def", stateStore.GetStates(b.Hash, new[] { "bar" })[0]);
AssertBencodexEqual((Text)"ghi", stateStore.GetStates(b.Hash, new[] { "baz" })[0]);
AssertBencodexEqual(
(Text)"ABC", stateStore.GetStates(b.Hash, new[] { new KeyBytes("foo") })[0]);
AssertBencodexEqual(
(Text)"def", stateStore.GetStates(b.Hash, new[] { new KeyBytes("bar") })[0]);
AssertBencodexEqual(
(Text)"ghi", stateStore.GetStates(b.Hash, new[] { new KeyBytes("baz") })[0]);
}

[Theory]
Expand Down
5 changes: 2 additions & 3 deletions Libplanet.Tests/Store/Trie/KeyBytesTest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Immutable;
using System.Text;
using Libplanet.Store.Trie;
using Xunit;
using static Libplanet.Tests.TestUtils;
Expand All @@ -15,7 +14,7 @@ public void Constructors()
AssertBytesEqual(ImmutableArray<byte>.Empty, default(KeyBytes).ByteArray);
AssertBytesEqual(
ImmutableArray<byte>.Empty,
new KeyBytes(string.Empty, Encoding.ASCII).ByteArray
new KeyBytes(string.Empty).ByteArray
);
AssertBytesEqual(
ImmutableArray<byte>.Empty.Add(1).Add(2).Add(3).Add(4),
Expand All @@ -27,7 +26,7 @@ public void Constructors()
);
AssertBytesEqual(
new KeyBytes(ImmutableArray.Create<byte>(0x66, 0x6f, 0x6f)).ByteArray,
new KeyBytes("foo", Encoding.ASCII).ByteArray
new KeyBytes("foo").ByteArray
);
}

Expand Down
Loading

0 comments on commit 1d75fde

Please sign in to comment.