Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export all states as JSON #1026

Merged
merged 3 commits into from Oct 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGES.md
Expand Up @@ -299,8 +299,8 @@ To be released.
- The `planet` command became installable using `npm`. [[#923], [#982]]
- Fixed a bug that <kbd>^H</kbd> had not removed the rightmost character
in passphrase prompts. [[#983], [#984]]
- Added a new sub-command `planet mpt`. [[#1023]]
- Introduced a configuration file. It's placed in: [[#1023]]
- Added a new sub-command `planet mpt`. [[#1023], [#1026]]
- Introduced a configuration file. It's placed in: [[#1023], [#1026]]
- Linux/macOS: *<var>$XDG_CONFIG_HOME</var>/planetarium/cli.json*
- Windows: *<var>%AppData%</var>\planetarium\cli.json*

Expand Down Expand Up @@ -377,6 +377,7 @@ To be released.
[#1021]: https://github.com/planetarium/libplanet/pull/1021
[#1022]: https://github.com/planetarium/libplanet/pull/1022
[#1023]: https://github.com/planetarium/libplanet/pull/1023
[#1026]: https://github.com/planetarium/libplanet/pull/1026
[sleep mode]: https://en.wikipedia.org/wiki/Sleep_mode


Expand Down
26 changes: 26 additions & 0 deletions Libplanet.Tests/ImmutableArrayEqualityComparer.cs
@@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

namespace Libplanet.Tests
{
public class ImmutableArrayEqualityComparer<T> : IEqualityComparer<ImmutableArray<T>>
{
public bool Equals(ImmutableArray<T> x, ImmutableArray<T> y)
=> x.Length == y.Length && x.SequenceEqual(y);

public int GetHashCode(ImmutableArray<T> obj)
{
int code = 0;
unchecked
{
foreach (T el in obj)
{
code = (code * 397) ^ el.GetHashCode();
}
}

return code;
}
}
}
38 changes: 38 additions & 0 deletions Libplanet.Tests/Store/Trie/MerkleTrieExtensionsTest.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Security.Cryptography;
using Bencodex.Types;
Expand Down Expand Up @@ -39,5 +40,42 @@ public void DifferentNodes()
Assert.Equal(trieA.Hash, differentNodes["03"][0].Root);
Assert.Equal(trieB.Hash, differentNodes["04"][0].Root);
}

[Fact]
public void ListAllStates()
{
IKeyValueStore keyValueStore = new MemoryKeyValueStore();
MerkleTrie trie = new MerkleTrie(keyValueStore);

trie.Set(new byte[] { 0x01, }, default(Null));
trie.Set(new byte[] { 0x02, }, default(Null));
trie.Set(new byte[] { 0x03, }, default(Null));
trie.Set(new byte[] { 0x04, }, default(Null));
trie.Set(new byte[] { 0xbe, 0xef }, Dictionary.Empty);

Dictionary<ImmutableArray<byte>, IValue> states =
trie.ListAllStates().ToDictionary(
pair => pair.Key,
pair => pair.Value,
new ImmutableArrayEqualityComparer<byte>());
Assert.Equal(5, states.Count);
Assert.Equal(default(Null), states[ImmutableArray<byte>.Empty.Add(0x01)]);
Assert.Equal(default(Null), states[ImmutableArray<byte>.Empty.Add(0x02)]);
Assert.Equal(default(Null), states[ImmutableArray<byte>.Empty.Add(0x03)]);
Assert.Equal(default(Null), states[ImmutableArray<byte>.Empty.Add(0x04)]);
Assert.Equal(Dictionary.Empty, states[ImmutableArray<byte>.Empty.Add(0xbe).Add(0xef)]);

trie = (MerkleTrie)trie.Commit();
states = trie.ListAllStates().ToDictionary(
pair => pair.Key,
pair => pair.Value,
new ImmutableArrayEqualityComparer<byte>());
Assert.Equal(5, states.Count);
Assert.Equal(default(Null), states[ImmutableArray<byte>.Empty.Add(0x01)]);
Assert.Equal(default(Null), states[ImmutableArray<byte>.Empty.Add(0x02)]);
Assert.Equal(default(Null), states[ImmutableArray<byte>.Empty.Add(0x03)]);
Assert.Equal(default(Null), states[ImmutableArray<byte>.Empty.Add(0x04)]);
Assert.Equal(Dictionary.Empty, states[ImmutableArray<byte>.Empty.Add(0xbe).Add(0xef)]);
}
}
}
47 changes: 47 additions & 0 deletions Libplanet.Tools/Mpt.cs
Expand Up @@ -5,6 +5,8 @@
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Bencodex;
using Cocona;
using Cocona.Help;
using Libplanet.RocksDBStore;
Expand Down Expand Up @@ -89,6 +91,51 @@ public class Mpt
}
}

[Command(Description = "Export all states of the state root hash as JSON.")]
public void Export(
[Argument(
Name = "KEY-VALUE-STORE",
Description = "The path where IKeyValueStore implementation was used at.")]
string kvStoreUri,
[Argument(
Name = "STATE-ROOT-HASH",
Description = "The state root hash to compare.")]
string stateRootHashHex,
[FromService] IConfigurationService<ToolConfiguration> configurationService)
{
// If it is not Uri format,
// try to get uri from configuration service by using it as alias.
if (!Uri.IsWellFormedUriString(kvStoreUri, UriKind.Absolute))
{
try
{
var configuration = configurationService.Load();
kvStoreUri = configuration.Mpt.Aliases[kvStoreUri];
}
catch (KeyNotFoundException)
{
var exceptionMessage =
$"The alias, '{kvStoreUri}' doesn't exist. " +
$"Please pass correct uri or alias.";
throw new CommandExitedException(
exceptionMessage,
-1);
}
}

IKeyValueStore keyValueStore = LoadKVStoreFromURI(kvStoreUri);
var trie = new MerkleTrie(
keyValueStore,
HashDigest<SHA256>.FromString(stateRootHashHex));
var codec = new Codec();
ImmutableDictionary<string, byte[]> decoratedStates = trie.ListAllStates()
.ToImmutableDictionary(
pair => Encoding.UTF8.GetString(pair.Key.ToArray()),
pair => codec.Encode(pair.Value));

Console.WriteLine(JsonSerializer.Serialize(decoratedStates));
}

// FIXME: Now, it works like `set` not `add`. It allows override.
[Command(Description="Add a new mpt store alias.")]
public void Add(
Expand Down
14 changes: 14 additions & 0 deletions Libplanet/Store/Trie/MerkleTrieExtensions.cs
Expand Up @@ -40,6 +40,20 @@ public static class MerkleTrieExtensions
group.Count() == 1 || !group.All(v => v.Value.Equals(group.First().Value)));
}

/// <summary>
/// Lists the all states key and the all states in the given <paramref name="merkleTrie"/>.
/// </summary>
/// <param name="merkleTrie">A trie to discover.</param>
/// <returns>All state keys and the all states.</returns>
public static IEnumerable<KeyValuePair<ImmutableArray<byte>, IValue>> ListAllStates(
this MerkleTrie merkleTrie)
{
return merkleTrie.IterateNodes().Where(pair => pair.Node is ValueNode).Select(pair =>
new KeyValuePair<ImmutableArray<byte>, IValue>(
FromKey(pair.Path),
((ValueNode)pair.Node).Value));
}

private static ImmutableArray<byte> FromKey(ImmutableArray<byte> bytes)
{
if (bytes.Length % 2 == 1)
Expand Down