Skip to content

Commit

Permalink
feat: prepare initial pos states
Browse files Browse the repository at this point in the history
  • Loading branch information
limebell committed Apr 2, 2024
1 parent 051c5c2 commit 3646f64
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 22 deletions.
92 changes: 92 additions & 0 deletions .Lib9c.Tests/Action/DPoS/DPoSActionTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
namespace Lib9c.Tests.Action.DPoS
{
using System.Linq;
using Libplanet.Action.State;
using Libplanet.Crypto;
using Libplanet.Types.Assets;
using Nekoyume.Action.DPoS;
using Nekoyume.Action.DPoS.Control;
using Nekoyume.Action.DPoS.Misc;
using Nekoyume.Action.DPoS.Sys;
using Nekoyume.Module;
using Xunit;

public class DPoSActionTest
{
[Fact]
public void Execute()
{
// Prepare initial state.
IWorld initialState = new World(new MockWorldState());
const int count = 4;
var validatorKeys = Enumerable.Range(0, count).Select(_ => new PrivateKey().PublicKey).ToArray();
initialState = validatorKeys.Aggregate(
initialState,
(current, key) => current.MintAsset(
new ActionContext(),
key.Address,
new FungibleAssetValue(Asset.GovernanceToken, 1, 0)));
foreach (var key in validatorKeys)
{
Assert.Equal(1, initialState.GetBalance(key.Address, Asset.GovernanceToken).MajorUnit);
Assert.Equal(0, initialState.GetBalance(key.Address, Asset.GovernanceToken).MinorUnit);
}

// Stake 1 for each validator.
foreach (var key in validatorKeys)
{
initialState = new PromoteValidator(
key,
new FungibleAssetValue(Asset.GovernanceToken, 1, 0)).Execute(
new ActionContext
{
PreviousState = initialState,
Signer = key.Address,
});
}

Assert.Equal(0, ValidatorSetCtrl.FetchBondedValidatorSet(initialState).Item2.Count);
Assert.Equal(0, initialState.GetValidatorSet().TotalCount);

// Execute the action.
initialState = new PoSAction().Execute(
new ActionContext
{
PreviousState = initialState,
LastCommit = null,
});

Assert.Equal(count, ValidatorSetCtrl.FetchBondedValidatorSet(initialState).Item2.Count);
Assert.Equal(count, initialState.GetValidatorSet().TotalCount);
Assert.Equal(
validatorKeys.ToHashSet(),
initialState.GetValidatorSet()
.Validators.Select(validator => validator.PublicKey)
.ToHashSet());

// TODO: Add gold distribution tests
initialState = new Undelegate(
validatorKeys[0].Address,
new FungibleAssetValue(Asset.Share, 1, 0)).Execute(
new ActionContext
{
PreviousState = initialState,
Signer = validatorKeys[0].Address,
});

Assert.Equal(count, ValidatorSetCtrl.FetchBondedValidatorSet(initialState).Item2.Count);
Assert.Equal(count, initialState.GetValidatorSet().TotalCount);

// Execute the action.
initialState = new PoSAction().Execute(
new ActionContext
{
PreviousState = initialState,
LastCommit = null,
});

Assert.Equal(count - 1, ValidatorSetCtrl.FetchBondedValidatorSet(initialState).Item2.Count);
Assert.Equal(count - 1, initialState.GetValidatorSet().TotalCount);
}
}
}
3 changes: 2 additions & 1 deletion Lib9c.MessagePack/Action/NCActionEvaluation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Libplanet.Common;
using Libplanet.Types.Tx;
using MessagePack;
using Nekoyume.Action.DPoS;

namespace Nekoyume.Action
{
Expand Down Expand Up @@ -81,7 +82,7 @@ public ActionEvaluation<ActionBase> ToActionEvaluation()
{
return new ActionEvaluation<ActionBase>
{
Action = Action is null ? new RewardGold() : Action,
Action = Action is null ? new PoSAction() : Action,
Signer = Signer,
BlockIndex = BlockIndex,
OutputState = OutputState,
Expand Down
3 changes: 2 additions & 1 deletion Lib9c.Policy/Policy/BlockPolicySource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Libplanet.Crypto;
using Libplanet.Types.Blocks;
using Libplanet.Types.Tx;
using Nekoyume.Action.DPoS;

#if UNITY_EDITOR || UNITY_STANDALONE
using UniRx;
Expand Down Expand Up @@ -130,7 +131,7 @@ internal IBlockPolicy GetPolicy(

// FIXME: Slight inconsistency due to pre-existing delegate.
return new BlockPolicy(
new RewardGold(),
new PoSAction(),
blockInterval: BlockInterval,
validateNextBlockTx: validateNextBlockTx,
validateNextBlock: validateNextBlock,
Expand Down
3 changes: 2 additions & 1 deletion Lib9c.Policy/Policy/DebugPolicy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Libplanet.Types.Blocks;
using Libplanet.Types.Tx;
using Nekoyume.Action;
using Nekoyume.Action.DPoS;

namespace Nekoyume.Blockchain.Policy
{
Expand All @@ -13,7 +14,7 @@ public DebugPolicy()
{
}

public IAction BlockAction { get; } = new RewardGold();
public IAction BlockAction { get; } = new PoSAction();

public TxPolicyViolationException ValidateNextBlockTx(
BlockChain blockChain, Transaction transaction)
Expand Down
5 changes: 3 additions & 2 deletions Lib9c.Renderers/Renderers/ActionRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Serilog;
using Bencodex.Types;
using Libplanet.Common;
using Nekoyume.Action.DPoS;
using Nekoyume.Action.Loader;
#if UNITY_EDITOR || UNITY_STANDALONE
using UniRx;
Expand Down Expand Up @@ -37,7 +38,7 @@ public void RenderAction(IValue action, ICommittedActionContext context, HashDig
ActionRenderSubject.OnNext(new ActionEvaluation<ActionBase>
{
Action = context.BlockAction
? new RewardGold()
? new PoSAction()
: (ActionBase)_actionLoader.LoadAction(context.BlockIndex, action),
Signer = context.Signer,
BlockIndex = context.BlockIndex,
Expand All @@ -53,7 +54,7 @@ public void RenderActionError(IValue action, ICommittedActionContext context, Ex
ActionRenderSubject.OnNext(new ActionEvaluation<ActionBase>
{
Action = context.BlockAction
? new RewardGold()
? new PoSAction()
: (ActionBase)_actionLoader.LoadAction(context.BlockIndex, action),
Signer = context.Signer,
BlockIndex = context.BlockIndex,
Expand Down
15 changes: 7 additions & 8 deletions Lib9c.Utils/BlockHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Libplanet.Types.Consensus;
using Libplanet.Types.Tx;
using Nekoyume.Action;
using Nekoyume.Action.DPoS;
using Nekoyume.Action.Loader;
using Nekoyume.Blockchain.Policy;
using Nekoyume.Model.State;
Expand All @@ -38,8 +39,8 @@ public static Block ProposeGenesisBlock(
DateTimeOffset? timestamp = null,
IEnumerable<ActionBase>? actionBases = null,
Currency? goldCurrency = null,
ISet<Address>? assetMinters = null
)
ISet<Address>? assetMinters = null,
Dictionary<Address, FungibleAssetValue>? initialFavs = null)
{
if (!tableSheets.TryGetValue(nameof(GameConfigSheet), out var csv))
{
Expand Down Expand Up @@ -74,7 +75,8 @@ public static Block ProposeGenesisBlock(
pendingActivationStates: pendingActivationStates,
authorizedMinersState: authorizedMinersState,
creditsState: credits is null ? null : new CreditsState(credits),
assetMinters: assetMinters
assetMinters: assetMinters,
initialFavs: initialFavs
);
List<ActionBase> actions = new List<ActionBase>
{
Expand All @@ -84,11 +86,8 @@ public static Block ProposeGenesisBlock(
{
new Initialize(
states: ImmutableDictionary.Create<Address, IValue>(),
validatorSet: new ValidatorSet(
initialValidators.Select(validator =>
new Validator(validator.Key, validator.Value)).ToList()
)
),
validatorSet: new ValidatorSet()),
new InitializeValidators(initialValidators)
};
if (!(actionBases is null))
{
Expand Down
94 changes: 94 additions & 0 deletions Lib9c/Action/DPoS/InitializeValidators.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Numerics;
using Bencodex.Types;
using Libplanet.Action;
using Libplanet.Action.State;
using Libplanet.Crypto;
using Libplanet.Types.Assets;
using Nekoyume.Action.DPoS.Control;
using Nekoyume.Action.DPoS.Misc;
using Nekoyume.Action.DPoS.Util;
using Nekoyume.Module;
using Serilog;

namespace Nekoyume.Action.DPoS
{
[ActionType(ActionTypeValue)]
public sealed class InitializeValidators : ActionBase
{
private const string ActionTypeValue = "initialize_validators";

public InitializeValidators(Dictionary<PublicKey, BigInteger> validators)
{
Validators = validators.ToImmutableDictionary();
}

public InitializeValidators()
{
}

public ImmutableDictionary<PublicKey, BigInteger> Validators { get; set; }

public override IValue PlainValue {
get
{
var validators = Dictionary.Empty;
#pragma warning disable LAA1002
foreach (var (validator, power) in Validators)
{
validators = validators.Add((Binary)validator.Serialize(), power);
}
#pragma warning restore LAA1002

return Dictionary.Empty
.Add("type_id", new Text(ActionTypeValue))
.Add("validators", validators);
}
}

public override void LoadPlainValue(IValue plainValue)
{
var dict = (Bencodex.Types.Dictionary)plainValue;
var validatorDict = (Dictionary)dict["validators"];
Validators = validatorDict.Select(
pair =>
{
var key = pair.Key.ToPublicKey();
var power = (Integer)pair.Value;
return new KeyValuePair<PublicKey, BigInteger>(key, power);
}).ToImmutableDictionary();
}

public override IWorld Execute(IActionContext context)
{
Log.Debug("InitializeValidators");
context.UseGas(1);
var states = context.PreviousState;

var nativeTokens = ImmutableHashSet.Create(
Asset.GovernanceToken, Asset.ConsensusToken, Asset.Share);
#pragma warning disable LAA1002
foreach(var (validator, power) in Validators)
{
var amount = new FungibleAssetValue(Asset.GovernanceToken, power, 0);
states = states.MintAsset(
context,
validator.Address,
amount);
states = ValidatorCtrl.Create(
states,
context,
validator.Address,
validator,
amount,
nativeTokens);
}
#pragma warning restore LAA1002

Log.Debug("InitializeValidators complete");
return states;
}
}
}
14 changes: 10 additions & 4 deletions Lib9c/Action/DPoS/PoSAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
using Nekoyume.Action.DPoS.Misc;
using Nekoyume.Action.DPoS.Model;
using Nekoyume.Module;
using Serilog;

namespace Nekoyume.Action.DPoS
{
/// <summary>
/// A block action for DPoS that updates <see cref="ValidatorSet"/>.
/// </summary>
public sealed class PoSAction : IAction
public sealed class PoSAction : ActionBase
{
/// <summary>
/// Creates a new instance of <see cref="PoSAction"/>.
Expand All @@ -22,17 +23,18 @@ public PoSAction()
}

/// <inheritdoc cref="IAction.PlainValue"/>
public IValue PlainValue => new Bencodex.Types.Boolean(true);
public override IValue PlainValue => new Bencodex.Types.Boolean(true);

/// <inheritdoc cref="IAction.LoadPlainValue(IValue)"/>
public void LoadPlainValue(IValue plainValue)
public override void LoadPlainValue(IValue plainValue)
{
// Method intentionally left empty.
}

/// <inheritdoc cref="IAction.Execute(IActionContext)"/>
public IWorld Execute(IActionContext context)
public override IWorld Execute(IActionContext context)
{
Log.Debug("Running PoSAction");
IActionContext ctx = context;
var states = ctx.PreviousState;

Expand All @@ -53,6 +55,10 @@ public IWorld Execute(IActionContext context)
var bondedSet = ValidatorSetCtrl.FetchBondedValidatorSet(states).Item2.Set;
foreach (var validator in bondedSet)
{
Log.Debug(
"Set Validator: {0} (Power: {1})",
validator.OperatorPublicKey,
validator.ConsensusToken.RawValue);
states = states.SetValidator(
new Libplanet.Types.Consensus.Validator(
validator.OperatorPublicKey,
Expand Down
8 changes: 4 additions & 4 deletions Lib9c/Action/DPoS/Sys/PromoteValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace Nekoyume.Action.DPoS.Sys
/// A system action for DPoS that promotes non-validator node to a validator.
/// </summary>
[ActionType(ActionTypeValue)]
public sealed class PromoteValidator : IAction
public sealed class PromoteValidator : ActionBase
{
private const string ActionTypeValue = "promote_validator";

Expand Down Expand Up @@ -50,21 +50,21 @@ public PromoteValidator()
public FungibleAssetValue Amount { get; set; }

/// <inheritdoc cref="IAction.PlainValue"/>
public IValue PlainValue => Bencodex.Types.Dictionary.Empty
public override IValue PlainValue => Bencodex.Types.Dictionary.Empty
.Add("type_id", new Text(ActionTypeValue))
.Add("validator", Validator.Serialize())
.Add("amount", Amount.Serialize());

/// <inheritdoc cref="IAction.LoadPlainValue(IValue)"/>
public void LoadPlainValue(IValue plainValue)
public override void LoadPlainValue(IValue plainValue)
{
var dict = (Bencodex.Types.Dictionary)plainValue;
Validator = dict["validator"].ToPublicKey();
Amount = dict["amount"].ToFungibleAssetValue();
}

/// <inheritdoc cref="IAction.Execute(IActionContext)"/>
public IWorld Execute(IActionContext context)
public override IWorld Execute(IActionContext context)
{
IActionContext ctx = context;
if (!ctx.Signer.Equals(Validator.Address))
Expand Down
Loading

0 comments on commit 3646f64

Please sign in to comment.