From 0bb5744e065de9ae56945dd2bdf625a37e44b5e1 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 24 Oct 2024 01:02:40 +0900 Subject: [PATCH 1/5] feat: Redesign claim action --- .../ClaimRewardValidatorTest.cs | 126 ++++++++++++++---- .Lib9c.Tests/Policy/BlockPolicyTest.cs | 2 +- Lib9c/Action/Guild/ClaimRewardGuild.cs | 65 +++++++++ .../ClaimRewardValidator.cs | 27 +++- .../ClaimRewardValidatorSelf.cs | 47 +++++++ Lib9c/Delegation/Delegator.cs | 4 +- Lib9c/Delegation/IDelegationRepository.cs | 2 + Lib9c/Model/Guild/Guild.cs | 2 +- Lib9c/Model/Guild/GuildParticipant.cs | 123 +++++++++++++++++ Lib9c/Model/Guild/GuildValidatorDelegatee.cs | 43 ++++++ Lib9c/Model/Guild/GuildValidatorDelegator.cs | 37 +++++ Lib9c/Model/Guild/GuildValidatorRepository.cs | 74 ++++++++++ 12 files changed, 520 insertions(+), 32 deletions(-) create mode 100644 Lib9c/Action/Guild/ClaimRewardGuild.cs create mode 100644 Lib9c/Action/ValidatorDelegation/ClaimRewardValidatorSelf.cs create mode 100644 Lib9c/Model/Guild/GuildValidatorDelegatee.cs create mode 100644 Lib9c/Model/Guild/GuildValidatorDelegator.cs create mode 100644 Lib9c/Model/Guild/GuildValidatorRepository.cs diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs index e68b0fa597..b0aa663e0b 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -29,9 +29,15 @@ private interface IClaimRewardFixture DelegatorInfo[] DelegatorInfos { get; } + GuildParticipantInfo[] GuildParticipantInfos { get; } + PrivateKey[] DelegatorKeys => DelegatorInfos.Select(i => i.Key).ToArray(); + PrivateKey[] GuildParticipantKeys => GuildParticipantInfos.Select(i => i.Key).ToArray(); + FungibleAssetValue[] DelegatorBalances => DelegatorInfos.Select(i => i.Balance).ToArray(); + + FungibleAssetValue[] GuildParticipantBalances => GuildParticipantInfos.Select(i => i.Balance).ToArray(); } public static IEnumerable RandomSeeds => new List @@ -70,7 +76,7 @@ public void Execute() // When var expectedBalance = allocatedReward; var lastCommit = CreateLastCommit(validatorKey, height - 1); - var claimRewardValidator = new ClaimRewardValidator(validatorKey.Address); + var claimRewardValidator = new ClaimRewardValidatorSelf(); var actionContext = new ActionContext { PreviousState = world, @@ -93,6 +99,25 @@ public void Execute() [InlineData(1)] public void Execute_Theory_OneDelegator(decimal totalReward) { + var delegatorInfos = new[] + { + new DelegatorInfo + { + Key = new PrivateKey(), + Balance = DelegationCurrency * 100, + }, + }; + + var guildParticipantInfos = new[] + { + new GuildParticipantInfo + { + Key = new PrivateKey(), + Balance = DelegationCurrency * 100, + GuildMasterAddress = delegatorInfos[0].Key.Address, + }, + }; + var fixture = new StaticFixture { DelegatorLength = 1, @@ -100,14 +125,8 @@ public void Execute_Theory_OneDelegator(decimal totalReward) ValidatorKey = new PrivateKey(), ValidatorBalance = DelegationCurrency * 100, ValidatorCash = DelegationCurrency * 10, - DelegatorInfos = new[] - { - new DelegatorInfo - { - Key = new PrivateKey(), - Balance = DelegationCurrency * 100, - }, - }, + DelegatorInfos = delegatorInfos, + GuildParticipantInfos = guildParticipantInfos, }; ExecuteWithFixture(fixture); } @@ -128,6 +147,36 @@ public void Execute_Theory_OneDelegator(decimal totalReward) [InlineData(34.29)] public void Execute_Theory_TwoDelegators(decimal totalReward) { + var delegatorInfos = new[] + { + new DelegatorInfo + { + Key = new PrivateKey(), + Balance = DelegationCurrency * 100, + }, + new DelegatorInfo + { + Key = new PrivateKey(), + Balance = DelegationCurrency * 100, + }, + }; + + var guildParticipantInfos = new[] + { + new GuildParticipantInfo + { + Key = new PrivateKey(), + Balance = DelegationCurrency * 100, + GuildMasterAddress = delegatorInfos[0].Key.Address, + }, + new GuildParticipantInfo + { + Key = new PrivateKey(), + Balance = DelegationCurrency * 100, + GuildMasterAddress = delegatorInfos[1].Key.Address, + }, + }; + var fixture = new StaticFixture { DelegatorLength = 2, @@ -135,19 +184,8 @@ public void Execute_Theory_TwoDelegators(decimal totalReward) ValidatorKey = new PrivateKey(), ValidatorBalance = DelegationCurrency * 100, ValidatorCash = DelegationCurrency * 10, - DelegatorInfos = new[] - { - new DelegatorInfo - { - Key = new PrivateKey(), - Balance = DelegationCurrency * 100, - }, - new DelegatorInfo - { - Key = new PrivateKey(), - Balance = DelegationCurrency * 100, - }, - }, + DelegatorInfos = delegatorInfos, + GuildParticipantInfos = guildParticipantInfos, }; ExecuteWithFixture(fixture); } @@ -177,6 +215,8 @@ private void ExecuteWithFixture(IClaimRewardFixture fixture) var world = World; var validatorKey = fixture.ValidatorKey; var delegatorKeys = fixture.DelegatorKeys; + var guildParticipantInfos = fixture.GuildParticipantInfos; + var guildParticipantKeys = fixture.GuildParticipantKeys; var delegatorBalances = fixture.DelegatorBalances; var height = 1L; var actionContext = new ActionContext(); @@ -189,6 +229,8 @@ private void ExecuteWithFixture(IClaimRewardFixture fixture) world = EnsureToMintAssets(world, delegatorKeys, delegatorBalances, height++); world = delegatorKeys.Aggregate(world, (w, d) => EnsureMakeGuild( w, d.Address, validatorKey.Address, height++, seed++)); + world = guildParticipantInfos.Aggregate(world, (w, i) => EnsureJoinGuild( + w, i.Key.Address, i.GuildMasterAddress, validatorKey.Address, height++)); world = EnsureRewardAllocatedValidator(world, validatorKey, totalReward, ref height); @@ -236,7 +278,7 @@ var expectedProposerReward Signer = validatorKey.Address, LastCommit = lastCommit, }; - world = new ClaimRewardValidator(validatorKey.Address).Execute(actionContext); + world = new ClaimRewardValidatorSelf().Execute(actionContext); for (var i = 0; i < length; i++) { actionContext = new ActionContext @@ -271,6 +313,18 @@ var expectedProposerReward Assert.Equal(expectedDelegatorBalances, actualDelegatorBalances); Assert.Equal(expectedValidatorReward, actualValidatorReward); Assert.Equal(expectedDelegatorClaims, actualDelegatorRewards); + + foreach (var key in guildParticipantKeys) + { + Assert.Throws( + () => new ClaimRewardValidator(validatorKey.Address).Execute(new ActionContext + { + PreviousState = world, + BlockIndex = height++, + Signer = key.Address, + LastCommit = lastCommit, + })); + } } private struct DelegatorInfo @@ -280,6 +334,15 @@ private struct DelegatorInfo public FungibleAssetValue Balance { get; set; } } + private struct GuildParticipantInfo + { + public PrivateKey Key { get; set; } + + public FungibleAssetValue Balance { get; set; } + + public Address GuildMasterAddress { get; set; } + } + private struct StaticFixture : IClaimRewardFixture { public int DelegatorLength { get; set; } @@ -293,6 +356,8 @@ private struct StaticFixture : IClaimRewardFixture public FungibleAssetValue ValidatorCash { get; set; } public DelegatorInfo[] DelegatorInfos { get; set; } + + public GuildParticipantInfo[] GuildParticipantInfos { get; set; } } private class RandomFixture : IClaimRewardFixture @@ -303,6 +368,7 @@ public RandomFixture(int randomSeed) { _random = new Random(randomSeed); DelegatorLength = _random.Next(3, 100); + GuildParticipantLength = _random.Next(1, 50); ValidatorKey = new PrivateKey(); TotalReward = GetRandomFAV(RewardCurrency, _random); ValidatorBalance = GetRandomFAV(DelegationCurrency, _random); @@ -316,10 +382,22 @@ public RandomFixture(int randomSeed) Balance = balance, }; }); + GuildParticipantInfos = CreateArray(GuildParticipantLength, _ => + { + var balance = GetRandomFAV(DelegationCurrency, _random); + return new GuildParticipantInfo + { + Key = new PrivateKey(), + Balance = balance, + GuildMasterAddress = DelegatorInfos[_random.Next(DelegatorLength)].Key.Address, + }; + }); } public int DelegatorLength { get; } + public int GuildParticipantLength { get; } + public FungibleAssetValue TotalReward { get; } public PrivateKey ValidatorKey { get; } @@ -329,5 +407,7 @@ public RandomFixture(int randomSeed) public FungibleAssetValue ValidatorCash { get; } public DelegatorInfo[] DelegatorInfos { get; } + + public GuildParticipantInfo[] GuildParticipantInfos { get; } } } diff --git a/.Lib9c.Tests/Policy/BlockPolicyTest.cs b/.Lib9c.Tests/Policy/BlockPolicyTest.cs index 0403edaf16..76b165d511 100644 --- a/.Lib9c.Tests/Policy/BlockPolicyTest.cs +++ b/.Lib9c.Tests/Policy/BlockPolicyTest.cs @@ -361,7 +361,7 @@ public void EarnMiningGoldWhenSuccessMining() // After claimed, mead have to be used? blockChain.MakeTransaction( adminPrivateKey, - new ActionBase[] { new ClaimRewardValidator(adminAddress), } + new ActionBase[] { new ClaimRewardValidatorSelf(), } ); block = blockChain.ProposeBlock(adminPrivateKey, commit); diff --git a/Lib9c/Action/Guild/ClaimRewardGuild.cs b/Lib9c/Action/Guild/ClaimRewardGuild.cs new file mode 100644 index 0000000000..8857439228 --- /dev/null +++ b/Lib9c/Action/Guild/ClaimRewardGuild.cs @@ -0,0 +1,65 @@ +using System; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Crypto; +using Nekoyume.Model.Guild; +using Nekoyume.Module.Guild; +using Nekoyume.TypedAddress; + +namespace Nekoyume.Action.ValidatorDelegation +{ + [ActionType(TypeIdentifier)] + public sealed class ClaimRewardGuild : ActionBase + { + public const string TypeIdentifier = "claim_reward_guild"; + + public ClaimRewardGuild() { } + + public ClaimRewardGuild(Address guildAddress) + { + GuildAddress = guildAddress; + } + + public Address GuildAddress { get; private set; } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", List.Empty + .Add(GuildAddress.Bencoded)); + + public override void LoadPlainValue(IValue plainValue) + { + var root = (Dictionary)plainValue; + if (plainValue is not Dictionary || + !root.TryGetValue((Text)"values", out var rawValues) || + rawValues is not List values) + { + throw new InvalidCastException(); + } + + GuildAddress = new Address(values[0]); + } + + public override IWorld Execute(IActionContext context) + { + GasTracer.UseGas(1); + + var world = context.PreviousState; + var guildRepository = new GuildRepository(world, context); + + var guildParticipant = guildRepository.GetGuildParticipant(context.Signer); + if (!(guildRepository.GetJoinedGuild(new AgentAddress(context.Signer)) + is GuildAddress guildAddress)) + { + throw new InvalidOperationException("Signer does not joind guild."); + } + + var guild = guildRepository.GetGuild(guildAddress); + + guildParticipant.ClaimReward(guild, context.BlockIndex); + + return guildRepository.World; + } + } +} diff --git a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs index 13dc8767b6..1e8faf035a 100644 --- a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs @@ -3,8 +3,9 @@ using Libplanet.Action.State; using Libplanet.Action; using Libplanet.Crypto; -using Nekoyume.Module.ValidatorDelegation; -using Nekoyume.ValidatorDelegation; +using Nekoyume.Model.Guild; +using Nekoyume.Module.Guild; +using Nekoyume.TypedAddress; namespace Nekoyume.Action.ValidatorDelegation { @@ -45,10 +46,26 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; - var repository = new ValidatorRepository(world, context); - repository.ClaimRewardValidator(ValidatorDelegatee); + var guildRepository = new GuildRepository(world, context); - return repository.World; + if (!(guildRepository.GetJoinedGuild(new AgentAddress(context.Signer)) + is GuildAddress guildAddress)) + { + throw new InvalidOperationException("Signer does not joined guild."); + } + + var guild = guildRepository.GetGuild(guildAddress); + if (context.Signer != guild.GuildMasterAddress) + { + throw new InvalidOperationException("Signer is not a guild master."); + } + + var guildValidatorRepository = new GuildValidatorRepository(world, context); + var guildValidatorDelegator = guildValidatorRepository.GetGuildValidatorDelegator(context.Signer); + var guildValidatorDelegatee = guildValidatorRepository.GetGuildValidatorDelegatee(ValidatorDelegatee); + guildValidatorDelegator.ClaimReward(guildValidatorDelegatee, context.BlockIndex); + + return guildValidatorRepository.World; } } } diff --git a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidatorSelf.cs b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidatorSelf.cs new file mode 100644 index 0000000000..57a0e0c7dc --- /dev/null +++ b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidatorSelf.cs @@ -0,0 +1,47 @@ +using System; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Crypto; +using Nekoyume.ValidatorDelegation; + +namespace Nekoyume.Action.ValidatorDelegation +{ + [ActionType(TypeIdentifier)] + public sealed class ClaimRewardValidatorSelf : ActionBase + { + public const string TypeIdentifier = "claim_reward_validator_self"; + + public ClaimRewardValidatorSelf() { } + + public Address ValidatorDelegatee { get; private set; } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", Null.Value); + + public override void LoadPlainValue(IValue plainValue) + { + var root = (Dictionary)plainValue; + if (plainValue is not Dictionary || + !root.TryGetValue((Text)"values", out var rawValues) || + rawValues is not Null) + { + throw new InvalidCastException(); + } + } + + public override IWorld Execute(IActionContext context) + { + GasTracer.UseGas(1); + + var world = context.PreviousState; + var repository = new ValidatorRepository(world, context); + var validatorDelegatee = repository.GetValidatorDelegatee(context.Signer); + var validatorDelegator = repository.GetValidatorDelegator(context.Signer, context.Signer); + validatorDelegator.ClaimReward(validatorDelegatee, context.BlockIndex); + + return repository.World; + } + } +} diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index d46d88caf3..92c036f850 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -83,7 +83,7 @@ void IDelegator.Delegate( IDelegatee delegatee, FungibleAssetValue fav, long height) => Delegate((T)delegatee, fav, height); - public void Undelegate( + public virtual void Undelegate( T delegatee, BigInteger share, long height) { if (share.Sign <= 0) @@ -127,7 +127,7 @@ void IDelegator.Undelegate( => Undelegate((T)delegatee, share, height); - public void Redelegate( + public virtual void Redelegate( T srcDelegatee, T dstDelegatee, BigInteger share, long height) { if (share.Sign <= 0) diff --git a/Lib9c/Delegation/IDelegationRepository.cs b/Lib9c/Delegation/IDelegationRepository.cs index 6d2c7a2f19..47c988a0b5 100644 --- a/Lib9c/Delegation/IDelegationRepository.cs +++ b/Lib9c/Delegation/IDelegationRepository.cs @@ -61,5 +61,7 @@ public interface IDelegationRepository void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord); void TransferAsset(Address sender, Address recipient, FungibleAssetValue value); + + void UpdateWorld(IWorld world); } } diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index 667f7ad874..81991b59dd 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -27,7 +27,7 @@ public Guild( GuildRepository repository) : base( address: address, - accountAddress: Addresses.Guild, + accountAddress: repository.DelegateeAccountAddress, delegationCurrency: Currencies.GuildGold, rewardCurrency: rewardCurrency, delegationPoolAddress: address, diff --git a/Lib9c/Model/Guild/GuildParticipant.cs b/Lib9c/Model/Guild/GuildParticipant.cs index 91cad3de83..8a1ae21b9a 100644 --- a/Lib9c/Model/Guild/GuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildParticipant.cs @@ -1,6 +1,8 @@ using System; +using System.Numerics; using Bencodex; using Bencodex.Types; +using Libplanet.Types.Assets; using Nekoyume.Action; using Nekoyume.Delegation; using Nekoyume.TypedAddress; @@ -26,6 +28,8 @@ public GuildParticipant( repository: repository) { GuildAddress = guildAddress; + GuildValidatorRepository = new GuildValidatorRepository( + repository.World, repository.ActionContext); } public GuildParticipant( @@ -50,6 +54,8 @@ public GuildParticipant( } GuildAddress = new GuildAddress(list[2]); + GuildValidatorRepository = new GuildValidatorRepository( + repository.World, repository.ActionContext); } public new AgentAddress Address => new AgentAddress(base.Address); @@ -59,8 +65,125 @@ public GuildParticipant( .Add(StateVersion) .Add(GuildAddress.Bencoded); + public GuildValidatorRepository GuildValidatorRepository { get; } + IValue IBencodable.Bencoded => Bencoded; + public override void Delegate(Guild delegatee, FungibleAssetValue fav, long height) + { + if (fav.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(fav), fav, "Fungible asset value must be positive."); + } + + if (delegatee.Tombstoned) + { + throw new InvalidOperationException("Delegatee is tombstoned."); + } + + var guildValidatorDelegatee = GuildValidatorRepository.GetDelegatee(delegatee.ValidatorAddress); + var guildValidatorDelegator = GuildValidatorRepository.GetDelegator(delegatee.Address); + + guildValidatorDelegatee.Bond(guildValidatorDelegator, fav, height); + Repository.UpdateWorld(GuildValidatorRepository.World); + Metadata.AddDelegatee(delegatee.Address); + Repository.TransferAsset(DelegationPoolAddress, delegatee.DelegationPoolAddress, fav); + Repository.SetDelegator(this); + } + + public override void Undelegate(Guild delegatee, BigInteger share, long height) + { + if (share.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(share), share, "Share must be positive."); + } + + if (height <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(height), height, "Height must be positive."); + } + + UnbondLockIn unbondLockIn = Repository.GetUnbondLockIn(delegatee, Address); + + if (unbondLockIn.IsFull) + { + throw new InvalidOperationException("Undelegation is full."); + } + + var guildValidatorDelegatee = GuildValidatorRepository.GetDelegatee(delegatee.ValidatorAddress); + var guildValidatorDelegator = GuildValidatorRepository.GetDelegator(delegatee.Address); + FungibleAssetValue fav = guildValidatorDelegatee.Unbond(guildValidatorDelegator, share, height); + Repository.UpdateWorld(GuildValidatorRepository.World); + unbondLockIn = unbondLockIn.LockIn( + fav, height, height + delegatee.UnbondingPeriod); + + if (!delegatee.Delegators.Contains(Address)) + { + Metadata.RemoveDelegatee(delegatee.Address); + } + + delegatee.AddUnbondingRef(UnbondingFactory.ToReference(unbondLockIn)); + + Repository.SetUnbondLockIn(unbondLockIn); + Repository.SetUnbondingSet( + Repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); + Repository.SetDelegator(this); + } + + public override void Redelegate( + Guild srcDelegatee, Guild dstDelegatee, BigInteger share, long height) + { + if (share.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(share), share, "Share must be positive."); + } + + if (height <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(height), height, "Height must be positive."); + } + + if (dstDelegatee.Tombstoned) + { + throw new InvalidOperationException("Destination delegatee is tombstoned."); + } + + var srcGuildValidatorDelegatee = GuildValidatorRepository.GetDelegatee(srcDelegatee.ValidatorAddress); + var srcGuildValidatorDelegator = GuildValidatorRepository.GetDelegator(srcDelegatee.Address); + var dstGuildValidatorDelegatee = GuildValidatorRepository.GetDelegatee(dstDelegatee.ValidatorAddress); + var dstGuildValidatorDelegator = GuildValidatorRepository.GetDelegator(dstDelegatee.Address); + + FungibleAssetValue fav = srcGuildValidatorDelegatee.Unbond( + srcGuildValidatorDelegator, share, height); + dstGuildValidatorDelegatee.Bond( + dstGuildValidatorDelegator, fav, height); + Repository.UpdateWorld(GuildValidatorRepository.World); + RebondGrace srcRebondGrace = Repository.GetRebondGrace(srcDelegatee, Address).Grace( + dstDelegatee.Address, + fav, + height, + height + srcDelegatee.UnbondingPeriod); + + if (!srcDelegatee.Delegators.Contains(Address)) + { + Metadata.RemoveDelegatee(srcDelegatee.Address); + } + + Metadata.AddDelegatee(dstDelegatee.Address); + + srcDelegatee.AddUnbondingRef(UnbondingFactory.ToReference(srcRebondGrace)); + + Repository.SetRebondGrace(srcRebondGrace); + Repository.SetUnbondingSet( + Repository.GetUnbondingSet().SetUnbonding(srcRebondGrace)); + Repository.SetDelegator(this); + } + public bool Equals(GuildParticipant other) { if (ReferenceEquals(null, other)) return false; diff --git a/Lib9c/Model/Guild/GuildValidatorDelegatee.cs b/Lib9c/Model/Guild/GuildValidatorDelegatee.cs new file mode 100644 index 0000000000..ee5e9adf37 --- /dev/null +++ b/Lib9c/Model/Guild/GuildValidatorDelegatee.cs @@ -0,0 +1,43 @@ +#nullable enable +using Lib9c; +using Libplanet.Crypto; +using System; +using Nekoyume.Delegation; + +namespace Nekoyume.Model.Guild +{ + public class GuildValidatorDelegatee + : Delegatee, IEquatable + { + public GuildValidatorDelegatee( + Address address, + Address delegationPoolAddress, + GuildValidatorRepository repository) + : base( + address: address, + accountAddress: repository.DelegateeAccountAddress, + delegationCurrency: Currencies.GuildGold, + rewardCurrency: Currencies.Mead, + delegationPoolAddress: address, + rewardRemainderPoolAddress: Addresses.CommunityPool, + slashedPoolAddress: Addresses.CommunityPool, + unbondingPeriod: 75600L, + maxUnbondLockInEntries: 10, + maxRebondGraceEntries: 10, + repository: repository) + { + } + + public GuildValidatorDelegatee( + Address address, + GuildValidatorRepository repository) + : base( + address: address, + repository: repository) + { + } + + public bool Equals(GuildValidatorDelegatee? other) + => Metadata.Equals(other?.Metadata); + } +} diff --git a/Lib9c/Model/Guild/GuildValidatorDelegator.cs b/Lib9c/Model/Guild/GuildValidatorDelegator.cs new file mode 100644 index 0000000000..01e3666c9c --- /dev/null +++ b/Lib9c/Model/Guild/GuildValidatorDelegator.cs @@ -0,0 +1,37 @@ +#nullable enable +using Libplanet.Crypto; +using Nekoyume.Delegation; +using Nekoyume.ValidatorDelegation; +using System; + +namespace Nekoyume.Model.Guild +{ + public class GuildValidatorDelegator + : Delegator, IEquatable + { + public GuildValidatorDelegator( + Address address, + Address delegationPoolAddress, + GuildValidatorRepository repository) + : base( + address: address, + accountAddress: repository.DelegatorAccountAddress, + delegationPoolAddress: delegationPoolAddress, + rewardAddress: address, + repository: repository) + { + } + + public GuildValidatorDelegator( + Address address, + GuildValidatorRepository repository) + : base( + address: address, + repository: repository) + { + } + + public bool Equals(GuildValidatorDelegator? other) + => Metadata.Equals(other?.Metadata); + } +} diff --git a/Lib9c/Model/Guild/GuildValidatorRepository.cs b/Lib9c/Model/Guild/GuildValidatorRepository.cs new file mode 100644 index 0000000000..0908e4785a --- /dev/null +++ b/Lib9c/Model/Guild/GuildValidatorRepository.cs @@ -0,0 +1,74 @@ +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Crypto; +using Nekoyume.Action; +using Nekoyume.Delegation; + +namespace Nekoyume.Model.Guild +{ + public class GuildValidatorRepository : DelegationRepository + { + public GuildValidatorRepository(IWorld world, IActionContext actionContext) + : base( + world: world, + actionContext: actionContext, + delegateeAccountAddress: Addresses.Guild, + delegatorAccountAddress: Addresses.GuildParticipant, + delegateeMetadataAccountAddress: Addresses.GuildMetadata, + delegatorMetadataAccountAddress: Addresses.GuildParticipantMetadata, + bondAccountAddress: Addresses.GuildBond, + unbondLockInAccountAddress: Addresses.GuildUnbondLockIn, + rebondGraceAccountAddress: Addresses.GuildRebondGrace, + unbondingSetAccountAddress: Addresses.GuildUnbondingSet, + lumpSumRewardRecordAccountAddress: Addresses.GuildLumpSumRewardsRecord) + { + } + + public GuildValidatorDelegatee GetGuildValidatorDelegatee(Address address) + { + try + { + return new GuildValidatorDelegatee(address, this); + } + catch (FailedLoadStateException) + { + return new GuildValidatorDelegatee(address, address, this); + } + } + + public override IDelegatee GetDelegatee(Address address) + => GetGuildValidatorDelegatee(address); + + + public GuildValidatorDelegator GetGuildValidatorDelegator(Address address) + { + try + { + return new GuildValidatorDelegator(address, this); + } + catch (FailedLoadStateException) + { + return new GuildValidatorDelegator(address, address, this); + } + } + + public override IDelegator GetDelegator(Address address) + => GetGuildValidatorDelegator(address); + + public void SetGuildValidatorDelegatee(GuildValidatorDelegatee guildValidatorDelegatee) + { + SetDelegateeMetadata(guildValidatorDelegatee.Metadata); + } + + public override void SetDelegatee(IDelegatee delegatee) + => SetGuildValidatorDelegatee(delegatee as GuildValidatorDelegatee); + + public void SetGuildValidatorDelegator(GuildValidatorDelegator guildValidatorDelegator) + { + SetDelegatorMetadata(guildValidatorDelegator.Metadata); + } + + public override void SetDelegator(IDelegator delegator) + => SetGuildValidatorDelegator(delegator as GuildValidatorDelegator); + } +} From 0af93ede809735c7e6cce5da1f700a5ca9a071c4 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 24 Oct 2024 12:26:46 +0900 Subject: [PATCH 2/5] chore: Remove GuildValidatorRepository prop from GuildParticipant --- Lib9c/Model/Guild/GuildParticipant.cs | 34 +++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Lib9c/Model/Guild/GuildParticipant.cs b/Lib9c/Model/Guild/GuildParticipant.cs index 8a1ae21b9a..fc33b4b960 100644 --- a/Lib9c/Model/Guild/GuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildParticipant.cs @@ -28,8 +28,6 @@ public GuildParticipant( repository: repository) { GuildAddress = guildAddress; - GuildValidatorRepository = new GuildValidatorRepository( - repository.World, repository.ActionContext); } public GuildParticipant( @@ -54,8 +52,6 @@ public GuildParticipant( } GuildAddress = new GuildAddress(list[2]); - GuildValidatorRepository = new GuildValidatorRepository( - repository.World, repository.ActionContext); } public new AgentAddress Address => new AgentAddress(base.Address); @@ -65,8 +61,6 @@ public GuildParticipant( .Add(StateVersion) .Add(GuildAddress.Bencoded); - public GuildValidatorRepository GuildValidatorRepository { get; } - IValue IBencodable.Bencoded => Bencoded; public override void Delegate(Guild delegatee, FungibleAssetValue fav, long height) @@ -82,11 +76,13 @@ public override void Delegate(Guild delegatee, FungibleAssetValue fav, long heig throw new InvalidOperationException("Delegatee is tombstoned."); } - var guildValidatorDelegatee = GuildValidatorRepository.GetDelegatee(delegatee.ValidatorAddress); - var guildValidatorDelegator = GuildValidatorRepository.GetDelegator(delegatee.Address); + var guildValidatorRepository = new GuildValidatorRepository( + Repository.World, Repository.ActionContext); + var guildValidatorDelegatee = guildValidatorRepository.GetDelegatee(delegatee.ValidatorAddress); + var guildValidatorDelegator = guildValidatorRepository.GetDelegator(delegatee.Address); guildValidatorDelegatee.Bond(guildValidatorDelegator, fav, height); - Repository.UpdateWorld(GuildValidatorRepository.World); + Repository.UpdateWorld(guildValidatorRepository.World); Metadata.AddDelegatee(delegatee.Address); Repository.TransferAsset(DelegationPoolAddress, delegatee.DelegationPoolAddress, fav); Repository.SetDelegator(this); @@ -113,10 +109,12 @@ public override void Undelegate(Guild delegatee, BigInteger share, long height) throw new InvalidOperationException("Undelegation is full."); } - var guildValidatorDelegatee = GuildValidatorRepository.GetDelegatee(delegatee.ValidatorAddress); - var guildValidatorDelegator = GuildValidatorRepository.GetDelegator(delegatee.Address); + var guildValidatorRepository = new GuildValidatorRepository( + Repository.World, Repository.ActionContext); + var guildValidatorDelegatee = guildValidatorRepository.GetDelegatee(delegatee.ValidatorAddress); + var guildValidatorDelegator = guildValidatorRepository.GetDelegator(delegatee.Address); FungibleAssetValue fav = guildValidatorDelegatee.Unbond(guildValidatorDelegator, share, height); - Repository.UpdateWorld(GuildValidatorRepository.World); + Repository.UpdateWorld(guildValidatorRepository.World); unbondLockIn = unbondLockIn.LockIn( fav, height, height + delegatee.UnbondingPeriod); @@ -153,16 +151,18 @@ public override void Redelegate( throw new InvalidOperationException("Destination delegatee is tombstoned."); } - var srcGuildValidatorDelegatee = GuildValidatorRepository.GetDelegatee(srcDelegatee.ValidatorAddress); - var srcGuildValidatorDelegator = GuildValidatorRepository.GetDelegator(srcDelegatee.Address); - var dstGuildValidatorDelegatee = GuildValidatorRepository.GetDelegatee(dstDelegatee.ValidatorAddress); - var dstGuildValidatorDelegator = GuildValidatorRepository.GetDelegator(dstDelegatee.Address); + var guildValidatorRepository = new GuildValidatorRepository( + Repository.World, Repository.ActionContext); + var srcGuildValidatorDelegatee = guildValidatorRepository.GetDelegatee(srcDelegatee.ValidatorAddress); + var srcGuildValidatorDelegator = guildValidatorRepository.GetDelegator(srcDelegatee.Address); + var dstGuildValidatorDelegatee = guildValidatorRepository.GetDelegatee(dstDelegatee.ValidatorAddress); + var dstGuildValidatorDelegator = guildValidatorRepository.GetDelegator(dstDelegatee.Address); FungibleAssetValue fav = srcGuildValidatorDelegatee.Unbond( srcGuildValidatorDelegator, share, height); dstGuildValidatorDelegatee.Bond( dstGuildValidatorDelegator, fav, height); - Repository.UpdateWorld(GuildValidatorRepository.World); + Repository.UpdateWorld(guildValidatorRepository.World); RebondGrace srcRebondGrace = Repository.GetRebondGrace(srcDelegatee, Address).Grace( dstDelegatee.Address, fav, From 23f2d6d2c5dea1e930f534c7f28dfc982b1abc90 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 25 Oct 2024 10:52:45 +0900 Subject: [PATCH 3/5] fix: Fix maldesigned guild delegation --- .../Action/Guild/BanGuildMemberTest.cs | 12 +- .Lib9c.Tests/Action/Guild/GuildTestBase.cs | 11 +- .Lib9c.Tests/Action/Guild/MoveGuildTest.cs | 2 +- .Lib9c.Tests/Action/Guild/QuitGuildTest.cs | 2 +- .Lib9c.Tests/Action/Guild/RemoveGuildTest.cs | 6 +- .../Action/Guild/UnbanGuildMemberTest.cs | 11 +- .../ClaimRewardValidatorTest.cs | 4 +- .Lib9c.Tests/Delegation/DummyDelegatee.cs | 3 +- .Lib9c.Tests/Delegation/TestDelegatee.cs | 3 +- Lib9c/Action/Guild/ClaimRewardGuild.cs | 15 +-- .../ClaimRewardValidator.cs | 19 +-- Lib9c/Addresses.cs | 89 +++++++-------- Lib9c/Delegation/Delegatee.cs | 19 ++- Lib9c/Delegation/DelegateeMetadata.cs | 28 +++-- Lib9c/Delegation/DelegationAddress.cs | 12 ++ Lib9c/Model/Guild/Guild.cs | 3 +- Lib9c/Model/Guild/GuildParticipant.cs | 108 +++++++----------- Lib9c/Model/Guild/GuildValidatorDelegatee.cs | 17 +-- Lib9c/Model/Guild/GuildValidatorDelegator.cs | 3 +- Lib9c/Model/Guild/GuildValidatorRepository.cs | 18 +-- Lib9c/Module/Guild/GuildParticipantModule.cs | 51 ++------- .../ValidatorDelegation/ValidatorDelegatee.cs | 1 + 22 files changed, 192 insertions(+), 245 deletions(-) diff --git a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs index cb31fc17b0..16f8ed521b 100644 --- a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs +++ b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs @@ -40,7 +40,7 @@ public void Execute() world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorKey.PublicKey); world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - world = EnsureToJoinGuild(world, guildAddress, targetGuildMemberAddress); + world = EnsureToJoinGuild(world, guildAddress, targetGuildMemberAddress, 1L); var banGuildMember = new BanGuildMember(targetGuildMemberAddress); var actionContext = new ActionContext @@ -71,9 +71,9 @@ public void Ban_By_GuildMaster() world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorKey.PublicKey); world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - world = EnsureToJoinGuild(world, guildAddress, guildMemberAddress); + world = EnsureToJoinGuild(world, guildAddress, guildMemberAddress, 1L); world = EnsureToMakeGuild(world, otherGuildAddress, otherGuildMasterAddress, validatorKey.Address); - world = EnsureToJoinGuild(world, otherGuildAddress, otherGuildMemberAddress); + world = EnsureToJoinGuild(world, otherGuildAddress, otherGuildMemberAddress, 1L); var repository = new GuildRepository(world, new ActionContext()); // Guild @@ -169,8 +169,8 @@ public void Ban_By_GuildMember() world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorKey.PublicKey); world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - world = EnsureToJoinGuild(world, guildAddress, guildMemberAddress); - world = EnsureToJoinGuild(world, guildAddress, targetGuildMemberAddress); + world = EnsureToJoinGuild(world, guildAddress, guildMemberAddress, 1L); + world = EnsureToJoinGuild(world, guildAddress, targetGuildMemberAddress, 1L); var repository = new GuildRepository(world, new ActionContext()); @@ -213,7 +213,7 @@ public void Ban_By_Other() world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorKey.PublicKey); world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - world = EnsureToJoinGuild(world, guildAddress, targetGuildMemberAddress); + world = EnsureToJoinGuild(world, guildAddress, targetGuildMemberAddress, 1L); var repository = new GuildRepository(world, new ActionContext()); diff --git a/.Lib9c.Tests/Action/Guild/GuildTestBase.cs b/.Lib9c.Tests/Action/Guild/GuildTestBase.cs index b24b8a4e38..b900ce4b9f 100644 --- a/.Lib9c.Tests/Action/Guild/GuildTestBase.cs +++ b/.Lib9c.Tests/Action/Guild/GuildTestBase.cs @@ -65,6 +65,7 @@ protected static IWorld EnsureToMakeGuild( var actionContext = new ActionContext { Signer = guildMasterAddress, + BlockIndex = 0L, }; var repository = new GuildRepository(world, actionContext); repository.MakeGuild(guildAddress, validatorAddress); @@ -74,14 +75,18 @@ protected static IWorld EnsureToMakeGuild( protected static IWorld EnsureToJoinGuild( IWorld world, GuildAddress guildAddress, - AgentAddress agentAddress) + AgentAddress guildParticipantAddress, + long blockHeight) { var actionContext = new ActionContext { - Signer = agentAddress, + PreviousState = world, + BlockIndex = blockHeight, + Signer = guildParticipantAddress, }; + var repository = new GuildRepository(world, actionContext); - repository.JoinGuild(guildAddress, agentAddress); + repository.JoinGuild(guildAddress, guildParticipantAddress); return repository.World; } diff --git a/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs b/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs index 06ce2738c4..d4e299d7f5 100644 --- a/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs @@ -46,7 +46,7 @@ public void Execute() world = EnsureToCreateValidator(world, validatorKey2.PublicKey); world = EnsureToMakeGuild(world, guildAddress1, guildMasterAddress1, validatorKey1.Address); world = EnsureToMakeGuild(world, guildAddress2, guildMasterAddress2, validatorKey2.Address); - world = EnsureToJoinGuild(world, guildAddress1, agentAddress); + world = EnsureToJoinGuild(world, guildAddress1, agentAddress, 1L); var moveGuild = new MoveGuild(guildAddress2); var actionContext = new ActionContext diff --git a/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs b/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs index 5542804221..51d10b2896 100644 --- a/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs @@ -39,7 +39,7 @@ public void Execute() world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorKey.PublicKey); world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - world = EnsureToJoinGuild(world, guildAddress, agentAddress); + world = EnsureToJoinGuild(world, guildAddress, agentAddress, 1L); var quitGuild = new QuitGuild(); var actionContext = new ActionContext diff --git a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs index aa68fe383d..7eebe282e0 100644 --- a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs @@ -65,7 +65,7 @@ public void Execute_ByGuildMember_Throw() world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorKey.PublicKey); world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - world = EnsureToJoinGuild(world, guildAddress, guildMemberAddress); + world = EnsureToJoinGuild(world, guildAddress, guildMemberAddress, 1L); var actionContext = new ActionContext { @@ -90,7 +90,7 @@ public void Execute_WhenDelegationExists_Throw() world = EnsureToCreateValidator(world, validatorKey.PublicKey); world = EnsureToMintAsset(world, StakeState.DeriveAddress(guildMasterAddress), GG * 100); world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - world = EnsureToJoinGuild(world, guildAddress, guildParticipantAddress); + world = EnsureToJoinGuild(world, guildAddress, guildParticipantAddress, 1L); var actionContext = new ActionContext { @@ -137,7 +137,7 @@ public void Execute_ResetBannedAddresses() world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorKey.PublicKey); world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - world = EnsureToJoinGuild(world, guildAddress, bannedAddress); + world = EnsureToJoinGuild(world, guildAddress, bannedAddress, 1L); world = EnsureToBanGuildMember(world, guildAddress, guildMasterAddress, bannedAddress); var actionContext = new ActionContext diff --git a/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs index 34af378fea..73f918653c 100644 --- a/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs +++ b/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs @@ -4,13 +4,8 @@ namespace Lib9c.Tests.Action.Guild using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Crypto; - using Libplanet.Mocks; - using Libplanet.Types.Assets; - using Nekoyume; using Nekoyume.Action.Guild; using Nekoyume.Model.Guild; - using Nekoyume.Model.State; - using Nekoyume.Module; using Nekoyume.Module.Guild; using Xunit; @@ -40,7 +35,7 @@ public void Execute() world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorKey.PublicKey); world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - world = EnsureToJoinGuild(world, guildAddress, targetGuildMemberAddress); + world = EnsureToJoinGuild(world, guildAddress, targetGuildMemberAddress, 1L); world = EnsureToBanGuildMember(world, guildAddress, guildMasterAddress, targetGuildMemberAddress); var unbanGuildMember = new UnbanGuildMember(targetGuildMemberAddress); @@ -71,7 +66,7 @@ public void Unban_By_GuildMember() world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorKey.PublicKey); world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - world = EnsureToJoinGuild(world, guildAddress, guildMemberAddress); + world = EnsureToJoinGuild(world, guildAddress, guildMemberAddress, 1L); world = EnsureToBanGuildMember(world, guildAddress, guildMasterAddress, targetGuildMemberAddress); var repository = new GuildRepository(world, new ActionContext()); @@ -105,7 +100,7 @@ public void Unban_By_GuildMaster() world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorKey.PublicKey); world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - world = EnsureToJoinGuild(world, guildAddress, targetGuildMemberAddress); + world = EnsureToJoinGuild(world, guildAddress, targetGuildMemberAddress, 1L); world = EnsureToBanGuildMember(world, guildAddress, guildMasterAddress, targetGuildMemberAddress); var repository = new GuildRepository(world, new ActionContext()); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs index b0aa663e0b..dcca4a3add 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -288,7 +288,7 @@ var expectedProposerReward Signer = delegatorKeys[i].Address, LastCommit = lastCommit, }; - world = new ClaimRewardValidator(validatorKey.Address).Execute(actionContext); + world = new ClaimRewardValidator().Execute(actionContext); } // Then @@ -317,7 +317,7 @@ var expectedProposerReward foreach (var key in guildParticipantKeys) { Assert.Throws( - () => new ClaimRewardValidator(validatorKey.Address).Execute(new ActionContext + () => new ClaimRewardValidator().Execute(new ActionContext { PreviousState = world, BlockIndex = height++, diff --git a/.Lib9c.Tests/Delegation/DummyDelegatee.cs b/.Lib9c.Tests/Delegation/DummyDelegatee.cs index ef5a942cc7..b95a5e9b92 100644 --- a/.Lib9c.Tests/Delegation/DummyDelegatee.cs +++ b/.Lib9c.Tests/Delegation/DummyDelegatee.cs @@ -15,7 +15,8 @@ public DummyDelegatee(Address address, Address accountAddress, DummyRepository r accountAddress, DelegationFixture.TestCurrency, DelegationFixture.TestCurrency, - address, + DelegationAddress.DelegationPoolAddress(address, accountAddress), + DelegationAddress.RewardPoolAddress(address, accountAddress), DelegationFixture.FixedPoolAddress, DelegationFixture.FixedPoolAddress, 3, diff --git a/.Lib9c.Tests/Delegation/TestDelegatee.cs b/.Lib9c.Tests/Delegation/TestDelegatee.cs index d754f787db..4087644a5b 100644 --- a/.Lib9c.Tests/Delegation/TestDelegatee.cs +++ b/.Lib9c.Tests/Delegation/TestDelegatee.cs @@ -16,7 +16,8 @@ public TestDelegatee(Address address, Address accountAddress, TestRepository rep accountAddress, DelegationFixture.TestCurrency, DelegationFixture.TestCurrency, - address, + DelegationAddress.DelegationPoolAddress(address, accountAddress), + DelegationAddress.RewardPoolAddress(address, accountAddress), DelegationFixture.FixedPoolAddress, DelegationFixture.FixedPoolAddress, 3, diff --git a/Lib9c/Action/Guild/ClaimRewardGuild.cs b/Lib9c/Action/Guild/ClaimRewardGuild.cs index 8857439228..c311051d36 100644 --- a/Lib9c/Action/Guild/ClaimRewardGuild.cs +++ b/Lib9c/Action/Guild/ClaimRewardGuild.cs @@ -2,7 +2,6 @@ using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Action; -using Libplanet.Crypto; using Nekoyume.Model.Guild; using Nekoyume.Module.Guild; using Nekoyume.TypedAddress; @@ -16,29 +15,19 @@ public sealed class ClaimRewardGuild : ActionBase public ClaimRewardGuild() { } - public ClaimRewardGuild(Address guildAddress) - { - GuildAddress = guildAddress; - } - - public Address GuildAddress { get; private set; } - public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) - .Add("values", List.Empty - .Add(GuildAddress.Bencoded)); + .Add("values", Null.Value); public override void LoadPlainValue(IValue plainValue) { var root = (Dictionary)plainValue; if (plainValue is not Dictionary || !root.TryGetValue((Text)"values", out var rawValues) || - rawValues is not List values) + rawValues is not Null) { throw new InvalidCastException(); } - - GuildAddress = new Address(values[0]); } public override IWorld Execute(IActionContext context) diff --git a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs index 1e8faf035a..79f81d0efd 100644 --- a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs @@ -2,7 +2,6 @@ using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Action; -using Libplanet.Crypto; using Nekoyume.Model.Guild; using Nekoyume.Module.Guild; using Nekoyume.TypedAddress; @@ -16,29 +15,19 @@ public sealed class ClaimRewardValidator : ActionBase public ClaimRewardValidator() { } - public ClaimRewardValidator(Address validatorDelegatee) - { - ValidatorDelegatee = validatorDelegatee; - } - - public Address ValidatorDelegatee { get; private set; } - public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) - .Add("values", List.Empty - .Add(ValidatorDelegatee.Bencoded)); + .Add("values", Null.Value); public override void LoadPlainValue(IValue plainValue) { var root = (Dictionary)plainValue; if (plainValue is not Dictionary || !root.TryGetValue((Text)"values", out var rawValues) || - rawValues is not List values) + rawValues is not Null) { throw new InvalidCastException(); - } - - ValidatorDelegatee = new Address(values[0]); + }; } public override IWorld Execute(IActionContext context) @@ -62,7 +51,7 @@ public override IWorld Execute(IActionContext context) var guildValidatorRepository = new GuildValidatorRepository(world, context); var guildValidatorDelegator = guildValidatorRepository.GetGuildValidatorDelegator(context.Signer); - var guildValidatorDelegatee = guildValidatorRepository.GetGuildValidatorDelegatee(ValidatorDelegatee); + var guildValidatorDelegatee = guildValidatorRepository.GetGuildValidatorDelegatee(guild.ValidatorAddress); guildValidatorDelegator.ClaimReward(guildValidatorDelegatee, context.BlockIndex); return guildValidatorRepository.World; diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index 99f1fa44d8..56adc24940 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -94,129 +94,128 @@ public static Address GetGuildBanAccountAddress(Address guildAddress) => public static readonly Address EmptyAccountAddress = new Address("ffffffffffffffffffffffffffffffffffffffff"); - /// - /// An address of an account having . - /// - public static readonly Address GuildUnbondingSet - = new Address("0000000000000000000000000000000000000300"); - /// /// An address of an account having . /// public static readonly Address GuildMetadata - = new Address("0000000000000000000000000000000000000301"); + = new Address("0000000000000000000000000000000000000210"); /// /// An address of an account having . /// public static readonly Address GuildParticipantMetadata - = new Address("0000000000000000000000000000000000000302"); + = new Address("0000000000000000000000000000000000000211"); /// /// An address of an account having . /// public static readonly Address GuildBond - = new Address("0000000000000000000000000000000000000303"); + = new Address("0000000000000000000000000000000000000212"); /// /// An address of an account having . /// public static readonly Address GuildUnbondLockIn - = new Address("0000000000000000000000000000000000000304"); + = new Address("0000000000000000000000000000000000000213"); /// /// An address of an account having . /// public static readonly Address GuildRebondGrace - = new Address("0000000000000000000000000000000000000305"); + = new Address("0000000000000000000000000000000000000214"); /// /// An address of an account having . /// public static readonly Address GuildLumpSumRewardsRecord - = new Address("0000000000000000000000000000000000000306"); - - #endregion - - #region Validator + = new Address("0000000000000000000000000000000000000215"); /// - /// An address for reward pool of validators. + /// An address of an account having . /// - public static readonly Address RewardPool - = new Address("0000000000000000000000000000000000000400"); + public static readonly Address GuildUnbondingSet + = new Address("0000000000000000000000000000000000000216"); - /// - /// An address of an account having . - /// - public static readonly Address ValidatorList - = new Address("0000000000000000000000000000000000000401"); + #endregion + #region Validator /// /// An address of an account having . /// public static readonly Address ValidatorDelegatee - = new Address("0000000000000000000000000000000000000402"); + = new Address("0000000000000000000000000000000000000300"); /// /// An address of an account having . /// public static readonly Address ValidatorDelegator - = new Address("0000000000000000000000000000000000000403"); - - /// - /// An address for community fund. - /// - public static readonly Address CommunityPool - = new Address("0000000000000000000000000000000000000499"); - - /// - /// An address of an account having . - /// - public static readonly Address ValidatorUnbondingSet - = new Address("0000000000000000000000000000000000000300"); + = new Address("0000000000000000000000000000000000000301"); /// /// An address of an account having . /// public static readonly Address ValidatorDelegateeMetadata - = new Address("0000000000000000000000000000000000000301"); + = new Address("0000000000000000000000000000000000000302"); /// /// An address of an account having . /// public static readonly Address ValidatorDelegatorMetadata - = new Address("0000000000000000000000000000000000000302"); + = new Address("0000000000000000000000000000000000000303"); /// /// An address of an account having . /// public static readonly Address ValidatorBond - = new Address("0000000000000000000000000000000000000303"); + = new Address("0000000000000000000000000000000000000304"); /// /// An address of an account having . /// public static readonly Address ValidatorUnbondLockIn - = new Address("0000000000000000000000000000000000000304"); + = new Address("0000000000000000000000000000000000000305"); /// /// An address of an account having . /// public static readonly Address ValidatorRebondGrace - = new Address("0000000000000000000000000000000000000305"); + = new Address("0000000000000000000000000000000000000306"); /// /// An address of an account having . /// public static readonly Address ValidatorLumpSumRewardsRecord - = new Address("0000000000000000000000000000000000000306"); + = new Address("0000000000000000000000000000000000000307"); + + /// + /// An address of an account having . + /// + public static readonly Address ValidatorUnbondingSet + = new Address("0000000000000000000000000000000000000308"); /// /// An address of an account having . /// public static readonly Address AbstainHistory - = new Address("0000000000000000000000000000000000000307"); + = new Address("0000000000000000000000000000000000000309"); + + /// + /// An address of an account having . + /// + public static readonly Address ValidatorList + = new Address("0000000000000000000000000000000000000310"); + + /// + /// An address for reward pool of validators. + /// + public static readonly Address RewardPool + = new Address("0000000000000000000000000000000000000311"); + + /// + /// An address for community fund. + /// + public static readonly Address CommunityPool + = new Address("0000000000000000000000000000000000000312"); #endregion diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 530ca1d372..410f84794e 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -19,6 +19,7 @@ public Delegatee( Currency delegationCurrency, Currency rewardCurrency, Address delegationPoolAddress, + Address rewardPoolAddress, Address rewardRemainderPoolAddress, Address slashedPoolAddress, long unbondingPeriod, @@ -32,6 +33,7 @@ public Delegatee( delegationCurrency, rewardCurrency, delegationPoolAddress, + rewardPoolAddress, rewardRemainderPoolAddress, slashedPoolAddress, unbondingPeriod, @@ -76,6 +78,8 @@ private Delegatee(DelegateeMetadata metadata, IDelegationRepository repository) public Address DelegationPoolAddress => Metadata.DelegationPoolAddress; + public Address RewardPoolAddress => Metadata.RewardPoolAddress; + public Address RewardRemainderPoolAddress => Metadata.RewardRemainderPoolAddress; public Address SlashedPoolAddress => Metadata.SlashedPoolAddress; @@ -86,8 +90,6 @@ private Delegatee(DelegateeMetadata metadata, IDelegationRepository repository) public int MaxRebondGraceEntries => Metadata.MaxRebondGraceEntries; - public Address RewardPoolAddress => Metadata.RewardPoolAddress; - public ImmutableSortedSet
Delegators => Metadata.Delegators; public FungibleAssetValue TotalDelegated => Metadata.TotalDelegatedFAV; @@ -255,7 +257,12 @@ public void DistributeReward(T delegator, long height) if (linkedStartHeight is long startHeightFromHigher && startHeightFromHigher != startHeight) { - throw new ArgumentException("lump sum reward record was started."); + throw new ArgumentException("Fetched wrong lump sum reward record."); + } + + if (!record.Delegators.Contains(delegator.Address)) + { + continue; } FungibleAssetValue reward = record.RewardsDuringPeriod(share); @@ -287,7 +294,11 @@ public void DistributeReward(T delegator, long height) } } - bond = bond.UpdateLastDistributeHeight(height); + if (bond.LastDistributeHeight != height) + { + bond = bond.UpdateLastDistributeHeight(height); + } + Repository.SetBond(bond); } diff --git a/Lib9c/Delegation/DelegateeMetadata.cs b/Lib9c/Delegation/DelegateeMetadata.cs index b0fcc9703d..971d20c366 100644 --- a/Lib9c/Delegation/DelegateeMetadata.cs +++ b/Lib9c/Delegation/DelegateeMetadata.cs @@ -21,6 +21,7 @@ public DelegateeMetadata( Currency delegationCurrency, Currency rewardCurrency, Address delegationPoolAddress, + Address rewardPoolAddress, Address rewardRemainderPoolAddress, Address slashedPoolAddress, long unbondingPeriod, @@ -32,6 +33,7 @@ public DelegateeMetadata( delegationCurrency, rewardCurrency, delegationPoolAddress, + rewardPoolAddress, rewardRemainderPoolAddress, slashedPoolAddress, unbondingPeriod, @@ -67,16 +69,17 @@ public DelegateeMetadata( new Address(bencoded[2]), new Address(bencoded[3]), new Address(bencoded[4]), - (Integer)bencoded[5], + new Address(bencoded[5]), (Integer)bencoded[6], (Integer)bencoded[7], - ((List)bencoded[8]).Select(item => new Address(item)), - new FungibleAssetValue(bencoded[9]), - (Integer)bencoded[10], - (Bencodex.Types.Boolean)bencoded[11], - (Integer)bencoded[12], - (Bencodex.Types.Boolean)bencoded[13], - ((List)bencoded[14]).Select(item => new UnbondingRef(item))) + (Integer)bencoded[8], + ((List)bencoded[9]).Select(item => new Address(item)), + new FungibleAssetValue(bencoded[10]), + (Integer)bencoded[11], + (Bencodex.Types.Boolean)bencoded[12], + (Integer)bencoded[13], + (Bencodex.Types.Boolean)bencoded[14], + ((List)bencoded[15]).Select(item => new UnbondingRef(item))) { } @@ -86,6 +89,7 @@ private DelegateeMetadata( Currency delegationCurrency, Currency rewardCurrency, Address delegationPoolAddress, + Address rewardPoolAddress, Address rewardRemainderPoolAddress, Address slashedPoolAddress, long unbondingPeriod, @@ -125,6 +129,7 @@ private DelegateeMetadata( DelegationCurrency = delegationCurrency; RewardCurrency = rewardCurrency; DelegationPoolAddress = delegationPoolAddress; + RewardPoolAddress = rewardPoolAddress; RewardRemainderPoolAddress = rewardRemainderPoolAddress; SlashedPoolAddress = slashedPoolAddress; UnbondingPeriod = unbondingPeriod; @@ -154,6 +159,8 @@ public Address Address public Address DelegationPoolAddress { get; } + public Address RewardPoolAddress { get; } + public Address RewardRemainderPoolAddress { get; } public Address SlashedPoolAddress { get; } @@ -164,9 +171,6 @@ public Address Address public int MaxRebondGraceEntries { get; } - public Address RewardPoolAddress - => DelegationAddress.RewardPoolAddress(Address); - public ImmutableSortedSet
Delegators { get; private set; } public FungibleAssetValue TotalDelegatedFAV { get; private set; } @@ -185,6 +189,7 @@ public Address RewardPoolAddress .Add(DelegationCurrency.Serialize()) .Add(RewardCurrency.Serialize()) .Add(DelegationPoolAddress.Bencoded) + .Add(RewardPoolAddress.Bencoded) .Add(RewardRemainderPoolAddress.Bencoded) .Add(SlashedPoolAddress.Bencoded) .Add(UnbondingPeriod) @@ -277,6 +282,7 @@ public virtual bool Equals(IDelegateeMetadata? other) && DelegationCurrency.Equals(delegatee.DelegationCurrency) && RewardCurrency.Equals(delegatee.RewardCurrency) && DelegationPoolAddress.Equals(delegatee.DelegationPoolAddress) + && RewardPoolAddress.Equals(delegatee.RewardPoolAddress) && RewardRemainderPoolAddress.Equals(delegatee.RewardRemainderPoolAddress) && SlashedPoolAddress.Equals(delegatee.SlashedPoolAddress) && UnbondingPeriod == delegatee.UnbondingPeriod diff --git a/Lib9c/Delegation/DelegationAddress.cs b/Lib9c/Delegation/DelegationAddress.cs index 5ea154cd6b..243b21e382 100644 --- a/Lib9c/Delegation/DelegationAddress.cs +++ b/Lib9c/Delegation/DelegationAddress.cs @@ -103,6 +103,18 @@ public static Address RewardPoolAddress( DelegationElementType.RewardPool, delegateeMetadataAddress); + public static Address DelegationPoolAddress( + Address delegateeAddress, Address delegateeAccountAddress) + => DeriveAddress( + DelegationElementType.DelegationPool, + DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress)); + + public static Address DelegationPoolAddress( + Address delegateeMetadataAddress) + => DeriveAddress( + DelegationElementType.DelegationPool, + delegateeMetadataAddress); + private static Address DeriveAddress( DelegationElementType identifier, Address address, diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index 81991b59dd..781bc34a6f 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -30,7 +30,8 @@ public Guild( accountAddress: repository.DelegateeAccountAddress, delegationCurrency: Currencies.GuildGold, rewardCurrency: rewardCurrency, - delegationPoolAddress: address, + delegationPoolAddress: DelegationAddress.DelegationPoolAddress(address, repository.DelegateeAccountAddress), + rewardPoolAddress: DelegationAddress.RewardPoolAddress(address, repository.DelegateeAccountAddress), rewardRemainderPoolAddress: Addresses.CommunityPool, slashedPoolAddress: Addresses.CommunityPool, unbondingPeriod: 0L, diff --git a/Lib9c/Model/Guild/GuildParticipant.cs b/Lib9c/Model/Guild/GuildParticipant.cs index fc33b4b960..dfbd21488a 100644 --- a/Lib9c/Model/Guild/GuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildParticipant.cs @@ -5,7 +5,9 @@ using Libplanet.Types.Assets; using Nekoyume.Action; using Nekoyume.Delegation; +using Nekoyume.Model.Stake; using Nekoyume.TypedAddress; +using Nekoyume.ValidatorDelegation; namespace Nekoyume.Model.Guild { @@ -23,7 +25,7 @@ public GuildParticipant( : base( address: address, accountAddress: Addresses.GuildParticipant, - delegationPoolAddress: address, + delegationPoolAddress: StakeState.DeriveAddress(address), rewardAddress: address, repository: repository) { @@ -71,21 +73,19 @@ public override void Delegate(Guild delegatee, FungibleAssetValue fav, long heig nameof(fav), fav, "Fungible asset value must be positive."); } - if (delegatee.Tombstoned) - { - throw new InvalidOperationException("Delegatee is tombstoned."); - } - - var guildValidatorRepository = new GuildValidatorRepository( - Repository.World, Repository.ActionContext); - var guildValidatorDelegatee = guildValidatorRepository.GetDelegatee(delegatee.ValidatorAddress); - var guildValidatorDelegator = guildValidatorRepository.GetDelegator(delegatee.Address); + delegatee.Bond(this, fav, height); + var guildValidatorRepository = new GuildValidatorRepository(Repository.World, Repository.ActionContext); + var guildValidatorDelegatee = guildValidatorRepository.GetGuildValidatorDelegatee(delegatee.ValidatorAddress); + var guildValidatorDelegator = guildValidatorRepository.GetGuildValidatorDelegator(delegatee.Address); guildValidatorDelegatee.Bond(guildValidatorDelegator, fav, height); Repository.UpdateWorld(guildValidatorRepository.World); - Metadata.AddDelegatee(delegatee.Address); - Repository.TransferAsset(DelegationPoolAddress, delegatee.DelegationPoolAddress, fav); - Repository.SetDelegator(this); + + var validatorRepository = new ValidatorRepository(Repository.World, Repository.ActionContext); + var validatorDelegatee = validatorRepository.GetValidatorDelegatee(delegatee.ValidatorAddress); + var validatorDelegator = validatorRepository.GetValidatorDelegator(Address, delegatee.Address); + validatorDelegator.Delegate(validatorDelegatee, fav, height); + Repository.UpdateWorld(validatorRepository.World); } public override void Undelegate(Guild delegatee, BigInteger share, long height) @@ -102,33 +102,19 @@ public override void Undelegate(Guild delegatee, BigInteger share, long height) nameof(height), height, "Height must be positive."); } - UnbondLockIn unbondLockIn = Repository.GetUnbondLockIn(delegatee, Address); + FungibleAssetValue fav = delegatee.Unbond(this, share, height); - if (unbondLockIn.IsFull) - { - throw new InvalidOperationException("Undelegation is full."); - } - - var guildValidatorRepository = new GuildValidatorRepository( - Repository.World, Repository.ActionContext); - var guildValidatorDelegatee = guildValidatorRepository.GetDelegatee(delegatee.ValidatorAddress); - var guildValidatorDelegator = guildValidatorRepository.GetDelegator(delegatee.Address); - FungibleAssetValue fav = guildValidatorDelegatee.Unbond(guildValidatorDelegator, share, height); + var guildValidatorRepository = new GuildValidatorRepository(Repository.World, Repository.ActionContext); + var guildValidatorDelegatee = guildValidatorRepository.GetGuildValidatorDelegatee(delegatee.ValidatorAddress); + var guildValidatorDelegator = guildValidatorRepository.GetGuildValidatorDelegator(delegatee.Address); + guildValidatorDelegatee.Unbond(guildValidatorDelegator, guildValidatorDelegatee.ShareFromFAV(fav), height); Repository.UpdateWorld(guildValidatorRepository.World); - unbondLockIn = unbondLockIn.LockIn( - fav, height, height + delegatee.UnbondingPeriod); - if (!delegatee.Delegators.Contains(Address)) - { - Metadata.RemoveDelegatee(delegatee.Address); - } - - delegatee.AddUnbondingRef(UnbondingFactory.ToReference(unbondLockIn)); - - Repository.SetUnbondLockIn(unbondLockIn); - Repository.SetUnbondingSet( - Repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); - Repository.SetDelegator(this); + var validatorRepository = new ValidatorRepository(Repository.World, Repository.ActionContext); + var validatorDelegatee = validatorRepository.GetValidatorDelegatee(delegatee.ValidatorAddress); + var validatorDelegator = validatorRepository.GetValidatorDelegator(Address, delegatee.Address); + validatorDelegator.Undelegate(validatorDelegatee, validatorDelegatee.ShareFromFAV(fav), height); + Repository.UpdateWorld(validatorRepository.World); } public override void Redelegate( @@ -146,42 +132,28 @@ public override void Redelegate( nameof(height), height, "Height must be positive."); } - if (dstDelegatee.Tombstoned) - { - throw new InvalidOperationException("Destination delegatee is tombstoned."); - } + FungibleAssetValue fav = srcDelegatee.Unbond(this, share, height); + dstDelegatee.Bond(this, fav, height); var guildValidatorRepository = new GuildValidatorRepository( Repository.World, Repository.ActionContext); - var srcGuildValidatorDelegatee = guildValidatorRepository.GetDelegatee(srcDelegatee.ValidatorAddress); - var srcGuildValidatorDelegator = guildValidatorRepository.GetDelegator(srcDelegatee.Address); - var dstGuildValidatorDelegatee = guildValidatorRepository.GetDelegatee(dstDelegatee.ValidatorAddress); - var dstGuildValidatorDelegator = guildValidatorRepository.GetDelegator(dstDelegatee.Address); - - FungibleAssetValue fav = srcGuildValidatorDelegatee.Unbond( - srcGuildValidatorDelegator, share, height); - dstGuildValidatorDelegatee.Bond( - dstGuildValidatorDelegator, fav, height); + var srcGuildValidatorDelegatee = guildValidatorRepository.GetGuildValidatorDelegatee(srcDelegatee.ValidatorAddress); + var srcGuildValidatorDelegator = guildValidatorRepository.GetGuildValidatorDelegator(srcDelegatee.Address); + var dstGuildValidatorDelegatee = guildValidatorRepository.GetGuildValidatorDelegatee(dstDelegatee.ValidatorAddress); + var dstGuildValidatorDelegator = guildValidatorRepository.GetGuildValidatorDelegator(dstDelegatee.Address); + srcGuildValidatorDelegatee.Unbond(srcGuildValidatorDelegator, share, height); + dstGuildValidatorDelegatee.Bond(dstGuildValidatorDelegator, fav, height); Repository.UpdateWorld(guildValidatorRepository.World); - RebondGrace srcRebondGrace = Repository.GetRebondGrace(srcDelegatee, Address).Grace( - dstDelegatee.Address, - fav, - height, - height + srcDelegatee.UnbondingPeriod); - if (!srcDelegatee.Delegators.Contains(Address)) - { - Metadata.RemoveDelegatee(srcDelegatee.Address); - } - - Metadata.AddDelegatee(dstDelegatee.Address); - - srcDelegatee.AddUnbondingRef(UnbondingFactory.ToReference(srcRebondGrace)); - - Repository.SetRebondGrace(srcRebondGrace); - Repository.SetUnbondingSet( - Repository.GetUnbondingSet().SetUnbonding(srcRebondGrace)); - Repository.SetDelegator(this); + var validatorRepository = new ValidatorRepository( + Repository.World, Repository.ActionContext); + var srcValidatorDelegatee = validatorRepository.GetValidatorDelegatee(srcDelegatee.ValidatorAddress); + var srcValidatorDelegator = validatorRepository.GetValidatorDelegator(Address, srcDelegatee.Address); + var dstValidatorDelegatee = validatorRepository.GetValidatorDelegatee(dstDelegatee.ValidatorAddress); + var dstValidatorDelegator = validatorRepository.GetValidatorDelegator(Address, dstDelegatee.Address); + srcValidatorDelegatee.Unbond(srcValidatorDelegator, share, height); + dstValidatorDelegatee.Bond(dstValidatorDelegator, fav, height); + Repository.UpdateWorld(validatorRepository.World); } public bool Equals(GuildParticipant other) diff --git a/Lib9c/Model/Guild/GuildValidatorDelegatee.cs b/Lib9c/Model/Guild/GuildValidatorDelegatee.cs index ee5e9adf37..1c372e115f 100644 --- a/Lib9c/Model/Guild/GuildValidatorDelegatee.cs +++ b/Lib9c/Model/Guild/GuildValidatorDelegatee.cs @@ -1,8 +1,8 @@ #nullable enable -using Lib9c; -using Libplanet.Crypto; using System; +using Libplanet.Crypto; using Nekoyume.Delegation; +using Nekoyume.ValidatorDelegation; namespace Nekoyume.Model.Guild { @@ -16,14 +16,15 @@ public GuildValidatorDelegatee( : base( address: address, accountAddress: repository.DelegateeAccountAddress, - delegationCurrency: Currencies.GuildGold, - rewardCurrency: Currencies.Mead, - delegationPoolAddress: address, + delegationCurrency: ValidatorDelegatee.ValidatorDelegationCurrency, + rewardCurrency: ValidatorDelegatee.ValidatorRewardCurrency, + delegationPoolAddress: ValidatorDelegatee.UnbondedPoolAddress, + rewardPoolAddress: DelegationAddress.RewardPoolAddress(address, repository.DelegateeAccountAddress), rewardRemainderPoolAddress: Addresses.CommunityPool, slashedPoolAddress: Addresses.CommunityPool, - unbondingPeriod: 75600L, - maxUnbondLockInEntries: 10, - maxRebondGraceEntries: 10, + unbondingPeriod: ValidatorDelegatee.ValidatorUnbondingPeriod, + maxUnbondLockInEntries: ValidatorDelegatee.ValidatorMaxUnbondLockInEntries, + maxRebondGraceEntries: ValidatorDelegatee.ValidatorMaxRebondGraceEntries, repository: repository) { } diff --git a/Lib9c/Model/Guild/GuildValidatorDelegator.cs b/Lib9c/Model/Guild/GuildValidatorDelegator.cs index 01e3666c9c..d6c9731237 100644 --- a/Lib9c/Model/Guild/GuildValidatorDelegator.cs +++ b/Lib9c/Model/Guild/GuildValidatorDelegator.cs @@ -1,8 +1,7 @@ #nullable enable +using System; using Libplanet.Crypto; using Nekoyume.Delegation; -using Nekoyume.ValidatorDelegation; -using System; namespace Nekoyume.Model.Guild { diff --git a/Lib9c/Model/Guild/GuildValidatorRepository.cs b/Lib9c/Model/Guild/GuildValidatorRepository.cs index 0908e4785a..30dfbb541d 100644 --- a/Lib9c/Model/Guild/GuildValidatorRepository.cs +++ b/Lib9c/Model/Guild/GuildValidatorRepository.cs @@ -12,15 +12,15 @@ public GuildValidatorRepository(IWorld world, IActionContext actionContext) : base( world: world, actionContext: actionContext, - delegateeAccountAddress: Addresses.Guild, - delegatorAccountAddress: Addresses.GuildParticipant, + delegateeAccountAddress: Addresses.ValidatorDelegatee, + delegatorAccountAddress: Addresses.ValidatorDelegator, delegateeMetadataAccountAddress: Addresses.GuildMetadata, - delegatorMetadataAccountAddress: Addresses.GuildParticipantMetadata, - bondAccountAddress: Addresses.GuildBond, - unbondLockInAccountAddress: Addresses.GuildUnbondLockIn, - rebondGraceAccountAddress: Addresses.GuildRebondGrace, - unbondingSetAccountAddress: Addresses.GuildUnbondingSet, - lumpSumRewardRecordAccountAddress: Addresses.GuildLumpSumRewardsRecord) + delegatorMetadataAccountAddress: Addresses.ValidatorDelegatorMetadata, + bondAccountAddress: Addresses.ValidatorBond, + unbondLockInAccountAddress: Addresses.ValidatorUnbondLockIn, + rebondGraceAccountAddress: Addresses.ValidatorRebondGrace, + unbondingSetAccountAddress: Addresses.ValidatorUnbondingSet, + lumpSumRewardRecordAccountAddress: Addresses.ValidatorLumpSumRewardsRecord) { } @@ -48,7 +48,7 @@ public GuildValidatorDelegator GetGuildValidatorDelegator(Address address) } catch (FailedLoadStateException) { - return new GuildValidatorDelegator(address, address, this); + return new GuildValidatorDelegator(address, address, this); } } diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index 327535785e..3eec749118 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -5,6 +5,7 @@ using Lib9c; using Libplanet.Types.Assets; using Nekoyume.Model.Guild; +using Nekoyume.Model.Stake; using Nekoyume.TypedAddress; using Nekoyume.ValidatorDelegation; @@ -27,7 +28,7 @@ public static GuildRepository JoinGuild( AgentAddress target) { var guildParticipant = new GuildParticipant(target, guildAddress, repository); - var guildGold = repository.GetBalance(target, Currencies.GuildGold); + var guildGold = repository.GetBalance(guildParticipant.DelegationPoolAddress, Currencies.GuildGold); repository.SetGuildParticipant(guildParticipant); repository.IncreaseGuildMemberCount(guildAddress); if (guildGold.RawValue > 0) @@ -141,14 +142,7 @@ private static GuildRepository Delegate( var height = repository.ActionContext.BlockIndex; var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); var guild = repository.GetGuild(guildParticipant.GuildAddress); - var validatorAddress = guild.ValidatorAddress; - var validatorRepository = new ValidatorRepository(repository.World, repository.ActionContext); - var validatorDelegatee = validatorRepository.GetValidatorDelegatee(validatorAddress); - var validatorDelegator = validatorRepository.GetValidatorDelegator( - guildParticipantAddress, guild.Address); - validatorDelegator.Delegate(validatorDelegatee, fav, height); - repository.UpdateWorld(validatorRepository.World); - guild.Bond(guildParticipant, fav, height); + guildParticipant.Delegate(guild, fav, height); return repository; } @@ -160,17 +154,8 @@ private static GuildRepository Undelegate( var height = repository.ActionContext.BlockIndex; var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); var guild = repository.GetGuild(guildParticipant.GuildAddress); - var validatorAddress = guild.ValidatorAddress; - var validatorRepository = new ValidatorRepository(repository.World, repository.ActionContext); - var validatorDelegatee = validatorRepository.GetValidatorDelegatee(validatorAddress); - var validatorDelegator = validatorRepository.GetValidatorDelegator( - guildParticipantAddress, guild.Address); - var bond = validatorRepository.GetBond(validatorDelegatee, guildParticipantAddress); - var share = bond.Share; - validatorDelegator.Undelegate(validatorDelegatee, share, height); - repository.UpdateWorld(validatorRepository.World); - var guildShare = repository.GetBond(guild, guildParticipantAddress).Share; - guild.Unbond(guildParticipant, guildShare, height); + var share = repository.GetBond(guild, guildParticipantAddress).Share; + guildParticipant.Undelegate(guild, share, height); return repository; } @@ -183,15 +168,7 @@ private static GuildRepository Undelegate( var height = repository.ActionContext.BlockIndex; var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); var guild = repository.GetGuild(guildParticipant.GuildAddress); - var validatorAddress = guild.ValidatorAddress; - var validatorRepository = new ValidatorRepository(repository.World, repository.ActionContext); - var validatorDelegatee = validatorRepository.GetValidatorDelegatee(validatorAddress); - var validatorDelegator = validatorRepository.GetValidatorDelegator( - guildParticipantAddress, guild.Address); - validatorDelegator.Undelegate(validatorDelegatee, share, height); - repository.UpdateWorld(validatorRepository.World); - var guildShare = repository.GetBond(guild, guildParticipantAddress).Share; - guild.Unbond(guildParticipant, guildShare, height); + guildParticipant.Undelegate(guild, share, height); return repository; } @@ -204,21 +181,9 @@ public static GuildRepository Redelegate( var height = repository.ActionContext.BlockIndex; var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); var guild = repository.GetGuild(guildParticipant.GuildAddress); - var srcValidatorAddress = guild.ValidatorAddress; + var share = repository.GetBond(guild, guildParticipantAddress).Share; var dstGuild = repository.GetGuild(dstGuildAddress); - var dstValidatorAddress = dstGuild.ValidatorAddress; - var validatorRepository = new ValidatorRepository(repository.World, repository.ActionContext); - var validatorSrcDelegatee = validatorRepository.GetValidatorDelegatee(srcValidatorAddress); - var validatorDstDelegatee = validatorRepository.GetValidatorDelegatee(dstValidatorAddress); - var validatorDelegator = validatorRepository.GetValidatorDelegator( - guildParticipantAddress, guild.Address); - var bond = validatorRepository.GetBond(validatorSrcDelegatee, guildParticipantAddress); - var share = bond.Share; - validatorDelegator.Redelegate(validatorSrcDelegatee, validatorDstDelegatee, share, height); - repository.UpdateWorld(validatorRepository.World); - var guildShare = repository.GetBond(guild, guildParticipantAddress).Share; - var guildRebondFAV = guild.Unbond(guildParticipant, guildShare, height); - dstGuild.Bond(guildParticipant, guildRebondFAV, height); + guildParticipant.Redelegate(guild, dstGuild, share, height); return repository; } diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index 3d1d04a767..050930b724 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -28,6 +28,7 @@ public ValidatorDelegatee( delegationCurrency: ValidatorDelegationCurrency, rewardCurrency: ValidatorRewardCurrency, delegationPoolAddress: UnbondedPoolAddress, + rewardPoolAddress: DelegationAddress.RewardPoolAddress(address, repository.DelegateeAccountAddress), rewardRemainderPoolAddress: Addresses.CommunityPool, slashedPoolAddress: Addresses.CommunityPool, unbondingPeriod: ValidatorUnbondingPeriod, From 3daaa1615ba2a7ca9d5eb8c100265ddbbe320035 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 25 Oct 2024 10:53:04 +0900 Subject: [PATCH 4/5] test: Add ClaimRewardGuildTest --- .../Action/Guild/ClaimRewardGuildTest.cs | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 .Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs diff --git a/.Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs b/.Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs new file mode 100644 index 0000000000..84197af259 --- /dev/null +++ b/.Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs @@ -0,0 +1,81 @@ +namespace Lib9c.Tests.Action.Guild +{ + using System.Linq; + using Lib9c.Tests.Util; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.Model.Guild; + using Nekoyume.Model.Stake; + using Xunit; + + public class ClaimRewardGuildTest : GuildTestBase + { + [Fact] + public void Serialization() + { + var action = new ClaimRewardGuild(); + var plainValue = action.PlainValue; + + var deserialized = new ClaimRewardGuild(); + deserialized.LoadPlainValue(plainValue); + } + + [Fact] + public void Execute() + { + // Given + var validatorKey = new PrivateKey(); + var agentAddress = AddressUtil.CreateAgentAddress(); + var guildMasterAddress = AddressUtil.CreateAgentAddress(); + var guildAddress = AddressUtil.CreateGuildAddress(); + var nParticipants = 10; + var guildParticipantAddresses = Enumerable.Range(0, nParticipants).Select( + _ => AddressUtil.CreateAgentAddress()).ToList(); + + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMintAsset(world, StakeState.DeriveAddress(guildMasterAddress), GG * 100); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + world = guildParticipantAddresses.Select((addr, idx) => (addr, idx)).Aggregate(world, (w, item) => + { + w = EnsureToMintAsset(w, item.addr, Mead * 100); + w = EnsureToMintAsset(w, StakeState.DeriveAddress(item.addr), GG * ((item.idx + 1) * 100)); + return EnsureToJoinGuild(w, guildAddress, item.addr, 1L); + }); + + // When + var repository = new GuildRepository(world, new ActionContext()); + var guild = repository.GetGuild(guildAddress); + var reward = NCG * 1000; + repository.UpdateWorld(EnsureToMintAsset(repository.World, guild.RewardPoolAddress, reward)); + guild.CollectRewards(1); + world = repository.World; + + for (var i = 0; i < nParticipants; i++) + { + var claimRewardGuild = new ClaimRewardGuild(); + var actionContext = new ActionContext + { + PreviousState = world, + Signer = guildParticipantAddresses[i], + BlockIndex = 2L, + }; + world = claimRewardGuild.Execute(actionContext); + } + + //Then + var expectedRepository = new GuildRepository(world, new ActionContext()); + for (var i = 0; i < nParticipants; i++) + { + var expectedGuild = expectedRepository.GetGuild(guildAddress); + var bond = expectedRepository.GetBond(expectedGuild, guildParticipantAddresses[i]); + var expectedReward = (reward * bond.Share).DivRem(expectedGuild.TotalShares).Quotient; + var actualReward = world.GetBalance(guildParticipantAddresses[i], NCG); + + Assert.Equal(expectedReward, actualReward); + } + } + } +} From 3fc3130fae49e25e535699856b1fe9b9479041c4 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 25 Oct 2024 11:37:04 +0900 Subject: [PATCH 5/5] test: Add SkipDuplicatedClaim test --- .../Action/Guild/ClaimRewardGuildTest.cs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/.Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs b/.Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs index 84197af259..a8ee2f06db 100644 --- a/.Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs @@ -77,5 +77,69 @@ public void Execute() Assert.Equal(expectedReward, actualReward); } } + + [Fact] + public void SkipDuplicatedClaim() + { + // Given + var validatorKey = new PrivateKey(); + var agentAddress = AddressUtil.CreateAgentAddress(); + var guildMasterAddress = AddressUtil.CreateAgentAddress(); + var guildAddress = AddressUtil.CreateGuildAddress(); + var nParticipants = 10; + var guildParticipantAddresses = Enumerable.Range(0, nParticipants).Select( + _ => AddressUtil.CreateAgentAddress()).ToList(); + + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMintAsset(world, StakeState.DeriveAddress(guildMasterAddress), GG * 100); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + world = guildParticipantAddresses.Select((addr, idx) => (addr, idx)).Aggregate(world, (w, item) => + { + w = EnsureToMintAsset(w, item.addr, Mead * 100); + w = EnsureToMintAsset(w, StakeState.DeriveAddress(item.addr), GG * ((item.idx + 1) * 100)); + return EnsureToJoinGuild(w, guildAddress, item.addr, 1L); + }); + + // When + var repository = new GuildRepository(world, new ActionContext()); + var guild = repository.GetGuild(guildAddress); + var reward = NCG * 1000; + repository.UpdateWorld(EnsureToMintAsset(repository.World, guild.RewardPoolAddress, reward)); + guild.CollectRewards(1); + world = repository.World; + + var claimRewardGuild = new ClaimRewardGuild(); + world = claimRewardGuild.Execute(new ActionContext + { + PreviousState = world, + Signer = guildParticipantAddresses[0], + BlockIndex = 2L, + }); + + world = claimRewardGuild.Execute(new ActionContext + { + PreviousState = world, + Signer = guildParticipantAddresses[0], + BlockIndex = 2L, + }); + + world = claimRewardGuild.Execute(new ActionContext + { + PreviousState = world, + Signer = guildParticipantAddresses[0], + BlockIndex = 3L, + }); + + //Then + var expectedRepository = new GuildRepository(world, new ActionContext()); + var expectedGuild = expectedRepository.GetGuild(guildAddress); + var bond = expectedRepository.GetBond(expectedGuild, guildParticipantAddresses[0]); + var expectedReward = (reward * bond.Share).DivRem(expectedGuild.TotalShares).Quotient; + var actualReward = world.GetBalance(guildParticipantAddresses[0], NCG); + + Assert.Equal(expectedReward, actualReward); + } } }