Skip to content

Commit

Permalink
feat: provide genesis config option
Browse files Browse the repository at this point in the history
  • Loading branch information
limebell committed Dec 26, 2024
1 parent 38f5fca commit f8c9b84
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 13 deletions.
11 changes: 9 additions & 2 deletions sdk/node/Libplanet.Node/Options/GenesisOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ public sealed class GenesisOptions : OptionsBase<GenesisOptions>
[PrivateKey]
[Description(
$"The PrivateKey used to generate the genesis block. " +
$"This property cannot be used with {nameof(GenesisBlockPath)}.")]
$"This property cannot be used with {nameof(GenesisBlockPath)} and " +
$"{nameof(GenesisConfigurationPath)}.")]
public string GenesisKey { get; set; } = string.Empty;

[PublicKeyArray]
Expand All @@ -26,6 +27,12 @@ public sealed class GenesisOptions : OptionsBase<GenesisOptions>

[Description(
$"The path of the genesis block, which can be a file path or a URI." +
$"This property cannot be used with {nameof(GenesisKey)}.")]
$"This property cannot be used with {nameof(GenesisKey)} and " +
$"{nameof(GenesisConfigurationPath)}.")]
public string GenesisBlockPath { get; set; } = string.Empty;

[Description(
$"The path of the genesis configuration, which can be a file path or a URI." +
$"This property cannot be used with {nameof(GenesisKey)} and {nameof(GenesisBlockPath)}.")]
public string GenesisConfigurationPath { get; set; } = string.Empty;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ internal sealed class GenesisOptionsConfigurator(
{
protected override void OnConfigure(GenesisOptions options)
{
if (options.GenesisBlockPath == string.Empty)
if (options.GenesisBlockPath == string.Empty &&
options.GenesisConfigurationPath == string.Empty)
{
if (options.GenesisKey == string.Empty)
{
Expand Down
34 changes: 34 additions & 0 deletions sdk/node/Libplanet.Node/Options/GenesisOptionsValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ protected override void OnValidate(string? name, GenesisOptions options)
failureMessages: [message]);
}

if (options.GenesisConfigurationPath != string.Empty)
{
var message = $"{nameof(options.GenesisConfigurationPath)} cannot be used with " +
$"{nameof(options.GenesisBlockPath)}.";
throw new OptionsValidationException(
optionsName: name ?? string.Empty,
optionsType: typeof(GenesisOptions),
failureMessages: [message]);
}

if (options.Validators.Length > 0)
{
var message = $"{nameof(options.Validators)} cannot be used with " +
Expand All @@ -39,5 +49,29 @@ protected override void OnValidate(string? name, GenesisOptions options)
failureMessages: [message]);
}
}

if (options.GenesisConfigurationPath != string.Empty)
{
if (options.GenesisBlockPath != string.Empty)
{
var message = $"{nameof(options.GenesisBlockPath)} cannot be used with " +
$"{nameof(options.GenesisConfigurationPath)}.";
throw new OptionsValidationException(
optionsName: name ?? string.Empty,
optionsType: typeof(GenesisOptions),
failureMessages: [message]);
}

if (!Uri.TryCreate(options.GenesisConfigurationPath, UriKind.Absolute, out _)
&& !File.Exists(options.GenesisConfigurationPath))
{
var message = $"{nameof(options.GenesisConfigurationPath)} must be a Uri or a " +
$"existing file path.";
throw new OptionsValidationException(
optionsName: name ?? string.Empty,
optionsType: typeof(GenesisOptions),
failureMessages: [message]);
}
}
}
}
85 changes: 76 additions & 9 deletions sdk/node/Libplanet.Node/Services/BlockChainService.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Numerics;
using System.Text.Json;
using Bencodex;
using Bencodex.Types;
using Libplanet.Action;
using Libplanet.Action.Loader;
using Libplanet.Action.State;
using Libplanet.Action.Sys;
using Libplanet.Blockchain;
using Libplanet.Blockchain.Policies;
using Libplanet.Blockchain.Renderers;
using Libplanet.Crypto;
using Libplanet.Node.Options;
using Libplanet.Store;
using Libplanet.Store.Trie;
using Libplanet.Types.Blocks;
using Libplanet.Types.Consensus;
using Libplanet.Types.Tx;
Expand Down Expand Up @@ -51,7 +54,7 @@ private static BlockChain CreateBlockChain(
policyActionsRegistry: policyActionsRegistry,
stateStore,
actionLoader);
var genesisBlock = CreateGenesisBlock(genesisOptions);
var genesisBlock = CreateGenesisBlock(genesisOptions, stateStore);
var policy = new BlockPolicy(
policyActionsRegistry: policyActionsRegistry,
blockInterval: TimeSpan.FromSeconds(8),
Expand Down Expand Up @@ -88,15 +91,10 @@ private static BlockChain CreateBlockChain(
renderers: renderers);
}

private static Block CreateGenesisBlock(GenesisOptions genesisOptions)
private static Block CreateGenesisBlock(
GenesisOptions genesisOptions,
IStateStore stateStore)
{
if (genesisOptions.GenesisKey != string.Empty)
{
var genesisKey = PrivateKey.FromString(genesisOptions.GenesisKey);
var validatorKeys = genesisOptions.Validators.Select(PublicKey.FromHex).ToArray();
return CreateGenesisBlock(genesisKey, validatorKeys);
}

if (genesisOptions.GenesisBlockPath != string.Empty)
{
return genesisOptions.GenesisBlockPath switch
Expand All @@ -108,6 +106,29 @@ private static Block CreateGenesisBlock(GenesisOptions genesisOptions)
};
}

if (genesisOptions.GenesisConfigurationPath != string.Empty)
{
var raw = genesisOptions.GenesisConfigurationPath switch
{
{ } path when Uri.TryCreate(path, UriKind.Absolute, out var uri)
=> LoadConfigurationFromUri(uri),
{ } path => LoadConfigurationFromFilePath(path),
_ => throw new NotSupportedException(),
};

return CreateGenesisBlockFromConfiguration(
PrivateKey.FromString(genesisOptions.GenesisKey),
raw,
stateStore);
}

if (genesisOptions.GenesisKey != string.Empty)
{
var genesisKey = PrivateKey.FromString(genesisOptions.GenesisKey);
var validatorKeys = genesisOptions.Validators.Select(PublicKey.FromHex).ToArray();
return CreateGenesisBlock(genesisKey, validatorKeys);
}

throw new UnreachableException("Genesis block path is not set.");
}

Expand Down Expand Up @@ -152,4 +173,50 @@ private static Block LoadGenesisBlockFromUrl(Uri genesisBlockUri)
var blockDict = (Dictionary)_codec.Decode(rawBlock);
return BlockMarshaler.UnmarshalBlock(blockDict);
}

private static byte[] LoadConfigurationFromFilePath(string configurationPath)
{
return File.ReadAllBytes(Path.GetFullPath(configurationPath));
}

private static byte[] LoadConfigurationFromUri(Uri configurationUri)
{
using var client = new HttpClient();
return client.GetByteArrayAsync(configurationUri).Result;
}

private static Block CreateGenesisBlockFromConfiguration(
PrivateKey genesisKey,
byte[] config,
IStateStore stateStore)
{
Dictionary<string, object>? data =
JsonSerializer.Deserialize<Dictionary<string, object>>(config);
var trie = stateStore.GetStateRoot(null);
if (data == null || data.Count == 0)
{
return BlockChain.ProposeGenesisBlock(
privateKey: genesisKey,
timestamp: DateTimeOffset.MinValue);
}

foreach (var kv in data)
{
try
{
var key = new Address(kv.Key);
trie = trie.Set(KeyConverters.ToStateKey(key), (IValue)kv.Value);
}
catch (Exception)
{
// skip value
}
}

trie = stateStore.Commit(trie);
return BlockChain.ProposeGenesisBlock(
privateKey: genesisKey,
stateRootHash: trie.Hash,
timestamp: DateTimeOffset.MinValue);
}
}
2 changes: 1 addition & 1 deletion src/Libplanet.Action/State/KeyConverters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Libplanet.Action.State
{
internal static class KeyConverters
public static class KeyConverters
{
// "___"
public static readonly KeyBytes ValidatorSetKey =
Expand Down

0 comments on commit f8c9b84

Please sign in to comment.