From f7d85de710f5eb710d3c2460df4a9bb8ec629585 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 26 Jul 2024 12:27:15 +0900 Subject: [PATCH 001/206] feat: Introduce IDelegatee, IDelegator, Delegation --- Lib9c/Delegation/Bond.cs | 57 ++++++++ Lib9c/Delegation/Delegatee.cs | 159 ++++++++++++++++++++++ Lib9c/Delegation/Delegation.cs | 94 +++++++++++++ Lib9c/Delegation/Delegator.cs | 125 +++++++++++++++++ Lib9c/Delegation/IDelegatee.cs | 39 ++++++ Lib9c/Delegation/IDelegator.cs | 36 +++++ Lib9c/Delegation/RebondGrace.cs | 164 ++++++++++++++++++++++ Lib9c/Delegation/UnbondLockIn.cs | 224 +++++++++++++++++++++++++++++++ Lib9c/Delegation/UnbondingSet.cs | 64 +++++++++ 9 files changed, 962 insertions(+) create mode 100644 Lib9c/Delegation/Bond.cs create mode 100644 Lib9c/Delegation/Delegatee.cs create mode 100644 Lib9c/Delegation/Delegation.cs create mode 100644 Lib9c/Delegation/Delegator.cs create mode 100644 Lib9c/Delegation/IDelegatee.cs create mode 100644 Lib9c/Delegation/IDelegator.cs create mode 100644 Lib9c/Delegation/RebondGrace.cs create mode 100644 Lib9c/Delegation/UnbondLockIn.cs create mode 100644 Lib9c/Delegation/UnbondingSet.cs diff --git a/Lib9c/Delegation/Bond.cs b/Lib9c/Delegation/Bond.cs new file mode 100644 index 0000000000..84841cc97e --- /dev/null +++ b/Lib9c/Delegation/Bond.cs @@ -0,0 +1,57 @@ +using System.Numerics; +using Bencodex; +using Bencodex.Types; +using Libplanet.Crypto; + +namespace Nekoyume.Delegation +{ + public class Bond : IBencodable + { + public Bond(Address address) + : this(address, BigInteger.Zero, 0) + { + } + + public Bond(Address address, IValue bencoded) + : this(address, (List)bencoded) + { + } + + public Bond(Address address, List bencoded) + : this(address, (Integer)bencoded[0], (Integer)bencoded[1]) + { + } + + private Bond(Address address, BigInteger share, long lastDistributeHeight) + { + Address = address; + Share = share; + LastDistributeHeight = lastDistributeHeight; + } + + public Address Address { get; } + + public BigInteger Share { get; private set; } + + public long LastDistributeHeight { get; private set; } + + public IValue Bencoded => List.Empty + .Add(Share) + .Add(LastDistributeHeight); + + public void AddShare(BigInteger share) + { + Share += share; + } + + public void SubtractShare(BigInteger share) + { + Share -= share; + } + + public void UpdateLastDistributeHeight(long height) + { + LastDistributeHeight = height; + } + } +} diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs new file mode 100644 index 0000000000..ea04c88dd3 --- /dev/null +++ b/Lib9c/Delegation/Delegatee.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Numerics; +using System.Security.Cryptography; +using Bencodex.Types; +using Libplanet.Crypto; +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public abstract class Delegatee : IDelegatee + where T : Delegator + where TSelf : Delegatee + { + protected readonly byte[] BondId = new byte[] { 0x44 }; // `D` + protected readonly byte[] UnbondLockInId = new byte[] { 0x55 }; // `U` + protected readonly byte[] RebondGraceId = new byte[] { 0x52 }; // `R` + protected readonly byte[] RewardPoolId = new byte[] { 0x72 }; // `r` + protected readonly byte[] PoolId = new byte[] { 0x70 }; // `p` + + public Delegatee(Address address) + { + Address = address; + Delegators = ImmutableSortedSet
.Empty; + TotalDelegated = Currency * 0; + TotalShares = BigInteger.Zero; + } + + public Delegatee(Address address, IValue bencoded) + : this(address, (List)bencoded) + { + } + + public Delegatee(Address address, List bencoded) + : this( + address, + ((List)bencoded[0]).Select(item => new Address(item)), + new FungibleAssetValue(bencoded[1]), + (Integer)bencoded[2]) + { + } + + private Delegatee( + Address address, + IEnumerable
delegators, + FungibleAssetValue totalDelegated, + BigInteger totalShares) + { + Address = address; + Delegators = delegators.ToImmutableSortedSet(); + TotalDelegated = totalDelegated; + TotalShares = totalShares; + } + + public Address Address { get; } + + public abstract Currency Currency { get; } + + public abstract long UnbondingPeriod { get; } + + public abstract byte[] DelegateeId { get; } + + public abstract Address PoolAddress { get; } + + public Address RewardPoolAddress => DeriveAddress(RewardPoolId); + + public ImmutableSortedSet
Delegators { get; private set; } + + public FungibleAssetValue TotalDelegated { get; private set; } + + public BigInteger TotalShares { get; private set; } + + public IValue Bencoded => List.Empty + .Add(new List(Delegators.Select(delegator => delegator.Bencoded))) + .Add(TotalDelegated.Serialize()) + .Add(TotalShares); + + public BigInteger ShareToBond(FungibleAssetValue fav) + => TotalShares.IsZero + ? fav.RawValue + : TotalShares * fav.RawValue / TotalDelegated.RawValue; + + public FungibleAssetValue FAVToUnbond(BigInteger share) + => TotalShares == share + ? TotalDelegated + : (TotalDelegated * share).DivRem(TotalShares, out _); + + public void Bond(IDelegator delegator, FungibleAssetValue fav, Delegation delegation) + => Bond((T)delegator, fav, delegation); + + public void Unbond(IDelegator delegator, BigInteger share, Delegation delegation) + => Unbond((T)delegator, share, delegation); + + public void Distribute() + { + // TODO: Implement this + } + + public Address BondAddress(Address delegatorAddress) + => DeriveAddress(BondId, delegatorAddress); + + public Address UnbondLockInAddress(Address delegatorAddress) + => DeriveAddress(UnbondLockInId, delegatorAddress); + + public Address RebondGraceAddress(Address delegatorAddress) + => DeriveAddress(RebondGraceId, delegatorAddress); + + protected Address DeriveAddress(byte[] typeId, Address address) + => DeriveAddress(typeId, address.ByteArray); + + protected Address DeriveAddress(byte[] typeId, IEnumerable bytes = null) + { + byte[] hashed; + using (var hmac = new HMACSHA1(DelegateeId.Concat(typeId).ToArray())) + { + hashed = hmac.ComputeHash( + Address.ByteArray.Concat(bytes ?? Array.Empty()).ToArray()); + } + + return new Address(hashed); + } + + private void Bond(T delegator, FungibleAssetValue fav, Delegation delegation) + { + if (!fav.Currency.Equals(Currency)) + { + throw new InvalidOperationException("Cannot bond with invalid currency."); + } + + BigInteger share = ShareToBond(fav); + delegation.AddBond(fav, share); + Delegators = Delegators.Add(delegator.Address); + TotalShares += share; + TotalDelegated += fav; + Distribute(); + } + + private void Unbond(T delegator, BigInteger share, Delegation delegation) + { + if (TotalShares.IsZero || TotalDelegated.RawValue.IsZero) + { + throw new InvalidOperationException("Cannot unbond without bonding."); + } + + FungibleAssetValue fav = FAVToUnbond(share); + delegation.CancelBond(fav, share); + if (delegation.Bond.Share.IsZero) + { + Delegators = Delegators.Remove(delegator.Address); + } + + TotalShares -= share; + TotalDelegated -= fav; + Distribute(); + } + } +} diff --git a/Lib9c/Delegation/Delegation.cs b/Lib9c/Delegation/Delegation.cs new file mode 100644 index 0000000000..52f6d44084 --- /dev/null +++ b/Lib9c/Delegation/Delegation.cs @@ -0,0 +1,94 @@ +using System; +using System.Numerics; +using Libplanet.Crypto; +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public class Delegation + { + public Delegation( + Bond bond = null, + UnbondLockIn unbondLockIn = null, + RebondGrace rebondGrace = null, + UnbondingSet unbondingSet = null) + { + Bond = bond; + UnbondLockIn = unbondLockIn; + RebondGrace = rebondGrace; + UnbondingSet = unbondingSet; + } + + public Bond Bond { get; } + + public UnbondLockIn UnbondLockIn { get; } + + public RebondGrace RebondGrace { get; } + + public UnbondingSet UnbondingSet { get; } + + public FungibleAssetValue? IncompleteBond { get; private set; } + + public FungibleAssetValue? IncompleteUnbond { get; private set; } + + public void AddBond(FungibleAssetValue fav, BigInteger share) + { + if (fav.Sign != 1) + { + throw new InvalidOperationException("Cannot bond negative FAV."); + } + + Bond.AddShare(share); + IncompleteBond = fav; + } + + public void CancelBond(FungibleAssetValue fav, BigInteger share) + { + if (share.Sign != 1) + { + throw new InvalidOperationException("Cannot unbond negative FAV."); + } + + if (Bond.Share < share) + { + throw new InvalidOperationException("Cannot unbond more than bonded."); + } + + Bond.SubtractShare(share); + IncompleteUnbond = fav; + } + + public void DoUnbondLockIn(FungibleAssetValue fav, long height, long completionHeight) + { + UnbondLockIn.LockIn(fav, height, completionHeight); + if (!UnbondingSet.UnbondLockIns.Contains(UnbondLockIn.Address)) + { + UnbondingSet.AddUnbondLockIn(UnbondLockIn.Address); + } + } + + public void CancelUnbondLockIn(FungibleAssetValue fav, long height) + { + UnbondLockIn.Cancel(fav, height); + if (UnbondLockIn.IsEmpty) + { + UnbondingSet.RemoveUnbondLockIn(UnbondLockIn.Address); + } + } + + public void DoRebondGrace(Address rebondeeAddress, FungibleAssetValue fav, long height, long completionHeight) + { + RebondGrace.Grace(rebondeeAddress, fav, height, completionHeight); + if (!UnbondingSet.RebondGraces.Contains(RebondGrace.Address)) + { + UnbondingSet.AddRebondGrace(RebondGrace.Address); + } + } + + public void Complete() + { + IncompleteBond = null; + IncompleteUnbond = null; + } + } +} diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs new file mode 100644 index 0000000000..d15edbe9e4 --- /dev/null +++ b/Lib9c/Delegation/Delegator.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Numerics; +using Bencodex.Types; +using Libplanet.Crypto; +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public abstract class Delegator : IDelegator + where T : Delegatee + where TSelf : Delegator + { + public Delegator(Address address) + { + Address = address; + Delegatees = ImmutableSortedSet
.Empty; + } + + public Delegator(Address address, IValue bencoded) + : this(address, (List)bencoded) + { + } + + private Delegator(Address address, List bencoded) + { + Address = address; + Delegatees = bencoded.Select(item => new Address(item)).ToImmutableSortedSet(); + } + + public Address Address { get; } + + public ImmutableSortedSet
Delegatees { get; private set; } + + public IValue Bencoded + => new List(Delegatees.Select(a => a.Bencoded)); + + public void Delegate(IDelegatee delegatee, FungibleAssetValue fav, Delegation delegation) + => Delegate((T)delegatee, fav, delegation); + + public void Undelegate(IDelegatee delegatee, BigInteger share, long height, Delegation delegation) + => Undelegate((T)delegatee, share, height, delegation); + + public void Redelegate( + IDelegatee srcDelegatee, + IDelegatee dstDelegatee, + BigInteger share, + long height, + Delegation srcDelegation, + Delegation dstDelegation) + => Redelegate((T)srcDelegatee, (T)dstDelegatee, share, height, srcDelegation, dstDelegation); + + public void Claim(IDelegatee delegatee) + { + // TODO: Implement this + } + + private void Delegate( + T delegatee, + FungibleAssetValue fav, + Delegation delegation) + { + delegatee.Bond((TSelf)this, fav, delegation); + Delegatees = Delegatees.Add(delegatee.Address); + } + + private void Undelegate( + T delegatee, + BigInteger share, + long height, + Delegation delegation) + { + if (delegation.UnbondLockIn.IsFull) + { + throw new InvalidOperationException("Undelegation is full."); + } + + delegatee.Unbond(this, share, delegation); + + if (!(delegation.IncompleteUnbond is FungibleAssetValue unbondToLockIn)) + { + throw new NullReferenceException("Bonding FAV is null."); + } + + delegation.DoUnbondLockIn(unbondToLockIn, height, height + delegatee.UnbondingPeriod); + + if (delegation.Bond.Share.IsZero) + { + Delegatees = Delegatees.Remove(delegatee.Address); + } + + delegation.Complete(); + } + + private void Redelegate( + T srcDelegatee, + T dstDelegatee, + BigInteger share, + long height, + Delegation srcDelegation, + Delegation dstDelegation) + { + srcDelegatee.Unbond(this, share, srcDelegation); + + if (!(srcDelegation.IncompleteUnbond is FungibleAssetValue unbondToGrace)) + { + throw new NullReferenceException("Bonding FAV is null."); + } + + dstDelegatee.Bond(this, unbondToGrace, dstDelegation); + + srcDelegation.DoRebondGrace(dstDelegatee.Address, unbondToGrace, height, height + srcDelegatee.UnbondingPeriod); + + if (srcDelegation.Bond.Share.IsZero) + { + Delegatees = Delegatees.Remove(srcDelegatee.Address); + } + + Delegatees = Delegatees.Add(dstDelegatee.Address); + + srcDelegation.Complete(); + } + } +} diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs new file mode 100644 index 0000000000..a17b6d3713 --- /dev/null +++ b/Lib9c/Delegation/IDelegatee.cs @@ -0,0 +1,39 @@ +using System.Collections.Immutable; +using System.Numerics; +using Bencodex; +using Libplanet.Crypto; +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public interface IDelegatee : IBencodable + { + Address Address { get; } + + Currency Currency { get; } + + Address PoolAddress { get; } + + long UnbondingPeriod { get; } + + Address RewardPoolAddress { get; } + + ImmutableSortedSet
Delegators { get; } + + FungibleAssetValue TotalDelegated { get; } + + BigInteger TotalShares { get; } + + void Bond(IDelegator delegator, FungibleAssetValue fav, Delegation delegation); + + void Unbond(IDelegator delegator, BigInteger share, Delegation delegation); + + void Distribute(); + + Address BondAddress(Address delegatorAddress); + + Address UnbondLockInAddress(Address delegatorAddress); + + Address RebondGraceAddress(Address delegatorAddress); + } +} diff --git a/Lib9c/Delegation/IDelegator.cs b/Lib9c/Delegation/IDelegator.cs new file mode 100644 index 0000000000..47797fe416 --- /dev/null +++ b/Lib9c/Delegation/IDelegator.cs @@ -0,0 +1,36 @@ +using System.Collections.Immutable; +using System.Numerics; +using Bencodex; +using Libplanet.Crypto; +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public interface IDelegator : IBencodable + { + Address Address { get; } + + ImmutableSortedSet
Delegatees { get; } + + void Delegate( + IDelegatee delegatee, + FungibleAssetValue fav, + Delegation delegation); + + void Undelegate( + IDelegatee delegatee, + BigInteger share, + long height, + Delegation delegation); + + void Redelegate( + IDelegatee srcDelegatee, + IDelegatee dstDelegatee, + BigInteger share, + long height, + Delegation srcDelegation, + Delegation dstDelegation); + + void Claim(IDelegatee delegatee); + } +} diff --git a/Lib9c/Delegation/RebondGrace.cs b/Lib9c/Delegation/RebondGrace.cs new file mode 100644 index 0000000000..5444d24ec2 --- /dev/null +++ b/Lib9c/Delegation/RebondGrace.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Bencodex; +using Bencodex.Types; +using Libplanet.Crypto; +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public class RebondGrace : IBencodable + { + public RebondGrace(Address address, int maxEntries) + { + Address = address; + MaxEntries = maxEntries; + Entries = ImmutableSortedDictionary>.Empty; + } + + public RebondGrace(Address address, int maxEntries, IValue bencoded) + : this(address, maxEntries, (List)bencoded) + { + } + + public RebondGrace(Address address, int maxEntries, List bencoded) + { + Address = address; + MaxEntries = maxEntries; + Entries = bencoded + .Select(kv => kv is List list + ? new KeyValuePair>( + (Integer)list[0], + ((List)list[1]).Select(e => new RebondGraceEntry(e)).ToImmutableList()) + : throw new InvalidCastException( + $"Unable to cast object of type '{kv.GetType()}' to type '{typeof(List)}'.")) + .ToImmutableSortedDictionary(); + } + + public RebondGrace(Address address, int maxEntries, IEnumerable entries) + : this(address, maxEntries) + { + foreach (var entry in entries) + { + AddEntry(entry); + } + } + + public Address Address { get; } + + public int MaxEntries { get; } + + public bool IsFull => Entries.Values.Sum(e => e.Count) >= MaxEntries; + + public bool IsEmpty => Entries.IsEmpty; + + public ImmutableSortedDictionary> Entries { get; private set; } + + + public IValue Bencoded + => new List( + Entries.Select( + sortedDict => new List( + (Integer)sortedDict.Key, + new List(sortedDict.Value.Select(e => e.Bencoded))))); + + public void Grace(Address rebondeeAddress, FungibleAssetValue initialGraceFAV, long creationHeight, long expireHeight) + => AddEntry(new RebondGraceEntry(rebondeeAddress, initialGraceFAV, creationHeight, expireHeight)); + + public void Release(long height) + { + foreach (var (expireHeight, entries) in Entries) + { + if (expireHeight <= height) + { + Entries = Entries.Remove(expireHeight); + } + else + { + break; + } + } + } + + private void AddEntry(RebondGraceEntry entry) + { + if (IsFull) + { + throw new InvalidOperationException("Cannot add more entries."); + } + + if (Entries.TryGetValue(entry.ExpireHeight, out var entries)) + { + Entries = Entries.SetItem(entry.ExpireHeight, entries.Add(entry)); + } + else + { + Entries = Entries.Add(entry.ExpireHeight, ImmutableList.Empty.Add(entry)); + } + } + + public class RebondGraceEntry : IBencodable + { + public RebondGraceEntry( + Address rebondeeAddress, + FungibleAssetValue initialGraceFAV, + long creationHeight, + long expireHeight) + { + RebondeeAddress = rebondeeAddress; + InitialGraceFAV = initialGraceFAV; + GraceFAV = initialGraceFAV; + CreationHeight = creationHeight; + ExpireHeight = expireHeight; + } + + public RebondGraceEntry(IValue bencoded) + : this((List)bencoded) + { + } + + private RebondGraceEntry(List bencoded) + : this( + new Address(bencoded[0]), + new FungibleAssetValue(bencoded[1]), + new FungibleAssetValue(bencoded[2]), + (Integer)bencoded[3], + (Integer)bencoded[4]) + { + } + + private RebondGraceEntry( + Address rebondeeAddress, + FungibleAssetValue initialGraceFAV, + FungibleAssetValue graceFAV, + long creationHeight, + long expireHeight) + { + RebondeeAddress = rebondeeAddress; + InitialGraceFAV = initialGraceFAV; + GraceFAV = graceFAV; + CreationHeight = creationHeight; + ExpireHeight = expireHeight; + } + + public Address RebondeeAddress { get; } + + public FungibleAssetValue InitialGraceFAV { get; } + + public FungibleAssetValue GraceFAV { get; private set; } + + public long CreationHeight { get; } + + public long ExpireHeight { get; } + + public IValue Bencoded => List.Empty + .Add(RebondeeAddress.Bencoded) + .Add(InitialGraceFAV.Serialize()) + .Add(GraceFAV.Serialize()) + .Add(CreationHeight) + .Add(ExpireHeight); + } + } +} diff --git a/Lib9c/Delegation/UnbondLockIn.cs b/Lib9c/Delegation/UnbondLockIn.cs new file mode 100644 index 0000000000..25bb832fa1 --- /dev/null +++ b/Lib9c/Delegation/UnbondLockIn.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Bencodex; +using Bencodex.Types; +using Libplanet.Crypto; +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public class UnbondLockIn : IBencodable + { + public UnbondLockIn(Address address, int maxEntries) + { + Address = address; + MaxEntries = maxEntries; + Entries = ImmutableSortedDictionary>.Empty; + } + + public UnbondLockIn(Address address, int maxEntries, IValue bencoded) + : this(address, maxEntries, (List)bencoded) + { + } + + public UnbondLockIn(Address address, int maxEntries, List bencoded) + { + Address = address; + MaxEntries = maxEntries; + Entries = bencoded + .Select(kv => kv is List list + ? new KeyValuePair>( + (Integer)list[0], + ((List)list[1]).Select(e => new UnbondLockInEntry(e)).ToImmutableList()) + : throw new InvalidCastException( + $"Unable to cast object of type '{kv.GetType()}' to type '{typeof(List)}'.")) + .ToImmutableSortedDictionary(); + } + + public UnbondLockIn(Address address, int maxEntries, IEnumerable entries) + : this(address, maxEntries) + { + foreach (var entry in entries) + { + AddEntry(entry); + } + } + + public Address Address { get; } + + public int MaxEntries { get; } + + public bool IsFull => Entries.Values.Sum(e => e.Count) >= MaxEntries; + + public bool IsEmpty => Entries.IsEmpty; + + public ImmutableSortedDictionary> Entries { get; private set; } + + public IValue Bencoded + => new List( + Entries.Select( + sortedDict => new List( + (Integer)sortedDict.Key, + new List(sortedDict.Value.Select(e => e.Bencoded))))); + + public void LockIn(FungibleAssetValue lockInFAV, long creationHeight, long expireHeight) + => AddEntry(new UnbondLockInEntry(lockInFAV, creationHeight, expireHeight)); + + public void Cancel(FungibleAssetValue cancellingFAV, long height) + { + if (Cancellable(height) < cancellingFAV) + { + throw new InvalidOperationException("Cannot cancel more than locked-in FAV."); + } + + foreach (var (expireHeight, entries) in Entries.Reverse()) + { + if (expireHeight <= height) + { + throw new InvalidOperationException("Cannot cancel released undelegation."); + } + + foreach (var entry in entries.Select((value, index) => (value, index)).Reverse()) + { + if (cancellingFAV.RawValue < 0) + { + throw new InvalidOperationException("Insufficient undelegation to cancel"); + } + + if (entry.value.LockInFAV < cancellingFAV) + { + cancellingFAV -= entry.value.LockInFAV; + Entries = Entries.SetItem( + expireHeight, + Entries[expireHeight].RemoveAt(entry.index)); + } + else + { + entry.value.Cancel(cancellingFAV); + Entries = Entries.SetItem( + expireHeight, + Entries[expireHeight].SetItem(entry.index, entry.value)); + break; + } + } + + if (Entries[expireHeight].IsEmpty) + { + Entries = Entries.Remove(expireHeight); + } + } + } + + public FungibleAssetValue? Release(long height) + { + FungibleAssetValue? releaseFAV = null; + foreach (var (expireHeight, entries) in Entries) + { + if (expireHeight <= height) + { + FungibleAssetValue entriesFAV = entries + .Select(e => e.LockInFAV) + .Aggregate((accum, next) => accum + next); + releaseFAV = releaseFAV is null + ? entriesFAV + : releaseFAV + entriesFAV; + Entries = Entries.Remove(expireHeight); + } + else + { + break; + } + } + + return releaseFAV; + } + + private FungibleAssetValue Cancellable(long height) + => - Entries + .Where(kv => kv.Key > height) + .SelectMany(kv => kv.Value) + .Select(e => e.LockInFAV) + .Aggregate((accum, next) => accum + next); + + private void AddEntry(UnbondLockInEntry entry) + { + if (IsFull) + { + throw new InvalidOperationException("Cannot add more entries."); + } + + if (Entries.TryGetValue(entry.ExpireHeight, out var entries)) + { + Entries = Entries.SetItem(entry.ExpireHeight, entries.Add(entry)); + } + else + { + Entries = Entries.Add(entry.ExpireHeight, ImmutableList.Empty.Add(entry)); + } + } + + public class UnbondLockInEntry : IBencodable + { + public UnbondLockInEntry( + FungibleAssetValue lockInFAV, + long creationHeight, + long expireHeight) + : this(lockInFAV, lockInFAV, creationHeight, expireHeight) + { + } + + public UnbondLockInEntry(IValue bencoded) + : this((List)bencoded) + { + } + + private UnbondLockInEntry(List bencoded) + : this( + new FungibleAssetValue(bencoded[0]), + new FungibleAssetValue(bencoded[1]), + (Integer)bencoded[2], + (Integer)bencoded[3]) + { + } + + private UnbondLockInEntry( + FungibleAssetValue initialLockInFAV, + FungibleAssetValue lockInFAV, + long creationHeight, + long expireHeight) + { + InitialLockInFAV = initialLockInFAV; + LockInFAV = lockInFAV; + CreationHeight = creationHeight; + ExpireHeight = expireHeight; + } + + public FungibleAssetValue InitialLockInFAV { get; private set; } + + public FungibleAssetValue LockInFAV { get; private set; } + + public long CreationHeight { get; } + + public long ExpireHeight { get; } + + public IValue Bencoded => List.Empty + .Add(InitialLockInFAV.Serialize()) + .Add(LockInFAV.Serialize()) + .Add(CreationHeight) + .Add(ExpireHeight); + + public void Cancel(FungibleAssetValue cancellingFAV) + { + if (LockInFAV <= cancellingFAV) + { + throw new InvalidOperationException("Cannot cancel more than locked-in FAV."); + } + + InitialLockInFAV -= cancellingFAV; + LockInFAV -= cancellingFAV; + } + } + } +} diff --git a/Lib9c/Delegation/UnbondingSet.cs b/Lib9c/Delegation/UnbondingSet.cs new file mode 100644 index 0000000000..b0c5db18dd --- /dev/null +++ b/Lib9c/Delegation/UnbondingSet.cs @@ -0,0 +1,64 @@ +using System.Collections.Immutable; +using System.Linq; +using Bencodex.Types; +using Libplanet.Crypto; + +namespace Nekoyume.Delegation +{ + public class UnbondingSet + { + public UnbondingSet() + { + UnbondLockIns = ImmutableSortedSet
.Empty; + RebondGraces = ImmutableSortedSet
.Empty; + } + + public UnbondingSet(IValue bencoded) + : this((List)bencoded) + { + } + + public UnbondingSet(List bencoded) + : this( + ((List)bencoded[0]).Select(e => new Address(e)).ToImmutableSortedSet(), + ((List)bencoded[1]).Select(e => new Address(e)).ToImmutableSortedSet()) + { + } + + private UnbondingSet(ImmutableSortedSet
unbondLockIns, ImmutableSortedSet
rebondGraces) + { + UnbondLockIns = unbondLockIns; + RebondGraces = rebondGraces; + } + + + public ImmutableSortedSet
UnbondLockIns { get; private set; } + + public ImmutableSortedSet
RebondGraces { get; private set; } + + public void AddUnbondLockIn(Address address) + { + UnbondLockIns = UnbondLockIns.Add(address); + } + + public void RemoveUnbondLockIn(Address address) + { + UnbondLockIns = UnbondLockIns.Remove(address); + } + + public void AddRebondGrace(Address address) + { + RebondGraces = RebondGraces.Add(address); + } + + public void RemoveRebondGrace(Address address) + { + RebondGraces = RebondGraces.Remove(address); + } + + public IValue Bencoded + => List.Empty + .Add(new List(UnbondLockIns.Select(a => a.Bencoded))) + .Add(new List(RebondGraces.Select(a => a.Bencoded))); + } +} From ea141798d51c821fd7dba7fe5790d541e8b1a26f Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 2 Aug 2024 00:01:40 +0900 Subject: [PATCH 002/206] test: Add tests for delegation --- .Lib9c.Tests/Delegation/DelegateeTest.cs | 275 +++++++++++++++++++ .Lib9c.Tests/Delegation/DelegationFixture.cs | 73 +++++ .Lib9c.Tests/Delegation/DelegatorTest.cs | 157 +++++++++++ .Lib9c.Tests/Delegation/DummyDelegatee.cs | 20 ++ .Lib9c.Tests/Delegation/DummyDelegator.cs | 10 + .Lib9c.Tests/Delegation/TestDelegatee.cs | 28 ++ .Lib9c.Tests/Delegation/TestDelegator.cs | 19 ++ 7 files changed, 582 insertions(+) create mode 100644 .Lib9c.Tests/Delegation/DelegateeTest.cs create mode 100644 .Lib9c.Tests/Delegation/DelegationFixture.cs create mode 100644 .Lib9c.Tests/Delegation/DelegatorTest.cs create mode 100644 .Lib9c.Tests/Delegation/DummyDelegatee.cs create mode 100644 .Lib9c.Tests/Delegation/DummyDelegator.cs create mode 100644 .Lib9c.Tests/Delegation/TestDelegatee.cs create mode 100644 .Lib9c.Tests/Delegation/TestDelegator.cs diff --git a/.Lib9c.Tests/Delegation/DelegateeTest.cs b/.Lib9c.Tests/Delegation/DelegateeTest.cs new file mode 100644 index 0000000000..ee1a923eec --- /dev/null +++ b/.Lib9c.Tests/Delegation/DelegateeTest.cs @@ -0,0 +1,275 @@ +namespace Lib9c.Tests.Delegation +{ + using System; + using System.Numerics; + using Libplanet.Crypto; + using Libplanet.Types.Assets; + using Xunit; + + public class DelegateeTest + { + private readonly DelegationFixture _fixture; + + public DelegateeTest() + { + _fixture = new DelegationFixture(); + } + + [Fact] + public void Ctor() + { + var address = new Address("0xe8327129891e1A0B2E3F0bfa295777912295942a"); + var delegatee = new TestDelegatee(address); + Assert.Equal(address, delegatee.Address); + Assert.Equal(DelegationFixture.TestCurrency, delegatee.Currency); + Assert.Equal(3, delegatee.UnbondingPeriod); + Assert.Equal(new byte[] { 0x01 }, delegatee.DelegateeId); + } + + [Fact] + public void CtorWithBencoded() + { + var address = new Address("0xe8327129891e1A0B2E3F0bfa295777912295942a"); + var delegatee = _fixture.TestDelegatee1; + var delegator = _fixture.TestDelegator1; + var delegation = _fixture.Delegation1To1; + delegatee.Bond(delegator, delegatee.Currency * 10, delegation); + + var delegateeRecon = new TestDelegatee(address, delegatee.Bencoded); + Assert.Equal(address, delegateeRecon.Address); + Assert.Equal(delegator.Address, Assert.Single(delegateeRecon.Delegators)); + Assert.Equal(delegatee.TotalDelegated, delegateeRecon.TotalDelegated); + Assert.Equal(delegatee.TotalShares, delegateeRecon.TotalShares); + } + + [Fact] + public void Exchange() + { + // TODO: Test exchange after slashing is implemented. + // (Delegatee.ShareToBond & Delegatee.BondToShare) + } + + [Fact] + public void Bond() + { + var testDelegatee = _fixture.TestDelegatee1; + var testDelegator1 = _fixture.TestDelegator1; + var testDelegator2 = _fixture.TestDelegator2; + var delegation1To1 = _fixture.Delegation1To1; + var delegation2To1 = _fixture.Delegation2To1; + + var share1 = BigInteger.Zero; + var share2 = BigInteger.Zero; + var totalShare = BigInteger.Zero; + var totalBonding = testDelegatee.Currency * 0; + + var bonding = testDelegatee.Currency * 10; + var share = testDelegatee.ShareToBond(bonding); + share1 += share; + totalShare += share; + totalBonding += bonding; + + testDelegatee.Bond(testDelegator1, bonding, delegation1To1); + Assert.Equal(testDelegator1.Address, Assert.Single(testDelegatee.Delegators)); + Assert.Equal(bonding, delegation1To1.IncompleteBond); + Assert.Null(delegation1To1.IncompleteUnbond); + Assert.Equal(share1, delegation1To1.Bond.Share); + Assert.Equal(totalShare, testDelegatee.TotalShares); + Assert.Equal(totalBonding, testDelegatee.TotalDelegated); + delegation1To1.Complete(); + + bonding = testDelegatee.Currency * 20; + share = testDelegatee.ShareToBond(bonding); + share1 += share; + totalShare += share; + totalBonding += bonding; + testDelegatee.Bond(testDelegator1, bonding, delegation1To1); + Assert.Equal(testDelegator1.Address, Assert.Single(testDelegatee.Delegators)); + Assert.Equal(bonding, delegation1To1.IncompleteBond); + Assert.Null(delegation1To1.IncompleteUnbond); + Assert.Equal(share1, delegation1To1.Bond.Share); + Assert.Equal(totalShare, testDelegatee.TotalShares); + Assert.Equal(totalBonding, testDelegatee.TotalDelegated); + delegation1To1.Complete(); + + bonding = testDelegatee.Currency * 30; + share = testDelegatee.ShareToBond(bonding); + share2 += share; + totalShare += share; + totalBonding += bonding; + testDelegatee.Bond(testDelegator2, bonding, delegation2To1); + Assert.Equal(2, testDelegatee.Delegators.Count); + Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); + Assert.Contains(testDelegator2.Address, testDelegatee.Delegators); + Assert.Equal(bonding, delegation2To1.IncompleteBond); + Assert.Null(delegation2To1.IncompleteUnbond); + Assert.Equal(share2, delegation2To1.Bond.Share); + Assert.Equal(totalShare, testDelegatee.TotalShares); + Assert.Equal(totalBonding, testDelegatee.TotalDelegated); + delegation2To1.Complete(); + } + + [Fact] + public void CannotBondInvalidDelegator() + { + var testDelegatee = _fixture.TestDelegatee1; + var testDelegator = _fixture.TestDelegator1; + var dummyDelegator = _fixture.DummyDelegator1; + var delegation = _fixture.Delegation1To1; + + Assert.Throws( + () => testDelegatee.Bond( + dummyDelegator, testDelegatee.Currency * 10, delegation)); + } + + [Fact] + public void CannotBondInvalidCurrency() + { + var testDelegatee = _fixture.TestDelegatee1; + var testDelegator = _fixture.TestDelegator1; + var dummyDelegator = _fixture.DummyDelegator1; + var delegation = _fixture.Delegation1To1; + var invalidCurrency = Currency.Uncapped("invalid", 3, null); + + Assert.Throws( + () => testDelegatee.Bond( + testDelegator, invalidCurrency * 10, delegation)); + } + + [Fact] + public void Unbond() + { + var testDelegatee = _fixture.TestDelegatee1; + var testDelegator1 = _fixture.TestDelegator1; + var testDelegator2 = _fixture.TestDelegator2; + var delegation1To1 = _fixture.Delegation1To1; + var delegation2To1 = _fixture.Delegation2To1; + + var share1 = BigInteger.Zero; + var share2 = BigInteger.Zero; + var totalShares = BigInteger.Zero; + var totalDelegated = testDelegatee.Currency * 0; + + var bonding = testDelegatee.Currency * 100; + var share = testDelegatee.ShareToBond(bonding); + share1 += share; + totalShares += share; + totalDelegated += bonding; + testDelegatee.Bond(testDelegator1, bonding, delegation1To1); + delegation1To1.Complete(); + + bonding = testDelegatee.Currency * 50; + share = testDelegatee.ShareToBond(bonding); + share2 += share; + totalShares += share; + totalDelegated += bonding; + testDelegatee.Bond(testDelegator2, bonding, delegation2To1); + delegation2To1.Complete(); + + var unbonding = share1 / 2; + share1 -= unbonding; + totalShares -= unbonding; + var unbondingFAV = testDelegatee.FAVToUnbond(unbonding); + totalDelegated -= unbondingFAV; + testDelegatee.Unbond(testDelegator1, unbonding, delegation1To1); + Assert.Equal(2, testDelegatee.Delegators.Count); + Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); + Assert.Contains(testDelegator2.Address, testDelegatee.Delegators); + Assert.Equal(unbondingFAV, delegation1To1.IncompleteUnbond); + Assert.Null(delegation1To1.IncompleteBond); + Assert.Equal(share1, delegation1To1.Bond.Share); + Assert.Equal(totalShares, testDelegatee.TotalShares); + Assert.Equal(totalDelegated, testDelegatee.TotalDelegated); + delegation1To1.Complete(); + + unbonding = share2 / 2; + share2 -= unbonding; + totalShares -= unbonding; + unbondingFAV = testDelegatee.FAVToUnbond(unbonding); + totalDelegated -= unbondingFAV; + testDelegatee.Unbond(testDelegator2, unbonding, delegation2To1); + Assert.Equal(2, testDelegatee.Delegators.Count); + Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); + Assert.Contains(testDelegator2.Address, testDelegatee.Delegators); + Assert.Equal(unbondingFAV, delegation2To1.IncompleteUnbond); + Assert.Null(delegation2To1.IncompleteBond); + Assert.Equal(share2, delegation2To1.Bond.Share); + Assert.Equal(totalShares, testDelegatee.TotalShares); + Assert.Equal(totalDelegated, testDelegatee.TotalDelegated); + delegation1To1.Complete(); + + totalShares -= share1; + unbondingFAV = testDelegatee.FAVToUnbond(share1); + totalDelegated -= unbondingFAV; + testDelegatee.Unbond(testDelegator1, share1, delegation1To1); + Assert.Equal(testDelegator2.Address, Assert.Single(testDelegatee.Delegators)); + Assert.Equal(unbondingFAV, delegation1To1.IncompleteUnbond); + Assert.Null(delegation1To1.IncompleteBond); + Assert.Equal(BigInteger.Zero, delegation1To1.Bond.Share); + Assert.Equal(totalShares, testDelegatee.TotalShares); + Assert.Equal(totalDelegated, testDelegatee.TotalDelegated); + delegation1To1.Complete(); + } + + [Fact] + public void CannotUnbondInvalidDelegator() + { + Assert.Throws( + () => _fixture.TestDelegatee1.Unbond(_fixture.DummyDelegator1, BigInteger.One, _fixture.Delegation1To1)); + } + + [Fact] + public void AddressConsistency() + { + var testDelegatee1 = _fixture.TestDelegatee1; + var testDelegatee2 = _fixture.TestDelegatee2; + var testDelegator1 = _fixture.TestDelegator1; + var testDelegator2 = _fixture.TestDelegator2; + var dummyDelegatee1 = _fixture.DummyDelegatee1; + + Assert.Equal( + testDelegatee1.BondAddress(testDelegator1.Address), + testDelegatee1.BondAddress(testDelegator1.Address)); + Assert.NotEqual( + testDelegatee1.BondAddress(testDelegator1.Address), + testDelegatee1.BondAddress(testDelegator2.Address)); + Assert.NotEqual( + testDelegatee1.BondAddress(testDelegator1.Address), + testDelegatee2.BondAddress(testDelegator1.Address)); + + Assert.Equal( + testDelegatee1.UnbondLockInAddress(testDelegator1.Address), + testDelegatee1.UnbondLockInAddress(testDelegator1.Address)); + Assert.NotEqual( + testDelegatee1.UnbondLockInAddress(testDelegator1.Address), + testDelegatee1.UnbondLockInAddress(testDelegator2.Address)); + Assert.NotEqual( + testDelegatee1.UnbondLockInAddress(testDelegator1.Address), + testDelegatee2.UnbondLockInAddress(testDelegator1.Address)); + + Assert.Equal( + testDelegatee1.RebondGraceAddress(testDelegator1.Address), + testDelegatee1.RebondGraceAddress(testDelegator1.Address)); + Assert.NotEqual( + testDelegatee1.RebondGraceAddress(testDelegator1.Address), + testDelegatee1.RebondGraceAddress(testDelegator2.Address)); + Assert.NotEqual( + testDelegatee1.RebondGraceAddress(testDelegator1.Address), + testDelegatee2.RebondGraceAddress(testDelegator1.Address)); + + Assert.Equal(testDelegatee1.Address, dummyDelegatee1.Address); + Assert.NotEqual( + testDelegatee1.RewardPoolAddress, + dummyDelegatee1.RewardPoolAddress); + Assert.NotEqual( + testDelegatee1.BondAddress(testDelegator1.Address), + dummyDelegatee1.BondAddress(testDelegator1.Address)); + Assert.NotEqual( + testDelegatee1.UnbondLockInAddress(testDelegator1.Address), + dummyDelegatee1.UnbondLockInAddress(testDelegator1.Address)); + Assert.NotEqual( + testDelegatee1.RebondGraceAddress(testDelegator1.Address), + dummyDelegatee1.RebondGraceAddress(testDelegator1.Address)); + } + } +} diff --git a/.Lib9c.Tests/Delegation/DelegationFixture.cs b/.Lib9c.Tests/Delegation/DelegationFixture.cs new file mode 100644 index 0000000000..cb70321c06 --- /dev/null +++ b/.Lib9c.Tests/Delegation/DelegationFixture.cs @@ -0,0 +1,73 @@ +namespace Lib9c.Tests.Delegation +{ + using Libplanet.Crypto; + using Libplanet.Types.Assets; + using Nekoyume.Delegation; + + public class DelegationFixture + { + public static readonly Currency TestCurrency = Currency.Uncapped("test-del", 5, null); + public static readonly Address FixedPoolAddress = new Address("0x75b21EbC56e5dAc817A1128Fb05d45853183117c"); + + public DelegationFixture() + { + TestDelegator1 = new TestDelegator(new Address("0x0054E98312C47E7Fa0ABed45C23Fa187e31C373a")); + TestDelegator2 = new TestDelegator(new Address("0x327CCff388255E9399207C3d5a09357D0BBc73dF")); + TestDelegatee1 = new TestDelegatee(new Address("0x67A44E11506b8f0Bb625fEECccb205b33265Bb48")); + TestDelegatee2 = new TestDelegatee(new Address("0xea1C4eedEfC99691DEfc6eF2753FAfa8C17F4584")); + DummyDelegatee1 = new DummyDelegatee(new Address("0x67A44E11506b8f0Bb625fEECccb205b33265Bb48")); + DummyDelegator1 = new DummyDelegator(new Address("0x0054E98312C47E7Fa0ABed45C23Fa187e31C373a")); + Bond1To1 = new Bond(TestDelegatee1.BondAddress(TestDelegator1.Address)); + Bond2To1 = new Bond(TestDelegatee1.BondAddress(TestDelegator2.Address)); + Bond1To2 = new Bond(TestDelegatee2.BondAddress(TestDelegator1.Address)); + Unbond1To1 = new UnbondLockIn(TestDelegatee1.BondAddress(TestDelegator1.Address), 10); + Unbond2To1 = new UnbondLockIn(TestDelegatee1.BondAddress(TestDelegator2.Address), 10); + Unbond1To2 = new UnbondLockIn(TestDelegatee2.BondAddress(TestDelegator1.Address), 10); + Rebond1To1 = new RebondGrace(TestDelegatee1.BondAddress(TestDelegator1.Address), 10); + Rebond2To1 = new RebondGrace(TestDelegatee1.BondAddress(TestDelegator2.Address), 10); + Rebond1To2 = new RebondGrace(TestDelegatee2.BondAddress(TestDelegator1.Address), 10); + UnbondingSet = new UnbondingSet(); + Delegation1To1 = new Delegation(Bond1To1, Unbond1To1, Rebond1To1, UnbondingSet); + Delegation2To1 = new Delegation(Bond2To1, Unbond2To1, Rebond2To1, UnbondingSet); + Delegation1To2 = new Delegation(Bond1To2, Unbond1To2, Rebond1To2, UnbondingSet); + } + + public TestDelegator TestDelegator1 { get; } + + public TestDelegator TestDelegator2 { get; } + + public TestDelegatee TestDelegatee1 { get; } + + public TestDelegatee TestDelegatee2 { get; } + + public DummyDelegatee DummyDelegatee1 { get; } + + public DummyDelegator DummyDelegator1 { get; } + + public Bond Bond1To1 { get; } + + public Bond Bond2To1 { get; } + + public Bond Bond1To2 { get; } + + public UnbondLockIn Unbond1To1 { get; } + + public UnbondLockIn Unbond2To1 { get; } + + public UnbondLockIn Unbond1To2 { get; } + + public RebondGrace Rebond1To1 { get; } + + public RebondGrace Rebond2To1 { get; } + + public RebondGrace Rebond1To2 { get; } + + public UnbondingSet UnbondingSet { get; } + + public Delegation Delegation1To1 { get; } + + public Delegation Delegation2To1 { get; } + + public Delegation Delegation1To2 { get; } + } +} diff --git a/.Lib9c.Tests/Delegation/DelegatorTest.cs b/.Lib9c.Tests/Delegation/DelegatorTest.cs new file mode 100644 index 0000000000..dda16cfa66 --- /dev/null +++ b/.Lib9c.Tests/Delegation/DelegatorTest.cs @@ -0,0 +1,157 @@ +namespace Lib9c.Tests.Delegation +{ + using Libplanet.Crypto; + using Xunit; + + public class DelegatorTest + { + private DelegationFixture _fixture; + + public DelegatorTest() + { + _fixture = new DelegationFixture(); + } + + [Fact] + public void Ctor() + { + var address = new Address("0x070e5719767CfB86712C31F5AB0072c48959d862"); + var delegator = new TestDelegator(address); + Assert.Equal(address, delegator.Address); + Assert.Empty(delegator.Delegatees); + } + + [Fact] + public void CtorWithBencoded() + { + var delegator = _fixture.TestDelegator1; + var delegatee = _fixture.TestDelegatee1; + var delegation = _fixture.Delegation1To1; + delegator.Delegate(delegatee, delegatee.Currency * 10, delegation); + + var delegatorRecon = new TestDelegator(delegator.Address, delegator.Bencoded); + Assert.Equal(delegator.Address, delegatorRecon.Address); + Assert.Equal(delegatee.Address, Assert.Single(delegatorRecon.Delegatees)); + } + + [Fact] + public void Delegate() + { + var delegator = _fixture.TestDelegator1; + var delegatee1 = _fixture.TestDelegatee1; + var delegatee2 = _fixture.TestDelegatee2; + var delegation1 = _fixture.Delegation1To1; + var delegation2 = _fixture.Delegation1To2; + var delegateFAV = delegatee1.Currency * 10; + + delegator.Delegate(delegatee1, delegateFAV, delegation1); + Assert.Equal(delegateFAV, delegation1.IncompleteBond); + Assert.Equal(delegatee1.Address, Assert.Single(delegator.Delegatees)); + delegation1.Complete(); + delegator.Delegate(delegatee2, delegateFAV, delegation2); + Assert.Equal(delegateFAV, delegation2.IncompleteBond); + Assert.Equal(2, delegator.Delegatees.Count); + Assert.Contains(delegatee1.Address, delegator.Delegatees); + Assert.Contains(delegatee2.Address, delegator.Delegatees); + delegation2.Complete(); + } + + [Fact] + public void Undelegate() + { + var delegator = _fixture.TestDelegator1; + var delegatee = _fixture.TestDelegatee1; + var delegation = _fixture.Delegation1To1; + delegator.Delegate(delegatee, delegatee.Currency * 10, delegation); + + var undelegatingShare = delegation.Bond.Share / 2; + var undelegatingFAV = delegatee.FAVToUnbond(undelegatingShare); + delegator.Undelegate(delegatee, undelegatingShare, 10L, delegation); + Assert.Null(delegation.IncompleteBond); + Assert.Null(delegation.IncompleteUnbond); + Assert.Equal(delegatee.Address, Assert.Single(delegator.Delegatees)); + var entriesByExpireHeight = Assert.Single(delegation.UnbondLockIn.Entries); + Assert.Equal(10L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); + var entry = Assert.Single(entriesByExpireHeight.Value); + Assert.Equal(undelegatingFAV, entry.InitialLockInFAV); + Assert.Equal(undelegatingFAV, entry.LockInFAV); + Assert.Equal(10L, entry.CreationHeight); + Assert.Equal(10L + delegatee.UnbondingPeriod, entry.ExpireHeight); + + undelegatingShare = delegation.Bond.Share; + delegator.Undelegate(delegatee, undelegatingShare, 12L, delegation); + Assert.Null(delegation.IncompleteBond); + Assert.Null(delegation.IncompleteUnbond); + Assert.Empty(delegator.Delegatees); + Assert.Equal(2, delegation.UnbondLockIn.Entries.Count); + + delegation.UnbondLockIn.Release(10L + delegatee.UnbondingPeriod - 1); + Assert.Equal(2, delegation.UnbondLockIn.Entries.Count); + + delegation.UnbondLockIn.Release(10L + delegatee.UnbondingPeriod); + entriesByExpireHeight = Assert.Single(delegation.UnbondLockIn.Entries); + Assert.Equal(12L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); + entry = Assert.Single(entriesByExpireHeight.Value); + Assert.Equal(undelegatingFAV, entry.InitialLockInFAV); + Assert.Equal(undelegatingFAV, entry.LockInFAV); + Assert.Equal(12L, entry.CreationHeight); + Assert.Equal(12L + delegatee.UnbondingPeriod, entry.ExpireHeight); + + delegation.UnbondLockIn.Release(12L + delegatee.UnbondingPeriod); + Assert.Empty(delegation.UnbondLockIn.Entries); + } + + [Fact] + public void Redelegate() + { + var delegator = _fixture.TestDelegator1; + var delegatee1 = _fixture.TestDelegatee1; + var delegatee2 = _fixture.TestDelegatee2; + var delegation1 = _fixture.Delegation1To1; + var delegation2 = _fixture.Delegation1To2; + delegator.Delegate(delegatee1, delegatee1.Currency * 10, delegation1); + Assert.Equal(delegatee1.Address, Assert.Single(delegator.Delegatees)); + + var redelegatingShare = delegation1.Bond.Share / 2; + var redelegatingFAV = delegatee1.FAVToUnbond(redelegatingShare); + delegator.Redelegate( + delegatee1, delegatee2, redelegatingShare, 10L, delegation1, delegation2); + Assert.Equal(redelegatingFAV, delegation2.IncompleteBond); + Assert.Equal(2, delegator.Delegatees.Count); + var entriesByExpireHeight = Assert.Single(delegation1.RebondGrace.Entries); + Assert.Equal(10L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); + var entry = Assert.Single(entriesByExpireHeight.Value); + Assert.Equal(delegatee2.Address, entry.RebondeeAddress); + Assert.Equal(redelegatingFAV, entry.InitialGraceFAV); + Assert.Equal(redelegatingFAV, entry.GraceFAV); + Assert.Equal(10L, entry.CreationHeight); + Assert.Equal(10L + delegatee1.UnbondingPeriod, entry.ExpireHeight); + delegation2.Complete(); + + redelegatingShare = delegation1.Bond.Share; + redelegatingFAV = delegatee1.FAVToUnbond(redelegatingShare); + delegator.Redelegate( + delegatee1, delegatee2, redelegatingShare, 12L, delegation1, delegation2); + Assert.Equal(redelegatingFAV, delegation2.IncompleteBond); + Assert.Equal(delegatee2.Address, Assert.Single(delegator.Delegatees)); + Assert.Equal(2, delegation1.RebondGrace.Entries.Count); + + delegation1.RebondGrace.Release(10L + delegatee1.UnbondingPeriod - 1); + Assert.Equal(2, delegation1.RebondGrace.Entries.Count); + + delegation1.RebondGrace.Release(10L + delegatee1.UnbondingPeriod); + entriesByExpireHeight = Assert.Single(delegation1.RebondGrace.Entries); + Assert.Equal(12L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); + entry = Assert.Single(entriesByExpireHeight.Value); + Assert.Equal(delegatee2.Address, entry.RebondeeAddress); + Assert.Equal(redelegatingFAV, entry.InitialGraceFAV); + Assert.Equal(redelegatingFAV, entry.GraceFAV); + Assert.Equal(12L, entry.CreationHeight); + Assert.Equal(12L + delegatee1.UnbondingPeriod, entry.ExpireHeight); + delegation2.Complete(); + + delegation1.RebondGrace.Release(12L + delegatee1.UnbondingPeriod); + Assert.Empty(delegation1.RebondGrace.Entries); + } + } +} diff --git a/.Lib9c.Tests/Delegation/DummyDelegatee.cs b/.Lib9c.Tests/Delegation/DummyDelegatee.cs new file mode 100644 index 0000000000..fe1d32a88c --- /dev/null +++ b/.Lib9c.Tests/Delegation/DummyDelegatee.cs @@ -0,0 +1,20 @@ +using Lib9c.Tests.Delegation; +using Libplanet.Crypto; +using Libplanet.Types.Assets; +using Nekoyume.Delegation; + +public sealed class DummyDelegatee : Delegatee +{ + public DummyDelegatee(Address address) + : base(address) + { + } + + public override Currency Currency => DelegationFixture.TestCurrency; + + public override Address PoolAddress => DelegationFixture.FixedPoolAddress; + + public override long UnbondingPeriod => 3; + + public override byte[] DelegateeId => new byte[] { 0x02 }; +} diff --git a/.Lib9c.Tests/Delegation/DummyDelegator.cs b/.Lib9c.Tests/Delegation/DummyDelegator.cs new file mode 100644 index 0000000000..12b107d3b3 --- /dev/null +++ b/.Lib9c.Tests/Delegation/DummyDelegator.cs @@ -0,0 +1,10 @@ +using Libplanet.Crypto; +using Nekoyume.Delegation; + +public sealed class DummyDelegator : Delegator +{ + public DummyDelegator(Address address) + : base(address) + { + } +} diff --git a/.Lib9c.Tests/Delegation/TestDelegatee.cs b/.Lib9c.Tests/Delegation/TestDelegatee.cs new file mode 100644 index 0000000000..0373f1fce0 --- /dev/null +++ b/.Lib9c.Tests/Delegation/TestDelegatee.cs @@ -0,0 +1,28 @@ +namespace Lib9c.Tests.Delegation +{ + using Bencodex.Types; + using Libplanet.Crypto; + using Libplanet.Types.Assets; + using Nekoyume.Delegation; + + public sealed class TestDelegatee : Delegatee + { + public TestDelegatee(Address address) + : base(address) + { + } + + public TestDelegatee(Address address, IValue bencoded) + : base(address, bencoded) + { + } + + public override Currency Currency => DelegationFixture.TestCurrency; + + public override long UnbondingPeriod => 3; + + public override Address PoolAddress => DeriveAddress(PoolId); + + public override byte[] DelegateeId => new byte[] { 0x01 }; + } +} diff --git a/.Lib9c.Tests/Delegation/TestDelegator.cs b/.Lib9c.Tests/Delegation/TestDelegator.cs new file mode 100644 index 0000000000..6c1ef86ae3 --- /dev/null +++ b/.Lib9c.Tests/Delegation/TestDelegator.cs @@ -0,0 +1,19 @@ +namespace Lib9c.Tests.Delegation +{ + using Bencodex.Types; + using Libplanet.Crypto; + using Nekoyume.Delegation; + + public sealed class TestDelegator : Delegator + { + public TestDelegator(Address address) + : base(address) + { + } + + public TestDelegator(Address address, IValue bencoded) + : base(address, bencoded) + { + } + } +} From e655098d2dccbb99922f17dac5bf22e5f696fa10 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 2 Aug 2024 11:22:34 +0900 Subject: [PATCH 003/206] feat: Inherit IEquatable, Apply reviews --- Lib9c/Delegation/Bond.cs | 56 +++++++++- Lib9c/Delegation/Delegatee.cs | 111 +++++++++++++------ Lib9c/Delegation/Delegation.cs | 85 +++++++++++++-- Lib9c/Delegation/Delegator.cs | 71 +++++++++--- Lib9c/Delegation/IDelegatee.cs | 3 +- Lib9c/Delegation/IDelegator.cs | 3 +- Lib9c/Delegation/RebondGrace.cs | 108 +++++++++++++++++- Lib9c/Delegation/UnbondLockIn.cs | 181 ++++++++++++++++++++++++++----- Lib9c/Delegation/UnbondingSet.cs | 3 +- 9 files changed, 518 insertions(+), 103 deletions(-) diff --git a/Lib9c/Delegation/Bond.cs b/Lib9c/Delegation/Bond.cs index 84841cc97e..1af48cb91b 100644 --- a/Lib9c/Delegation/Bond.cs +++ b/Lib9c/Delegation/Bond.cs @@ -1,3 +1,4 @@ +using System; using System.Numerics; using Bencodex; using Bencodex.Types; @@ -5,7 +6,7 @@ namespace Nekoyume.Delegation { - public class Bond : IBencodable + public sealed class Bond : IBencodable, IEquatable { public Bond(Address address) : this(address, BigInteger.Zero, 0) @@ -24,6 +25,20 @@ public Bond(Address address, List bencoded) private Bond(Address address, BigInteger share, long lastDistributeHeight) { + if (share.Sign < 0) + { + throw new ArgumentOutOfRangeException( + nameof(share), share, "Share must be non-negative."); + } + + if (lastDistributeHeight < 0) + { + throw new ArgumentOutOfRangeException( + nameof(lastDistributeHeight), + lastDistributeHeight, + "Last distribute height must be non-negative."); + } + Address = address; Share = share; LastDistributeHeight = lastDistributeHeight; @@ -39,18 +54,51 @@ private Bond(Address address, BigInteger share, long lastDistributeHeight) .Add(Share) .Add(LastDistributeHeight); - public void AddShare(BigInteger share) + + public override bool Equals(object obj) + => obj is Bond other && Equals(other); + + public bool Equals(Bond other) + => ReferenceEquals(this, other) + || (Address.Equals(other.Address) + && Share.Equals(other.Share) + && LastDistributeHeight.Equals(other.LastDistributeHeight)); + + public override int GetHashCode() + => Address.GetHashCode(); + + internal void AddShare(BigInteger share) { + if (share.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(share), share, "share must be positive."); + } + Share += share; } - public void SubtractShare(BigInteger share) + internal void SubtractShare(BigInteger share) { + if (share > Share) + { + throw new ArgumentOutOfRangeException( + nameof(share), share, "share must be less than or equal to the current share."); + } + Share -= share; } - public void UpdateLastDistributeHeight(long height) + internal void UpdateLastDistributeHeight(long height) { + if (height <= LastDistributeHeight) + { + throw new ArgumentOutOfRangeException( + nameof(height), + height, + "height must be greater than the last distribute height."); + } + LastDistributeHeight = height; } } diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index ea04c88dd3..e0543e9b62 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -48,6 +48,27 @@ private Delegatee( FungibleAssetValue totalDelegated, BigInteger totalShares) { + if (!totalDelegated.Currency.Equals(Currency)) + { + throw new InvalidOperationException("Invalid currency."); + } + + if (totalDelegated.Sign < 0) + { + throw new ArgumentOutOfRangeException( + nameof(totalDelegated), + totalDelegated, + "Total delegated must be non-negative."); + } + + if (totalShares.Sign < 0) + { + throw new ArgumentOutOfRangeException( + nameof(totalShares), + totalShares, + "Total shares must be non-negative."); + } + Address = address; Delegators = delegators.ToImmutableSortedSet(); TotalDelegated = totalDelegated; @@ -58,12 +79,12 @@ private Delegatee( public abstract Currency Currency { get; } + public abstract Address PoolAddress { get; } + public abstract long UnbondingPeriod { get; } public abstract byte[] DelegateeId { get; } - public abstract Address PoolAddress { get; } - public Address RewardPoolAddress => DeriveAddress(RewardPoolId); public ImmutableSortedSet
Delegators { get; private set; } @@ -87,42 +108,13 @@ public FungibleAssetValue FAVToUnbond(BigInteger share) ? TotalDelegated : (TotalDelegated * share).DivRem(TotalShares, out _); - public void Bond(IDelegator delegator, FungibleAssetValue fav, Delegation delegation) + void IDelegatee.Bond(IDelegator delegator, FungibleAssetValue fav, Delegation delegation) => Bond((T)delegator, fav, delegation); - public void Unbond(IDelegator delegator, BigInteger share, Delegation delegation) + void IDelegatee.Unbond(IDelegator delegator, BigInteger share, Delegation delegation) => Unbond((T)delegator, share, delegation); - public void Distribute() - { - // TODO: Implement this - } - - public Address BondAddress(Address delegatorAddress) - => DeriveAddress(BondId, delegatorAddress); - - public Address UnbondLockInAddress(Address delegatorAddress) - => DeriveAddress(UnbondLockInId, delegatorAddress); - - public Address RebondGraceAddress(Address delegatorAddress) - => DeriveAddress(RebondGraceId, delegatorAddress); - - protected Address DeriveAddress(byte[] typeId, Address address) - => DeriveAddress(typeId, address.ByteArray); - - protected Address DeriveAddress(byte[] typeId, IEnumerable bytes = null) - { - byte[] hashed; - using (var hmac = new HMACSHA1(DelegateeId.Concat(typeId).ToArray())) - { - hashed = hmac.ComputeHash( - Address.ByteArray.Concat(bytes ?? Array.Empty()).ToArray()); - } - - return new Address(hashed); - } - - private void Bond(T delegator, FungibleAssetValue fav, Delegation delegation) + public void Bond(T delegator, FungibleAssetValue fav, Delegation delegation) { if (!fav.Currency.Equals(Currency)) { @@ -137,7 +129,7 @@ private void Bond(T delegator, FungibleAssetValue fav, Delegation delegation) Distribute(); } - private void Unbond(T delegator, BigInteger share, Delegation delegation) + public void Unbond(T delegator, BigInteger share, Delegation delegation) { if (TotalShares.IsZero || TotalDelegated.RawValue.IsZero) { @@ -155,5 +147,54 @@ private void Unbond(T delegator, BigInteger share, Delegation delegation) TotalDelegated -= fav; Distribute(); } + + public void Distribute() + { + // TODO: Implement this + } + + public Address BondAddress(Address delegatorAddress) + => DeriveAddress(BondId, delegatorAddress); + + public Address UnbondLockInAddress(Address delegatorAddress) + => DeriveAddress(UnbondLockInId, delegatorAddress); + + public Address RebondGraceAddress(Address delegatorAddress) + => DeriveAddress(RebondGraceId, delegatorAddress); + + public override bool Equals(object obj) + => obj is IDelegatee other && Equals(other); + + public bool Equals(IDelegatee other) + => ReferenceEquals(this, other) + || (other is Delegatee delegatee + && (GetType() != delegatee.GetType()) + && Address.Equals(delegatee.Address) + && Currency.Equals(delegatee.Currency) + && PoolAddress.Equals(delegatee.PoolAddress) + && UnbondingPeriod == delegatee.UnbondingPeriod + && RewardPoolAddress.Equals(delegatee.RewardPoolAddress) + && Delegators.SequenceEqual(delegatee.Delegators) + && TotalDelegated.Equals(delegatee.TotalDelegated) + && TotalShares.Equals(delegatee.TotalShares) + && DelegateeId.SequenceEqual(delegatee.DelegateeId)); + + public override int GetHashCode() + => Address.GetHashCode(); + + protected Address DeriveAddress(byte[] typeId, Address address) + => DeriveAddress(typeId, address.ByteArray); + + protected Address DeriveAddress(byte[] typeId, IEnumerable bytes = null) + { + byte[] hashed; + using (var hmac = new HMACSHA1(DelegateeId.Concat(typeId).ToArray())) + { + hashed = hmac.ComputeHash( + Address.ByteArray.Concat(bytes ?? Array.Empty()).ToArray()); + } + + return new Address(hashed); + } } } diff --git a/Lib9c/Delegation/Delegation.cs b/Lib9c/Delegation/Delegation.cs index 52f6d44084..3ba9c10107 100644 --- a/Lib9c/Delegation/Delegation.cs +++ b/Lib9c/Delegation/Delegation.cs @@ -5,7 +5,7 @@ namespace Nekoyume.Delegation { - public class Delegation + public sealed class Delegation { public Delegation( Bond bond = null, @@ -33,9 +33,16 @@ public Delegation( public void AddBond(FungibleAssetValue fav, BigInteger share) { - if (fav.Sign != 1) + if (fav.Sign <= 0) { - throw new InvalidOperationException("Cannot bond negative FAV."); + throw new ArgumentOutOfRangeException( + nameof(fav), fav, "Fungible asset value must be positive."); + } + + if (share.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(share), share, "Share must be positive."); } Bond.AddShare(share); @@ -44,23 +51,45 @@ public void AddBond(FungibleAssetValue fav, BigInteger share) public void CancelBond(FungibleAssetValue fav, BigInteger share) { - if (share.Sign != 1) + if (fav.Sign <= 0) { - throw new InvalidOperationException("Cannot unbond negative FAV."); + throw new ArgumentOutOfRangeException( + nameof(fav), fav, "Fungible asset value must be positive."); } - if (Bond.Share < share) + if (share.Sign <= 0) { - throw new InvalidOperationException("Cannot unbond more than bonded."); + throw new ArgumentOutOfRangeException( + nameof(share), share, "Share must be positive."); } Bond.SubtractShare(share); IncompleteUnbond = fav; } - public void DoUnbondLockIn(FungibleAssetValue fav, long height, long completionHeight) + public void DoUnbondLockIn(FungibleAssetValue fav, long height, long expireHeight) { - UnbondLockIn.LockIn(fav, height, completionHeight); + if (fav.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(fav), fav, "Fungible asset value must be positive."); + } + + if (height < 0) + { + throw new ArgumentOutOfRangeException( + nameof(height), height, "Height must be greater than or equal to zero."); + } + + if (expireHeight < height) + { + throw new ArgumentOutOfRangeException( + nameof(expireHeight), + expireHeight, + "Expire height must be greater than or equal to the height."); + } + + UnbondLockIn.LockIn(fav, height, expireHeight); if (!UnbondingSet.UnbondLockIns.Contains(UnbondLockIn.Address)) { UnbondingSet.AddUnbondLockIn(UnbondLockIn.Address); @@ -69,6 +98,18 @@ public void DoUnbondLockIn(FungibleAssetValue fav, long height, long completionH public void CancelUnbondLockIn(FungibleAssetValue fav, long height) { + if (fav.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(fav), fav, "Fungible asset value must be positive."); + } + + if (height < 0) + { + throw new ArgumentOutOfRangeException( + nameof(height), height, "Height must be greater than or equal to zero."); + } + UnbondLockIn.Cancel(fav, height); if (UnbondLockIn.IsEmpty) { @@ -76,13 +117,33 @@ public void CancelUnbondLockIn(FungibleAssetValue fav, long height) } } - public void DoRebondGrace(Address rebondeeAddress, FungibleAssetValue fav, long height, long completionHeight) + public void DoRebondGrace(Address rebondeeAddress, FungibleAssetValue fav, long height, long expireHeight) { - RebondGrace.Grace(rebondeeAddress, fav, height, completionHeight); + if (fav.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(fav), fav, "Fungible asset value must be positive."); + } + + if (height < 0) + { + throw new ArgumentOutOfRangeException( + nameof(height), height, "Height must be greater than or equal to zero."); + } + + RebondGrace.Grace(rebondeeAddress, fav, height, expireHeight); if (!UnbondingSet.RebondGraces.Contains(RebondGrace.Address)) { UnbondingSet.AddRebondGrace(RebondGrace.Address); - } + } + + if (expireHeight < height) + { + throw new ArgumentOutOfRangeException( + nameof(expireHeight), + expireHeight, + "Expire height must be greater than or equal to the height."); + } } public void Complete() diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index d15edbe9e4..c746f647f8 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -36,13 +36,13 @@ private Delegator(Address address, List bencoded) public IValue Bencoded => new List(Delegatees.Select(a => a.Bencoded)); - public void Delegate(IDelegatee delegatee, FungibleAssetValue fav, Delegation delegation) + void IDelegator.Delegate(IDelegatee delegatee, FungibleAssetValue fav, Delegation delegation) => Delegate((T)delegatee, fav, delegation); - public void Undelegate(IDelegatee delegatee, BigInteger share, long height, Delegation delegation) + void IDelegator.Undelegate(IDelegatee delegatee, BigInteger share, long height, Delegation delegation) => Undelegate((T)delegatee, share, height, delegation); - public void Redelegate( + void IDelegator.Redelegate( IDelegatee srcDelegatee, IDelegatee dstDelegatee, BigInteger share, @@ -51,32 +51,45 @@ public void Redelegate( Delegation dstDelegation) => Redelegate((T)srcDelegatee, (T)dstDelegatee, share, height, srcDelegation, dstDelegation); - public void Claim(IDelegatee delegatee) - { - // TODO: Implement this - } - - private void Delegate( + public void Delegate( T delegatee, FungibleAssetValue fav, Delegation delegation) { + if (fav.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(fav), fav, "Fungible asset value must be positive."); + } + delegatee.Bond((TSelf)this, fav, delegation); Delegatees = Delegatees.Add(delegatee.Address); } - private void Undelegate( + public void Undelegate( T delegatee, BigInteger share, long height, Delegation delegation) { + 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 (delegation.UnbondLockIn.IsFull) { throw new InvalidOperationException("Undelegation is full."); } - delegatee.Unbond(this, share, delegation); + delegatee.Unbond((TSelf)this, share, delegation); if (!(delegation.IncompleteUnbond is FungibleAssetValue unbondToLockIn)) { @@ -93,7 +106,7 @@ private void Undelegate( delegation.Complete(); } - private void Redelegate( + public void Redelegate( T srcDelegatee, T dstDelegatee, BigInteger share, @@ -101,14 +114,26 @@ private void Redelegate( Delegation srcDelegation, Delegation dstDelegation) { - srcDelegatee.Unbond(this, share, srcDelegation); + 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."); + } + + srcDelegatee.Unbond((TSelf)this, share, srcDelegation); if (!(srcDelegation.IncompleteUnbond is FungibleAssetValue unbondToGrace)) { throw new NullReferenceException("Bonding FAV is null."); } - dstDelegatee.Bond(this, unbondToGrace, dstDelegation); + dstDelegatee.Bond((TSelf)this, unbondToGrace, dstDelegation); srcDelegation.DoRebondGrace(dstDelegatee.Address, unbondToGrace, height, height + srcDelegatee.UnbondingPeriod); @@ -121,5 +146,23 @@ private void Redelegate( srcDelegation.Complete(); } + + public void Claim(IDelegatee delegatee) + { + // TODO: Implement this + } + + public override bool Equals(object obj) + => obj is IDelegator other && Equals(other); + + public bool Equals(IDelegator other) + => ReferenceEquals(this, other) + || (other is Delegator delegator + && GetType() != delegator.GetType() + && Address.Equals(delegator.Address) + && Delegatees.SequenceEqual(delegator.Delegatees)); + + public override int GetHashCode() + => Address.GetHashCode(); } } diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index a17b6d3713..bf040bf8b9 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Immutable; using System.Numerics; using Bencodex; @@ -6,7 +7,7 @@ namespace Nekoyume.Delegation { - public interface IDelegatee : IBencodable + public interface IDelegatee : IBencodable, IEquatable { Address Address { get; } diff --git a/Lib9c/Delegation/IDelegator.cs b/Lib9c/Delegation/IDelegator.cs index 47797fe416..a29cd288a6 100644 --- a/Lib9c/Delegation/IDelegator.cs +++ b/Lib9c/Delegation/IDelegator.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Immutable; using System.Numerics; using Bencodex; @@ -6,7 +7,7 @@ namespace Nekoyume.Delegation { - public interface IDelegator : IBencodable + public interface IDelegator : IBencodable, IEquatable { Address Address { get; } diff --git a/Lib9c/Delegation/RebondGrace.cs b/Lib9c/Delegation/RebondGrace.cs index 5444d24ec2..4f9c45e8f0 100644 --- a/Lib9c/Delegation/RebondGrace.cs +++ b/Lib9c/Delegation/RebondGrace.cs @@ -9,10 +9,18 @@ namespace Nekoyume.Delegation { - public class RebondGrace : IBencodable + public sealed class RebondGrace : IBencodable, IEquatable { public RebondGrace(Address address, int maxEntries) { + if (maxEntries < 0) + { + throw new ArgumentOutOfRangeException( + nameof(maxEntries), + maxEntries, + "The max entries must be greater than or equal to zero."); + } + Address = address; MaxEntries = maxEntries; Entries = ImmutableSortedDictionary>.Empty; @@ -25,6 +33,14 @@ public RebondGrace(Address address, int maxEntries, IValue bencoded) public RebondGrace(Address address, int maxEntries, List bencoded) { + if (maxEntries < 0) + { + throw new ArgumentOutOfRangeException( + nameof(maxEntries), + maxEntries, + "The max entries must be greater than or equal to zero."); + } + Address = address; MaxEntries = maxEntries; Entries = bencoded @@ -56,6 +72,8 @@ public RebondGrace(Address address, int maxEntries, IEnumerable> Entries { get; private set; } + public ImmutableArray FlattenedEntries + => Entries.Values.SelectMany(e => e).ToImmutableArray(); public IValue Bencoded => new List( @@ -64,11 +82,16 @@ public IValue Bencoded (Integer)sortedDict.Key, new List(sortedDict.Value.Select(e => e.Bencoded))))); - public void Grace(Address rebondeeAddress, FungibleAssetValue initialGraceFAV, long creationHeight, long expireHeight) - => AddEntry(new RebondGraceEntry(rebondeeAddress, initialGraceFAV, creationHeight, expireHeight)); - public void Release(long height) { + if (height <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(height), + height, + "The height must be greater than zero."); + } + foreach (var (expireHeight, entries) in Entries) { if (expireHeight <= height) @@ -82,6 +105,33 @@ public void Release(long height) } } + [Obsolete("This method is not implemented yet.")] + public void Slash() + => throw new NotImplementedException(); + + public override bool Equals(object obj) + => obj is RebondGrace other && Equals(other); + + public bool Equals(RebondGrace other) + => ReferenceEquals(this, other) + || (Address.Equals(other.Address) + && MaxEntries == other.MaxEntries + && FlattenedEntries.SequenceEqual(other.FlattenedEntries)); + + public override int GetHashCode() + => Address.GetHashCode(); + + internal void Grace( + Address rebondeeAddress, FungibleAssetValue initialGraceFAV, long creationHeight, long expireHeight) + { + if (expireHeight == creationHeight) + { + return; + } + + AddEntry(new RebondGraceEntry(rebondeeAddress, initialGraceFAV, creationHeight, expireHeight)); + } + private void AddEntry(RebondGraceEntry entry) { if (IsFull) @@ -99,7 +149,7 @@ private void AddEntry(RebondGraceEntry entry) } } - public class RebondGraceEntry : IBencodable + public class RebondGraceEntry : IBencodable, IEquatable { public RebondGraceEntry( Address rebondeeAddress, @@ -136,6 +186,46 @@ private RebondGraceEntry( long creationHeight, long expireHeight) { + if (initialGraceFAV.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(initialGraceFAV), + initialGraceFAV, + "The initial grace FAV must be greater than zero."); + } + + if (graceFAV.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(graceFAV), + graceFAV, + "The grace FAV must be greater than zero."); + } + + if (graceFAV >= initialGraceFAV) + { + throw new ArgumentOutOfRangeException( + nameof(graceFAV), + graceFAV, + "The grace FAV must be less than the initial grace FAV."); + } + + if (creationHeight < 0) + { + throw new ArgumentOutOfRangeException( + nameof(creationHeight), + creationHeight, + "The creation height must be greater than or equal to zero."); + } + + if (expireHeight <= creationHeight) + { + throw new ArgumentOutOfRangeException( + nameof(expireHeight), + expireHeight, + "The expire height must be greater than the creation height."); + } + RebondeeAddress = rebondeeAddress; InitialGraceFAV = initialGraceFAV; GraceFAV = graceFAV; @@ -159,6 +249,14 @@ private RebondGraceEntry( .Add(GraceFAV.Serialize()) .Add(CreationHeight) .Add(ExpireHeight); + + public bool Equals(RebondGraceEntry other) + => ReferenceEquals(this, other) + || (RebondeeAddress.Equals(other.RebondeeAddress) + && InitialGraceFAV.Equals(other.InitialGraceFAV) + && GraceFAV.Equals(other.GraceFAV) + && CreationHeight == other.CreationHeight + && ExpireHeight == other.ExpireHeight); } } } diff --git a/Lib9c/Delegation/UnbondLockIn.cs b/Lib9c/Delegation/UnbondLockIn.cs index 25bb832fa1..312a8e6e13 100644 --- a/Lib9c/Delegation/UnbondLockIn.cs +++ b/Lib9c/Delegation/UnbondLockIn.cs @@ -9,10 +9,18 @@ namespace Nekoyume.Delegation { - public class UnbondLockIn : IBencodable + public sealed class UnbondLockIn : IBencodable, IEquatable { public UnbondLockIn(Address address, int maxEntries) { + if (maxEntries < 0) + { + throw new ArgumentOutOfRangeException( + nameof(maxEntries), + maxEntries, + "The max entries must be greater than or equal to zero."); + } + Address = address; MaxEntries = maxEntries; Entries = ImmutableSortedDictionary>.Empty; @@ -25,6 +33,14 @@ public UnbondLockIn(Address address, int maxEntries, IValue bencoded) public UnbondLockIn(Address address, int maxEntries, List bencoded) { + if (maxEntries < 0) + { + throw new ArgumentOutOfRangeException( + nameof(maxEntries), + maxEntries, + "The max entries must be greater than or equal to zero."); + } + Address = address; MaxEntries = maxEntries; Entries = bencoded @@ -56,6 +72,9 @@ public UnbondLockIn(Address address, int maxEntries, IEnumerable> Entries { get; private set; } + public ImmutableArray FlattenedEntries + => Entries.Values.SelectMany(e => e).ToImmutableArray(); + public IValue Bencoded => new List( Entries.Select( @@ -63,11 +82,82 @@ public IValue Bencoded (Integer)sortedDict.Key, new List(sortedDict.Value.Select(e => e.Bencoded))))); - public void LockIn(FungibleAssetValue lockInFAV, long creationHeight, long expireHeight) - => AddEntry(new UnbondLockInEntry(lockInFAV, creationHeight, expireHeight)); + public FungibleAssetValue? Release(long height) + { + if (height <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(height), + height, + "The height must be greater than zero."); + } + + FungibleAssetValue? releaseFAV = null; + foreach (var (expireHeight, entries) in Entries) + { + if (expireHeight <= height) + { + FungibleAssetValue entriesFAV = entries + .Select(e => e.LockInFAV) + .Aggregate((accum, next) => accum + next); + releaseFAV = releaseFAV is null + ? entriesFAV + : releaseFAV + entriesFAV; + Entries = Entries.Remove(expireHeight); + } + else + { + break; + } + } + + return releaseFAV; + } + + [Obsolete("This method is not implemented yet.")] + public void Slash() + => throw new NotImplementedException(); + + public override bool Equals(object obj) + => obj is UnbondLockIn other && Equals(other); - public void Cancel(FungibleAssetValue cancellingFAV, long height) + public bool Equals(UnbondLockIn other) + => ReferenceEquals(this, other) + || (Address.Equals(other.Address) + && MaxEntries == other.MaxEntries + && FlattenedEntries.SequenceEqual(other.FlattenedEntries)); + + public override int GetHashCode() + => Address.GetHashCode(); + + internal void LockIn(FungibleAssetValue lockInFAV, long creationHeight, long expireHeight) { + if (expireHeight == creationHeight) + { + return; + } + + AddEntry(new UnbondLockInEntry(lockInFAV, creationHeight, expireHeight)); + } + + internal void Cancel(FungibleAssetValue cancellingFAV, long height) + { + if (cancellingFAV.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(cancellingFAV), + cancellingFAV, + "The cancelling FAV must be greater than zero."); + } + + if (height <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(height), + height, + "The height must be greater than zero."); + } + if (Cancellable(height) < cancellingFAV) { throw new InvalidOperationException("Cannot cancel more than locked-in FAV."); @@ -111,30 +201,6 @@ public void Cancel(FungibleAssetValue cancellingFAV, long height) } } - public FungibleAssetValue? Release(long height) - { - FungibleAssetValue? releaseFAV = null; - foreach (var (expireHeight, entries) in Entries) - { - if (expireHeight <= height) - { - FungibleAssetValue entriesFAV = entries - .Select(e => e.LockInFAV) - .Aggregate((accum, next) => accum + next); - releaseFAV = releaseFAV is null - ? entriesFAV - : releaseFAV + entriesFAV; - Entries = Entries.Remove(expireHeight); - } - else - { - break; - } - } - - return releaseFAV; - } - private FungibleAssetValue Cancellable(long height) => - Entries .Where(kv => kv.Key > height) @@ -159,7 +225,7 @@ private void AddEntry(UnbondLockInEntry entry) } } - public class UnbondLockInEntry : IBencodable + public class UnbondLockInEntry : IBencodable, IEquatable { public UnbondLockInEntry( FungibleAssetValue lockInFAV, @@ -189,6 +255,46 @@ private UnbondLockInEntry( long creationHeight, long expireHeight) { + if (initialLockInFAV.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(initialLockInFAV), + initialLockInFAV, + "The initial lock-in FAV must be greater than zero."); + } + + if (lockInFAV.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(lockInFAV), + lockInFAV, + "The lock-in FAV must be greater than zero."); + } + + if (lockInFAV >= initialLockInFAV) + { + throw new ArgumentOutOfRangeException( + nameof(lockInFAV), + lockInFAV, + "The lock-in FAV must be less than the initial lock-in FAV."); + } + + if (creationHeight < 0) + { + throw new ArgumentOutOfRangeException( + nameof(creationHeight), + creationHeight, + "The creation height must be greater than or equal to zero."); + } + + if (expireHeight <= creationHeight) + { + throw new ArgumentOutOfRangeException( + nameof(expireHeight), + expireHeight, + "The expire height must be greater than the creation height."); + } + InitialLockInFAV = initialLockInFAV; LockInFAV = lockInFAV; CreationHeight = creationHeight; @@ -209,8 +315,23 @@ private UnbondLockInEntry( .Add(CreationHeight) .Add(ExpireHeight); - public void Cancel(FungibleAssetValue cancellingFAV) + public bool Equals(UnbondLockInEntry other) + => ReferenceEquals(this, other) + || (InitialLockInFAV.Equals(other.InitialLockInFAV) + && LockInFAV.Equals(other.LockInFAV) + && CreationHeight == other.CreationHeight + && ExpireHeight == other.ExpireHeight); + + internal void Cancel(FungibleAssetValue cancellingFAV) { + if (cancellingFAV.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(cancellingFAV), + cancellingFAV, + "The cancelling FAV must be greater than zero."); + } + if (LockInFAV <= cancellingFAV) { throw new InvalidOperationException("Cannot cancel more than locked-in FAV."); diff --git a/Lib9c/Delegation/UnbondingSet.cs b/Lib9c/Delegation/UnbondingSet.cs index b0c5db18dd..ed6f677e59 100644 --- a/Lib9c/Delegation/UnbondingSet.cs +++ b/Lib9c/Delegation/UnbondingSet.cs @@ -1,11 +1,12 @@ using System.Collections.Immutable; using System.Linq; +using Bencodex; using Bencodex.Types; using Libplanet.Crypto; namespace Nekoyume.Delegation { - public class UnbondingSet + public sealed class UnbondingSet : IBencodable { public UnbondingSet() { From 9e2e0549fea8e720f03e73441e5b3c95fbbb442c Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 2 Aug 2024 11:43:18 +0900 Subject: [PATCH 004/206] test: Update test from interface change --- .Lib9c.Tests/Delegation/DelegateeTest.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.Lib9c.Tests/Delegation/DelegateeTest.cs b/.Lib9c.Tests/Delegation/DelegateeTest.cs index ee1a923eec..255a8c88b4 100644 --- a/.Lib9c.Tests/Delegation/DelegateeTest.cs +++ b/.Lib9c.Tests/Delegation/DelegateeTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Delegation using System.Numerics; using Libplanet.Crypto; using Libplanet.Types.Assets; + using Nekoyume.Delegation; using Xunit; public class DelegateeTest @@ -112,7 +113,7 @@ public void Bond() [Fact] public void CannotBondInvalidDelegator() { - var testDelegatee = _fixture.TestDelegatee1; + IDelegatee testDelegatee = _fixture.TestDelegatee1; var testDelegator = _fixture.TestDelegator1; var dummyDelegator = _fixture.DummyDelegator1; var delegation = _fixture.Delegation1To1; @@ -214,8 +215,10 @@ public void Unbond() [Fact] public void CannotUnbondInvalidDelegator() { + IDelegatee delegatee = _fixture.TestDelegatee1; Assert.Throws( - () => _fixture.TestDelegatee1.Unbond(_fixture.DummyDelegator1, BigInteger.One, _fixture.Delegation1To1)); + () => delegatee.Unbond( + _fixture.DummyDelegator1, BigInteger.One, _fixture.Delegation1To1)); } [Fact] From cf41e50cf74109efedc128ab5ec449430a2b0ed1 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 2 Aug 2024 23:33:11 +0900 Subject: [PATCH 005/206] feat: Make Delegation immutable --- Lib9c/Delegation/Bond.cs | 17 ++-- Lib9c/Delegation/Delegatee.cs | 12 ++- Lib9c/Delegation/Delegation.cs | 91 ++++++++++------- Lib9c/Delegation/Delegator.cs | 28 +++--- Lib9c/Delegation/IDelegatee.cs | 4 +- Lib9c/Delegation/RebondGrace.cs | 108 +++++++++++---------- Lib9c/Delegation/UnbondLockIn.cs | 162 ++++++++++++++++++------------- Lib9c/Delegation/UnbondingSet.cs | 36 +++---- 8 files changed, 261 insertions(+), 197 deletions(-) diff --git a/Lib9c/Delegation/Bond.cs b/Lib9c/Delegation/Bond.cs index 1af48cb91b..189f835bb4 100644 --- a/Lib9c/Delegation/Bond.cs +++ b/Lib9c/Delegation/Bond.cs @@ -46,15 +46,14 @@ private Bond(Address address, BigInteger share, long lastDistributeHeight) public Address Address { get; } - public BigInteger Share { get; private set; } + public BigInteger Share { get; } - public long LastDistributeHeight { get; private set; } + public long LastDistributeHeight { get; } public IValue Bencoded => List.Empty .Add(Share) .Add(LastDistributeHeight); - public override bool Equals(object obj) => obj is Bond other && Equals(other); @@ -67,7 +66,7 @@ public bool Equals(Bond other) public override int GetHashCode() => Address.GetHashCode(); - internal void AddShare(BigInteger share) + internal Bond AddShare(BigInteger share) { if (share.Sign <= 0) { @@ -75,10 +74,10 @@ internal void AddShare(BigInteger share) nameof(share), share, "share must be positive."); } - Share += share; + return new Bond(Address, Share + share, LastDistributeHeight); } - internal void SubtractShare(BigInteger share) + internal Bond SubtractShare(BigInteger share) { if (share > Share) { @@ -86,10 +85,10 @@ internal void SubtractShare(BigInteger share) nameof(share), share, "share must be less than or equal to the current share."); } - Share -= share; + return new Bond(Address, Share - share, LastDistributeHeight); } - internal void UpdateLastDistributeHeight(long height) + internal Bond UpdateLastDistributeHeight(long height) { if (height <= LastDistributeHeight) { @@ -99,7 +98,7 @@ internal void UpdateLastDistributeHeight(long height) "height must be greater than the last distribute height."); } - LastDistributeHeight = height; + return new Bond(Address, Share, height); } } } diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index e0543e9b62..4d24406d16 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -108,13 +108,13 @@ public FungibleAssetValue FAVToUnbond(BigInteger share) ? TotalDelegated : (TotalDelegated * share).DivRem(TotalShares, out _); - void IDelegatee.Bond(IDelegator delegator, FungibleAssetValue fav, Delegation delegation) + Delegation IDelegatee.Bond(IDelegator delegator, FungibleAssetValue fav, Delegation delegation) => Bond((T)delegator, fav, delegation); - void IDelegatee.Unbond(IDelegator delegator, BigInteger share, Delegation delegation) + Delegation IDelegatee.Unbond(IDelegator delegator, BigInteger share, Delegation delegation) => Unbond((T)delegator, share, delegation); - public void Bond(T delegator, FungibleAssetValue fav, Delegation delegation) + public Delegation Bond(T delegator, FungibleAssetValue fav, Delegation delegation) { if (!fav.Currency.Equals(Currency)) { @@ -127,9 +127,11 @@ public void Bond(T delegator, FungibleAssetValue fav, Delegation delegation) TotalShares += share; TotalDelegated += fav; Distribute(); + + return delegation; } - public void Unbond(T delegator, BigInteger share, Delegation delegation) + public Delegation Unbond(T delegator, BigInteger share, Delegation delegation) { if (TotalShares.IsZero || TotalDelegated.RawValue.IsZero) { @@ -146,6 +148,8 @@ public void Unbond(T delegator, BigInteger share, Delegation delegation) TotalShares -= share; TotalDelegated -= fav; Distribute(); + + return delegation; } public void Distribute() diff --git a/Lib9c/Delegation/Delegation.cs b/Lib9c/Delegation/Delegation.cs index 3ba9c10107..01c01a94ce 100644 --- a/Lib9c/Delegation/Delegation.cs +++ b/Lib9c/Delegation/Delegation.cs @@ -7,16 +7,20 @@ namespace Nekoyume.Delegation { public sealed class Delegation { + private FungibleAssetValue? _netBondedFAV; + public Delegation( - Bond bond = null, - UnbondLockIn unbondLockIn = null, - RebondGrace rebondGrace = null, - UnbondingSet unbondingSet = null) + Bond bond, + UnbondLockIn unbondLockIn, + RebondGrace rebondGrace, + UnbondingSet unbondingSet, + FungibleAssetValue? netBondedFAV = null) { Bond = bond; UnbondLockIn = unbondLockIn; RebondGrace = rebondGrace; UnbondingSet = unbondingSet; + _netBondedFAV = netBondedFAV; } public Bond Bond { get; } @@ -27,11 +31,7 @@ public Delegation( public UnbondingSet UnbondingSet { get; } - public FungibleAssetValue? IncompleteBond { get; private set; } - - public FungibleAssetValue? IncompleteUnbond { get; private set; } - - public void AddBond(FungibleAssetValue fav, BigInteger share) + public Delegation AddBond(FungibleAssetValue fav, BigInteger share) { if (fav.Sign <= 0) { @@ -45,11 +45,15 @@ public void AddBond(FungibleAssetValue fav, BigInteger share) nameof(share), share, "Share must be positive."); } - Bond.AddShare(share); - IncompleteBond = fav; + return new Delegation( + Bond.AddShare(share), + UnbondLockIn, + RebondGrace, + UnbondingSet, + _netBondedFAV is null ? fav : _netBondedFAV + fav); } - public void CancelBond(FungibleAssetValue fav, BigInteger share) + public Delegation CancelBond(FungibleAssetValue fav, BigInteger share) { if (fav.Sign <= 0) { @@ -63,11 +67,15 @@ public void CancelBond(FungibleAssetValue fav, BigInteger share) nameof(share), share, "Share must be positive."); } - Bond.SubtractShare(share); - IncompleteUnbond = fav; + return new Delegation( + Bond.SubtractShare(share), + UnbondLockIn, + RebondGrace, + UnbondingSet, + _netBondedFAV is null ? fav : _netBondedFAV - fav); } - public void DoUnbondLockIn(FungibleAssetValue fav, long height, long expireHeight) + public Delegation DoUnbondLockIn(FungibleAssetValue fav, long height, long expireHeight) { if (fav.Sign <= 0) { @@ -89,14 +97,15 @@ public void DoUnbondLockIn(FungibleAssetValue fav, long height, long expireHeigh "Expire height must be greater than or equal to the height."); } - UnbondLockIn.LockIn(fav, height, expireHeight); - if (!UnbondingSet.UnbondLockIns.Contains(UnbondLockIn.Address)) - { - UnbondingSet.AddUnbondLockIn(UnbondLockIn.Address); - } + return new Delegation( + Bond, + UnbondLockIn.LockIn(fav, height, expireHeight), + RebondGrace, + UnbondingSet.AddUnbondLockIn(UnbondLockIn.Address), + _netBondedFAV); } - public void CancelUnbondLockIn(FungibleAssetValue fav, long height) + public Delegation CancelUnbondLockIn(FungibleAssetValue fav, long height) { if (fav.Sign <= 0) { @@ -110,14 +119,23 @@ public void CancelUnbondLockIn(FungibleAssetValue fav, long height) nameof(height), height, "Height must be greater than or equal to zero."); } - UnbondLockIn.Cancel(fav, height); - if (UnbondLockIn.IsEmpty) + var updatedUnbondLockIn = UnbondLockIn.Cancel(fav, height); + var updatedUnbondingSet = UnbondingSet; + + if (updatedUnbondLockIn.IsEmpty) { - UnbondingSet.RemoveUnbondLockIn(UnbondLockIn.Address); + updatedUnbondingSet = UnbondingSet.RemoveUnbondLockIn(UnbondLockIn.Address); } + + return new Delegation( + Bond, + updatedUnbondLockIn, + RebondGrace, + updatedUnbondingSet, + _netBondedFAV); } - public void DoRebondGrace(Address rebondeeAddress, FungibleAssetValue fav, long height, long expireHeight) + public Delegation DoRebondGrace(Address rebondeeAddress, FungibleAssetValue fav, long height, long expireHeight) { if (fav.Sign <= 0) { @@ -131,12 +149,6 @@ public void DoRebondGrace(Address rebondeeAddress, FungibleAssetValue fav, long nameof(height), height, "Height must be greater than or equal to zero."); } - RebondGrace.Grace(rebondeeAddress, fav, height, expireHeight); - if (!UnbondingSet.RebondGraces.Contains(RebondGrace.Address)) - { - UnbondingSet.AddRebondGrace(RebondGrace.Address); - } - if (expireHeight < height) { throw new ArgumentOutOfRangeException( @@ -144,12 +156,23 @@ public void DoRebondGrace(Address rebondeeAddress, FungibleAssetValue fav, long expireHeight, "Expire height must be greater than or equal to the height."); } + + return new Delegation( + Bond, + UnbondLockIn, + RebondGrace.Grace(rebondeeAddress, fav, height, expireHeight), + UnbondingSet.AddRebondGrace(RebondGrace.Address), + _netBondedFAV); } - public void Complete() + public FungibleAssetValue? FlushReleasedFAV() + => UnbondLockIn.FlushReleasedFAV(); + + public FungibleAssetValue? FlushNetBondedFAV() { - IncompleteBond = null; - IncompleteUnbond = null; + var netBondedFAV = _netBondedFAV; + _netBondedFAV = null; + return netBondedFAV; } } } diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index c746f647f8..2ab39b644d 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -91,19 +91,22 @@ public void Undelegate( delegatee.Unbond((TSelf)this, share, delegation); - if (!(delegation.IncompleteUnbond is FungibleAssetValue unbondToLockIn)) + if (!(delegation.FlushNetBondedFAV() is FungibleAssetValue netBondedFAV)) { - throw new NullReferenceException("Bonding FAV is null."); + throw new NullReferenceException("Net bonded FAV is null."); } - delegation.DoUnbondLockIn(unbondToLockIn, height, height + delegatee.UnbondingPeriod); + if (netBondedFAV.Sign >= 0) + { + throw new InvalidOperationException("Net bonded FAV must be negative."); + } + + delegation.DoUnbondLockIn(-netBondedFAV, height, height + delegatee.UnbondingPeriod); if (delegation.Bond.Share.IsZero) { Delegatees = Delegatees.Remove(delegatee.Address); } - - delegation.Complete(); } public void Redelegate( @@ -128,14 +131,19 @@ public void Redelegate( srcDelegatee.Unbond((TSelf)this, share, srcDelegation); - if (!(srcDelegation.IncompleteUnbond is FungibleAssetValue unbondToGrace)) + if (!(srcDelegation.FlushNetBondedFAV() is FungibleAssetValue netBondedFAV)) { - throw new NullReferenceException("Bonding FAV is null."); + throw new NullReferenceException("Net bonded FAV is null."); } - dstDelegatee.Bond((TSelf)this, unbondToGrace, dstDelegation); + if (netBondedFAV.Sign >= 0) + { + throw new InvalidOperationException("Net bonded FAV must be negative."); + } - srcDelegation.DoRebondGrace(dstDelegatee.Address, unbondToGrace, height, height + srcDelegatee.UnbondingPeriod); + dstDelegatee.Bond((TSelf)this, -netBondedFAV, dstDelegation); + + srcDelegation.DoRebondGrace(dstDelegatee.Address, -netBondedFAV, height, height + srcDelegatee.UnbondingPeriod); if (srcDelegation.Bond.Share.IsZero) { @@ -143,8 +151,6 @@ public void Redelegate( } Delegatees = Delegatees.Add(dstDelegatee.Address); - - srcDelegation.Complete(); } public void Claim(IDelegatee delegatee) diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index bf040bf8b9..c59c08e2c3 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -25,9 +25,9 @@ public interface IDelegatee : IBencodable, IEquatable BigInteger TotalShares { get; } - void Bond(IDelegator delegator, FungibleAssetValue fav, Delegation delegation); + Delegation Bond(IDelegator delegator, FungibleAssetValue fav, Delegation delegation); - void Unbond(IDelegator delegator, BigInteger share, Delegation delegation); + Delegation Unbond(IDelegator delegator, BigInteger share, Delegation delegation); void Distribute(); diff --git a/Lib9c/Delegation/RebondGrace.cs b/Lib9c/Delegation/RebondGrace.cs index 4f9c45e8f0..f885eff71b 100644 --- a/Lib9c/Delegation/RebondGrace.cs +++ b/Lib9c/Delegation/RebondGrace.cs @@ -12,18 +12,11 @@ namespace Nekoyume.Delegation public sealed class RebondGrace : IBencodable, IEquatable { public RebondGrace(Address address, int maxEntries) + : this( + address, + maxEntries, + ImmutableSortedDictionary>.Empty) { - if (maxEntries < 0) - { - throw new ArgumentOutOfRangeException( - nameof(maxEntries), - maxEntries, - "The max entries must be greater than or equal to zero."); - } - - Address = address; - MaxEntries = maxEntries; - Entries = ImmutableSortedDictionary>.Empty; } public RebondGrace(Address address, int maxEntries, IValue bencoded) @@ -32,25 +25,17 @@ public RebondGrace(Address address, int maxEntries, IValue bencoded) } public RebondGrace(Address address, int maxEntries, List bencoded) + : this( + address, + maxEntries, + bencoded.Select(kv => kv is List list + ? new KeyValuePair>( + (Integer)list[0], + ((List)list[1]).Select(e => new RebondGraceEntry(e)).ToImmutableList()) + : throw new InvalidCastException( + $"Unable to cast object of type '{kv.GetType()}' to type '{typeof(List)}'.")) + .ToImmutableSortedDictionary()) { - if (maxEntries < 0) - { - throw new ArgumentOutOfRangeException( - nameof(maxEntries), - maxEntries, - "The max entries must be greater than or equal to zero."); - } - - Address = address; - MaxEntries = maxEntries; - Entries = bencoded - .Select(kv => kv is List list - ? new KeyValuePair>( - (Integer)list[0], - ((List)list[1]).Select(e => new RebondGraceEntry(e)).ToImmutableList()) - : throw new InvalidCastException( - $"Unable to cast object of type '{kv.GetType()}' to type '{typeof(List)}'.")) - .ToImmutableSortedDictionary(); } public RebondGrace(Address address, int maxEntries, IEnumerable entries) @@ -62,6 +47,24 @@ public RebondGrace(Address address, int maxEntries, IEnumerable> entries) + { + if (maxEntries < 0) + { + throw new ArgumentOutOfRangeException( + nameof(maxEntries), + maxEntries, + "The max entries must be greater than or equal to zero."); + } + + Address = address; + MaxEntries = maxEntries; + Entries = entries; + } + public Address Address { get; } public int MaxEntries { get; } @@ -70,7 +73,7 @@ public RebondGrace(Address address, int maxEntries, IEnumerable Entries.IsEmpty; - public ImmutableSortedDictionary> Entries { get; private set; } + public ImmutableSortedDictionary> Entries { get; } public ImmutableArray FlattenedEntries => Entries.Values.SelectMany(e => e).ToImmutableArray(); @@ -82,7 +85,7 @@ public IValue Bencoded (Integer)sortedDict.Key, new List(sortedDict.Value.Select(e => e.Bencoded))))); - public void Release(long height) + public RebondGrace Release(long height) { if (height <= 0) { @@ -92,21 +95,24 @@ public void Release(long height) "The height must be greater than zero."); } - foreach (var (expireHeight, entries) in Entries) + var updatedEntries = Entries; + foreach (var (expireHeight, entries) in updatedEntries) { if (expireHeight <= height) { - Entries = Entries.Remove(expireHeight); + updatedEntries = updatedEntries.Remove(expireHeight); } else { break; } } + + return UpdateEntries(updatedEntries); } [Obsolete("This method is not implemented yet.")] - public void Slash() + public RebondGrace Slash() => throw new NotImplementedException(); public override bool Equals(object obj) @@ -121,18 +127,18 @@ public bool Equals(RebondGrace other) public override int GetHashCode() => Address.GetHashCode(); - internal void Grace( + internal RebondGrace Grace( Address rebondeeAddress, FungibleAssetValue initialGraceFAV, long creationHeight, long expireHeight) { if (expireHeight == creationHeight) { - return; + return this; } - AddEntry(new RebondGraceEntry(rebondeeAddress, initialGraceFAV, creationHeight, expireHeight)); + return AddEntry(new RebondGraceEntry(rebondeeAddress, initialGraceFAV, creationHeight, expireHeight)); } - private void AddEntry(RebondGraceEntry entry) + private RebondGrace AddEntry(RebondGraceEntry entry) { if (IsFull) { @@ -141,27 +147,27 @@ private void AddEntry(RebondGraceEntry entry) if (Entries.TryGetValue(entry.ExpireHeight, out var entries)) { - Entries = Entries.SetItem(entry.ExpireHeight, entries.Add(entry)); + return UpdateEntries(Entries.SetItem(entry.ExpireHeight, entries.Add(entry))); } else { - Entries = Entries.Add(entry.ExpireHeight, ImmutableList.Empty.Add(entry)); + return UpdateEntries(Entries.Add(entry.ExpireHeight, ImmutableList.Empty.Add(entry))); } } + private RebondGrace UpdateEntries( + ImmutableSortedDictionary> entries) + => new RebondGrace(Address, MaxEntries, entries); + public class RebondGraceEntry : IBencodable, IEquatable { public RebondGraceEntry( Address rebondeeAddress, - FungibleAssetValue initialGraceFAV, + FungibleAssetValue graceFAV, long creationHeight, long expireHeight) + : this(rebondeeAddress, graceFAV, graceFAV, creationHeight, expireHeight) { - RebondeeAddress = rebondeeAddress; - InitialGraceFAV = initialGraceFAV; - GraceFAV = initialGraceFAV; - CreationHeight = creationHeight; - ExpireHeight = expireHeight; } public RebondGraceEntry(IValue bencoded) @@ -202,12 +208,12 @@ private RebondGraceEntry( "The grace FAV must be greater than zero."); } - if (graceFAV >= initialGraceFAV) + if (graceFAV > initialGraceFAV) { throw new ArgumentOutOfRangeException( nameof(graceFAV), graceFAV, - "The grace FAV must be less than the initial grace FAV."); + "The grace FAV must be less than or equal to the initial grace FAV."); } if (creationHeight < 0) @@ -237,7 +243,7 @@ private RebondGraceEntry( public FungibleAssetValue InitialGraceFAV { get; } - public FungibleAssetValue GraceFAV { get; private set; } + public FungibleAssetValue GraceFAV { get; } public long CreationHeight { get; } @@ -257,6 +263,10 @@ public bool Equals(RebondGraceEntry other) && GraceFAV.Equals(other.GraceFAV) && CreationHeight == other.CreationHeight && ExpireHeight == other.ExpireHeight); + + [Obsolete("This method is not implemented yet.")] + public RebondGraceEntry Slash() + => throw new NotImplementedException(); } } } diff --git a/Lib9c/Delegation/UnbondLockIn.cs b/Lib9c/Delegation/UnbondLockIn.cs index 312a8e6e13..3e34fe9766 100644 --- a/Lib9c/Delegation/UnbondLockIn.cs +++ b/Lib9c/Delegation/UnbondLockIn.cs @@ -11,19 +11,14 @@ namespace Nekoyume.Delegation { public sealed class UnbondLockIn : IBencodable, IEquatable { + private FungibleAssetValue? _releasedFAV; + public UnbondLockIn(Address address, int maxEntries) + : this( + address, + maxEntries, + ImmutableSortedDictionary>.Empty) { - if (maxEntries < 0) - { - throw new ArgumentOutOfRangeException( - nameof(maxEntries), - maxEntries, - "The max entries must be greater than or equal to zero."); - } - - Address = address; - MaxEntries = maxEntries; - Entries = ImmutableSortedDictionary>.Empty; } public UnbondLockIn(Address address, int maxEntries, IValue bencoded) @@ -32,6 +27,33 @@ public UnbondLockIn(Address address, int maxEntries, IValue bencoded) } public UnbondLockIn(Address address, int maxEntries, List bencoded) + : this( + address, + maxEntries, + bencoded.Select(kv => kv is List list + ? new KeyValuePair>( + (Integer)list[0], + ((List)list[1]).Select(e => new UnbondLockInEntry(e)).ToImmutableList()) + : throw new InvalidCastException( + $"Unable to cast object of type '{kv.GetType()}' to type '{typeof(List)}'.")) + .ToImmutableSortedDictionary()) + { + } + + public UnbondLockIn(Address address, int maxEntries, IEnumerable entries) + : this(address, maxEntries) + { + foreach (var entry in entries) + { + AddEntry(entry); + } + } + + private UnbondLockIn( + Address address, + int maxEntries, + ImmutableSortedDictionary> entries, + FungibleAssetValue? releasedFAV = null) { if (maxEntries < 0) { @@ -43,23 +65,8 @@ public UnbondLockIn(Address address, int maxEntries, List bencoded) Address = address; MaxEntries = maxEntries; - Entries = bencoded - .Select(kv => kv is List list - ? new KeyValuePair>( - (Integer)list[0], - ((List)list[1]).Select(e => new UnbondLockInEntry(e)).ToImmutableList()) - : throw new InvalidCastException( - $"Unable to cast object of type '{kv.GetType()}' to type '{typeof(List)}'.")) - .ToImmutableSortedDictionary(); - } - - public UnbondLockIn(Address address, int maxEntries, IEnumerable entries) - : this(address, maxEntries) - { - foreach (var entry in entries) - { - AddEntry(entry); - } + Entries = entries; + _releasedFAV = releasedFAV; } public Address Address { get; } @@ -70,7 +77,7 @@ public UnbondLockIn(Address address, int maxEntries, IEnumerable Entries.IsEmpty; - public ImmutableSortedDictionary> Entries { get; private set; } + public ImmutableSortedDictionary> Entries { get; } public ImmutableArray FlattenedEntries => Entries.Values.SelectMany(e => e).ToImmutableArray(); @@ -82,7 +89,7 @@ public IValue Bencoded (Integer)sortedDict.Key, new List(sortedDict.Value.Select(e => e.Bencoded))))); - public FungibleAssetValue? Release(long height) + public UnbondLockIn Release(long height) { if (height <= 0) { @@ -92,18 +99,19 @@ public IValue Bencoded "The height must be greater than zero."); } - FungibleAssetValue? releaseFAV = null; - foreach (var (expireHeight, entries) in Entries) + var updatedEntries = Entries; + FungibleAssetValue? releasedFAV = null; + foreach (var (expireHeight, entries) in updatedEntries) { if (expireHeight <= height) { FungibleAssetValue entriesFAV = entries .Select(e => e.LockInFAV) .Aggregate((accum, next) => accum + next); - releaseFAV = releaseFAV is null + releasedFAV = _releasedFAV is null ? entriesFAV - : releaseFAV + entriesFAV; - Entries = Entries.Remove(expireHeight); + : _releasedFAV + entriesFAV; + updatedEntries = updatedEntries.Remove(expireHeight); } else { @@ -111,13 +119,20 @@ public IValue Bencoded } } - return releaseFAV; + return UpdateEntries(updatedEntries, releasedFAV); } [Obsolete("This method is not implemented yet.")] - public void Slash() + public UnbondLockIn Slash() => throw new NotImplementedException(); + public FungibleAssetValue? FlushReleasedFAV() + { + var releasedFAV = _releasedFAV; + _releasedFAV = null; + return releasedFAV; + } + public override bool Equals(object obj) => obj is UnbondLockIn other && Equals(other); @@ -130,17 +145,17 @@ public bool Equals(UnbondLockIn other) public override int GetHashCode() => Address.GetHashCode(); - internal void LockIn(FungibleAssetValue lockInFAV, long creationHeight, long expireHeight) + internal UnbondLockIn LockIn(FungibleAssetValue lockInFAV, long creationHeight, long expireHeight) { if (expireHeight == creationHeight) { - return; + return this; } - AddEntry(new UnbondLockInEntry(lockInFAV, creationHeight, expireHeight)); + return AddEntry(new UnbondLockInEntry(lockInFAV, creationHeight, expireHeight)); } - internal void Cancel(FungibleAssetValue cancellingFAV, long height) + internal UnbondLockIn Cancel(FungibleAssetValue cancellingFAV, long height) { if (cancellingFAV.Sign <= 0) { @@ -163,7 +178,8 @@ internal void Cancel(FungibleAssetValue cancellingFAV, long height) throw new InvalidOperationException("Cannot cancel more than locked-in FAV."); } - foreach (var (expireHeight, entries) in Entries.Reverse()) + var updatedEntries = Entries; + foreach (var (expireHeight, entries) in updatedEntries.Reverse()) { if (expireHeight <= height) { @@ -172,43 +188,50 @@ internal void Cancel(FungibleAssetValue cancellingFAV, long height) foreach (var entry in entries.Select((value, index) => (value, index)).Reverse()) { - if (cancellingFAV.RawValue < 0) + if (cancellingFAV.Sign == 0) { - throw new InvalidOperationException("Insufficient undelegation to cancel"); + break; } - if (entry.value.LockInFAV < cancellingFAV) + if (entry.value.LockInFAV <= cancellingFAV) { - cancellingFAV -= entry.value.LockInFAV; - Entries = Entries.SetItem( + cancellingFAV -= entry.value.LockInFAV; ; + updatedEntries = updatedEntries.SetItem( expireHeight, - Entries[expireHeight].RemoveAt(entry.index)); + updatedEntries[expireHeight].RemoveAt(entry.index)); } else { - entry.value.Cancel(cancellingFAV); - Entries = Entries.SetItem( + var cancelledEntry = entry.value.Cancel(cancellingFAV); + cancellingFAV -= entry.value.LockInFAV; ; + updatedEntries = updatedEntries.SetItem( expireHeight, - Entries[expireHeight].SetItem(entry.index, entry.value)); - break; + updatedEntries[expireHeight].SetItem(entry.index, cancelledEntry)); } } - if (Entries[expireHeight].IsEmpty) + if (updatedEntries[expireHeight].IsEmpty) { - Entries = Entries.Remove(expireHeight); + updatedEntries = updatedEntries.Remove(expireHeight); } } + + return UpdateEntries(updatedEntries); } - private FungibleAssetValue Cancellable(long height) - => - Entries + internal FungibleAssetValue Cancellable(long height) + => Entries .Where(kv => kv.Key > height) .SelectMany(kv => kv.Value) .Select(e => e.LockInFAV) .Aggregate((accum, next) => accum + next); - private void AddEntry(UnbondLockInEntry entry) + private UnbondLockIn UpdateEntries( + ImmutableSortedDictionary> entries, + FungibleAssetValue? releasedFAV=null) + => new UnbondLockIn(Address, MaxEntries, entries, releasedFAV); + + private UnbondLockIn AddEntry(UnbondLockInEntry entry) { if (IsFull) { @@ -217,11 +240,11 @@ private void AddEntry(UnbondLockInEntry entry) if (Entries.TryGetValue(entry.ExpireHeight, out var entries)) { - Entries = Entries.SetItem(entry.ExpireHeight, entries.Add(entry)); + return UpdateEntries(Entries.SetItem(entry.ExpireHeight, entries.Add(entry))); } else { - Entries = Entries.Add(entry.ExpireHeight, ImmutableList.Empty.Add(entry)); + return UpdateEntries(Entries.Add(entry.ExpireHeight, ImmutableList.Empty.Add(entry))); } } @@ -271,12 +294,12 @@ private UnbondLockInEntry( "The lock-in FAV must be greater than zero."); } - if (lockInFAV >= initialLockInFAV) + if (lockInFAV > initialLockInFAV) { throw new ArgumentOutOfRangeException( nameof(lockInFAV), lockInFAV, - "The lock-in FAV must be less than the initial lock-in FAV."); + "The lock-in FAV must be less than or equal to the initial lock-in FAV."); } if (creationHeight < 0) @@ -301,9 +324,9 @@ private UnbondLockInEntry( ExpireHeight = expireHeight; } - public FungibleAssetValue InitialLockInFAV { get; private set; } + public FungibleAssetValue InitialLockInFAV { get; } - public FungibleAssetValue LockInFAV { get; private set; } + public FungibleAssetValue LockInFAV { get; } public long CreationHeight { get; } @@ -322,7 +345,11 @@ public bool Equals(UnbondLockInEntry other) && CreationHeight == other.CreationHeight && ExpireHeight == other.ExpireHeight); - internal void Cancel(FungibleAssetValue cancellingFAV) + [Obsolete("This method is not implemented yet.")] + public UnbondLockInEntry Slash() + => throw new NotImplementedException(); + + internal UnbondLockInEntry Cancel(FungibleAssetValue cancellingFAV) { if (cancellingFAV.Sign <= 0) { @@ -337,8 +364,11 @@ internal void Cancel(FungibleAssetValue cancellingFAV) throw new InvalidOperationException("Cannot cancel more than locked-in FAV."); } - InitialLockInFAV -= cancellingFAV; - LockInFAV -= cancellingFAV; + return new UnbondLockInEntry( + InitialLockInFAV - cancellingFAV, + LockInFAV - cancellingFAV, + CreationHeight, + ExpireHeight); } } } diff --git a/Lib9c/Delegation/UnbondingSet.cs b/Lib9c/Delegation/UnbondingSet.cs index ed6f677e59..522dbf8f14 100644 --- a/Lib9c/Delegation/UnbondingSet.cs +++ b/Lib9c/Delegation/UnbondingSet.cs @@ -33,33 +33,25 @@ private UnbondingSet(ImmutableSortedSet
unbondLockIns, ImmutableSortedS } - public ImmutableSortedSet
UnbondLockIns { get; private set; } + public ImmutableSortedSet
UnbondLockIns { get; } - public ImmutableSortedSet
RebondGraces { get; private set; } - - public void AddUnbondLockIn(Address address) - { - UnbondLockIns = UnbondLockIns.Add(address); - } - - public void RemoveUnbondLockIn(Address address) - { - UnbondLockIns = UnbondLockIns.Remove(address); - } - - public void AddRebondGrace(Address address) - { - RebondGraces = RebondGraces.Add(address); - } - - public void RemoveRebondGrace(Address address) - { - RebondGraces = RebondGraces.Remove(address); - } + public ImmutableSortedSet
RebondGraces { get; } public IValue Bencoded => List.Empty .Add(new List(UnbondLockIns.Select(a => a.Bencoded))) .Add(new List(RebondGraces.Select(a => a.Bencoded))); + + public UnbondingSet AddUnbondLockIn(Address address) + => new UnbondingSet(UnbondLockIns.Add(address), RebondGraces); + + public UnbondingSet RemoveUnbondLockIn(Address address) + => new UnbondingSet(UnbondLockIns.Remove(address), RebondGraces); + + public UnbondingSet AddRebondGrace(Address address) + => new UnbondingSet(UnbondLockIns, RebondGraces.Add(address)); + + public UnbondingSet RemoveRebondGrace(Address address) + => new UnbondingSet(UnbondLockIns, RebondGraces.Remove(address)); } } From 44ca3cbe3e279f1e91f25f33ac3bd8572ba62664 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 2 Aug 2024 23:33:36 +0900 Subject: [PATCH 006/206] feat: Add result types for delegations --- Lib9c/Delegation/DelegateResult.cs | 23 ++++++++++++++++++++++ Lib9c/Delegation/RedelegateResult.cs | 29 ++++++++++++++++++++++++++++ Lib9c/Delegation/UndelegateResult.cs | 21 ++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 Lib9c/Delegation/DelegateResult.cs create mode 100644 Lib9c/Delegation/RedelegateResult.cs create mode 100644 Lib9c/Delegation/UndelegateResult.cs diff --git a/Lib9c/Delegation/DelegateResult.cs b/Lib9c/Delegation/DelegateResult.cs new file mode 100644 index 0000000000..00667832a8 --- /dev/null +++ b/Lib9c/Delegation/DelegateResult.cs @@ -0,0 +1,23 @@ +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public class DelegateResult + { + public DelegateResult( + IDelegatee delegatee, + Bond bond, + FungibleAssetValue delegatedFAV) + { + Delegatee = delegatee; + Bond = bond; + DelegatedFAV = delegatedFAV; + } + + IDelegatee Delegatee { get; } + + Bond Bond { get; } + + FungibleAssetValue DelegatedFAV { get; } + } +} diff --git a/Lib9c/Delegation/RedelegateResult.cs b/Lib9c/Delegation/RedelegateResult.cs new file mode 100644 index 0000000000..ff505b3658 --- /dev/null +++ b/Lib9c/Delegation/RedelegateResult.cs @@ -0,0 +1,29 @@ +namespace Nekoyume.Delegation +{ + public class RedelegateResult + { + public RedelegateResult( + IDelegatee srcDelegatee, + IDelegatee dstDelegatee, + Bond srcBond, + Bond dstBond, + RebondGrace rebondGrace) + { + SrcDelegatee = srcDelegatee; + DstDelegatee = dstDelegatee; + SrcBond = srcBond; + DstBond = dstBond; + RebondGrace = rebondGrace; + } + + public IDelegatee SrcDelegatee { get; } + + public IDelegatee DstDelegatee { get; } + + public Bond SrcBond { get; } + + public Bond DstBond { get; } + + public RebondGrace RebondGrace { get; } + } +} diff --git a/Lib9c/Delegation/UndelegateResult.cs b/Lib9c/Delegation/UndelegateResult.cs new file mode 100644 index 0000000000..6be90686e0 --- /dev/null +++ b/Lib9c/Delegation/UndelegateResult.cs @@ -0,0 +1,21 @@ +namespace Nekoyume.Delegation +{ + public class UndelegateResult + { + public UndelegateResult( + IDelegatee delegatee, + Bond bond, + UnbondLockIn unbondLockIn) + { + Delegatee = delegatee; + Bond = bond; + UnbondLockIn = unbondLockIn; + } + + public IDelegatee Delegatee { get; } + + public Bond Bond { get; } + + public UnbondLockIn UnbondLockIn { get; } + } +} From bc561056edc2a73449de04e98d1d8005fee5de1a Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sat, 3 Aug 2024 20:42:55 +0900 Subject: [PATCH 007/206] feat: Update to use Result types instead of using Delegation --- Lib9c/Delegation/Bond.cs | 12 +- Lib9c/Delegation/BondResult.cs | 18 +++ Lib9c/Delegation/DelegateResult.cs | 13 +- Lib9c/Delegation/Delegatee.cs | 30 +++-- Lib9c/Delegation/Delegation.cs | 178 -------------------------- Lib9c/Delegation/Delegator.cs | 159 ++++++++++++++++------- Lib9c/Delegation/IDelegateResult.cs | 13 ++ Lib9c/Delegation/IDelegatee.cs | 4 +- Lib9c/Delegation/IDelegator.cs | 18 ++- Lib9c/Delegation/IRedelegateResult.cs | 17 +++ Lib9c/Delegation/IUndelegateResult.cs | 13 ++ Lib9c/Delegation/RebondGrace.cs | 13 +- Lib9c/Delegation/RedelegateResult.cs | 21 ++- Lib9c/Delegation/UnbondLockIn.cs | 13 +- Lib9c/Delegation/UnbondResult.cs | 18 +++ Lib9c/Delegation/UnbondingSet.cs | 3 +- Lib9c/Delegation/UndelegateResult.cs | 15 ++- 17 files changed, 288 insertions(+), 270 deletions(-) create mode 100644 Lib9c/Delegation/BondResult.cs delete mode 100644 Lib9c/Delegation/Delegation.cs create mode 100644 Lib9c/Delegation/IDelegateResult.cs create mode 100644 Lib9c/Delegation/IRedelegateResult.cs create mode 100644 Lib9c/Delegation/IUndelegateResult.cs create mode 100644 Lib9c/Delegation/UnbondResult.cs diff --git a/Lib9c/Delegation/Bond.cs b/Lib9c/Delegation/Bond.cs index 189f835bb4..c5f38f07f4 100644 --- a/Lib9c/Delegation/Bond.cs +++ b/Lib9c/Delegation/Bond.cs @@ -28,7 +28,9 @@ private Bond(Address address, BigInteger share, long lastDistributeHeight) if (share.Sign < 0) { throw new ArgumentOutOfRangeException( - nameof(share), share, "Share must be non-negative."); + nameof(share), + share, + "Share must be non-negative."); } if (lastDistributeHeight < 0) @@ -71,7 +73,9 @@ internal Bond AddShare(BigInteger share) if (share.Sign <= 0) { throw new ArgumentOutOfRangeException( - nameof(share), share, "share must be positive."); + nameof(share), + share, + "share must be positive."); } return new Bond(Address, Share + share, LastDistributeHeight); @@ -82,7 +86,9 @@ internal Bond SubtractShare(BigInteger share) if (share > Share) { throw new ArgumentOutOfRangeException( - nameof(share), share, "share must be less than or equal to the current share."); + nameof(share), + share, + "share must be less than or equal to the current share."); } return new Bond(Address, Share - share, LastDistributeHeight); diff --git a/Lib9c/Delegation/BondResult.cs b/Lib9c/Delegation/BondResult.cs new file mode 100644 index 0000000000..caac6c2205 --- /dev/null +++ b/Lib9c/Delegation/BondResult.cs @@ -0,0 +1,18 @@ +using System.Numerics; + +namespace Nekoyume.Delegation +{ + public class BondResult + { + public BondResult(Bond bond, BigInteger bondedShare) + { + Bond = bond; + + BondedShare = bondedShare; + } + + public Bond Bond { get; } + + public BigInteger BondedShare { get; } + } +} diff --git a/Lib9c/Delegation/DelegateResult.cs b/Lib9c/Delegation/DelegateResult.cs index 00667832a8..455e11993f 100644 --- a/Lib9c/Delegation/DelegateResult.cs +++ b/Lib9c/Delegation/DelegateResult.cs @@ -2,10 +2,11 @@ namespace Nekoyume.Delegation { - public class DelegateResult + public class DelegateResult : IDelegateResult + where T : IDelegatee { public DelegateResult( - IDelegatee delegatee, + T delegatee, Bond bond, FungibleAssetValue delegatedFAV) { @@ -14,10 +15,12 @@ public DelegateResult( DelegatedFAV = delegatedFAV; } - IDelegatee Delegatee { get; } + IDelegatee IDelegateResult.Delegatee => Delegatee; - Bond Bond { get; } + public T Delegatee { get; } - FungibleAssetValue DelegatedFAV { get; } + public Bond Bond { get; } + + public FungibleAssetValue DelegatedFAV { get; } } } diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 4d24406d16..e45a699119 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -108,39 +108,43 @@ public FungibleAssetValue FAVToUnbond(BigInteger share) ? TotalDelegated : (TotalDelegated * share).DivRem(TotalShares, out _); - Delegation IDelegatee.Bond(IDelegator delegator, FungibleAssetValue fav, Delegation delegation) - => Bond((T)delegator, fav, delegation); + BondResult IDelegatee.Bond( + IDelegator delegator, FungibleAssetValue fav, Bond bond) + => Bond((T)delegator, fav, bond); - Delegation IDelegatee.Unbond(IDelegator delegator, BigInteger share, Delegation delegation) - => Unbond((T)delegator, share, delegation); + UnbondResult IDelegatee.Unbond( + IDelegator delegator, BigInteger share, Bond bond) + => Unbond((T)delegator, share, bond); - public Delegation Bond(T delegator, FungibleAssetValue fav, Delegation delegation) + public BondResult Bond(T delegator, FungibleAssetValue fav, Bond bond) { if (!fav.Currency.Equals(Currency)) { - throw new InvalidOperationException("Cannot bond with invalid currency."); + throw new InvalidOperationException( + "Cannot bond with invalid currency."); } BigInteger share = ShareToBond(fav); - delegation.AddBond(fav, share); + bond = bond.AddShare(share); Delegators = Delegators.Add(delegator.Address); TotalShares += share; TotalDelegated += fav; Distribute(); - return delegation; + return new BondResult(bond, share); } - public Delegation Unbond(T delegator, BigInteger share, Delegation delegation) + public UnbondResult Unbond(T delegator, BigInteger share, Bond bond) { if (TotalShares.IsZero || TotalDelegated.RawValue.IsZero) { - throw new InvalidOperationException("Cannot unbond without bonding."); + throw new InvalidOperationException( + "Cannot unbond without bonding."); } FungibleAssetValue fav = FAVToUnbond(share); - delegation.CancelBond(fav, share); - if (delegation.Bond.Share.IsZero) + bond = bond.SubtractShare(share); + if (bond.Share.IsZero) { Delegators = Delegators.Remove(delegator.Address); } @@ -149,7 +153,7 @@ public Delegation Unbond(T delegator, BigInteger share, Delegation delegation) TotalDelegated -= fav; Distribute(); - return delegation; + return new UnbondResult(bond, fav); } public void Distribute() diff --git a/Lib9c/Delegation/Delegation.cs b/Lib9c/Delegation/Delegation.cs deleted file mode 100644 index 01c01a94ce..0000000000 --- a/Lib9c/Delegation/Delegation.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Numerics; -using Libplanet.Crypto; -using Libplanet.Types.Assets; - -namespace Nekoyume.Delegation -{ - public sealed class Delegation - { - private FungibleAssetValue? _netBondedFAV; - - public Delegation( - Bond bond, - UnbondLockIn unbondLockIn, - RebondGrace rebondGrace, - UnbondingSet unbondingSet, - FungibleAssetValue? netBondedFAV = null) - { - Bond = bond; - UnbondLockIn = unbondLockIn; - RebondGrace = rebondGrace; - UnbondingSet = unbondingSet; - _netBondedFAV = netBondedFAV; - } - - public Bond Bond { get; } - - public UnbondLockIn UnbondLockIn { get; } - - public RebondGrace RebondGrace { get; } - - public UnbondingSet UnbondingSet { get; } - - public Delegation AddBond(FungibleAssetValue fav, BigInteger share) - { - if (fav.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(fav), fav, "Fungible asset value must be positive."); - } - - if (share.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(share), share, "Share must be positive."); - } - - return new Delegation( - Bond.AddShare(share), - UnbondLockIn, - RebondGrace, - UnbondingSet, - _netBondedFAV is null ? fav : _netBondedFAV + fav); - } - - public Delegation CancelBond(FungibleAssetValue fav, BigInteger share) - { - if (fav.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(fav), fav, "Fungible asset value must be positive."); - } - - if (share.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(share), share, "Share must be positive."); - } - - return new Delegation( - Bond.SubtractShare(share), - UnbondLockIn, - RebondGrace, - UnbondingSet, - _netBondedFAV is null ? fav : _netBondedFAV - fav); - } - - public Delegation DoUnbondLockIn(FungibleAssetValue fav, long height, long expireHeight) - { - if (fav.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(fav), fav, "Fungible asset value must be positive."); - } - - if (height < 0) - { - throw new ArgumentOutOfRangeException( - nameof(height), height, "Height must be greater than or equal to zero."); - } - - if (expireHeight < height) - { - throw new ArgumentOutOfRangeException( - nameof(expireHeight), - expireHeight, - "Expire height must be greater than or equal to the height."); - } - - return new Delegation( - Bond, - UnbondLockIn.LockIn(fav, height, expireHeight), - RebondGrace, - UnbondingSet.AddUnbondLockIn(UnbondLockIn.Address), - _netBondedFAV); - } - - public Delegation CancelUnbondLockIn(FungibleAssetValue fav, long height) - { - if (fav.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(fav), fav, "Fungible asset value must be positive."); - } - - if (height < 0) - { - throw new ArgumentOutOfRangeException( - nameof(height), height, "Height must be greater than or equal to zero."); - } - - var updatedUnbondLockIn = UnbondLockIn.Cancel(fav, height); - var updatedUnbondingSet = UnbondingSet; - - if (updatedUnbondLockIn.IsEmpty) - { - updatedUnbondingSet = UnbondingSet.RemoveUnbondLockIn(UnbondLockIn.Address); - } - - return new Delegation( - Bond, - updatedUnbondLockIn, - RebondGrace, - updatedUnbondingSet, - _netBondedFAV); - } - - public Delegation DoRebondGrace(Address rebondeeAddress, FungibleAssetValue fav, long height, long expireHeight) - { - if (fav.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(fav), fav, "Fungible asset value must be positive."); - } - - if (height < 0) - { - throw new ArgumentOutOfRangeException( - nameof(height), height, "Height must be greater than or equal to zero."); - } - - if (expireHeight < height) - { - throw new ArgumentOutOfRangeException( - nameof(expireHeight), - expireHeight, - "Expire height must be greater than or equal to the height."); - } - - return new Delegation( - Bond, - UnbondLockIn, - RebondGrace.Grace(rebondeeAddress, fav, height, expireHeight), - UnbondingSet.AddRebondGrace(RebondGrace.Address), - _netBondedFAV); - } - - public FungibleAssetValue? FlushReleasedFAV() - => UnbondLockIn.FlushReleasedFAV(); - - public FungibleAssetValue? FlushNetBondedFAV() - { - var netBondedFAV = _netBondedFAV; - _netBondedFAV = null; - return netBondedFAV; - } - } -} diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index 2ab39b644d..c9777a556e 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -36,25 +36,50 @@ private Delegator(Address address, List bencoded) public IValue Bencoded => new List(Delegatees.Select(a => a.Bencoded)); - void IDelegator.Delegate(IDelegatee delegatee, FungibleAssetValue fav, Delegation delegation) - => Delegate((T)delegatee, fav, delegation); - - void IDelegator.Undelegate(IDelegatee delegatee, BigInteger share, long height, Delegation delegation) - => Undelegate((T)delegatee, share, height, delegation); + IDelegateResult IDelegator.Delegate( + IDelegatee delegatee, + FungibleAssetValue fav, + Bond bond) + => Delegate((T)delegatee, fav, bond); - void IDelegator.Redelegate( + IUndelegateResult IDelegator.Undelegate( + IDelegatee delegatee, + BigInteger share, + long height, + Bond bond, + UnbondLockIn unbondLockIn, + UnbondingSet unbondingSet) + => Undelegate( + (T)delegatee, + share, + height, + bond, + unbondLockIn, + unbondingSet); + + IRedelegateResult IDelegator.Redelegate( IDelegatee srcDelegatee, IDelegatee dstDelegatee, BigInteger share, long height, - Delegation srcDelegation, - Delegation dstDelegation) - => Redelegate((T)srcDelegatee, (T)dstDelegatee, share, height, srcDelegation, dstDelegation); - - public void Delegate( + Bond srcBond, + Bond dstBond, + RebondGrace srcRebondGrace, + UnbondingSet unbondingSet) + => Redelegate( + (T)srcDelegatee, + (T)dstDelegatee, + share, + height, + srcBond, + dstBond, + srcRebondGrace, + unbondingSet); + + public DelegateResult Delegate( T delegatee, FungibleAssetValue fav, - Delegation delegation) + Bond bond) { if (fav.Sign <= 0) { @@ -62,15 +87,19 @@ public void Delegate( nameof(fav), fav, "Fungible asset value must be positive."); } - delegatee.Bond((TSelf)this, fav, delegation); + BondResult bondResult = delegatee.Bond((TSelf)this, fav, bond); Delegatees = Delegatees.Add(delegatee.Address); + + return new DelegateResult(delegatee, bondResult.Bond, fav); } - public void Undelegate( + public UndelegateResult Undelegate( T delegatee, BigInteger share, long height, - Delegation delegation) + Bond bond, + UnbondLockIn unbondLockIn, + UnbondingSet unbondingSet) { if (share.Sign <= 0) { @@ -84,38 +113,38 @@ public void Undelegate( nameof(height), height, "Height must be positive."); } - if (delegation.UnbondLockIn.IsFull) + if (unbondLockIn.IsFull) { throw new InvalidOperationException("Undelegation is full."); } - delegatee.Unbond((TSelf)this, share, delegation); - - if (!(delegation.FlushNetBondedFAV() is FungibleAssetValue netBondedFAV)) - { - throw new NullReferenceException("Net bonded FAV is null."); - } + UnbondResult unbondResult = delegatee.Unbond((TSelf)this, share, bond); + unbondLockIn = unbondLockIn.LockIn( + unbondResult.UnbondedFAV, height, height + delegatee.UnbondingPeriod); - if (netBondedFAV.Sign >= 0) + if (unbondResult.Bond.Share.IsZero) { - throw new InvalidOperationException("Net bonded FAV must be negative."); + Delegatees = Delegatees.Remove(delegatee.Address); } - delegation.DoUnbondLockIn(-netBondedFAV, height, height + delegatee.UnbondingPeriod); + unbondingSet = unbondingSet.AddUnbondLockIn(unbondLockIn.Address); - if (delegation.Bond.Share.IsZero) - { - Delegatees = Delegatees.Remove(delegatee.Address); - } + return new UndelegateResult( + delegatee, + unbondResult.Bond, + unbondLockIn, + unbondingSet); } - public void Redelegate( + public RedelegateResult Redelegate( T srcDelegatee, T dstDelegatee, BigInteger share, long height, - Delegation srcDelegation, - Delegation dstDelegation) + Bond srcBond, + Bond dstBond, + RebondGrace srcRebondGrace, + UnbondingSet unbondingSet) { if (share.Sign <= 0) { @@ -129,28 +158,70 @@ public void Redelegate( nameof(height), height, "Height must be positive."); } - srcDelegatee.Unbond((TSelf)this, share, srcDelegation); - - if (!(srcDelegation.FlushNetBondedFAV() is FungibleAssetValue netBondedFAV)) + UnbondResult srcUnbondResult = srcDelegatee.Unbond( + (TSelf)this, share, srcBond); + BondResult dstBondResult = dstDelegatee.Bond( + (TSelf)this, srcUnbondResult.UnbondedFAV, dstBond); + srcRebondGrace = srcRebondGrace.Grace( + dstDelegatee.Address, + srcUnbondResult.UnbondedFAV, + height, + height + srcDelegatee.UnbondingPeriod); + + if (srcUnbondResult.Bond.Share.IsZero) { - throw new NullReferenceException("Net bonded FAV is null."); + Delegatees = Delegatees.Remove(srcDelegatee.Address); } - if (netBondedFAV.Sign >= 0) + Delegatees = Delegatees.Add(dstDelegatee.Address); + unbondingSet.AddRebondGrace(srcRebondGrace.Address); + + return new RedelegateResult( + srcDelegatee, + dstDelegatee, + srcUnbondResult.Bond, + dstBondResult.Bond, + srcRebondGrace, + unbondingSet); + } + + public UndelegateResult CancelUndelegate( + T delegatee, + FungibleAssetValue fav, + long height, + Bond bond, + UnbondLockIn unbondLockIn, + UnbondingSet unbondingSet) + { + if (fav.Sign <= 0) { - throw new InvalidOperationException("Net bonded FAV must be negative."); + throw new ArgumentOutOfRangeException( + nameof(fav), fav, "Fungible asset value must be positive."); } - dstDelegatee.Bond((TSelf)this, -netBondedFAV, dstDelegation); - - srcDelegation.DoRebondGrace(dstDelegatee.Address, -netBondedFAV, height, height + srcDelegatee.UnbondingPeriod); + if (height <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(height), height, "Height must be positive."); + } - if (srcDelegation.Bond.Share.IsZero) + if (unbondLockIn.IsFull) { - Delegatees = Delegatees.Remove(srcDelegatee.Address); + throw new InvalidOperationException("Undelegation is full."); } - Delegatees = Delegatees.Add(dstDelegatee.Address); + BondResult bondResult = delegatee.Bond((TSelf)this, fav, bond); + unbondLockIn = unbondLockIn.Cancel(fav, height); + Delegatees = Delegatees.Add(delegatee.Address); + unbondingSet = unbondLockIn.IsEmpty + ? unbondingSet.RemoveUnbondLockIn(unbondLockIn.Address) + : unbondingSet; + + return new UndelegateResult( + delegatee, + bondResult.Bond, + unbondLockIn, + unbondingSet); } public void Claim(IDelegatee delegatee) diff --git a/Lib9c/Delegation/IDelegateResult.cs b/Lib9c/Delegation/IDelegateResult.cs new file mode 100644 index 0000000000..499bec3646 --- /dev/null +++ b/Lib9c/Delegation/IDelegateResult.cs @@ -0,0 +1,13 @@ +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public interface IDelegateResult + { + IDelegatee Delegatee { get; } + + Bond Bond { get; } + + FungibleAssetValue DelegatedFAV { get; } + } +} diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index c59c08e2c3..c6bf989dcd 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -25,9 +25,9 @@ public interface IDelegatee : IBencodable, IEquatable BigInteger TotalShares { get; } - Delegation Bond(IDelegator delegator, FungibleAssetValue fav, Delegation delegation); + BondResult Bond(IDelegator delegator, FungibleAssetValue fav, Bond bond); - Delegation Unbond(IDelegator delegator, BigInteger share, Delegation delegation); + UnbondResult Unbond(IDelegator delegator, BigInteger share, Bond bond); void Distribute(); diff --git a/Lib9c/Delegation/IDelegator.cs b/Lib9c/Delegation/IDelegator.cs index a29cd288a6..d4d2df8d67 100644 --- a/Lib9c/Delegation/IDelegator.cs +++ b/Lib9c/Delegation/IDelegator.cs @@ -13,24 +13,28 @@ public interface IDelegator : IBencodable, IEquatable ImmutableSortedSet
Delegatees { get; } - void Delegate( + IDelegateResult Delegate( IDelegatee delegatee, FungibleAssetValue fav, - Delegation delegation); + Bond bond); - void Undelegate( + IUndelegateResult Undelegate( IDelegatee delegatee, BigInteger share, long height, - Delegation delegation); + Bond bond, + UnbondLockIn unbondLockIn, + UnbondingSet unbondingSet); - void Redelegate( + IRedelegateResult Redelegate( IDelegatee srcDelegatee, IDelegatee dstDelegatee, BigInteger share, long height, - Delegation srcDelegation, - Delegation dstDelegation); + Bond srcBond, + Bond dstBond, + RebondGrace srcRebondGrace, + UnbondingSet unbondingSet); void Claim(IDelegatee delegatee); } diff --git a/Lib9c/Delegation/IRedelegateResult.cs b/Lib9c/Delegation/IRedelegateResult.cs new file mode 100644 index 0000000000..28fbc33305 --- /dev/null +++ b/Lib9c/Delegation/IRedelegateResult.cs @@ -0,0 +1,17 @@ +namespace Nekoyume.Delegation +{ + public interface IRedelegateResult + { + IDelegatee SrcDelegatee { get; } + + IDelegatee DstDelegatee { get; } + + Bond SrcBond { get; } + + Bond DstBond { get; } + + RebondGrace RebondGrace { get; } + + UnbondingSet UnbondingSet { get; } + } +} diff --git a/Lib9c/Delegation/IUndelegateResult.cs b/Lib9c/Delegation/IUndelegateResult.cs new file mode 100644 index 0000000000..bb345930c4 --- /dev/null +++ b/Lib9c/Delegation/IUndelegateResult.cs @@ -0,0 +1,13 @@ +namespace Nekoyume.Delegation +{ + public interface IUndelegateResult + { + IDelegatee Delegatee { get; } + + Bond Bond { get; } + + UnbondLockIn UnbondLockIn { get; } + + UnbondingSet UnbondingSet { get; } + } +} diff --git a/Lib9c/Delegation/RebondGrace.cs b/Lib9c/Delegation/RebondGrace.cs index f885eff71b..f1052dbddb 100644 --- a/Lib9c/Delegation/RebondGrace.cs +++ b/Lib9c/Delegation/RebondGrace.cs @@ -128,14 +128,19 @@ public override int GetHashCode() => Address.GetHashCode(); internal RebondGrace Grace( - Address rebondeeAddress, FungibleAssetValue initialGraceFAV, long creationHeight, long expireHeight) + Address rebondeeAddress, + FungibleAssetValue initialGraceFAV, + long creationHeight, + long expireHeight) { if (expireHeight == creationHeight) { return this; } - return AddEntry(new RebondGraceEntry(rebondeeAddress, initialGraceFAV, creationHeight, expireHeight)); + return AddEntry( + new RebondGraceEntry( + rebondeeAddress, initialGraceFAV, creationHeight, expireHeight)); } private RebondGrace AddEntry(RebondGraceEntry entry) @@ -151,7 +156,9 @@ private RebondGrace AddEntry(RebondGraceEntry entry) } else { - return UpdateEntries(Entries.Add(entry.ExpireHeight, ImmutableList.Empty.Add(entry))); + return UpdateEntries( + Entries.Add( + entry.ExpireHeight, ImmutableList.Empty.Add(entry))); } } diff --git a/Lib9c/Delegation/RedelegateResult.cs b/Lib9c/Delegation/RedelegateResult.cs index ff505b3658..84b80615e6 100644 --- a/Lib9c/Delegation/RedelegateResult.cs +++ b/Lib9c/Delegation/RedelegateResult.cs @@ -1,29 +1,38 @@ namespace Nekoyume.Delegation { - public class RedelegateResult + public class RedelegateResult : IRedelegateResult + where T : IDelegatee { public RedelegateResult( - IDelegatee srcDelegatee, - IDelegatee dstDelegatee, + T srcDelegatee, + T dstDelegatee, Bond srcBond, Bond dstBond, - RebondGrace rebondGrace) + RebondGrace rebondGrace, + UnbondingSet unbondingSet) { SrcDelegatee = srcDelegatee; DstDelegatee = dstDelegatee; SrcBond = srcBond; DstBond = dstBond; RebondGrace = rebondGrace; + UnbondingSet = unbondingSet; } - public IDelegatee SrcDelegatee { get; } + IDelegatee IRedelegateResult.SrcDelegatee => SrcDelegatee; - public IDelegatee DstDelegatee { get; } + IDelegatee IRedelegateResult.DstDelegatee => DstDelegatee; + + public T SrcDelegatee { get; } + + public T DstDelegatee { get; } public Bond SrcBond { get; } public Bond DstBond { get; } public RebondGrace RebondGrace { get; } + + public UnbondingSet UnbondingSet { get; } } } diff --git a/Lib9c/Delegation/UnbondLockIn.cs b/Lib9c/Delegation/UnbondLockIn.cs index 3e34fe9766..18c3915f48 100644 --- a/Lib9c/Delegation/UnbondLockIn.cs +++ b/Lib9c/Delegation/UnbondLockIn.cs @@ -35,12 +35,14 @@ public UnbondLockIn(Address address, int maxEntries, List bencoded) (Integer)list[0], ((List)list[1]).Select(e => new UnbondLockInEntry(e)).ToImmutableList()) : throw new InvalidCastException( - $"Unable to cast object of type '{kv.GetType()}' to type '{typeof(List)}'.")) + $"Unable to cast object of type '{kv.GetType()}' " + + $"to type '{typeof(List)}'.")) .ToImmutableSortedDictionary()) { } - public UnbondLockIn(Address address, int maxEntries, IEnumerable entries) + public UnbondLockIn( + Address address, int maxEntries, IEnumerable entries) : this(address, maxEntries) { foreach (var entry in entries) @@ -145,7 +147,8 @@ public bool Equals(UnbondLockIn other) public override int GetHashCode() => Address.GetHashCode(); - internal UnbondLockIn LockIn(FungibleAssetValue lockInFAV, long creationHeight, long expireHeight) + internal UnbondLockIn LockIn( + FungibleAssetValue lockInFAV, long creationHeight, long expireHeight) { if (expireHeight == creationHeight) { @@ -244,7 +247,9 @@ private UnbondLockIn AddEntry(UnbondLockInEntry entry) } else { - return UpdateEntries(Entries.Add(entry.ExpireHeight, ImmutableList.Empty.Add(entry))); + return UpdateEntries( + Entries.Add( + entry.ExpireHeight, ImmutableList.Empty.Add(entry))); } } diff --git a/Lib9c/Delegation/UnbondResult.cs b/Lib9c/Delegation/UnbondResult.cs new file mode 100644 index 0000000000..ba292c9fac --- /dev/null +++ b/Lib9c/Delegation/UnbondResult.cs @@ -0,0 +1,18 @@ +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public class UnbondResult + { + public UnbondResult(Bond bond, FungibleAssetValue unbondedFAV) + { + Bond = bond; + + UnbondedFAV = unbondedFAV; + } + + public Bond Bond { get; } + + public FungibleAssetValue UnbondedFAV { get; } + } +} diff --git a/Lib9c/Delegation/UnbondingSet.cs b/Lib9c/Delegation/UnbondingSet.cs index 522dbf8f14..7bd3f4c954 100644 --- a/Lib9c/Delegation/UnbondingSet.cs +++ b/Lib9c/Delegation/UnbondingSet.cs @@ -26,7 +26,8 @@ public UnbondingSet(List bencoded) { } - private UnbondingSet(ImmutableSortedSet
unbondLockIns, ImmutableSortedSet
rebondGraces) + private UnbondingSet( + ImmutableSortedSet
unbondLockIns, ImmutableSortedSet
rebondGraces) { UnbondLockIns = unbondLockIns; RebondGraces = rebondGraces; diff --git a/Lib9c/Delegation/UndelegateResult.cs b/Lib9c/Delegation/UndelegateResult.cs index 6be90686e0..47608cc247 100644 --- a/Lib9c/Delegation/UndelegateResult.cs +++ b/Lib9c/Delegation/UndelegateResult.cs @@ -1,21 +1,28 @@ namespace Nekoyume.Delegation { - public class UndelegateResult + public class UndelegateResult : IUndelegateResult + where T : IDelegatee { public UndelegateResult( - IDelegatee delegatee, + T delegatee, Bond bond, - UnbondLockIn unbondLockIn) + UnbondLockIn unbondLockIn, + UnbondingSet unbondingSet) { Delegatee = delegatee; Bond = bond; UnbondLockIn = unbondLockIn; + UnbondingSet = unbondingSet; } - public IDelegatee Delegatee { get; } + IDelegatee IUndelegateResult.Delegatee => Delegatee; + + public T Delegatee { get; } public Bond Bond { get; } public UnbondLockIn UnbondLockIn { get; } + + public UnbondingSet UnbondingSet { get; } } } From e0ea88a4a598bb79f84be6d16a7f64977c510fd6 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sat, 3 Aug 2024 20:43:16 +0900 Subject: [PATCH 008/206] test: Update tests to use Result types --- .Lib9c.Tests/Delegation/DelegateeTest.cs | 78 ++++----- .Lib9c.Tests/Delegation/DelegationFixture.cs | 9 -- .Lib9c.Tests/Delegation/DelegatorTest.cs | 157 +++++++++++++------ 3 files changed, 140 insertions(+), 104 deletions(-) diff --git a/.Lib9c.Tests/Delegation/DelegateeTest.cs b/.Lib9c.Tests/Delegation/DelegateeTest.cs index 255a8c88b4..cf02a4ffc3 100644 --- a/.Lib9c.Tests/Delegation/DelegateeTest.cs +++ b/.Lib9c.Tests/Delegation/DelegateeTest.cs @@ -33,8 +33,8 @@ public void CtorWithBencoded() var address = new Address("0xe8327129891e1A0B2E3F0bfa295777912295942a"); var delegatee = _fixture.TestDelegatee1; var delegator = _fixture.TestDelegator1; - var delegation = _fixture.Delegation1To1; - delegatee.Bond(delegator, delegatee.Currency * 10, delegation); + var bond = _fixture.Bond1To1; + delegatee.Bond(delegator, delegatee.Currency * 10, bond); var delegateeRecon = new TestDelegatee(address, delegatee.Bencoded); Assert.Equal(address, delegateeRecon.Address); @@ -56,8 +56,8 @@ public void Bond() var testDelegatee = _fixture.TestDelegatee1; var testDelegator1 = _fixture.TestDelegator1; var testDelegator2 = _fixture.TestDelegator2; - var delegation1To1 = _fixture.Delegation1To1; - var delegation2To1 = _fixture.Delegation2To1; + var bond1To1 = _fixture.Bond1To1; + var bond2To1 = _fixture.Bond2To1; var share1 = BigInteger.Zero; var share2 = BigInteger.Zero; @@ -70,44 +70,39 @@ public void Bond() totalShare += share; totalBonding += bonding; - testDelegatee.Bond(testDelegator1, bonding, delegation1To1); + var bondResult = testDelegatee.Bond(testDelegator1, bonding, bond1To1); + bond1To1 = bondResult.Bond; Assert.Equal(testDelegator1.Address, Assert.Single(testDelegatee.Delegators)); - Assert.Equal(bonding, delegation1To1.IncompleteBond); - Assert.Null(delegation1To1.IncompleteUnbond); - Assert.Equal(share1, delegation1To1.Bond.Share); + Assert.Equal(share, bondResult.BondedShare); + Assert.Equal(share1, bondResult.Bond.Share); Assert.Equal(totalShare, testDelegatee.TotalShares); Assert.Equal(totalBonding, testDelegatee.TotalDelegated); - delegation1To1.Complete(); bonding = testDelegatee.Currency * 20; share = testDelegatee.ShareToBond(bonding); share1 += share; totalShare += share; totalBonding += bonding; - testDelegatee.Bond(testDelegator1, bonding, delegation1To1); + bondResult = testDelegatee.Bond(testDelegator1, bonding, bond1To1); Assert.Equal(testDelegator1.Address, Assert.Single(testDelegatee.Delegators)); - Assert.Equal(bonding, delegation1To1.IncompleteBond); - Assert.Null(delegation1To1.IncompleteUnbond); - Assert.Equal(share1, delegation1To1.Bond.Share); + Assert.Equal(share, bondResult.BondedShare); + Assert.Equal(share1, bondResult.Bond.Share); Assert.Equal(totalShare, testDelegatee.TotalShares); Assert.Equal(totalBonding, testDelegatee.TotalDelegated); - delegation1To1.Complete(); bonding = testDelegatee.Currency * 30; share = testDelegatee.ShareToBond(bonding); share2 += share; totalShare += share; totalBonding += bonding; - testDelegatee.Bond(testDelegator2, bonding, delegation2To1); + bondResult = testDelegatee.Bond(testDelegator2, bonding, bond2To1); Assert.Equal(2, testDelegatee.Delegators.Count); Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); Assert.Contains(testDelegator2.Address, testDelegatee.Delegators); - Assert.Equal(bonding, delegation2To1.IncompleteBond); - Assert.Null(delegation2To1.IncompleteUnbond); - Assert.Equal(share2, delegation2To1.Bond.Share); + Assert.Equal(share, bondResult.BondedShare); + Assert.Equal(share2, bondResult.Bond.Share); Assert.Equal(totalShare, testDelegatee.TotalShares); Assert.Equal(totalBonding, testDelegatee.TotalDelegated); - delegation2To1.Complete(); } [Fact] @@ -116,11 +111,11 @@ public void CannotBondInvalidDelegator() IDelegatee testDelegatee = _fixture.TestDelegatee1; var testDelegator = _fixture.TestDelegator1; var dummyDelegator = _fixture.DummyDelegator1; - var delegation = _fixture.Delegation1To1; + var bond = _fixture.Bond1To1; Assert.Throws( () => testDelegatee.Bond( - dummyDelegator, testDelegatee.Currency * 10, delegation)); + dummyDelegator, testDelegatee.Currency * 10, bond)); } [Fact] @@ -129,12 +124,12 @@ public void CannotBondInvalidCurrency() var testDelegatee = _fixture.TestDelegatee1; var testDelegator = _fixture.TestDelegator1; var dummyDelegator = _fixture.DummyDelegator1; - var delegation = _fixture.Delegation1To1; + var bond = _fixture.Bond1To1; var invalidCurrency = Currency.Uncapped("invalid", 3, null); Assert.Throws( () => testDelegatee.Bond( - testDelegator, invalidCurrency * 10, delegation)); + testDelegator, invalidCurrency * 10, bond)); } [Fact] @@ -143,8 +138,8 @@ public void Unbond() var testDelegatee = _fixture.TestDelegatee1; var testDelegator1 = _fixture.TestDelegator1; var testDelegator2 = _fixture.TestDelegator2; - var delegation1To1 = _fixture.Delegation1To1; - var delegation2To1 = _fixture.Delegation2To1; + var bond1To1 = _fixture.Bond1To1; + var bond2To1 = _fixture.Bond2To1; var share1 = BigInteger.Zero; var share2 = BigInteger.Zero; @@ -156,60 +151,53 @@ public void Unbond() share1 += share; totalShares += share; totalDelegated += bonding; - testDelegatee.Bond(testDelegator1, bonding, delegation1To1); - delegation1To1.Complete(); + bond1To1 = testDelegatee.Bond(testDelegator1, bonding, bond1To1).Bond; bonding = testDelegatee.Currency * 50; share = testDelegatee.ShareToBond(bonding); share2 += share; totalShares += share; totalDelegated += bonding; - testDelegatee.Bond(testDelegator2, bonding, delegation2To1); - delegation2To1.Complete(); + bond2To1 = testDelegatee.Bond(testDelegator2, bonding, bond2To1).Bond; var unbonding = share1 / 2; share1 -= unbonding; totalShares -= unbonding; var unbondingFAV = testDelegatee.FAVToUnbond(unbonding); totalDelegated -= unbondingFAV; - testDelegatee.Unbond(testDelegator1, unbonding, delegation1To1); + var unbondResult = testDelegatee.Unbond(testDelegator1, unbonding, bond1To1); + bond1To1 = unbondResult.Bond; Assert.Equal(2, testDelegatee.Delegators.Count); Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); Assert.Contains(testDelegator2.Address, testDelegatee.Delegators); - Assert.Equal(unbondingFAV, delegation1To1.IncompleteUnbond); - Assert.Null(delegation1To1.IncompleteBond); - Assert.Equal(share1, delegation1To1.Bond.Share); + Assert.Equal(unbondingFAV, unbondResult.UnbondedFAV); + Assert.Equal(share1, unbondResult.Bond.Share); Assert.Equal(totalShares, testDelegatee.TotalShares); Assert.Equal(totalDelegated, testDelegatee.TotalDelegated); - delegation1To1.Complete(); unbonding = share2 / 2; share2 -= unbonding; totalShares -= unbonding; unbondingFAV = testDelegatee.FAVToUnbond(unbonding); totalDelegated -= unbondingFAV; - testDelegatee.Unbond(testDelegator2, unbonding, delegation2To1); + unbondResult = testDelegatee.Unbond(testDelegator2, unbonding, bond2To1); Assert.Equal(2, testDelegatee.Delegators.Count); Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); Assert.Contains(testDelegator2.Address, testDelegatee.Delegators); - Assert.Equal(unbondingFAV, delegation2To1.IncompleteUnbond); - Assert.Null(delegation2To1.IncompleteBond); - Assert.Equal(share2, delegation2To1.Bond.Share); + Assert.Equal(unbondingFAV, unbondResult.UnbondedFAV); + Assert.Equal(share2, unbondResult.Bond.Share); Assert.Equal(totalShares, testDelegatee.TotalShares); Assert.Equal(totalDelegated, testDelegatee.TotalDelegated); - delegation1To1.Complete(); totalShares -= share1; unbondingFAV = testDelegatee.FAVToUnbond(share1); totalDelegated -= unbondingFAV; - testDelegatee.Unbond(testDelegator1, share1, delegation1To1); + unbondResult = testDelegatee.Unbond(testDelegator1, share1, bond1To1); Assert.Equal(testDelegator2.Address, Assert.Single(testDelegatee.Delegators)); - Assert.Equal(unbondingFAV, delegation1To1.IncompleteUnbond); - Assert.Null(delegation1To1.IncompleteBond); - Assert.Equal(BigInteger.Zero, delegation1To1.Bond.Share); + Assert.Equal(unbondingFAV, unbondResult.UnbondedFAV); + Assert.Equal(BigInteger.Zero, unbondResult.Bond.Share); Assert.Equal(totalShares, testDelegatee.TotalShares); Assert.Equal(totalDelegated, testDelegatee.TotalDelegated); - delegation1To1.Complete(); } [Fact] @@ -218,7 +206,7 @@ public void CannotUnbondInvalidDelegator() IDelegatee delegatee = _fixture.TestDelegatee1; Assert.Throws( () => delegatee.Unbond( - _fixture.DummyDelegator1, BigInteger.One, _fixture.Delegation1To1)); + _fixture.DummyDelegator1, BigInteger.One, _fixture.Bond1To1)); } [Fact] diff --git a/.Lib9c.Tests/Delegation/DelegationFixture.cs b/.Lib9c.Tests/Delegation/DelegationFixture.cs index cb70321c06..669517c752 100644 --- a/.Lib9c.Tests/Delegation/DelegationFixture.cs +++ b/.Lib9c.Tests/Delegation/DelegationFixture.cs @@ -27,9 +27,6 @@ public DelegationFixture() Rebond2To1 = new RebondGrace(TestDelegatee1.BondAddress(TestDelegator2.Address), 10); Rebond1To2 = new RebondGrace(TestDelegatee2.BondAddress(TestDelegator1.Address), 10); UnbondingSet = new UnbondingSet(); - Delegation1To1 = new Delegation(Bond1To1, Unbond1To1, Rebond1To1, UnbondingSet); - Delegation2To1 = new Delegation(Bond2To1, Unbond2To1, Rebond2To1, UnbondingSet); - Delegation1To2 = new Delegation(Bond1To2, Unbond1To2, Rebond1To2, UnbondingSet); } public TestDelegator TestDelegator1 { get; } @@ -63,11 +60,5 @@ public DelegationFixture() public RebondGrace Rebond1To2 { get; } public UnbondingSet UnbondingSet { get; } - - public Delegation Delegation1To1 { get; } - - public Delegation Delegation2To1 { get; } - - public Delegation Delegation1To2 { get; } } } diff --git a/.Lib9c.Tests/Delegation/DelegatorTest.cs b/.Lib9c.Tests/Delegation/DelegatorTest.cs index dda16cfa66..27c647fe59 100644 --- a/.Lib9c.Tests/Delegation/DelegatorTest.cs +++ b/.Lib9c.Tests/Delegation/DelegatorTest.cs @@ -26,8 +26,8 @@ public void CtorWithBencoded() { var delegator = _fixture.TestDelegator1; var delegatee = _fixture.TestDelegatee1; - var delegation = _fixture.Delegation1To1; - delegator.Delegate(delegatee, delegatee.Currency * 10, delegation); + var bond = _fixture.Bond1To1; + delegator.Delegate(delegatee, delegatee.Currency * 10, bond); var delegatorRecon = new TestDelegator(delegator.Address, delegator.Bencoded); Assert.Equal(delegator.Address, delegatorRecon.Address); @@ -40,20 +40,38 @@ public void Delegate() var delegator = _fixture.TestDelegator1; var delegatee1 = _fixture.TestDelegatee1; var delegatee2 = _fixture.TestDelegatee2; - var delegation1 = _fixture.Delegation1To1; - var delegation2 = _fixture.Delegation1To2; + var bond1 = _fixture.Bond1To1; + var bond2 = _fixture.Bond1To2; var delegateFAV = delegatee1.Currency * 10; + var delegateShare = delegatee1.ShareToBond(delegateFAV); + var result = delegator.Delegate(delegatee1, delegateFAV, bond1); + bond1 = result.Bond; + delegatee1 = result.Delegatee; + Assert.Equal(delegateFAV, result.DelegatedFAV); + Assert.Equal(delegateShare, result.Bond.Share); + Assert.Equal(delegateFAV, delegatee1.TotalDelegated); + Assert.Equal(delegateShare, delegatee1.TotalShares); + Assert.Equal(delegator.Address, Assert.Single(delegatee1.Delegators)); + Assert.Equal(delegatee1.Address, Assert.Single(delegator.Delegatees)); - delegator.Delegate(delegatee1, delegateFAV, delegation1); - Assert.Equal(delegateFAV, delegation1.IncompleteBond); + var delegateFAV2 = delegatee1.Currency * 20; + var delegateShare2 = delegatee1.ShareToBond(delegateFAV2); + result = delegator.Delegate(delegatee1, delegateFAV2, bond1); + Assert.Equal(delegateFAV2, result.DelegatedFAV); + Assert.Equal(delegateShare + delegateShare2, result.Bond.Share); + Assert.Equal(delegateFAV + delegateFAV2, delegatee1.TotalDelegated); + Assert.Equal(delegateShare + delegateShare2, delegatee1.TotalShares); + Assert.Equal(delegator.Address, Assert.Single(delegatee1.Delegators)); Assert.Equal(delegatee1.Address, Assert.Single(delegator.Delegatees)); - delegation1.Complete(); - delegator.Delegate(delegatee2, delegateFAV, delegation2); - Assert.Equal(delegateFAV, delegation2.IncompleteBond); + + result = delegator.Delegate(delegatee2, delegateFAV, bond2); + Assert.Equal(delegateFAV, result.DelegatedFAV); + Assert.Equal(delegateShare, result.Bond.Share); + Assert.Equal(delegateFAV, delegatee2.TotalDelegated); + Assert.Equal(delegateShare, delegatee2.TotalShares); Assert.Equal(2, delegator.Delegatees.Count); Assert.Contains(delegatee1.Address, delegator.Delegatees); Assert.Contains(delegatee2.Address, delegator.Delegatees); - delegation2.Complete(); } [Fact] @@ -61,16 +79,25 @@ public void Undelegate() { var delegator = _fixture.TestDelegator1; var delegatee = _fixture.TestDelegatee1; - var delegation = _fixture.Delegation1To1; - delegator.Delegate(delegatee, delegatee.Currency * 10, delegation); - - var undelegatingShare = delegation.Bond.Share / 2; + var bond = _fixture.Bond1To1; + var unbondLockIn = _fixture.Unbond1To1; + var unbondingSet = _fixture.UnbondingSet; + var delegateResult = delegator.Delegate( + delegatee, delegatee.Currency * 10, bond); + delegatee = delegateResult.Delegatee; + bond = delegateResult.Bond; + + var undelegatingShare = delegateResult.Bond.Share / 2; var undelegatingFAV = delegatee.FAVToUnbond(undelegatingShare); - delegator.Undelegate(delegatee, undelegatingShare, 10L, delegation); - Assert.Null(delegation.IncompleteBond); - Assert.Null(delegation.IncompleteUnbond); + var undelegateResult = delegator.Undelegate( + delegatee, undelegatingShare, 10L, bond, unbondLockIn, unbondingSet); + delegatee = undelegateResult.Delegatee; + bond = undelegateResult.Bond; + unbondLockIn = undelegateResult.UnbondLockIn; + unbondingSet = undelegateResult.UnbondingSet; Assert.Equal(delegatee.Address, Assert.Single(delegator.Delegatees)); - var entriesByExpireHeight = Assert.Single(delegation.UnbondLockIn.Entries); + Assert.Single(unbondingSet.UnbondLockIns); + var entriesByExpireHeight = Assert.Single(unbondLockIn.Entries); Assert.Equal(10L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); var entry = Assert.Single(entriesByExpireHeight.Value); Assert.Equal(undelegatingFAV, entry.InitialLockInFAV); @@ -78,18 +105,21 @@ public void Undelegate() Assert.Equal(10L, entry.CreationHeight); Assert.Equal(10L + delegatee.UnbondingPeriod, entry.ExpireHeight); - undelegatingShare = delegation.Bond.Share; - delegator.Undelegate(delegatee, undelegatingShare, 12L, delegation); - Assert.Null(delegation.IncompleteBond); - Assert.Null(delegation.IncompleteUnbond); + undelegatingShare = bond.Share; + undelegateResult = delegator.Undelegate( + delegatee, undelegatingShare, 12L, bond, unbondLockIn, unbondingSet); + delegatee = undelegateResult.Delegatee; + bond = undelegateResult.Bond; + unbondLockIn = undelegateResult.UnbondLockIn; + unbondingSet = undelegateResult.UnbondingSet; Assert.Empty(delegator.Delegatees); - Assert.Equal(2, delegation.UnbondLockIn.Entries.Count); + Assert.Equal(2, unbondLockIn.Entries.Count); - delegation.UnbondLockIn.Release(10L + delegatee.UnbondingPeriod - 1); - Assert.Equal(2, delegation.UnbondLockIn.Entries.Count); + unbondLockIn = unbondLockIn.Release(10L + delegatee.UnbondingPeriod - 1); + Assert.Equal(2, unbondLockIn.Entries.Count); - delegation.UnbondLockIn.Release(10L + delegatee.UnbondingPeriod); - entriesByExpireHeight = Assert.Single(delegation.UnbondLockIn.Entries); + unbondLockIn = unbondLockIn.Release(10L + delegatee.UnbondingPeriod); + entriesByExpireHeight = Assert.Single(unbondLockIn.Entries); Assert.Equal(12L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); entry = Assert.Single(entriesByExpireHeight.Value); Assert.Equal(undelegatingFAV, entry.InitialLockInFAV); @@ -97,8 +127,8 @@ public void Undelegate() Assert.Equal(12L, entry.CreationHeight); Assert.Equal(12L + delegatee.UnbondingPeriod, entry.ExpireHeight); - delegation.UnbondLockIn.Release(12L + delegatee.UnbondingPeriod); - Assert.Empty(delegation.UnbondLockIn.Entries); + unbondLockIn = unbondLockIn.Release(12L + delegatee.UnbondingPeriod); + Assert.Empty(unbondLockIn.Entries); } [Fact] @@ -107,18 +137,35 @@ public void Redelegate() var delegator = _fixture.TestDelegator1; var delegatee1 = _fixture.TestDelegatee1; var delegatee2 = _fixture.TestDelegatee2; - var delegation1 = _fixture.Delegation1To1; - var delegation2 = _fixture.Delegation1To2; - delegator.Delegate(delegatee1, delegatee1.Currency * 10, delegation1); + var bond1 = _fixture.Bond1To1; + var bond2 = _fixture.Bond1To2; + var rebondGrace = _fixture.Rebond1To1; + var unbondingSet = _fixture.UnbondingSet; + var delegateResult = delegator.Delegate( + delegatee1, delegatee1.Currency * 10, bond1); + delegatee1 = delegateResult.Delegatee; + bond1 = delegateResult.Bond; Assert.Equal(delegatee1.Address, Assert.Single(delegator.Delegatees)); - var redelegatingShare = delegation1.Bond.Share / 2; + var redelegatingShare = bond1.Share / 2; var redelegatingFAV = delegatee1.FAVToUnbond(redelegatingShare); - delegator.Redelegate( - delegatee1, delegatee2, redelegatingShare, 10L, delegation1, delegation2); - Assert.Equal(redelegatingFAV, delegation2.IncompleteBond); + var redelegateResult = delegator.Redelegate( + delegatee1, + delegatee2, + redelegatingShare, + 10L, + bond1, + bond2, + rebondGrace, + unbondingSet); + delegatee1 = redelegateResult.SrcDelegatee; + delegatee2 = redelegateResult.DstDelegatee; + bond1 = redelegateResult.SrcBond; + bond2 = redelegateResult.DstBond; + rebondGrace = redelegateResult.RebondGrace; + unbondingSet = redelegateResult.UnbondingSet; Assert.Equal(2, delegator.Delegatees.Count); - var entriesByExpireHeight = Assert.Single(delegation1.RebondGrace.Entries); + var entriesByExpireHeight = Assert.Single(rebondGrace.Entries); Assert.Equal(10L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); var entry = Assert.Single(entriesByExpireHeight.Value); Assert.Equal(delegatee2.Address, entry.RebondeeAddress); @@ -126,21 +173,32 @@ public void Redelegate() Assert.Equal(redelegatingFAV, entry.GraceFAV); Assert.Equal(10L, entry.CreationHeight); Assert.Equal(10L + delegatee1.UnbondingPeriod, entry.ExpireHeight); - delegation2.Complete(); - redelegatingShare = delegation1.Bond.Share; + redelegatingShare = bond1.Share; redelegatingFAV = delegatee1.FAVToUnbond(redelegatingShare); - delegator.Redelegate( - delegatee1, delegatee2, redelegatingShare, 12L, delegation1, delegation2); - Assert.Equal(redelegatingFAV, delegation2.IncompleteBond); + redelegateResult = delegator.Redelegate( + delegatee1, + delegatee2, + redelegatingShare, + 12L, + bond1, + bond2, + rebondGrace, + unbondingSet); + delegatee1 = redelegateResult.SrcDelegatee; + delegatee2 = redelegateResult.DstDelegatee; + bond1 = redelegateResult.SrcBond; + bond2 = redelegateResult.DstBond; + rebondGrace = redelegateResult.RebondGrace; + unbondingSet = redelegateResult.UnbondingSet; Assert.Equal(delegatee2.Address, Assert.Single(delegator.Delegatees)); - Assert.Equal(2, delegation1.RebondGrace.Entries.Count); + Assert.Equal(2, rebondGrace.Entries.Count); - delegation1.RebondGrace.Release(10L + delegatee1.UnbondingPeriod - 1); - Assert.Equal(2, delegation1.RebondGrace.Entries.Count); + rebondGrace = rebondGrace.Release(10L + delegatee1.UnbondingPeriod - 1); + Assert.Equal(2, rebondGrace.Entries.Count); - delegation1.RebondGrace.Release(10L + delegatee1.UnbondingPeriod); - entriesByExpireHeight = Assert.Single(delegation1.RebondGrace.Entries); + rebondGrace = rebondGrace.Release(10L + delegatee1.UnbondingPeriod); + entriesByExpireHeight = Assert.Single(rebondGrace.Entries); Assert.Equal(12L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); entry = Assert.Single(entriesByExpireHeight.Value); Assert.Equal(delegatee2.Address, entry.RebondeeAddress); @@ -148,10 +206,9 @@ public void Redelegate() Assert.Equal(redelegatingFAV, entry.GraceFAV); Assert.Equal(12L, entry.CreationHeight); Assert.Equal(12L + delegatee1.UnbondingPeriod, entry.ExpireHeight); - delegation2.Complete(); - delegation1.RebondGrace.Release(12L + delegatee1.UnbondingPeriod); - Assert.Empty(delegation1.RebondGrace.Entries); + rebondGrace = rebondGrace.Release(12L + delegatee1.UnbondingPeriod); + Assert.Empty(rebondGrace.Entries); } } } From c2e957ccd3bff8076250f38f5da3179b669e8233 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sun, 4 Aug 2024 17:46:26 +0900 Subject: [PATCH 009/206] chore: Clean up for Delegation --- .Lib9c.Tests/Delegation/DummyDelegatee.cs | 4 ++ .Lib9c.Tests/Delegation/TestDelegatee.cs | 4 ++ Lib9c/Delegation/Bond.cs | 16 ++++--- Lib9c/Delegation/BondResult.cs | 1 + Lib9c/Delegation/DelegateResult.cs | 5 +- Lib9c/Delegation/Delegatee.cs | 16 +++++-- Lib9c/Delegation/Delegator.cs | 10 ++-- Lib9c/Delegation/IDelegateResult.cs | 1 + Lib9c/Delegation/IDelegatee.cs | 5 ++ Lib9c/Delegation/IDelegator.cs | 1 + Lib9c/Delegation/IRedelegateResult.cs | 1 + Lib9c/Delegation/IUndelegateResult.cs | 1 + Lib9c/Delegation/RebondGrace.cs | 56 +++++++++++++++++------ Lib9c/Delegation/RedelegateResult.cs | 9 ++-- Lib9c/Delegation/UnbondLockIn.cs | 53 ++++++++++++++++----- Lib9c/Delegation/UnbondResult.cs | 1 + Lib9c/Delegation/UnbondingSet.cs | 12 ++++- Lib9c/Delegation/UndelegateResult.cs | 5 +- 18 files changed, 153 insertions(+), 48 deletions(-) diff --git a/.Lib9c.Tests/Delegation/DummyDelegatee.cs b/.Lib9c.Tests/Delegation/DummyDelegatee.cs index fe1d32a88c..1db4fd453c 100644 --- a/.Lib9c.Tests/Delegation/DummyDelegatee.cs +++ b/.Lib9c.Tests/Delegation/DummyDelegatee.cs @@ -17,4 +17,8 @@ public DummyDelegatee(Address address) public override long UnbondingPeriod => 3; public override byte[] DelegateeId => new byte[] { 0x02 }; + + public override int MaxUnbondLockInEntries => 5; + + public override int MaxRebondGraceEntries => 5; } diff --git a/.Lib9c.Tests/Delegation/TestDelegatee.cs b/.Lib9c.Tests/Delegation/TestDelegatee.cs index 0373f1fce0..47a85c8271 100644 --- a/.Lib9c.Tests/Delegation/TestDelegatee.cs +++ b/.Lib9c.Tests/Delegation/TestDelegatee.cs @@ -24,5 +24,9 @@ public TestDelegatee(Address address, IValue bencoded) public override Address PoolAddress => DeriveAddress(PoolId); public override byte[] DelegateeId => new byte[] { 0x01 }; + + public override int MaxUnbondLockInEntries => 5; + + public override int MaxRebondGraceEntries => 5; } } diff --git a/Lib9c/Delegation/Bond.cs b/Lib9c/Delegation/Bond.cs index c5f38f07f4..71a38cc173 100644 --- a/Lib9c/Delegation/Bond.cs +++ b/Lib9c/Delegation/Bond.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Numerics; using Bencodex; @@ -52,18 +53,21 @@ private Bond(Address address, BigInteger share, long lastDistributeHeight) public long LastDistributeHeight { get; } - public IValue Bencoded => List.Empty + public List Bencoded => List.Empty .Add(Share) .Add(LastDistributeHeight); - public override bool Equals(object obj) + IValue IBencodable.Bencoded => Bencoded; + + public override bool Equals(object? obj) => obj is Bond other && Equals(other); - public bool Equals(Bond other) + public bool Equals(Bond? other) => ReferenceEquals(this, other) - || (Address.Equals(other.Address) - && Share.Equals(other.Share) - && LastDistributeHeight.Equals(other.LastDistributeHeight)); + || (other is Bond bond + && Address.Equals(bond.Address) + && Share.Equals(bond.Share) + && LastDistributeHeight.Equals(bond.LastDistributeHeight)); public override int GetHashCode() => Address.GetHashCode(); diff --git a/Lib9c/Delegation/BondResult.cs b/Lib9c/Delegation/BondResult.cs index caac6c2205..bc858c9142 100644 --- a/Lib9c/Delegation/BondResult.cs +++ b/Lib9c/Delegation/BondResult.cs @@ -1,3 +1,4 @@ +#nullable enable using System.Numerics; namespace Nekoyume.Delegation diff --git a/Lib9c/Delegation/DelegateResult.cs b/Lib9c/Delegation/DelegateResult.cs index 455e11993f..fa815fcaa4 100644 --- a/Lib9c/Delegation/DelegateResult.cs +++ b/Lib9c/Delegation/DelegateResult.cs @@ -1,3 +1,4 @@ +#nullable enable using Libplanet.Types.Assets; namespace Nekoyume.Delegation @@ -15,10 +16,10 @@ public DelegateResult( DelegatedFAV = delegatedFAV; } - IDelegatee IDelegateResult.Delegatee => Delegatee; - public T Delegatee { get; } + IDelegatee IDelegateResult.Delegatee => Delegatee; + public Bond Bond { get; } public FungibleAssetValue DelegatedFAV { get; } diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index e45a699119..30671d63ef 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -1,9 +1,11 @@ +#nullable enable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Numerics; using System.Security.Cryptography; +using Bencodex; using Bencodex.Types; using Libplanet.Crypto; using Libplanet.Types.Assets; @@ -85,6 +87,10 @@ private Delegatee( public abstract byte[] DelegateeId { get; } + public abstract int MaxUnbondLockInEntries { get; } + + public abstract int MaxRebondGraceEntries { get; } + public Address RewardPoolAddress => DeriveAddress(RewardPoolId); public ImmutableSortedSet
Delegators { get; private set; } @@ -93,11 +99,13 @@ private Delegatee( public BigInteger TotalShares { get; private set; } - public IValue Bencoded => List.Empty + public List Bencoded => List.Empty .Add(new List(Delegators.Select(delegator => delegator.Bencoded))) .Add(TotalDelegated.Serialize()) .Add(TotalShares); + IValue IBencodable.Bencoded => Bencoded; + public BigInteger ShareToBond(FungibleAssetValue fav) => TotalShares.IsZero ? fav.RawValue @@ -170,10 +178,10 @@ public Address UnbondLockInAddress(Address delegatorAddress) public Address RebondGraceAddress(Address delegatorAddress) => DeriveAddress(RebondGraceId, delegatorAddress); - public override bool Equals(object obj) + public override bool Equals(object? obj) => obj is IDelegatee other && Equals(other); - public bool Equals(IDelegatee other) + public bool Equals(IDelegatee? other) => ReferenceEquals(this, other) || (other is Delegatee delegatee && (GetType() != delegatee.GetType()) @@ -193,7 +201,7 @@ public override int GetHashCode() protected Address DeriveAddress(byte[] typeId, Address address) => DeriveAddress(typeId, address.ByteArray); - protected Address DeriveAddress(byte[] typeId, IEnumerable bytes = null) + protected Address DeriveAddress(byte[] typeId, IEnumerable? bytes = null) { byte[] hashed; using (var hmac = new HMACSHA1(DelegateeId.Concat(typeId).ToArray())) diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index c9777a556e..480618c98c 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -1,7 +1,9 @@ +#nullable enable using System; using System.Collections.Immutable; using System.Linq; using System.Numerics; +using Bencodex; using Bencodex.Types; using Libplanet.Crypto; using Libplanet.Types.Assets; @@ -33,9 +35,11 @@ private Delegator(Address address, List bencoded) public ImmutableSortedSet
Delegatees { get; private set; } - public IValue Bencoded + public List Bencoded => new List(Delegatees.Select(a => a.Bencoded)); + IValue IBencodable.Bencoded => Bencoded; + IDelegateResult IDelegator.Delegate( IDelegatee delegatee, FungibleAssetValue fav, @@ -229,10 +233,10 @@ public void Claim(IDelegatee delegatee) // TODO: Implement this } - public override bool Equals(object obj) + public override bool Equals(object? obj) => obj is IDelegator other && Equals(other); - public bool Equals(IDelegator other) + public bool Equals(IDelegator? other) => ReferenceEquals(this, other) || (other is Delegator delegator && GetType() != delegator.GetType() diff --git a/Lib9c/Delegation/IDelegateResult.cs b/Lib9c/Delegation/IDelegateResult.cs index 499bec3646..18ea9ff2dd 100644 --- a/Lib9c/Delegation/IDelegateResult.cs +++ b/Lib9c/Delegation/IDelegateResult.cs @@ -1,3 +1,4 @@ +#nullable enable using Libplanet.Types.Assets; namespace Nekoyume.Delegation diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index c6bf989dcd..2401c4e6d2 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Collections.Immutable; using System.Numerics; @@ -17,6 +18,10 @@ public interface IDelegatee : IBencodable, IEquatable long UnbondingPeriod { get; } + int MaxUnbondLockInEntries { get; } + + int MaxRebondGraceEntries { get; } + Address RewardPoolAddress { get; } ImmutableSortedSet
Delegators { get; } diff --git a/Lib9c/Delegation/IDelegator.cs b/Lib9c/Delegation/IDelegator.cs index d4d2df8d67..d744d00da7 100644 --- a/Lib9c/Delegation/IDelegator.cs +++ b/Lib9c/Delegation/IDelegator.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Collections.Immutable; using System.Numerics; diff --git a/Lib9c/Delegation/IRedelegateResult.cs b/Lib9c/Delegation/IRedelegateResult.cs index 28fbc33305..7536ae4571 100644 --- a/Lib9c/Delegation/IRedelegateResult.cs +++ b/Lib9c/Delegation/IRedelegateResult.cs @@ -1,3 +1,4 @@ +#nullable enable namespace Nekoyume.Delegation { public interface IRedelegateResult diff --git a/Lib9c/Delegation/IUndelegateResult.cs b/Lib9c/Delegation/IUndelegateResult.cs index bb345930c4..10ecd5a8c2 100644 --- a/Lib9c/Delegation/IUndelegateResult.cs +++ b/Lib9c/Delegation/IUndelegateResult.cs @@ -1,3 +1,4 @@ +#nullable enable namespace Nekoyume.Delegation { public interface IUndelegateResult diff --git a/Lib9c/Delegation/RebondGrace.cs b/Lib9c/Delegation/RebondGrace.cs index f1052dbddb..ff17f99c20 100644 --- a/Lib9c/Delegation/RebondGrace.cs +++ b/Lib9c/Delegation/RebondGrace.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -78,13 +79,15 @@ private RebondGrace( public ImmutableArray FlattenedEntries => Entries.Values.SelectMany(e => e).ToImmutableArray(); - public IValue Bencoded + public List Bencoded => new List( Entries.Select( sortedDict => new List( (Integer)sortedDict.Key, new List(sortedDict.Value.Select(e => e.Bencoded))))); + IValue IBencodable.Bencoded => Bencoded; + public RebondGrace Release(long height) { if (height <= 0) @@ -115,14 +118,15 @@ public RebondGrace Release(long height) public RebondGrace Slash() => throw new NotImplementedException(); - public override bool Equals(object obj) + public override bool Equals(object? obj) => obj is RebondGrace other && Equals(other); - public bool Equals(RebondGrace other) + public bool Equals(RebondGrace? other) => ReferenceEquals(this, other) - || (Address.Equals(other.Address) - && MaxEntries == other.MaxEntries - && FlattenedEntries.SequenceEqual(other.FlattenedEntries)); + || (other is RebondGrace rebondGrace + && Address.Equals(rebondGrace.Address) + && MaxEntries == rebondGrace.MaxEntries + && FlattenedEntries.SequenceEqual(rebondGrace.FlattenedEntries)); public override int GetHashCode() => Address.GetHashCode(); @@ -168,6 +172,8 @@ private RebondGrace UpdateEntries( public class RebondGraceEntry : IBencodable, IEquatable { + private int? _cachedHashCode; + public RebondGraceEntry( Address rebondeeAddress, FungibleAssetValue graceFAV, @@ -256,20 +262,44 @@ private RebondGraceEntry( public long ExpireHeight { get; } - public IValue Bencoded => List.Empty + public List Bencoded => List.Empty .Add(RebondeeAddress.Bencoded) .Add(InitialGraceFAV.Serialize()) .Add(GraceFAV.Serialize()) .Add(CreationHeight) .Add(ExpireHeight); - public bool Equals(RebondGraceEntry other) + IValue IBencodable.Bencoded => Bencoded; + + public override bool Equals(object? obj) + => obj is RebondGraceEntry other && Equals(other); + + public bool Equals(RebondGraceEntry? other) => ReferenceEquals(this, other) - || (RebondeeAddress.Equals(other.RebondeeAddress) - && InitialGraceFAV.Equals(other.InitialGraceFAV) - && GraceFAV.Equals(other.GraceFAV) - && CreationHeight == other.CreationHeight - && ExpireHeight == other.ExpireHeight); + || (other is RebondGraceEntry rebondGraceEntry + && RebondeeAddress.Equals(rebondGraceEntry.RebondeeAddress) + && InitialGraceFAV.Equals(rebondGraceEntry.InitialGraceFAV) + && GraceFAV.Equals(rebondGraceEntry.GraceFAV) + && CreationHeight == rebondGraceEntry.CreationHeight + && ExpireHeight == rebondGraceEntry.ExpireHeight); + + public override int GetHashCode() + { + if (_cachedHashCode is int cached) + { + return cached; + } + + int hash = HashCode.Combine( + RebondeeAddress, + InitialGraceFAV, + GraceFAV, + CreationHeight, + ExpireHeight); + + _cachedHashCode = hash; + return hash; + } [Obsolete("This method is not implemented yet.")] public RebondGraceEntry Slash() diff --git a/Lib9c/Delegation/RedelegateResult.cs b/Lib9c/Delegation/RedelegateResult.cs index 84b80615e6..01181ca606 100644 --- a/Lib9c/Delegation/RedelegateResult.cs +++ b/Lib9c/Delegation/RedelegateResult.cs @@ -1,3 +1,4 @@ +#nullable enable namespace Nekoyume.Delegation { public class RedelegateResult : IRedelegateResult @@ -19,14 +20,14 @@ public RedelegateResult( UnbondingSet = unbondingSet; } - IDelegatee IRedelegateResult.SrcDelegatee => SrcDelegatee; - - IDelegatee IRedelegateResult.DstDelegatee => DstDelegatee; - public T SrcDelegatee { get; } + IDelegatee IRedelegateResult.SrcDelegatee => SrcDelegatee; + public T DstDelegatee { get; } + IDelegatee IRedelegateResult.DstDelegatee => DstDelegatee; + public Bond SrcBond { get; } public Bond DstBond { get; } diff --git a/Lib9c/Delegation/UnbondLockIn.cs b/Lib9c/Delegation/UnbondLockIn.cs index 18c3915f48..b8e5ba20c2 100644 --- a/Lib9c/Delegation/UnbondLockIn.cs +++ b/Lib9c/Delegation/UnbondLockIn.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -84,13 +85,15 @@ private UnbondLockIn( public ImmutableArray FlattenedEntries => Entries.Values.SelectMany(e => e).ToImmutableArray(); - public IValue Bencoded + public List Bencoded => new List( Entries.Select( sortedDict => new List( (Integer)sortedDict.Key, new List(sortedDict.Value.Select(e => e.Bencoded))))); + IValue IBencodable.Bencoded => Bencoded; + public UnbondLockIn Release(long height) { if (height <= 0) @@ -135,14 +138,15 @@ public UnbondLockIn Slash() return releasedFAV; } - public override bool Equals(object obj) + public override bool Equals(object? obj) => obj is UnbondLockIn other && Equals(other); - public bool Equals(UnbondLockIn other) + public bool Equals(UnbondLockIn? other) => ReferenceEquals(this, other) - || (Address.Equals(other.Address) - && MaxEntries == other.MaxEntries - && FlattenedEntries.SequenceEqual(other.FlattenedEntries)); + || (other is UnbondLockIn unbondLockIn + && Address.Equals(unbondLockIn.Address) + && MaxEntries == unbondLockIn.MaxEntries + && FlattenedEntries.SequenceEqual(unbondLockIn.FlattenedEntries)); public override int GetHashCode() => Address.GetHashCode(); @@ -255,6 +259,8 @@ private UnbondLockIn AddEntry(UnbondLockInEntry entry) public class UnbondLockInEntry : IBencodable, IEquatable { + private int? _cachedHashCode; + public UnbondLockInEntry( FungibleAssetValue lockInFAV, long creationHeight, @@ -337,18 +343,41 @@ private UnbondLockInEntry( public long ExpireHeight { get; } - public IValue Bencoded => List.Empty + public List Bencoded => List.Empty .Add(InitialLockInFAV.Serialize()) .Add(LockInFAV.Serialize()) .Add(CreationHeight) .Add(ExpireHeight); - public bool Equals(UnbondLockInEntry other) + IValue IBencodable.Bencoded => Bencoded; + + public override bool Equals(object? obj) + => obj is UnbondLockInEntry other && Equals(other); + + public bool Equals(UnbondLockInEntry? other) => ReferenceEquals(this, other) - || (InitialLockInFAV.Equals(other.InitialLockInFAV) - && LockInFAV.Equals(other.LockInFAV) - && CreationHeight == other.CreationHeight - && ExpireHeight == other.ExpireHeight); + || (other is UnbondLockInEntry unbondLockInEntry + && InitialLockInFAV.Equals(unbondLockInEntry.InitialLockInFAV) + && LockInFAV.Equals(unbondLockInEntry.LockInFAV) + && CreationHeight == unbondLockInEntry.CreationHeight + && ExpireHeight == unbondLockInEntry.ExpireHeight); + + public override int GetHashCode() + { + if (_cachedHashCode is int cached) + { + return cached; + } + + int hash = HashCode.Combine( + InitialLockInFAV, + LockInFAV, + CreationHeight, + ExpireHeight); + + _cachedHashCode = hash; + return hash; + } [Obsolete("This method is not implemented yet.")] public UnbondLockInEntry Slash() diff --git a/Lib9c/Delegation/UnbondResult.cs b/Lib9c/Delegation/UnbondResult.cs index ba292c9fac..a21f99fcc1 100644 --- a/Lib9c/Delegation/UnbondResult.cs +++ b/Lib9c/Delegation/UnbondResult.cs @@ -1,3 +1,4 @@ +#nullable enable using Libplanet.Types.Assets; namespace Nekoyume.Delegation diff --git a/Lib9c/Delegation/UnbondingSet.cs b/Lib9c/Delegation/UnbondingSet.cs index 7bd3f4c954..d6323966da 100644 --- a/Lib9c/Delegation/UnbondingSet.cs +++ b/Lib9c/Delegation/UnbondingSet.cs @@ -1,3 +1,4 @@ +#nullable enable using System.Collections.Immutable; using System.Linq; using Bencodex; @@ -27,22 +28,29 @@ public UnbondingSet(List bencoded) } private UnbondingSet( - ImmutableSortedSet
unbondLockIns, ImmutableSortedSet
rebondGraces) + ImmutableSortedSet
unbondLockIns, + ImmutableSortedSet
rebondGraces) { UnbondLockIns = unbondLockIns; RebondGraces = rebondGraces; } + public static Address Address => new Address( + ImmutableArray.Create( + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); public ImmutableSortedSet
UnbondLockIns { get; } public ImmutableSortedSet
RebondGraces { get; } - public IValue Bencoded + public List Bencoded => List.Empty .Add(new List(UnbondLockIns.Select(a => a.Bencoded))) .Add(new List(RebondGraces.Select(a => a.Bencoded))); + IValue IBencodable.Bencoded => Bencoded; + public UnbondingSet AddUnbondLockIn(Address address) => new UnbondingSet(UnbondLockIns.Add(address), RebondGraces); diff --git a/Lib9c/Delegation/UndelegateResult.cs b/Lib9c/Delegation/UndelegateResult.cs index 47608cc247..6a216d9ea9 100644 --- a/Lib9c/Delegation/UndelegateResult.cs +++ b/Lib9c/Delegation/UndelegateResult.cs @@ -1,3 +1,4 @@ +#nullable enable namespace Nekoyume.Delegation { public class UndelegateResult : IUndelegateResult @@ -15,10 +16,10 @@ public UndelegateResult( UnbondingSet = unbondingSet; } - IDelegatee IUndelegateResult.Delegatee => Delegatee; - public T Delegatee { get; } + IDelegatee IUndelegateResult.Delegatee => Delegatee; + public Bond Bond { get; } public UnbondLockIn UnbondLockIn { get; } From ec764802c04f2a6361fff16e12cace3368ba5d0c Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sat, 3 Aug 2024 22:23:41 +0900 Subject: [PATCH 010/206] feat: Inject delegation interface to guild --- Lib9c/Currencies.cs | 3 +++ Lib9c/Model/Guild/Guild.cs | 28 ++++++++++++++++++++++----- Lib9c/Model/Guild/GuildParticipant.cs | 17 +++++++++++----- 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/Lib9c/Currencies.cs b/Lib9c/Currencies.cs index c13a0620b2..4f702b8548 100644 --- a/Lib9c/Currencies.cs +++ b/Lib9c/Currencies.cs @@ -65,6 +65,9 @@ public static class Currencies public static readonly Currency Mead = Currency.Legacy("Mead", 18, null); + public static readonly Currency GuildGold = Currency.Uncapped( + "GUILD_GOLD", 18, null); + /// /// Covers the reward.CurrencyTicker is following cases: /// - Currencies.Crystal.Ticker diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index 7f38a88579..76e4d63bcb 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -1,13 +1,16 @@ using System; using Bencodex; using Bencodex.Types; +using Lib9c; using Libplanet.Crypto; +using Libplanet.Types.Assets; using Nekoyume.Action; +using Nekoyume.Delegation; using Nekoyume.TypedAddress; namespace Nekoyume.Model.Guild { - public class Guild : IEquatable, IBencodable + public class Guild : Delegatee, IEquatable, IBencodable { private const string StateTypeName = "guild"; private const long StateVersion = 1; @@ -15,12 +18,16 @@ public class Guild : IEquatable, IBencodable public readonly AgentAddress GuildMasterAddress; public Guild(AgentAddress guildMasterAddress) + : base(guildMasterAddress) { GuildMasterAddress = guildMasterAddress; } - public Guild(List list) : this(new AgentAddress(list[2])) + public Guild(List list) + : base(new Address(list[2]), list[3]) { + GuildMasterAddress = new AgentAddress(list[2]); + if (list[0] is not Text text || text != StateTypeName || list[1] is not Integer integer) { throw new InvalidCastException(); @@ -30,12 +37,22 @@ public Guild(List list) : this(new AgentAddress(list[2])) { throw new FailedLoadStateException("Un-deserializable state."); } + } - public List Bencoded => List.Empty + public override Currency Currency => Currencies.GuildGold; + + public override Address PoolAddress => DeriveAddress(PoolId); + + public override long UnbondingPeriod => 75600L; + + public override byte[] DelegateeId => new byte[] { 0x047 }; // `G` + + public new List Bencoded => List.Empty .Add(StateTypeName) .Add(StateVersion) - .Add(GuildMasterAddress.Bencoded); + .Add(GuildMasterAddress.Bencoded) + .Add(base.Bencoded); IValue IBencodable.Bencoded => Bencoded; @@ -43,7 +60,8 @@ public bool Equals(Guild other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return GuildMasterAddress.Equals(other.GuildMasterAddress); + return GuildMasterAddress.Equals(other.GuildMasterAddress) + && base.Equals(other); } public override bool Equals(object obj) diff --git a/Lib9c/Model/Guild/GuildParticipant.cs b/Lib9c/Model/Guild/GuildParticipant.cs index 4b40798073..56b509ca9c 100644 --- a/Lib9c/Model/Guild/GuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildParticipant.cs @@ -3,11 +3,12 @@ using Bencodex.Types; using Libplanet.Crypto; using Nekoyume.Action; +using Nekoyume.Delegation; using Nekoyume.TypedAddress; namespace Nekoyume.Model.Guild { - public class GuildParticipant : IBencodable, IEquatable + public class GuildParticipant : Delegator, IBencodable, IEquatable { private const string StateTypeName = "guild_participant"; private const long StateVersion = 1; @@ -15,12 +16,16 @@ public class GuildParticipant : IBencodable, IEquatable public readonly GuildAddress GuildAddress; public GuildParticipant(GuildAddress guildAddress) + : base(guildAddress) { GuildAddress = guildAddress; } - public GuildParticipant(List list) : this(new GuildAddress(list[2])) + public GuildParticipant(List list) + : base(new Address(list[2]), list[3]) { + GuildAddress = new GuildAddress(list[2]); + if (list[0] is not Text text || text != StateTypeName || list[1] is not Integer integer) { throw new InvalidCastException(); @@ -32,10 +37,11 @@ public GuildParticipant(List list) : this(new GuildAddress(list[2])) } } - public List Bencoded => List.Empty + public new List Bencoded => List.Empty .Add(StateTypeName) .Add(StateVersion) - .Add(GuildAddress.Bencoded); + .Add(GuildAddress.Bencoded) + .Add(base.Bencoded); IValue IBencodable.Bencoded => Bencoded; @@ -43,7 +49,8 @@ public bool Equals(GuildParticipant other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return GuildAddress.Equals(other.GuildAddress); + return GuildAddress.Equals(other.GuildAddress) + && base.Equals(other); } public override bool Equals(object obj) From 166fa2c284ea1d5de6891faea2ff94cae4795210 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sun, 4 Aug 2024 17:44:51 +0900 Subject: [PATCH 011/206] feat: Introduce Module.Delegation --- Lib9c/Addresses.cs | 28 +++++++++ Lib9c/Model/Guild/Guild.cs | 4 ++ Lib9c/Module/Delegation/BondModule.cs | 50 ++++++++++++++++ Lib9c/Module/Delegation/RebondGraceModule.cs | 57 ++++++++++++++++++ Lib9c/Module/Delegation/UnbondLockInModule.cs | 58 +++++++++++++++++++ Lib9c/Module/Delegation/UnbondingSetModule.cs | 44 ++++++++++++++ 6 files changed, 241 insertions(+) create mode 100644 Lib9c/Module/Delegation/BondModule.cs create mode 100644 Lib9c/Module/Delegation/RebondGraceModule.cs create mode 100644 Lib9c/Module/Delegation/UnbondLockInModule.cs create mode 100644 Lib9c/Module/Delegation/UnbondingSetModule.cs diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index f5881717af..473f9701f5 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -96,6 +96,34 @@ public static Address GetGuildBanAccountAddress(Address guildAddress) => #endregion + #region Delegation + + /// + /// An address of an account having . + /// + public static readonly Address UnbondingSet + = new Address("0000000000000000000000000000000000000300"); + + /// + /// An address of an account having . + /// + public static readonly Address Bond + = new Address("0000000000000000000000000000000000000301"); + + /// + /// An address of an account having . + /// + public static readonly Address UnbondLockIn + = new Address("0000000000000000000000000000000000000302"); + + /// + /// An address of an account having . + /// + public static readonly Address RebondGrace + = new Address("0000000000000000000000000000000000000303"); + + #endregion + public static Address GetSheetAddress() where T : ISheet => GetSheetAddress(typeof(T).Name); public static Address GetSheetAddress(string sheetName) => TableSheet.Derive(sheetName); diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index 76e4d63bcb..e49ec70400 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -48,6 +48,10 @@ public Guild(List list) public override byte[] DelegateeId => new byte[] { 0x047 }; // `G` + public override int MaxUnbondLockInEntries => 10; + + public override int MaxRebondGraceEntries => 10; + public new List Bencoded => List.Empty .Add(StateTypeName) .Add(StateVersion) diff --git a/Lib9c/Module/Delegation/BondModule.cs b/Lib9c/Module/Delegation/BondModule.cs new file mode 100644 index 0000000000..da804049d7 --- /dev/null +++ b/Lib9c/Module/Delegation/BondModule.cs @@ -0,0 +1,50 @@ +#nullable enable +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Delegation; +using Nekoyume.Extensions; + +namespace Nekoyume.Module.Delegation +{ + public static class BondModule + { + public static Bond GetBond( + this IWorldState world, IDelegatee delegatee, Address delegatorAddress) + => GetBond(world, delegatee.BondAddress(delegatorAddress)); + + public static Bond GetBond(this IWorldState world, Address address) + => TryGetBond(world, address, out var bond) + ? bond! + : new Bond(address); + + public static bool TryGetBond( + this IWorldState world, Address address, out Bond? bond) + { + try + { + var value = world.GetAccountState(Addresses.Bond).GetState(address); + if (!(value is List list)) + { + bond = null; + return false; + } + + bond = new Bond(address, list); + return true; + } + catch + { + bond = null; + return false; + } + } + + public static IWorld SetBond(this IWorld world, Bond bond) + => world.MutateAccount( + Addresses.Bond, + account => bond.Share.IsZero + ? account.RemoveState(bond.Address) + : account.SetState(bond.Address, bond.Bencoded)); + } +} diff --git a/Lib9c/Module/Delegation/RebondGraceModule.cs b/Lib9c/Module/Delegation/RebondGraceModule.cs new file mode 100644 index 0000000000..6bb3d7d461 --- /dev/null +++ b/Lib9c/Module/Delegation/RebondGraceModule.cs @@ -0,0 +1,57 @@ +#nullable enable +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Delegation; +using Nekoyume.Extensions; + +namespace Nekoyume.Module.Delegation +{ + public static class RebondGraceModule + { + public static RebondGrace GetRebondGrace( + this IWorldState world, IDelegatee delegatee, Address delegatorAddress) + => GetRebondGrace( + world, + delegatee.RebondGraceAddress(delegatorAddress), + delegatee.MaxRebondGraceEntries); + + public static RebondGrace GetRebondGrace( + this IWorldState world, Address address, int maxEntries) + => TryGetRebondGrace(world, address, maxEntries, out var rebondGrace) + ? rebondGrace! + : new RebondGrace(address, maxEntries); + + public static bool TryGetRebondGrace( + this IWorldState world, + Address address, + int maxEntries, + out RebondGrace? rebondGrace) + { + try + { + var value = world.GetAccountState(Addresses.RebondGrace).GetState(address); + if (!(value is Bencodex.Types.List list)) + { + rebondGrace = null; + return false; + } + + rebondGrace = new RebondGrace(address, maxEntries, list); + return true; + } + catch + { + rebondGrace = null; + return false; + } + } + + public static IWorld SetRebondGrace( + this IWorld world, RebondGrace rebondGrace) + => world.MutateAccount( + Addresses.RebondGrace, + account => rebondGrace.IsEmpty + ? account.RemoveState(rebondGrace.Address) + : account.SetState(rebondGrace.Address, rebondGrace.Bencoded)); + } +} diff --git a/Lib9c/Module/Delegation/UnbondLockInModule.cs b/Lib9c/Module/Delegation/UnbondLockInModule.cs new file mode 100644 index 0000000000..03cef17ac8 --- /dev/null +++ b/Lib9c/Module/Delegation/UnbondLockInModule.cs @@ -0,0 +1,58 @@ +#nullable enable +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Delegation; +using Nekoyume.Extensions; + +namespace Nekoyume.Module.Delegation +{ + public static class UnbondLockInModule + { + public static UnbondLockIn GetUnbondLockIn( + this IWorldState world, IDelegatee delegatee, Address delegatorAddress) + => GetUnbondLockIn( + world, + delegatee.UnbondLockInAddress(delegatorAddress), + delegatee.MaxUnbondLockInEntries); + + public static UnbondLockIn GetUnbondLockIn( + this IWorldState world, Address address, int maxEntries) + => TryGetUnbondLockIn(world, address, maxEntries, out var unbondLockIn) + ? unbondLockIn! + : new UnbondLockIn(address, maxEntries); + + public static bool TryGetUnbondLockIn( + this IWorldState world, + Address address, + int maxEntries, + out UnbondLockIn? unbondLockIn) + { + try + { + var value = world.GetAccountState(Addresses.UnbondLockIn).GetState(address); + if (!(value is List list)) + { + unbondLockIn = null; + return false; + } + + unbondLockIn = new UnbondLockIn(address, maxEntries, list); + return true; + } + catch + { + unbondLockIn = null; + return false; + } + } + + public static IWorld SetUnbondLockIn( + this IWorld world, UnbondLockIn unbondLockIn) + => world.MutateAccount( + Addresses.UnbondLockIn, + account => unbondLockIn.IsEmpty + ? account.RemoveState(unbondLockIn.Address) + : account.SetState(unbondLockIn.Address, unbondLockIn.Bencoded)); + } +} diff --git a/Lib9c/Module/Delegation/UnbondingSetModule.cs b/Lib9c/Module/Delegation/UnbondingSetModule.cs new file mode 100644 index 0000000000..5cb2d081c7 --- /dev/null +++ b/Lib9c/Module/Delegation/UnbondingSetModule.cs @@ -0,0 +1,44 @@ +#nullable enable +using Bencodex.Types; +using Libplanet.Action.State; +using Nekoyume.Delegation; +using Nekoyume.Extensions; + +namespace Nekoyume.Module.Delegation +{ + public static class UnbondingSetModule + { + public static UnbondingSet GetUnbondingSet(this IWorldState world) + => TryGetUnbondingSet(world, out var unbondingSet) + ? unbondingSet! + : new UnbondingSet(); + + public static bool TryGetUnbondingSet( + this IWorldState world, out UnbondingSet? unbondingSet) + { + try + { + var value = world.GetAccountState(Addresses.UnbondingSet) + .GetState(UnbondingSet.Address); + if (!(value is List list)) + { + unbondingSet = null; + return false; + } + + unbondingSet = new UnbondingSet(list); + return true; + } + catch + { + unbondingSet = null; + return false; + } + } + + public static IWorld SetUnbondingSet(this IWorld world, UnbondingSet unbondingSet) + => world.MutateAccount( + Addresses.UnbondingSet, + account => account.SetState(UnbondingSet.Address, unbondingSet.Bencoded)); + } +} From 5ebdb210d6d21eaa580f65625e86be4c7a327dbc Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 5 Aug 2024 15:36:20 +0900 Subject: [PATCH 012/206] feat: Sort entries of unbonding entities --- Lib9c/Delegation/RebondGrace.cs | 58 +++++++++++++++++++++++++++++++- Lib9c/Delegation/UnbondLockIn.cs | 56 ++++++++++++++++++++++++++++-- 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/Lib9c/Delegation/RebondGrace.cs b/Lib9c/Delegation/RebondGrace.cs index ff17f99c20..6703cb3806 100644 --- a/Lib9c/Delegation/RebondGrace.cs +++ b/Lib9c/Delegation/RebondGrace.cs @@ -12,6 +12,9 @@ namespace Nekoyume.Delegation { public sealed class RebondGrace : IBencodable, IEquatable { + private static readonly IComparer _entryComparer + = new RebondGraceEntryComparer(); + public RebondGrace(Address address, int maxEntries) : this( address, @@ -70,6 +73,8 @@ private RebondGrace( public int MaxEntries { get; } + public long lowestExpireHeight => Entries.First().Key; + public bool IsFull => Entries.Values.Sum(e => e.Count) >= MaxEntries; public bool IsEmpty => Entries.IsEmpty; @@ -156,7 +161,11 @@ private RebondGrace AddEntry(RebondGraceEntry entry) if (Entries.TryGetValue(entry.ExpireHeight, out var entries)) { - return UpdateEntries(Entries.SetItem(entry.ExpireHeight, entries.Add(entry))); + int index = entries.BinarySearch(entry, _entryComparer); + return UpdateEntries( + Entries.SetItem( + entry.ExpireHeight, + entries.Insert(index < 0 ? ~index : index, entry))); } else { @@ -305,5 +314,52 @@ public override int GetHashCode() public RebondGraceEntry Slash() => throw new NotImplementedException(); } + + public class RebondGraceEntryComparer : IComparer + { + public int Compare(RebondGraceEntry? x, RebondGraceEntry? y) + { + if (ReferenceEquals(x, y)) + { + return 0; + } + + if (x is null) + { + return -1; + } + + if (y is null) + { + return 1; + } + + int comparison = x.ExpireHeight.CompareTo(y.ExpireHeight); + if (comparison != 0) + { + return comparison; + } + + comparison = x.CreationHeight.CompareTo(y.CreationHeight); + if (comparison != 0) + { + return comparison; + } + + comparison = -x.InitialGraceFAV.CompareTo(y.InitialGraceFAV); + if (comparison != 0) + { + return comparison; + } + + comparison = -x.GraceFAV.CompareTo(y.GraceFAV); + if (comparison != 0) + { + return comparison; + } + + return x.RebondeeAddress.CompareTo(y.RebondeeAddress); + } + } } } diff --git a/Lib9c/Delegation/UnbondLockIn.cs b/Lib9c/Delegation/UnbondLockIn.cs index b8e5ba20c2..e3812c1b40 100644 --- a/Lib9c/Delegation/UnbondLockIn.cs +++ b/Lib9c/Delegation/UnbondLockIn.cs @@ -12,6 +12,9 @@ namespace Nekoyume.Delegation { public sealed class UnbondLockIn : IBencodable, IEquatable { + private static readonly IComparer _entryComparer + = new UnbondLockInEntryComparer(); + private FungibleAssetValue? _releasedFAV; public UnbondLockIn(Address address, int maxEntries) @@ -76,6 +79,8 @@ private UnbondLockIn( public int MaxEntries { get; } + public long lowestExpireHeight => Entries.First().Key; + public bool IsFull => Entries.Values.Sum(e => e.Count) >= MaxEntries; public bool IsEmpty => Entries.IsEmpty; @@ -202,7 +207,7 @@ internal UnbondLockIn Cancel(FungibleAssetValue cancellingFAV, long height) if (entry.value.LockInFAV <= cancellingFAV) { - cancellingFAV -= entry.value.LockInFAV; ; + cancellingFAV -= entry.value.LockInFAV; updatedEntries = updatedEntries.SetItem( expireHeight, updatedEntries[expireHeight].RemoveAt(entry.index)); @@ -210,7 +215,7 @@ internal UnbondLockIn Cancel(FungibleAssetValue cancellingFAV, long height) else { var cancelledEntry = entry.value.Cancel(cancellingFAV); - cancellingFAV -= entry.value.LockInFAV; ; + cancellingFAV -= entry.value.LockInFAV; updatedEntries = updatedEntries.SetItem( expireHeight, updatedEntries[expireHeight].SetItem(entry.index, cancelledEntry)); @@ -247,7 +252,11 @@ private UnbondLockIn AddEntry(UnbondLockInEntry entry) if (Entries.TryGetValue(entry.ExpireHeight, out var entries)) { - return UpdateEntries(Entries.SetItem(entry.ExpireHeight, entries.Add(entry))); + int index = entries.BinarySearch(entry, _entryComparer); + return UpdateEntries( + Entries.SetItem( + entry.ExpireHeight, + entries.Insert(index < 0 ? ~index : index, entry))); } else { @@ -405,5 +414,46 @@ internal UnbondLockInEntry Cancel(FungibleAssetValue cancellingFAV) ExpireHeight); } } + + public class UnbondLockInEntryComparer : IComparer + { + public int Compare(UnbondLockInEntry? x, UnbondLockInEntry? y) + { + if (ReferenceEquals(x, y)) + { + return 0; + } + + if (x is null) + { + return -1; + } + + if (y is null) + { + return 1; + } + + int comparison = x.ExpireHeight.CompareTo(y.ExpireHeight); + if (comparison != 0) + { + return comparison; + } + + comparison = x.CreationHeight.CompareTo(y.CreationHeight); + if (comparison != 0) + { + return comparison; + } + + comparison = -x.InitialLockInFAV.CompareTo(y.InitialLockInFAV); + if (comparison != 0) + { + return comparison; + } + + return -x.LockInFAV.CompareTo(y.LockInFAV); + } + } } } From e526843869be649ce344753a75a80e8852cd7e3e Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 7 Aug 2024 11:41:42 +0900 Subject: [PATCH 013/206] feat: Complete release of unbondings --- Lib9c/Delegation/IUnbonding.cs | 19 ++ Lib9c/Delegation/IUnbondingEntry.cs | 7 + Lib9c/Delegation/RebondGrace.cs | 21 ++- Lib9c/Delegation/UnbondLockIn.cs | 21 ++- Lib9c/Delegation/UnbondingSet.cs | 168 +++++++++++++++--- Lib9c/Module/Delegation/UnbondingSetModule.cs | 36 ++++ 6 files changed, 233 insertions(+), 39 deletions(-) create mode 100644 Lib9c/Delegation/IUnbonding.cs create mode 100644 Lib9c/Delegation/IUnbondingEntry.cs diff --git a/Lib9c/Delegation/IUnbonding.cs b/Lib9c/Delegation/IUnbonding.cs new file mode 100644 index 0000000000..5df39151ef --- /dev/null +++ b/Lib9c/Delegation/IUnbonding.cs @@ -0,0 +1,19 @@ +using Libplanet.Crypto; + +namespace Nekoyume.Delegation +{ + public interface IUnbonding + { + Address Address { get; } + + long LowestExpireHeight { get; } + + bool IsFull { get; } + + bool IsEmpty { get; } + + IUnbonding Release(long height); + + IUnbonding Slash(); + } +} diff --git a/Lib9c/Delegation/IUnbondingEntry.cs b/Lib9c/Delegation/IUnbondingEntry.cs new file mode 100644 index 0000000000..01cf2d802c --- /dev/null +++ b/Lib9c/Delegation/IUnbondingEntry.cs @@ -0,0 +1,7 @@ +namespace Nekoyume.Delegation +{ + public interface IUnbondingEntry + { + long ExpireHeight { get; } + } +} diff --git a/Lib9c/Delegation/RebondGrace.cs b/Lib9c/Delegation/RebondGrace.cs index 6703cb3806..d19c761edf 100644 --- a/Lib9c/Delegation/RebondGrace.cs +++ b/Lib9c/Delegation/RebondGrace.cs @@ -10,7 +10,7 @@ namespace Nekoyume.Delegation { - public sealed class RebondGrace : IBencodable, IEquatable + public sealed class RebondGrace : IUnbonding, IBencodable, IEquatable { private static readonly IComparer _entryComparer = new RebondGraceEntryComparer(); @@ -73,12 +73,13 @@ private RebondGrace( public int MaxEntries { get; } - public long lowestExpireHeight => Entries.First().Key; + public long LowestExpireHeight => Entries.First().Key; public bool IsFull => Entries.Values.Sum(e => e.Count) >= MaxEntries; public bool IsEmpty => Entries.IsEmpty; + // TODO: Use better custom collection type public ImmutableSortedDictionary> Entries { get; } public ImmutableArray FlattenedEntries @@ -119,10 +120,14 @@ public RebondGrace Release(long height) return UpdateEntries(updatedEntries); } + IUnbonding IUnbonding.Release(long height) => Release(height); + [Obsolete("This method is not implemented yet.")] public RebondGrace Slash() => throw new NotImplementedException(); + IUnbonding IUnbonding.Slash() => Slash(); + public override bool Equals(object? obj) => obj is RebondGrace other && Equals(other); @@ -167,19 +172,17 @@ private RebondGrace AddEntry(RebondGraceEntry entry) entry.ExpireHeight, entries.Insert(index < 0 ? ~index : index, entry))); } - else - { - return UpdateEntries( - Entries.Add( - entry.ExpireHeight, ImmutableList.Empty.Add(entry))); - } + + return UpdateEntries( + Entries.Add( + entry.ExpireHeight, ImmutableList.Empty.Add(entry))); } private RebondGrace UpdateEntries( ImmutableSortedDictionary> entries) => new RebondGrace(Address, MaxEntries, entries); - public class RebondGraceEntry : IBencodable, IEquatable + public class RebondGraceEntry : IUnbondingEntry, IBencodable, IEquatable { private int? _cachedHashCode; diff --git a/Lib9c/Delegation/UnbondLockIn.cs b/Lib9c/Delegation/UnbondLockIn.cs index e3812c1b40..7647b3c698 100644 --- a/Lib9c/Delegation/UnbondLockIn.cs +++ b/Lib9c/Delegation/UnbondLockIn.cs @@ -10,7 +10,7 @@ namespace Nekoyume.Delegation { - public sealed class UnbondLockIn : IBencodable, IEquatable + public sealed class UnbondLockIn : IUnbonding, IBencodable, IEquatable { private static readonly IComparer _entryComparer = new UnbondLockInEntryComparer(); @@ -79,12 +79,13 @@ private UnbondLockIn( public int MaxEntries { get; } - public long lowestExpireHeight => Entries.First().Key; + public long LowestExpireHeight => Entries.First().Key; public bool IsFull => Entries.Values.Sum(e => e.Count) >= MaxEntries; public bool IsEmpty => Entries.IsEmpty; + // TODO: Use better custom collection type public ImmutableSortedDictionary> Entries { get; } public ImmutableArray FlattenedEntries @@ -132,10 +133,14 @@ public UnbondLockIn Release(long height) return UpdateEntries(updatedEntries, releasedFAV); } + IUnbonding IUnbonding.Release(long height) => Release(height); + [Obsolete("This method is not implemented yet.")] public UnbondLockIn Slash() => throw new NotImplementedException(); + IUnbonding IUnbonding.Slash() => Slash(); + public FungibleAssetValue? FlushReleasedFAV() { var releasedFAV = _releasedFAV; @@ -258,15 +263,13 @@ private UnbondLockIn AddEntry(UnbondLockInEntry entry) entry.ExpireHeight, entries.Insert(index < 0 ? ~index : index, entry))); } - else - { - return UpdateEntries( - Entries.Add( - entry.ExpireHeight, ImmutableList.Empty.Add(entry))); - } + + return UpdateEntries( + Entries.Add( + entry.ExpireHeight, ImmutableList.Empty.Add(entry))); } - public class UnbondLockInEntry : IBencodable, IEquatable + public class UnbondLockInEntry : IUnbondingEntry, IBencodable, IEquatable { private int? _cachedHashCode; diff --git a/Lib9c/Delegation/UnbondingSet.cs b/Lib9c/Delegation/UnbondingSet.cs index d6323966da..61a1dacbbd 100644 --- a/Lib9c/Delegation/UnbondingSet.cs +++ b/Lib9c/Delegation/UnbondingSet.cs @@ -1,4 +1,6 @@ #nullable enable +using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Bencodex; @@ -9,10 +11,18 @@ namespace Nekoyume.Delegation { public sealed class UnbondingSet : IBencodable { + private static readonly byte[] _unbondLockInTypeBytes = new byte[] { 0x75 }; // 'u' + private static readonly byte[] _rebondGraceTypeBytes = new byte[] { 0x72 }; // 'r' + + private ImmutableSortedDictionary _lowestExpireHeights; + private ImmutableSortedDictionary _typeDict; + public UnbondingSet() + : this( + ImmutableSortedDictionary>.Empty, + ImmutableSortedDictionary.Empty, + ImmutableSortedDictionary.Empty) { - UnbondLockIns = ImmutableSortedSet
.Empty; - RebondGraces = ImmutableSortedSet
.Empty; } public UnbondingSet(IValue bencoded) @@ -22,17 +32,32 @@ public UnbondingSet(IValue bencoded) public UnbondingSet(List bencoded) : this( - ((List)bencoded[0]).Select(e => new Address(e)).ToImmutableSortedSet(), - ((List)bencoded[1]).Select(e => new Address(e)).ToImmutableSortedSet()) + ((List)bencoded[1]).Select( + kv => new KeyValuePair>( + (Integer)((List)kv)[0], + ((List)((List)kv)[1]).Select(a => new Address(a)).ToImmutableSortedSet())) + .ToImmutableSortedDictionary(), + ((List)bencoded[2]).Select( + kv => new KeyValuePair( + new Address(((List)kv)[0]), + (Integer)((List)kv)[1])) + .ToImmutableSortedDictionary(), + ((List)bencoded[2]).Select( + kv => new KeyValuePair( + new Address(((List)kv)[0]), + ((Binary)((List)kv)[1]).ToArray())) + .ToImmutableSortedDictionary()) { } private UnbondingSet( - ImmutableSortedSet
unbondLockIns, - ImmutableSortedSet
rebondGraces) + ImmutableSortedDictionary> unbondings, + ImmutableSortedDictionary lowestExpireHeights, + ImmutableSortedDictionary typeDict) { - UnbondLockIns = unbondLockIns; - RebondGraces = rebondGraces; + Unbondings = unbondings; + _lowestExpireHeights = lowestExpireHeights; + _typeDict = typeDict; } public static Address Address => new Address( @@ -40,27 +65,128 @@ private UnbondingSet( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); - public ImmutableSortedSet
UnbondLockIns { get; } - - public ImmutableSortedSet
RebondGraces { get; } + public ImmutableSortedDictionary> Unbondings { get; } public List Bencoded => List.Empty - .Add(new List(UnbondLockIns.Select(a => a.Bencoded))) - .Add(new List(RebondGraces.Select(a => a.Bencoded))); + .Add(new List( + Unbondings.Select( + sortedDict => new List( + (Integer)sortedDict.Key, + new List(sortedDict.Value.Select(a => a.Bencoded)))))) + .Add(new List( + _lowestExpireHeights.Select( + sortedDict => new List( + sortedDict.Key.Bencoded, + (Integer)sortedDict.Value)))) + .Add(new List( + _typeDict.Select( + sortedDict => new List( + sortedDict.Key.Bencoded, + (Binary)sortedDict.Value)))); IValue IBencodable.Bencoded => Bencoded; - public UnbondingSet AddUnbondLockIn(Address address) - => new UnbondingSet(UnbondLockIns.Add(address), RebondGraces); + public UnbondingSet SetUnbondings(IEnumerable unbondings) + { + UnbondingSet result = this; + foreach (var unbonding in unbondings) + { + result = SetUnbonding(unbonding); + } + + return result; + } + + public UnbondingSet SetUnbonding(IUnbonding unbonding) + { + if (unbonding.IsEmpty) + { + return RemoveUnbonding(unbonding.Address); + } + + if (_lowestExpireHeights.TryGetValue(unbonding.Address, out var lowestExpireHeight)) + { + if (lowestExpireHeight == unbonding.LowestExpireHeight) + { + return this; + } + + var addresses = Unbondings[lowestExpireHeight]; + return new UnbondingSet( + Unbondings.SetItem( + unbonding.LowestExpireHeight, + addresses.Add(unbonding.Address)), + _lowestExpireHeights.SetItem( + unbonding.Address, unbonding.LowestExpireHeight), + _typeDict.SetItem( + unbonding.Address, ToTypeBytes(unbonding))); + } - public UnbondingSet RemoveUnbondLockIn(Address address) - => new UnbondingSet(UnbondLockIns.Remove(address), RebondGraces); + return new UnbondingSet( + Unbondings.SetItem( + unbonding.LowestExpireHeight, + ImmutableSortedSet
.Empty.Add(unbonding.Address)), + _lowestExpireHeights.SetItem( + unbonding.Address, unbonding.LowestExpireHeight), + _typeDict.SetItem( + unbonding.Address, ToTypeBytes(unbonding))); + } + + public UnbondingSet RemoveUnbonding(Address address) + { + if (_lowestExpireHeights.TryGetValue(address, out var expireHeight) + && Unbondings.TryGetValue(expireHeight, out var addresses)) + { + return new UnbondingSet( + Unbondings.SetItem(expireHeight, addresses.Remove(address)), + _lowestExpireHeights.Remove(address), + _typeDict.Remove(address)); + } + else + { + throw new ArgumentException("The address is not in the unbonding set."); + } + } - public UnbondingSet AddRebondGrace(Address address) - => new UnbondingSet(UnbondLockIns, RebondGraces.Add(address)); + public IUnbonding[] ReleaseUnbondings(long height, Func bencodedGetter) + { + return Unbondings + .TakeWhile(kv => kv.Key <= height) + .SelectMany(kv => kv.Value) + .Select(address => ( + Address: address, + Type: ToUnbondingType(_typeDict[address]))) + .Select(tuple => LoadUnbonding( + tuple.Address, + tuple.Type, + bencodedGetter(tuple.Address, tuple.Type))) + .Select(u => u.Release(height)).ToArray(); + } - public UnbondingSet RemoveRebondGrace(Address address) - => new UnbondingSet(UnbondLockIns, RebondGraces.Remove(address)); + private static byte[] ToTypeBytes(IUnbonding unbonding) + => unbonding switch + { + UnbondLockIn _ => _unbondLockInTypeBytes, + RebondGrace _ => _rebondGraceTypeBytes, + _ => throw new ArgumentException("Invalid unbonding type.") + }; + + private static Type ToUnbondingType(byte[] typeBytes) => typeBytes switch + { + _ when typeBytes.SequenceEqual(_unbondLockInTypeBytes) + => typeof(UnbondLockIn), + _ when typeBytes.SequenceEqual(_rebondGraceTypeBytes) + => typeof(RebondGrace), + _ => throw new ArgumentException("Invalid type bytes.") + }; + + private static IUnbonding LoadUnbonding(Address address, Type type, IValue bencoded) + => type switch + { + var t when t == typeof(UnbondLockIn) => new UnbondLockIn(address, int.MaxValue, bencoded), + var t when t == typeof(RebondGrace) => new RebondGrace(address, int.MaxValue, bencoded), + _ => throw new ArgumentException("Invalid unbonding type.") + }; } } diff --git a/Lib9c/Module/Delegation/UnbondingSetModule.cs b/Lib9c/Module/Delegation/UnbondingSetModule.cs index 5cb2d081c7..4f61a9e468 100644 --- a/Lib9c/Module/Delegation/UnbondingSetModule.cs +++ b/Lib9c/Module/Delegation/UnbondingSetModule.cs @@ -1,6 +1,10 @@ #nullable enable +using System; using Bencodex.Types; +using Libplanet.Action; using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Action; using Nekoyume.Delegation; using Nekoyume.Extensions; @@ -40,5 +44,37 @@ public static IWorld SetUnbondingSet(this IWorld world, UnbondingSet unbondingSe => world.MutateAccount( Addresses.UnbondingSet, account => account.SetState(UnbondingSet.Address, unbondingSet.Bencoded)); + + public static IWorld ReleaseUnbondigSet(this IWorld world, IActionContext context, UnbondingSet unbondingSet) + { + var releasedUnbondings = unbondingSet.ReleaseUnbondings( + context.BlockIndex, + (address, type) => world.GetAccount(AccountAddress(type)).GetState(address) + ?? throw new FailedLoadStateException( + $"Tried to release unbonding on {address}, but unbonding does not exist.")); + + foreach (var unbonding in releasedUnbondings) + { + world = unbonding switch + { + UnbondLockIn unbondLockIn => world.SetUnbondLockIn(unbondLockIn), + RebondGrace rebondGrace => world.SetRebondGrace(rebondGrace), + _ => throw new ArgumentException("Invalid unbonding type.") + }; + + unbondingSet = unbondingSet.SetUnbonding(unbonding); + } + + world = SetUnbondingSet(world, unbondingSet); + + return world; + } + + private static Address AccountAddress(Type type) => type switch + { + var t when t == typeof(UnbondLockIn) => Addresses.UnbondLockIn, + var t when t == typeof(RebondGrace) => Addresses.RebondGrace, + _ => throw new ArgumentException("Invalid unbonding type.") + }; } } From b9c3116714c2da1bcaf342d3c2863ce9522b1ade Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 7 Aug 2024 18:53:35 +0900 Subject: [PATCH 014/206] feat: Prepare delegation with guild --- Lib9c/Action/Delegate/ReleaseUnbondings.cs | 31 +++++++ Lib9c/Model/Guild/GuildParticipant.cs | 10 +-- Lib9c/Module/Delegation/UnbondingSetModule.cs | 27 +++--- Lib9c/Module/Guild/GuildModule.cs | 5 ++ Lib9c/Module/Guild/GuildParticipantModule.cs | 90 ++++++++++++++++++- 5 files changed, 143 insertions(+), 20 deletions(-) create mode 100644 Lib9c/Action/Delegate/ReleaseUnbondings.cs diff --git a/Lib9c/Action/Delegate/ReleaseUnbondings.cs b/Lib9c/Action/Delegate/ReleaseUnbondings.cs new file mode 100644 index 0000000000..9210165ffd --- /dev/null +++ b/Lib9c/Action/Delegate/ReleaseUnbondings.cs @@ -0,0 +1,31 @@ +using System; +using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.State; +using Nekoyume.Module.Delegation; + +namespace Nekoyume.Action.Delegate +{ + public class ReleaseUnbondings : ActionBase + { + public override IValue PlainValue => Null.Value; + + public override void LoadPlainValue(IValue plainValue) + { + throw new InvalidOperationException("Policy action shouldn't be serialized."); + } + + public override IWorld Execute(IActionContext context) + { + if(!context.IsPolicyAction) + { + throw new InvalidOperationException( + "This action must be called when it is a policy action."); + } + + var world = context.PreviousState; + + return world.Release(context); + } + } +} diff --git a/Lib9c/Model/Guild/GuildParticipant.cs b/Lib9c/Model/Guild/GuildParticipant.cs index 56b509ca9c..b96fd934d1 100644 --- a/Lib9c/Model/Guild/GuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildParticipant.cs @@ -15,14 +15,14 @@ public class GuildParticipant : Delegator, IBencodable, public readonly GuildAddress GuildAddress; - public GuildParticipant(GuildAddress guildAddress) - : base(guildAddress) + public GuildParticipant(AgentAddress agentAddress, GuildAddress guildAddress) + : base(agentAddress) { GuildAddress = guildAddress; } - public GuildParticipant(List list) - : base(new Address(list[2]), list[3]) + public GuildParticipant(AgentAddress agentAddress, List list) + : base(agentAddress, list[3]) { GuildAddress = new GuildAddress(list[2]); @@ -63,7 +63,7 @@ public override bool Equals(object obj) public override int GetHashCode() { - return GuildAddress.GetHashCode(); + return Address.GetHashCode(); } } } diff --git a/Lib9c/Module/Delegation/UnbondingSetModule.cs b/Lib9c/Module/Delegation/UnbondingSetModule.cs index 4f61a9e468..80a1ee45ff 100644 --- a/Lib9c/Module/Delegation/UnbondingSetModule.cs +++ b/Lib9c/Module/Delegation/UnbondingSetModule.cs @@ -45,31 +45,32 @@ public static IWorld SetUnbondingSet(this IWorld world, UnbondingSet unbondingSe Addresses.UnbondingSet, account => account.SetState(UnbondingSet.Address, unbondingSet.Bencoded)); - public static IWorld ReleaseUnbondigSet(this IWorld world, IActionContext context, UnbondingSet unbondingSet) + public static IWorld Release(this IWorld world, IActionContext context) { - var releasedUnbondings = unbondingSet.ReleaseUnbondings( - context.BlockIndex, - (address, type) => world.GetAccount(AccountAddress(type)).GetState(address) - ?? throw new FailedLoadStateException( - $"Tried to release unbonding on {address}, but unbonding does not exist.")); - - foreach (var unbonding in releasedUnbondings) + var unbondingSet = world.GetUnbondingSet(); + var releaseds = world.ReleaseUnbondings(context, unbondingSet); + foreach (var released in releaseds) { - world = unbonding switch + world = released switch { UnbondLockIn unbondLockIn => world.SetUnbondLockIn(unbondLockIn), RebondGrace rebondGrace => world.SetRebondGrace(rebondGrace), _ => throw new ArgumentException("Invalid unbonding type.") }; - unbondingSet = unbondingSet.SetUnbonding(unbonding); + unbondingSet = unbondingSet.SetUnbonding(released); } - world = SetUnbondingSet(world, unbondingSet); - - return world; + return world.SetUnbondingSet(unbondingSet); } + public static IUnbonding[] ReleaseUnbondings(this IWorld world, IActionContext context, UnbondingSet unbondingSet) + => unbondingSet.ReleaseUnbondings( + context.BlockIndex, + (address, type) => world.GetAccount(AccountAddress(type)).GetState(address) + ?? throw new FailedLoadStateException( + $"Tried to release unbonding on {address}, but unbonding does not exist.")); + private static Address AccountAddress(Type type) => type switch { var t when t == typeof(UnbondLockIn) => Addresses.UnbondLockIn, diff --git a/Lib9c/Module/Guild/GuildModule.cs b/Lib9c/Module/Guild/GuildModule.cs index 04a422734c..c4528e03c9 100644 --- a/Lib9c/Module/Guild/GuildModule.cs +++ b/Lib9c/Module/Guild/GuildModule.cs @@ -83,5 +83,10 @@ public static IWorld RemoveGuild(this IWorld world, AgentAddress signer) .MutateAccount(Addresses.Guild, account => account.RemoveState(guildAddress)) .RemoveBanList(guildAddress); } + + public static IWorld SetGuild(this IWorld world, Model.Guild.Guild guild) + => world.MutateAccount( + Addresses.Guild, + account => account.SetState(guild.Address, guild.Bencoded)); } } diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index e863a6bac9..7ab7479458 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -1,10 +1,16 @@ #nullable enable using System; using System.Diagnostics.CodeAnalysis; +using System.Numerics; using Bencodex.Types; +using Lib9c; +using Libplanet.Action; using Libplanet.Action.State; +using Libplanet.Types.Assets; using Nekoyume.Action; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; +using Nekoyume.Module.Delegation; using Nekoyume.TypedAddress; namespace Nekoyume.Module.Guild @@ -20,12 +26,35 @@ public static class GuildParticipantModule : null; } + public static IWorld JoinGuildWithDelegate( + this IWorld world, + IActionContext context, + GuildAddress guildAddress) + => world + .JoinGuild(guildAddress, new AgentAddress(context.Signer)) + .Delegate(context, guildAddress, world.GetBalance(context.Signer, Currencies.GuildGold)); + + public static IWorld LeaveGuildWithUndelegate( + this IWorld world, + IActionContext context) + { + var guild = world.GetJoinedGuild(new AgentAddress(context.Signer)) is GuildAddress guildAddr + ? world.GetGuild(guildAddr) + : throw new InvalidOperationException("The signer does not join any guild."); + + return world + .Undelegate(context, guildAddr, world.GetBond(guild, context.Signer).Share) + .LeaveGuild(new AgentAddress(context.Signer)); + } + + // TODO: Implement `MoveGuild()`, `MoveGuildWithRedelegate()`, `Redelegate()` method. + public static IWorld JoinGuild( this IWorld world, GuildAddress guildAddress, AgentAddress target) { - var guildParticipant = new Model.Guild.GuildParticipant(guildAddress); + var guildParticipant = new Model.Guild.GuildParticipant(target, guildAddress); return world.MutateAccount(Addresses.GuildParticipant, account => account.SetState(target, guildParticipant.Bencoded)) .IncreaseGuildMemberCount(guildAddress); @@ -72,7 +101,7 @@ private static Model.Guild.GuildParticipant GetGuildParticipant(this IWorldState .GetState(agentAddress); if (value is List list) { - return new Model.Guild.GuildParticipant(list); + return new Model.Guild.GuildParticipant(agentAddress, list); } throw new FailedLoadStateException("It may not join any guild."); @@ -99,5 +128,62 @@ private static IWorld RemoveGuildParticipant(this IWorld world, AgentAddress age return world.MutateAccount(Addresses.GuildParticipant, account => account.RemoveState(agentAddress)); } + + private static IWorld Delegate( + this IWorld world, + IActionContext context, + GuildAddress guildAddress, + FungibleAssetValue fav) + { + var agentAddress = new AgentAddress(context.Signer); + var guildParticipant = world.TryGetGuildParticipant(agentAddress, out var p) + ? p + : new GuildParticipant(agentAddress, guildAddress); + var guild = world.TryGetGuild(guildAddress, out var g) + ? g + : throw new InvalidOperationException("The guild does not exist."); + var bond = world.GetBond(guild, agentAddress); + var result = guildParticipant.Delegate(guild, fav, bond); + + return world + .SetBond(result.Bond) + .SetGuild(result.Delegatee) + .SetGuildParticipant(guildParticipant) + .TransferAsset(context, agentAddress, guildAddress, result.DelegatedFAV); + } + + private static IWorld Undelegate( + this IWorld world, + IActionContext context, + GuildAddress guildAddress, + BigInteger share) + { + var agentAddress = new AgentAddress(context.Signer); + var guildParticipant = world.TryGetGuildParticipant(agentAddress, out var p) + ? p + : new GuildParticipant(agentAddress, guildAddress); + var guild = world.TryGetGuild(guildAddress, out var g) + ? g + : throw new InvalidOperationException("The guild does not exist."); + var bond = world.GetBond(guild, agentAddress); + var unbondLockIn = world.GetUnbondLockIn(guild, agentAddress); + var unbondingSet = world.GetUnbondingSet(); + var result = guildParticipant.Undelegate( + guild, share, context.BlockIndex, bond, unbondLockIn, unbondingSet); + + return world + .SetBond(result.Bond) + .SetGuild(result.Delegatee) + .SetGuildParticipant(guildParticipant) + .SetUnbondLockIn(result.UnbondLockIn) + .SetUnbondingSet(result.UnbondingSet); + } + + private static IWorld SetGuildParticipant( + this IWorld world, + GuildParticipant guildParticipant) + => world.MutateAccount( + Addresses.GuildParticipant, + account => account.SetState(guildParticipant.Address, guildParticipant.Bencoded)); } } From 1e2d5cb5641fff0485b7393d63d609f85264f010 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 9 Aug 2024 14:15:25 +0900 Subject: [PATCH 015/206] feat: Implement reward system --- .Lib9c.Tests/Delegation/DelegateeTest.cs | 24 ++-- .Lib9c.Tests/Delegation/DelegatorTest.cs | 14 +-- .Lib9c.Tests/Delegation/DummyDelegatee.cs | 2 + .Lib9c.Tests/Delegation/TestDelegatee.cs | 2 + .../Model/Guild/GuildParticipantTest.cs | 5 +- .Lib9c.Tests/Model/Guild/GuildTest.cs | 9 +- Lib9c/Addresses.cs | 6 + Lib9c/Delegation/BondResult.cs | 6 +- Lib9c/Delegation/ClaimRewardResult.cs | 24 ++++ Lib9c/Delegation/DelegateResult.cs | 6 +- Lib9c/Delegation/Delegatee.cs | 102 +++++++++++---- Lib9c/Delegation/Delegator.cs | 87 ++++++++++--- Lib9c/Delegation/IClaimRewardResult.cs | 13 ++ Lib9c/Delegation/IDelegateResult.cs | 2 + Lib9c/Delegation/IDelegatee.cs | 14 ++- Lib9c/Delegation/IDelegator.cs | 8 +- Lib9c/Delegation/IRedelegateResult.cs | 4 + Lib9c/Delegation/IUndelegateResult.cs | 2 + Lib9c/Delegation/LumpSumRewardsRecord.cs | 119 ++++++++++++++++++ Lib9c/Delegation/RedelegateResult.cs | 10 +- Lib9c/Delegation/RewardResult.cs | 17 +++ Lib9c/Delegation/UnbondResult.cs | 6 +- Lib9c/Delegation/UndelegateResult.cs | 6 +- Lib9c/Model/Guild/Guild.cs | 8 +- Lib9c/Model/Guild/GuildParticipant.cs | 2 + .../Delegation/LumpSumRewardsRecordModule.cs | 66 ++++++++++ Lib9c/Module/Guild/GuildModule.cs | 4 +- Lib9c/Module/Guild/GuildParticipantModule.cs | 32 ++++- 28 files changed, 520 insertions(+), 80 deletions(-) create mode 100644 Lib9c/Delegation/ClaimRewardResult.cs create mode 100644 Lib9c/Delegation/IClaimRewardResult.cs create mode 100644 Lib9c/Delegation/LumpSumRewardsRecord.cs create mode 100644 Lib9c/Delegation/RewardResult.cs create mode 100644 Lib9c/Module/Delegation/LumpSumRewardsRecordModule.cs diff --git a/.Lib9c.Tests/Delegation/DelegateeTest.cs b/.Lib9c.Tests/Delegation/DelegateeTest.cs index cf02a4ffc3..50121bba57 100644 --- a/.Lib9c.Tests/Delegation/DelegateeTest.cs +++ b/.Lib9c.Tests/Delegation/DelegateeTest.cs @@ -34,7 +34,7 @@ public void CtorWithBencoded() var delegatee = _fixture.TestDelegatee1; var delegator = _fixture.TestDelegator1; var bond = _fixture.Bond1To1; - delegatee.Bond(delegator, delegatee.Currency * 10, bond); + delegatee.Bond(delegator, delegatee.Currency * 10, 10L, bond); var delegateeRecon = new TestDelegatee(address, delegatee.Bencoded); Assert.Equal(address, delegateeRecon.Address); @@ -70,7 +70,7 @@ public void Bond() totalShare += share; totalBonding += bonding; - var bondResult = testDelegatee.Bond(testDelegator1, bonding, bond1To1); + var bondResult = testDelegatee.Bond(testDelegator1, bonding, 10L, bond1To1); bond1To1 = bondResult.Bond; Assert.Equal(testDelegator1.Address, Assert.Single(testDelegatee.Delegators)); Assert.Equal(share, bondResult.BondedShare); @@ -83,7 +83,7 @@ public void Bond() share1 += share; totalShare += share; totalBonding += bonding; - bondResult = testDelegatee.Bond(testDelegator1, bonding, bond1To1); + bondResult = testDelegatee.Bond(testDelegator1, bonding, 20L, bond1To1); Assert.Equal(testDelegator1.Address, Assert.Single(testDelegatee.Delegators)); Assert.Equal(share, bondResult.BondedShare); Assert.Equal(share1, bondResult.Bond.Share); @@ -95,7 +95,7 @@ public void Bond() share2 += share; totalShare += share; totalBonding += bonding; - bondResult = testDelegatee.Bond(testDelegator2, bonding, bond2To1); + bondResult = testDelegatee.Bond(testDelegator2, bonding, 30L, bond2To1); Assert.Equal(2, testDelegatee.Delegators.Count); Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); Assert.Contains(testDelegator2.Address, testDelegatee.Delegators); @@ -115,7 +115,7 @@ public void CannotBondInvalidDelegator() Assert.Throws( () => testDelegatee.Bond( - dummyDelegator, testDelegatee.Currency * 10, bond)); + dummyDelegator, testDelegatee.Currency * 10, 10L, bond)); } [Fact] @@ -129,7 +129,7 @@ public void CannotBondInvalidCurrency() Assert.Throws( () => testDelegatee.Bond( - testDelegator, invalidCurrency * 10, bond)); + testDelegator, invalidCurrency * 10, 10L, bond)); } [Fact] @@ -151,21 +151,21 @@ public void Unbond() share1 += share; totalShares += share; totalDelegated += bonding; - bond1To1 = testDelegatee.Bond(testDelegator1, bonding, bond1To1).Bond; + bond1To1 = testDelegatee.Bond(testDelegator1, bonding, 1L, bond1To1).Bond; bonding = testDelegatee.Currency * 50; share = testDelegatee.ShareToBond(bonding); share2 += share; totalShares += share; totalDelegated += bonding; - bond2To1 = testDelegatee.Bond(testDelegator2, bonding, bond2To1).Bond; + bond2To1 = testDelegatee.Bond(testDelegator2, bonding, 2L, bond2To1).Bond; var unbonding = share1 / 2; share1 -= unbonding; totalShares -= unbonding; var unbondingFAV = testDelegatee.FAVToUnbond(unbonding); totalDelegated -= unbondingFAV; - var unbondResult = testDelegatee.Unbond(testDelegator1, unbonding, bond1To1); + var unbondResult = testDelegatee.Unbond(testDelegator1, unbonding, 3L, bond1To1); bond1To1 = unbondResult.Bond; Assert.Equal(2, testDelegatee.Delegators.Count); Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); @@ -180,7 +180,7 @@ public void Unbond() totalShares -= unbonding; unbondingFAV = testDelegatee.FAVToUnbond(unbonding); totalDelegated -= unbondingFAV; - unbondResult = testDelegatee.Unbond(testDelegator2, unbonding, bond2To1); + unbondResult = testDelegatee.Unbond(testDelegator2, unbonding, 4L, bond2To1); Assert.Equal(2, testDelegatee.Delegators.Count); Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); Assert.Contains(testDelegator2.Address, testDelegatee.Delegators); @@ -192,7 +192,7 @@ public void Unbond() totalShares -= share1; unbondingFAV = testDelegatee.FAVToUnbond(share1); totalDelegated -= unbondingFAV; - unbondResult = testDelegatee.Unbond(testDelegator1, share1, bond1To1); + unbondResult = testDelegatee.Unbond(testDelegator1, share1, 5L, bond1To1); Assert.Equal(testDelegator2.Address, Assert.Single(testDelegatee.Delegators)); Assert.Equal(unbondingFAV, unbondResult.UnbondedFAV); Assert.Equal(BigInteger.Zero, unbondResult.Bond.Share); @@ -206,7 +206,7 @@ public void CannotUnbondInvalidDelegator() IDelegatee delegatee = _fixture.TestDelegatee1; Assert.Throws( () => delegatee.Unbond( - _fixture.DummyDelegator1, BigInteger.One, _fixture.Bond1To1)); + _fixture.DummyDelegator1, BigInteger.One, 10L, _fixture.Bond1To1)); } [Fact] diff --git a/.Lib9c.Tests/Delegation/DelegatorTest.cs b/.Lib9c.Tests/Delegation/DelegatorTest.cs index 27c647fe59..4b319132fd 100644 --- a/.Lib9c.Tests/Delegation/DelegatorTest.cs +++ b/.Lib9c.Tests/Delegation/DelegatorTest.cs @@ -27,7 +27,7 @@ public void CtorWithBencoded() var delegator = _fixture.TestDelegator1; var delegatee = _fixture.TestDelegatee1; var bond = _fixture.Bond1To1; - delegator.Delegate(delegatee, delegatee.Currency * 10, bond); + delegator.Delegate(delegatee, delegatee.Currency * 10, 10L, bond); var delegatorRecon = new TestDelegator(delegator.Address, delegator.Bencoded); Assert.Equal(delegator.Address, delegatorRecon.Address); @@ -44,7 +44,7 @@ public void Delegate() var bond2 = _fixture.Bond1To2; var delegateFAV = delegatee1.Currency * 10; var delegateShare = delegatee1.ShareToBond(delegateFAV); - var result = delegator.Delegate(delegatee1, delegateFAV, bond1); + var result = delegator.Delegate(delegatee1, delegateFAV, 1L, bond1); bond1 = result.Bond; delegatee1 = result.Delegatee; Assert.Equal(delegateFAV, result.DelegatedFAV); @@ -56,7 +56,7 @@ public void Delegate() var delegateFAV2 = delegatee1.Currency * 20; var delegateShare2 = delegatee1.ShareToBond(delegateFAV2); - result = delegator.Delegate(delegatee1, delegateFAV2, bond1); + result = delegator.Delegate(delegatee1, delegateFAV2, 2L, bond1); Assert.Equal(delegateFAV2, result.DelegatedFAV); Assert.Equal(delegateShare + delegateShare2, result.Bond.Share); Assert.Equal(delegateFAV + delegateFAV2, delegatee1.TotalDelegated); @@ -64,7 +64,7 @@ public void Delegate() Assert.Equal(delegator.Address, Assert.Single(delegatee1.Delegators)); Assert.Equal(delegatee1.Address, Assert.Single(delegator.Delegatees)); - result = delegator.Delegate(delegatee2, delegateFAV, bond2); + result = delegator.Delegate(delegatee2, delegateFAV, 3L, bond2); Assert.Equal(delegateFAV, result.DelegatedFAV); Assert.Equal(delegateShare, result.Bond.Share); Assert.Equal(delegateFAV, delegatee2.TotalDelegated); @@ -83,7 +83,7 @@ public void Undelegate() var unbondLockIn = _fixture.Unbond1To1; var unbondingSet = _fixture.UnbondingSet; var delegateResult = delegator.Delegate( - delegatee, delegatee.Currency * 10, bond); + delegatee, delegatee.Currency * 10, 10L, bond); delegatee = delegateResult.Delegatee; bond = delegateResult.Bond; @@ -96,7 +96,7 @@ public void Undelegate() unbondLockIn = undelegateResult.UnbondLockIn; unbondingSet = undelegateResult.UnbondingSet; Assert.Equal(delegatee.Address, Assert.Single(delegator.Delegatees)); - Assert.Single(unbondingSet.UnbondLockIns); + Assert.Single(unbondingSet.Unbondings); var entriesByExpireHeight = Assert.Single(unbondLockIn.Entries); Assert.Equal(10L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); var entry = Assert.Single(entriesByExpireHeight.Value); @@ -142,7 +142,7 @@ public void Redelegate() var rebondGrace = _fixture.Rebond1To1; var unbondingSet = _fixture.UnbondingSet; var delegateResult = delegator.Delegate( - delegatee1, delegatee1.Currency * 10, bond1); + delegatee1, delegatee1.Currency * 10, 1L, bond1); delegatee1 = delegateResult.Delegatee; bond1 = delegateResult.Bond; Assert.Equal(delegatee1.Address, Assert.Single(delegator.Delegatees)); diff --git a/.Lib9c.Tests/Delegation/DummyDelegatee.cs b/.Lib9c.Tests/Delegation/DummyDelegatee.cs index 1db4fd453c..57642d00d1 100644 --- a/.Lib9c.Tests/Delegation/DummyDelegatee.cs +++ b/.Lib9c.Tests/Delegation/DummyDelegatee.cs @@ -12,6 +12,8 @@ public DummyDelegatee(Address address) public override Currency Currency => DelegationFixture.TestCurrency; + public override Currency RewardCurrency => DelegationFixture.TestCurrency; + public override Address PoolAddress => DelegationFixture.FixedPoolAddress; public override long UnbondingPeriod => 3; diff --git a/.Lib9c.Tests/Delegation/TestDelegatee.cs b/.Lib9c.Tests/Delegation/TestDelegatee.cs index 47a85c8271..adc6e0682e 100644 --- a/.Lib9c.Tests/Delegation/TestDelegatee.cs +++ b/.Lib9c.Tests/Delegation/TestDelegatee.cs @@ -19,6 +19,8 @@ public TestDelegatee(Address address, IValue bencoded) public override Currency Currency => DelegationFixture.TestCurrency; + public override Currency RewardCurrency => DelegationFixture.TestCurrency; + public override long UnbondingPeriod => 3; public override Address PoolAddress => DeriveAddress(PoolId); diff --git a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.cs b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.cs index 2a4e868f52..0ab88739e5 100644 --- a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.cs +++ b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.cs @@ -19,6 +19,7 @@ public GuildParticipantTest() public Task Snapshot() { var guild = new Nekoyume.Model.Guild.GuildParticipant( + new AgentAddress("0xB52B7F66B8464986f56053d82F0D80cA412A6F33"), new GuildAddress("0xd928ae87311dead490c986c24cc23c37eff892f2")); return Verifier.Verify(guild.Bencoded); @@ -28,9 +29,11 @@ public Task Snapshot() public void Serialization() { var guildParticipant = new Nekoyume.Model.Guild.GuildParticipant( + AddressUtil.CreateAgentAddress(), AddressUtil.CreateGuildAddress()); var newGuildParticipant = - new Nekoyume.Model.Guild.GuildParticipant(guildParticipant.Bencoded); + new Nekoyume.Model.Guild.GuildParticipant( + guildParticipant.AgentAddress, guildParticipant.Bencoded); Assert.Equal(guildParticipant.GuildAddress, newGuildParticipant.GuildAddress); } diff --git a/.Lib9c.Tests/Model/Guild/GuildTest.cs b/.Lib9c.Tests/Model/Guild/GuildTest.cs index 9fc1ce19e2..84e153d1fa 100644 --- a/.Lib9c.Tests/Model/Guild/GuildTest.cs +++ b/.Lib9c.Tests/Model/Guild/GuildTest.cs @@ -2,6 +2,7 @@ namespace Lib9c.Tests.Model.Guild { using System.Threading.Tasks; using Lib9c.Tests.Util; + using Libplanet.Types.Assets; using Nekoyume.TypedAddress; using VerifyTests; using VerifyXunit; @@ -19,7 +20,8 @@ public GuildTest() public Task Snapshot() { var guild = new Nekoyume.Model.Guild.Guild( - new AgentAddress("0xd928ae87311dead490c986c24cc23c37eff892f2")); + new AgentAddress("0xd928ae87311dead490c986c24cc23c37eff892f2"), + Currency.Legacy("NCG", 2, null)); return Verifier.Verify(guild.Bencoded); } @@ -28,8 +30,9 @@ public Task Snapshot() public void Serialization() { var guild = new Nekoyume.Model.Guild.Guild( - AddressUtil.CreateAgentAddress()); - var newGuild = new Nekoyume.Model.Guild.Guild(guild.Bencoded); + AddressUtil.CreateAgentAddress(), + Currency.Legacy("NCG", 2, null)); + var newGuild = new Nekoyume.Model.Guild.Guild(guild.Bencoded, Currency.Legacy("NCG", 2, null)); Assert.Equal(guild.GuildMasterAddress, newGuild.GuildMasterAddress); } diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index 473f9701f5..2a1adaee1d 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -122,6 +122,12 @@ public static readonly Address UnbondLockIn public static readonly Address RebondGrace = new Address("0000000000000000000000000000000000000303"); + /// + /// An address of an account having . + /// + public static readonly Address LumpSumRewardsRecord + = new Address("0000000000000000000000000000000000000304"); + #endregion public static Address GetSheetAddress() where T : ISheet => GetSheetAddress(typeof(T).Name); diff --git a/Lib9c/Delegation/BondResult.cs b/Lib9c/Delegation/BondResult.cs index bc858c9142..253e8ee88a 100644 --- a/Lib9c/Delegation/BondResult.cs +++ b/Lib9c/Delegation/BondResult.cs @@ -5,15 +5,17 @@ namespace Nekoyume.Delegation { public class BondResult { - public BondResult(Bond bond, BigInteger bondedShare) + public BondResult(Bond bond, BigInteger bondedShare, LumpSumRewardsRecord lumpSumRewardsRecord) { Bond = bond; - BondedShare = bondedShare; + LumpSumRewardsRecord = lumpSumRewardsRecord; } public Bond Bond { get; } public BigInteger BondedShare { get; } + + public LumpSumRewardsRecord LumpSumRewardsRecord { get; } } } diff --git a/Lib9c/Delegation/ClaimRewardResult.cs b/Lib9c/Delegation/ClaimRewardResult.cs new file mode 100644 index 0000000000..78e3d37c50 --- /dev/null +++ b/Lib9c/Delegation/ClaimRewardResult.cs @@ -0,0 +1,24 @@ +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public class ClaimRewardResult : IClaimRewardResult + where T: IDelegatee + { + public ClaimRewardResult( + T delegatee, FungibleAssetValue reward, LumpSumRewardsRecord lumpSumRewardsRecord) + { + Delegatee = delegatee; + Reward = reward; + LumpSumRewardsRecord = lumpSumRewardsRecord; + } + + public T Delegatee { get; } + + IDelegatee IClaimRewardResult.Delegatee => Delegatee; + + public FungibleAssetValue Reward { get; } + + public LumpSumRewardsRecord LumpSumRewardsRecord { get; } + } +} diff --git a/Lib9c/Delegation/DelegateResult.cs b/Lib9c/Delegation/DelegateResult.cs index fa815fcaa4..e7d10efd1d 100644 --- a/Lib9c/Delegation/DelegateResult.cs +++ b/Lib9c/Delegation/DelegateResult.cs @@ -9,11 +9,13 @@ public class DelegateResult : IDelegateResult public DelegateResult( T delegatee, Bond bond, - FungibleAssetValue delegatedFAV) + FungibleAssetValue delegatedFAV, + LumpSumRewardsRecord lumpSumRewardsRecord) { Delegatee = delegatee; Bond = bond; DelegatedFAV = delegatedFAV; + LumpSumRewardsRecord = lumpSumRewardsRecord; } public T Delegatee { get; } @@ -23,5 +25,7 @@ public DelegateResult( public Bond Bond { get; } public FungibleAssetValue DelegatedFAV { get; } + + public LumpSumRewardsRecord LumpSumRewardsRecord { get; } } } diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 30671d63ef..7b1ee57af7 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -16,11 +16,12 @@ public abstract class Delegatee : IDelegatee where T : Delegator where TSelf : Delegatee { - protected readonly byte[] BondId = new byte[] { 0x44 }; // `D` - protected readonly byte[] UnbondLockInId = new byte[] { 0x55 }; // `U` - protected readonly byte[] RebondGraceId = new byte[] { 0x52 }; // `R` - protected readonly byte[] RewardPoolId = new byte[] { 0x72 }; // `r` - protected readonly byte[] PoolId = new byte[] { 0x70 }; // `p` + protected readonly byte[] BondId = new byte[] { 0x44 }; // `D` + protected readonly byte[] UnbondLockInId = new byte[] { 0x55 }; // `U` + protected readonly byte[] RebondGraceId = new byte[] { 0x52 }; // `R` + protected readonly byte[] LumpSumRewardsRecordId = new byte[] { 0x4c }; // `L` + protected readonly byte[] RewardPoolId = new byte[] { 0x72 }; // `r` + protected readonly byte[] PoolId = new byte[] { 0x70 }; // `p` public Delegatee(Address address) { @@ -28,6 +29,7 @@ public Delegatee(Address address) Delegators = ImmutableSortedSet
.Empty; TotalDelegated = Currency * 0; TotalShares = BigInteger.Zero; + LastRewardPeriodStartHeight = -1; } public Delegatee(Address address, IValue bencoded) @@ -40,7 +42,8 @@ public Delegatee(Address address, List bencoded) address, ((List)bencoded[0]).Select(item => new Address(item)), new FungibleAssetValue(bencoded[1]), - (Integer)bencoded[2]) + (Integer)bencoded[2], + (Integer)bencoded[3]) { } @@ -48,7 +51,8 @@ private Delegatee( Address address, IEnumerable
delegators, FungibleAssetValue totalDelegated, - BigInteger totalShares) + BigInteger totalShares, + long lastRewardPeriodStartHeight) { if (!totalDelegated.Currency.Equals(Currency)) { @@ -75,12 +79,15 @@ private Delegatee( Delegators = delegators.ToImmutableSortedSet(); TotalDelegated = totalDelegated; TotalShares = totalShares; + LastRewardPeriodStartHeight = lastRewardPeriodStartHeight; } public Address Address { get; } public abstract Currency Currency { get; } + public abstract Currency RewardCurrency { get; } + public abstract Address PoolAddress { get; } public abstract long UnbondingPeriod { get; } @@ -99,10 +106,13 @@ private Delegatee( public BigInteger TotalShares { get; private set; } + public long LastRewardPeriodStartHeight { get; private set; } + public List Bencoded => List.Empty .Add(new List(Delegators.Select(delegator => delegator.Bencoded))) .Add(TotalDelegated.Serialize()) - .Add(TotalShares); + .Add(TotalShares) + .Add(LastRewardPeriodStartHeight); IValue IBencodable.Bencoded => Bencoded; @@ -117,14 +127,14 @@ public FungibleAssetValue FAVToUnbond(BigInteger share) : (TotalDelegated * share).DivRem(TotalShares, out _); BondResult IDelegatee.Bond( - IDelegator delegator, FungibleAssetValue fav, Bond bond) - => Bond((T)delegator, fav, bond); + IDelegator delegator, FungibleAssetValue fav, long height, Bond bond) + => Bond((T)delegator, fav, height, bond); UnbondResult IDelegatee.Unbond( - IDelegator delegator, BigInteger share, Bond bond) - => Unbond((T)delegator, share, bond); + IDelegator delegator, BigInteger share, long height, Bond bond) + => Unbond((T)delegator, share, height, bond); - public BondResult Bond(T delegator, FungibleAssetValue fav, Bond bond) + public BondResult Bond(T delegator, FungibleAssetValue fav, long height, Bond bond) { if (!fav.Currency.Equals(Currency)) { @@ -137,12 +147,14 @@ public BondResult Bond(T delegator, FungibleAssetValue fav, Bond bond) Delegators = Delegators.Add(delegator.Address); TotalShares += share; TotalDelegated += fav; - Distribute(); - - return new BondResult(bond, share); + var lumpSumRewardsRecord = StartNewRewardPeriod(LastRewardPeriodStartHeight); + LastRewardPeriodStartHeight = height; + + return new BondResult( + bond, share, lumpSumRewardsRecord); } - public UnbondResult Unbond(T delegator, BigInteger share, Bond bond) + public UnbondResult Unbond(T delegator, BigInteger share, long height, Bond bond) { if (TotalShares.IsZero || TotalDelegated.RawValue.IsZero) { @@ -159,14 +171,47 @@ public UnbondResult Unbond(T delegator, BigInteger share, Bond bond) TotalShares -= share; TotalDelegated -= fav; - Distribute(); + var lumpSumRewardsRecord = StartNewRewardPeriod(LastRewardPeriodStartHeight); + LastRewardPeriodStartHeight = height; - return new UnbondResult(bond, fav); + return new UnbondResult( + bond, fav, lumpSumRewardsRecord); } - public void Distribute() + public RewardResult Reward( + BigInteger share, + long height, + IEnumerable lumpSumRewardsRecords) { - // TODO: Implement this + FungibleAssetValue reward = RewardCurrency * 0; + long? linkedStartHeight = null; + + foreach (LumpSumRewardsRecord record in lumpSumRewardsRecords) + { + if (!(record.StartHeight is long startHeight)) + { + throw new ArgumentException("lump sum reward record wasn't started."); + } + + if (linkedStartHeight is long startHeightFromHigher + && startHeightFromHigher != startHeight) + { + throw new ArgumentException("lump sum reward record was started."); + } + + reward += record.RewardsDuringPeriod(share); + linkedStartHeight = record.LastStartHeight; + + if (linkedStartHeight == -1) + { + break; + } + } + + var lumpSumRewardsRecord = StartNewRewardPeriod(LastRewardPeriodStartHeight); + LastRewardPeriodStartHeight = height; + + return new RewardResult(reward, lumpSumRewardsRecord); } public Address BondAddress(Address delegatorAddress) @@ -178,6 +223,11 @@ public Address UnbondLockInAddress(Address delegatorAddress) public Address RebondGraceAddress(Address delegatorAddress) => DeriveAddress(RebondGraceId, delegatorAddress); + public Address LumpSumRewardsRecordAddress(long? height = null) + => height is long heightValue + ? DeriveAddress(LumpSumRewardsRecordId, BitConverter.GetBytes(heightValue)) + : DeriveAddress(LumpSumRewardsRecordId); + public override bool Equals(object? obj) => obj is IDelegatee other && Equals(other); @@ -193,6 +243,7 @@ public bool Equals(IDelegatee? other) && Delegators.SequenceEqual(delegatee.Delegators) && TotalDelegated.Equals(delegatee.TotalDelegated) && TotalShares.Equals(delegatee.TotalShares) + && LastRewardPeriodStartHeight.Equals(delegatee.LastRewardPeriodStartHeight) && DelegateeId.SequenceEqual(delegatee.DelegateeId)); public override int GetHashCode() @@ -212,5 +263,14 @@ protected Address DeriveAddress(byte[] typeId, IEnumerable? bytes = null) return new Address(hashed); } + + private LumpSumRewardsRecord StartNewRewardPeriod(long lastStartHeight = -1) + { + return new LumpSumRewardsRecord( + LumpSumRewardsRecordAddress(), + RewardCurrency, + TotalShares, + lastStartHeight); + } } } diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index 480618c98c..822dc56c9d 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Numerics; @@ -15,9 +16,8 @@ public abstract class Delegator : IDelegator where TSelf : Delegator { public Delegator(Address address) + : this(address, ImmutableSortedSet
.Empty, 0L) { - Address = address; - Delegatees = ImmutableSortedSet
.Empty; } public Delegator(Address address, IValue bencoded) @@ -25,26 +25,41 @@ public Delegator(Address address, IValue bencoded) { } - private Delegator(Address address, List bencoded) + public Delegator(Address address, List bencoded) + : this( + address, + ((List)bencoded[0]).Select(item => new Address(item)).ToImmutableSortedSet(), + (Integer)bencoded[1]) + { + } + + private Delegator( + Address address, ImmutableSortedSet
delegatees, long lastClaimRewardHeight) { Address = address; - Delegatees = bencoded.Select(item => new Address(item)).ToImmutableSortedSet(); + Delegatees = delegatees; + LastClaimRewardHeight = lastClaimRewardHeight; } public Address Address { get; } public ImmutableSortedSet
Delegatees { get; private set; } + public long LastClaimRewardHeight { get; private set; } + public List Bencoded - => new List(Delegatees.Select(a => a.Bencoded)); + => List.Empty + .Add(new List(Delegatees.Select(a => a.Bencoded))) + .Add(LastClaimRewardHeight); IValue IBencodable.Bencoded => Bencoded; IDelegateResult IDelegator.Delegate( IDelegatee delegatee, FungibleAssetValue fav, + long height, Bond bond) - => Delegate((T)delegatee, fav, bond); + => Delegate((T)delegatee, fav, height, bond); IUndelegateResult IDelegator.Undelegate( IDelegatee delegatee, @@ -80,9 +95,21 @@ IRedelegateResult IDelegator.Redelegate( srcRebondGrace, unbondingSet); + IClaimRewardResult IDelegator.ClaimReward( + IDelegatee delegatee, + IEnumerable lumpSumRewardRecords, + Bond bond, + long height) + => ClaimReward( + (T)delegatee, + lumpSumRewardRecords, + bond, + height); + public DelegateResult Delegate( T delegatee, FungibleAssetValue fav, + long height, Bond bond) { if (fav.Sign <= 0) @@ -91,10 +118,14 @@ public DelegateResult Delegate( nameof(fav), fav, "Fungible asset value must be positive."); } - BondResult bondResult = delegatee.Bond((TSelf)this, fav, bond); + BondResult bondResult = delegatee.Bond((TSelf)this, fav, height, bond); Delegatees = Delegatees.Add(delegatee.Address); - return new DelegateResult(delegatee, bondResult.Bond, fav); + return new DelegateResult( + delegatee, + bondResult.Bond, + fav, + bondResult.LumpSumRewardsRecord); } public UndelegateResult Undelegate( @@ -122,7 +153,7 @@ public UndelegateResult Undelegate( throw new InvalidOperationException("Undelegation is full."); } - UnbondResult unbondResult = delegatee.Unbond((TSelf)this, share, bond); + UnbondResult unbondResult = delegatee.Unbond((TSelf)this, share, height, bond); unbondLockIn = unbondLockIn.LockIn( unbondResult.UnbondedFAV, height, height + delegatee.UnbondingPeriod); @@ -131,13 +162,14 @@ public UndelegateResult Undelegate( Delegatees = Delegatees.Remove(delegatee.Address); } - unbondingSet = unbondingSet.AddUnbondLockIn(unbondLockIn.Address); + unbondingSet = unbondingSet.SetUnbonding(unbondLockIn); return new UndelegateResult( delegatee, unbondResult.Bond, unbondLockIn, - unbondingSet); + unbondingSet, + unbondResult.LumpSumRewardsRecord); } public RedelegateResult Redelegate( @@ -163,9 +195,9 @@ public RedelegateResult Redelegate( } UnbondResult srcUnbondResult = srcDelegatee.Unbond( - (TSelf)this, share, srcBond); + (TSelf)this, share, height, srcBond); BondResult dstBondResult = dstDelegatee.Bond( - (TSelf)this, srcUnbondResult.UnbondedFAV, dstBond); + (TSelf)this, srcUnbondResult.UnbondedFAV, height, dstBond); srcRebondGrace = srcRebondGrace.Grace( dstDelegatee.Address, srcUnbondResult.UnbondedFAV, @@ -178,7 +210,7 @@ public RedelegateResult Redelegate( } Delegatees = Delegatees.Add(dstDelegatee.Address); - unbondingSet.AddRebondGrace(srcRebondGrace.Address); + unbondingSet.SetUnbonding(srcRebondGrace); return new RedelegateResult( srcDelegatee, @@ -186,7 +218,9 @@ public RedelegateResult Redelegate( srcUnbondResult.Bond, dstBondResult.Bond, srcRebondGrace, - unbondingSet); + unbondingSet, + srcUnbondResult.LumpSumRewardsRecord, + dstBondResult.LumpSumRewardsRecord); } public UndelegateResult CancelUndelegate( @@ -214,23 +248,36 @@ public UndelegateResult CancelUndelegate( throw new InvalidOperationException("Undelegation is full."); } - BondResult bondResult = delegatee.Bond((TSelf)this, fav, bond); + BondResult bondResult = delegatee.Bond((TSelf)this, fav, height, bond); unbondLockIn = unbondLockIn.Cancel(fav, height); Delegatees = Delegatees.Add(delegatee.Address); unbondingSet = unbondLockIn.IsEmpty - ? unbondingSet.RemoveUnbondLockIn(unbondLockIn.Address) + ? unbondingSet.RemoveUnbonding(unbondLockIn.Address) : unbondingSet; return new UndelegateResult( delegatee, bondResult.Bond, unbondLockIn, - unbondingSet); + unbondingSet, + bondResult.LumpSumRewardsRecord); } - public void Claim(IDelegatee delegatee) + public ClaimRewardResult ClaimReward( + T delegatee, + IEnumerable lumpSumRewardRecords, + Bond bond, + long height) { - // TODO: Implement this + RewardResult rewardResult = delegatee.Reward( + bond.Share, + height, + lumpSumRewardRecords); + LastClaimRewardHeight = height; + return new ClaimRewardResult( + delegatee, + rewardResult.Reward, + rewardResult.LumpSumRewardsRecord); } public override bool Equals(object? obj) diff --git a/Lib9c/Delegation/IClaimRewardResult.cs b/Lib9c/Delegation/IClaimRewardResult.cs new file mode 100644 index 0000000000..045a02bc02 --- /dev/null +++ b/Lib9c/Delegation/IClaimRewardResult.cs @@ -0,0 +1,13 @@ +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public interface IClaimRewardResult + { + IDelegatee Delegatee { get; } + + FungibleAssetValue Reward { get; } + + LumpSumRewardsRecord LumpSumRewardsRecord { get; } + } +} diff --git a/Lib9c/Delegation/IDelegateResult.cs b/Lib9c/Delegation/IDelegateResult.cs index 18ea9ff2dd..5e521e122c 100644 --- a/Lib9c/Delegation/IDelegateResult.cs +++ b/Lib9c/Delegation/IDelegateResult.cs @@ -10,5 +10,7 @@ public interface IDelegateResult Bond Bond { get; } FungibleAssetValue DelegatedFAV { get; } + + LumpSumRewardsRecord LumpSumRewardsRecord { get; } } } diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index 2401c4e6d2..223e6e9b93 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Numerics; using Bencodex; @@ -14,6 +15,8 @@ public interface IDelegatee : IBencodable, IEquatable Currency Currency { get; } + Currency RewardCurrency { get; } + Address PoolAddress { get; } long UnbondingPeriod { get; } @@ -30,16 +33,21 @@ public interface IDelegatee : IBencodable, IEquatable BigInteger TotalShares { get; } - BondResult Bond(IDelegator delegator, FungibleAssetValue fav, Bond bond); + BondResult Bond(IDelegator delegator, FungibleAssetValue fav, long height, Bond bond); - UnbondResult Unbond(IDelegator delegator, BigInteger share, Bond bond); + UnbondResult Unbond(IDelegator delegator, BigInteger share, long height, Bond bond); - void Distribute(); + RewardResult Reward( + BigInteger share, + long height, + IEnumerable lumpSumRewardsRecords); Address BondAddress(Address delegatorAddress); Address UnbondLockInAddress(Address delegatorAddress); Address RebondGraceAddress(Address delegatorAddress); + + Address LumpSumRewardsRecordAddress(long? height = null); } } diff --git a/Lib9c/Delegation/IDelegator.cs b/Lib9c/Delegation/IDelegator.cs index d744d00da7..e1c7db0cf7 100644 --- a/Lib9c/Delegation/IDelegator.cs +++ b/Lib9c/Delegation/IDelegator.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Numerics; using Bencodex; @@ -17,6 +18,7 @@ public interface IDelegator : IBencodable, IEquatable IDelegateResult Delegate( IDelegatee delegatee, FungibleAssetValue fav, + long height, Bond bond); IUndelegateResult Undelegate( @@ -37,6 +39,10 @@ IRedelegateResult Redelegate( RebondGrace srcRebondGrace, UnbondingSet unbondingSet); - void Claim(IDelegatee delegatee); + IClaimRewardResult ClaimReward( + IDelegatee delegatee, + IEnumerable lumpSumRewardRecords, + Bond bond, + long height); } } diff --git a/Lib9c/Delegation/IRedelegateResult.cs b/Lib9c/Delegation/IRedelegateResult.cs index 7536ae4571..462286716d 100644 --- a/Lib9c/Delegation/IRedelegateResult.cs +++ b/Lib9c/Delegation/IRedelegateResult.cs @@ -14,5 +14,9 @@ public interface IRedelegateResult RebondGrace RebondGrace { get; } UnbondingSet UnbondingSet { get; } + + LumpSumRewardsRecord SrcLumpSumRewardsRecord { get; } + + LumpSumRewardsRecord DstLumpSumRewardsRecord { get; } } } diff --git a/Lib9c/Delegation/IUndelegateResult.cs b/Lib9c/Delegation/IUndelegateResult.cs index 10ecd5a8c2..78d9067ff5 100644 --- a/Lib9c/Delegation/IUndelegateResult.cs +++ b/Lib9c/Delegation/IUndelegateResult.cs @@ -10,5 +10,7 @@ public interface IUndelegateResult UnbondLockIn UnbondLockIn { get; } UnbondingSet UnbondingSet { get; } + + LumpSumRewardsRecord LumpSumRewardsRecord { get; } } } diff --git a/Lib9c/Delegation/LumpSumRewardsRecord.cs b/Lib9c/Delegation/LumpSumRewardsRecord.cs new file mode 100644 index 0000000000..a38aedfe0c --- /dev/null +++ b/Lib9c/Delegation/LumpSumRewardsRecord.cs @@ -0,0 +1,119 @@ +#nullable enable +using System; +using System.Linq; +using System.Numerics; +using Bencodex.Types; +using Bencodex; +using Libplanet.Crypto; +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public class LumpSumRewardsRecord : IBencodable, IEquatable + { + public LumpSumRewardsRecord( + Address address, + Currency currency, + BigInteger totalShares) + : this(address, totalShares, -1, currency * 0, null) + { + } + + public LumpSumRewardsRecord( + Address address, + Currency currency, + BigInteger totalShares, + long lastStartHeight) + : this(address, totalShares, lastStartHeight, currency * 0, null) + { + } + + public LumpSumRewardsRecord( + Address address, + BigInteger totalShares, + long lastStartHeight, + FungibleAssetValue lumpSumRewards, + long? startHeight) + { + Address = address; + TotalShares = totalShares; + LastStartHeight = lastStartHeight; + LumpSumRewards = lumpSumRewards; + StartHeight = startHeight; + } + + public LumpSumRewardsRecord(Address address, IValue bencoded) + : this(address, (List)bencoded) + { + } + + public LumpSumRewardsRecord(Address address, List bencoded) + : this( + address, + (Integer)bencoded[0], + (Integer)bencoded[1], + new FungibleAssetValue(bencoded[2]), + (Integer?)bencoded.ElementAtOrDefault(3)) + { + } + + public Address Address { get; } + + public BigInteger TotalShares { get; } + + public long LastStartHeight { get; } + + public FungibleAssetValue LumpSumRewards { get; } + + public long? StartHeight { get; } + + public bool IsStarted => StartHeight is long; + + public List Bencoded + { + get + { + var bencoded = List.Empty + .Add(TotalShares) + .Add(LastStartHeight) + .Add(LumpSumRewards.Serialize()); + + return StartHeight is long startHeight + ? bencoded.Add(startHeight) + : bencoded; + } + } + + IValue IBencodable.Bencoded => Bencoded; + + public LumpSumRewardsRecord Start(long height) + => new LumpSumRewardsRecord( + Address, TotalShares, LastStartHeight, LumpSumRewards, height); + + public LumpSumRewardsRecord AddLumpSumReward(FungibleAssetValue reward) + => new LumpSumRewardsRecord( + Address, + TotalShares, + LastStartHeight, + LumpSumRewards + reward, + StartHeight); + + public FungibleAssetValue RewardsDuringPeriod(BigInteger share) + => (LumpSumRewards * share).DivRem(TotalShares, out _); + + public override bool Equals(object? obj) + => obj is LumpSumRewardsRecord other && Equals(other); + + public bool Equals(LumpSumRewardsRecord? other) + => ReferenceEquals(this, other) + || (other is LumpSumRewardsRecord lumpSumRewardRecord + && Address == lumpSumRewardRecord.Address + && TotalShares == lumpSumRewardRecord.TotalShares + && StartHeight == lumpSumRewardRecord.StartHeight + && LumpSumRewards.Equals(lumpSumRewardRecord.LumpSumRewards) + && LastStartHeight == lumpSumRewardRecord.LastStartHeight); + + public override int GetHashCode() + => Address.GetHashCode(); + } +} diff --git a/Lib9c/Delegation/RedelegateResult.cs b/Lib9c/Delegation/RedelegateResult.cs index 01181ca606..3523291343 100644 --- a/Lib9c/Delegation/RedelegateResult.cs +++ b/Lib9c/Delegation/RedelegateResult.cs @@ -10,7 +10,9 @@ public RedelegateResult( Bond srcBond, Bond dstBond, RebondGrace rebondGrace, - UnbondingSet unbondingSet) + UnbondingSet unbondingSet, + LumpSumRewardsRecord srcLumpSumRewardsRecord, + LumpSumRewardsRecord dstLumpSumRewardsRecord) { SrcDelegatee = srcDelegatee; DstDelegatee = dstDelegatee; @@ -18,6 +20,8 @@ public RedelegateResult( DstBond = dstBond; RebondGrace = rebondGrace; UnbondingSet = unbondingSet; + SrcLumpSumRewardsRecord = srcLumpSumRewardsRecord; + DstLumpSumRewardsRecord = dstLumpSumRewardsRecord; } public T SrcDelegatee { get; } @@ -35,5 +39,9 @@ public RedelegateResult( public RebondGrace RebondGrace { get; } public UnbondingSet UnbondingSet { get; } + + public LumpSumRewardsRecord SrcLumpSumRewardsRecord { get; } + + public LumpSumRewardsRecord DstLumpSumRewardsRecord { get; } } } diff --git a/Lib9c/Delegation/RewardResult.cs b/Lib9c/Delegation/RewardResult.cs new file mode 100644 index 0000000000..514affb60c --- /dev/null +++ b/Lib9c/Delegation/RewardResult.cs @@ -0,0 +1,17 @@ +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public class RewardResult + { + public RewardResult(FungibleAssetValue reward, LumpSumRewardsRecord lumpSumRewardsRecord) + { + Reward = reward; + LumpSumRewardsRecord = lumpSumRewardsRecord; + } + + public FungibleAssetValue Reward { get; } + + public LumpSumRewardsRecord LumpSumRewardsRecord { get; } + } +} diff --git a/Lib9c/Delegation/UnbondResult.cs b/Lib9c/Delegation/UnbondResult.cs index a21f99fcc1..4bc6fef506 100644 --- a/Lib9c/Delegation/UnbondResult.cs +++ b/Lib9c/Delegation/UnbondResult.cs @@ -5,15 +5,17 @@ namespace Nekoyume.Delegation { public class UnbondResult { - public UnbondResult(Bond bond, FungibleAssetValue unbondedFAV) + public UnbondResult(Bond bond, FungibleAssetValue unbondedFAV, LumpSumRewardsRecord lumpSumRewardsRecord) { Bond = bond; - UnbondedFAV = unbondedFAV; + LumpSumRewardsRecord = lumpSumRewardsRecord; } public Bond Bond { get; } public FungibleAssetValue UnbondedFAV { get; } + + public LumpSumRewardsRecord LumpSumRewardsRecord { get; } } } diff --git a/Lib9c/Delegation/UndelegateResult.cs b/Lib9c/Delegation/UndelegateResult.cs index 6a216d9ea9..0bbf0bbf71 100644 --- a/Lib9c/Delegation/UndelegateResult.cs +++ b/Lib9c/Delegation/UndelegateResult.cs @@ -8,12 +8,14 @@ public UndelegateResult( T delegatee, Bond bond, UnbondLockIn unbondLockIn, - UnbondingSet unbondingSet) + UnbondingSet unbondingSet, + LumpSumRewardsRecord lumpSumRewardsRecord) { Delegatee = delegatee; Bond = bond; UnbondLockIn = unbondLockIn; UnbondingSet = unbondingSet; + LumpSumRewardsRecord = lumpSumRewardsRecord; } public T Delegatee { get; } @@ -25,5 +27,7 @@ public UndelegateResult( public UnbondLockIn UnbondLockIn { get; } public UnbondingSet UnbondingSet { get; } + + public LumpSumRewardsRecord LumpSumRewardsRecord { get; } } } diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index e49ec70400..82110fb0f6 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -17,16 +17,18 @@ public class Guild : Delegatee, IEquatable, IBen public readonly AgentAddress GuildMasterAddress; - public Guild(AgentAddress guildMasterAddress) + public Guild(AgentAddress guildMasterAddress, Currency rewardCurrency) : base(guildMasterAddress) { GuildMasterAddress = guildMasterAddress; + RewardCurrency = rewardCurrency; } - public Guild(List list) + public Guild(List list, Currency rewardCurrency) : base(new Address(list[2]), list[3]) { GuildMasterAddress = new AgentAddress(list[2]); + RewardCurrency = rewardCurrency; if (list[0] is not Text text || text != StateTypeName || list[1] is not Integer integer) { @@ -42,6 +44,8 @@ public Guild(List list) public override Currency Currency => Currencies.GuildGold; + public override Currency RewardCurrency { get; } + public override Address PoolAddress => DeriveAddress(PoolId); public override long UnbondingPeriod => 75600L; diff --git a/Lib9c/Model/Guild/GuildParticipant.cs b/Lib9c/Model/Guild/GuildParticipant.cs index b96fd934d1..89737c019a 100644 --- a/Lib9c/Model/Guild/GuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildParticipant.cs @@ -37,6 +37,8 @@ public GuildParticipant(AgentAddress agentAddress, List list) } } + public AgentAddress AgentAddress => new AgentAddress(Address); + public new List Bencoded => List.Empty .Add(StateTypeName) .Add(StateVersion) diff --git a/Lib9c/Module/Delegation/LumpSumRewardsRecordModule.cs b/Lib9c/Module/Delegation/LumpSumRewardsRecordModule.cs new file mode 100644 index 0000000000..a3f00b7d49 --- /dev/null +++ b/Lib9c/Module/Delegation/LumpSumRewardsRecordModule.cs @@ -0,0 +1,66 @@ +#nullable enable +using System; +using System.Collections.Generic; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Delegation; +using Nekoyume.Extensions; + +namespace Nekoyume.Module.Delegation +{ + public static class LumpSumRewardsRecordModule + { + public static List GetLumpSumRewardsRecords( + this IWorldState world, IDelegatee delegatee, long height, long startHeight) + { + var records = new List(); + LumpSumRewardsRecord record; + while (height >= startHeight) + { + record = world.GetLumpSumRewardsRecord(delegatee, height); + records.Add(record); + height = record.LastStartHeight; + } + + return records; + } + + public static LumpSumRewardsRecord GetLumpSumRewardsRecord( + this IWorldState world, IDelegatee delegatee, long height) + => GetLumpSumRewardsRecord(world, delegatee.LumpSumRewardsRecordAddress(height)); + + public static LumpSumRewardsRecord GetLumpSumRewardsRecord( + this IWorldState world, Address address) + => TryGetLumpSumRewardsRecord(world, address, out var lumpSumRewardsRecord) + ? lumpSumRewardsRecord! + : throw new InvalidOperationException("Failed to get LumpSumRewardsRecord."); + + public static bool TryGetLumpSumRewardsRecord( + this IWorldState world, Address address, out LumpSumRewardsRecord? lumpSumRewardsRecord) + { + try + { + var value = world.GetAccountState(Addresses.LumpSumRewardsRecord).GetState(address); + if (!(value is List list)) + { + lumpSumRewardsRecord = null; + return false; + } + + lumpSumRewardsRecord = new LumpSumRewardsRecord(address, list); + return true; + } + catch + { + lumpSumRewardsRecord = null; + return false; + } + } + + public static IWorld SetLumpSumRewardsRecord(this IWorld world, LumpSumRewardsRecord lumpSumRewardsRecord) + => world.MutateAccount( + Addresses.LumpSumRewardsRecord, + account => account.SetState(lumpSumRewardsRecord.Address, lumpSumRewardsRecord.Bencoded)); + } +} diff --git a/Lib9c/Module/Guild/GuildModule.cs b/Lib9c/Module/Guild/GuildModule.cs index c4528e03c9..30c0e1cefc 100644 --- a/Lib9c/Module/Guild/GuildModule.cs +++ b/Lib9c/Module/Guild/GuildModule.cs @@ -16,7 +16,7 @@ public static Model.Guild.Guild GetGuild(this IWorldState worldState, GuildAddre var value = worldState.GetAccountState(Addresses.Guild).GetState(guildAddress); if (value is List list) { - return new Model.Guild.Guild(list); + return new Model.Guild.Guild(list, worldState.GetGoldCurrency()); } throw new FailedLoadStateException("There is no such guild."); @@ -52,7 +52,7 @@ public static IWorld MakeGuild(this IWorld world, GuildAddress guildAddress, Age return world.MutateAccount(Addresses.Guild, account => account.SetState(guildAddress, - new Model.Guild.Guild(signer).Bencoded)) + new Model.Guild.Guild(signer, world.GetGoldCurrency()).Bencoded)) .JoinGuild(guildAddress, signer); } diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index 7ab7479458..7e5e9fcaf8 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -135,6 +135,8 @@ private static IWorld Delegate( GuildAddress guildAddress, FungibleAssetValue fav) { + world = world.ClaimReward(context, guildAddress); + var agentAddress = new AgentAddress(context.Signer); var guildParticipant = world.TryGetGuildParticipant(agentAddress, out var p) ? p @@ -143,7 +145,7 @@ private static IWorld Delegate( ? g : throw new InvalidOperationException("The guild does not exist."); var bond = world.GetBond(guild, agentAddress); - var result = guildParticipant.Delegate(guild, fav, bond); + var result = guildParticipant.Delegate(guild, fav, context.BlockIndex, bond); return world .SetBond(result.Bond) @@ -158,6 +160,8 @@ private static IWorld Undelegate( GuildAddress guildAddress, BigInteger share) { + world = world.ClaimReward(context, guildAddress); + var agentAddress = new AgentAddress(context.Signer); var guildParticipant = world.TryGetGuildParticipant(agentAddress, out var p) ? p @@ -179,6 +183,32 @@ private static IWorld Undelegate( .SetUnbondingSet(result.UnbondingSet); } + private static IWorld ClaimReward( + this IWorld world, + IActionContext context, + GuildAddress guildAddress) + { + var agentAddress = new AgentAddress(context.Signer); + var guildParticipant = world.TryGetGuildParticipant(agentAddress, out var p) + ? p + : new GuildParticipant(agentAddress, guildAddress); + var guild = world.TryGetGuild(guildAddress, out var g) + ? g + : throw new InvalidOperationException("The guild does not exist."); + var bond = world.GetBond(guild, agentAddress); + var rewardRecords = world.GetLumpSumRewardsRecords( + guild, context.BlockIndex, guildParticipant.LastClaimRewardHeight); + + var claimRewardResult = guildParticipant.ClaimReward( + guild, rewardRecords, bond, context.BlockIndex); + + return world + .SetLumpSumRewardsRecord(claimRewardResult.LumpSumRewardsRecord) + .SetGuild(claimRewardResult.Delegatee) + .SetGuildParticipant(guildParticipant) + .TransferAsset(context, guild.RewardPoolAddress, agentAddress, claimRewardResult.Reward); + } + private static IWorld SetGuildParticipant( this IWorld world, GuildParticipant guildParticipant) From 46db1e0e63a56b3de21a4d70604a8c5843151357 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sat, 10 Aug 2024 13:31:51 +0900 Subject: [PATCH 016/206] fix: RewardPeriod --- Lib9c/Delegation/Delegatee.cs | 56 +++++++++++++++++------- Lib9c/Delegation/Delegator.cs | 2 +- Lib9c/Delegation/LumpSumRewardsRecord.cs | 46 +++++++++---------- 3 files changed, 62 insertions(+), 42 deletions(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 7b1ee57af7..4d04a605af 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -29,7 +29,7 @@ public Delegatee(Address address) Delegators = ImmutableSortedSet
.Empty; TotalDelegated = Currency * 0; TotalShares = BigInteger.Zero; - LastRewardPeriodStartHeight = -1; + LastRewardPeriodStartHeight = null; } public Delegatee(Address address, IValue bencoded) @@ -43,7 +43,7 @@ public Delegatee(Address address, List bencoded) ((List)bencoded[0]).Select(item => new Address(item)), new FungibleAssetValue(bencoded[1]), (Integer)bencoded[2], - (Integer)bencoded[3]) + (Integer?)bencoded.ElementAtOrDefault(3)) { } @@ -52,7 +52,7 @@ private Delegatee( IEnumerable
delegators, FungibleAssetValue totalDelegated, BigInteger totalShares, - long lastRewardPeriodStartHeight) + long? lastRewardPeriodStartHeight) { if (!totalDelegated.Currency.Equals(Currency)) { @@ -106,13 +106,23 @@ private Delegatee( public BigInteger TotalShares { get; private set; } - public long LastRewardPeriodStartHeight { get; private set; } + public long? LastRewardPeriodStartHeight { get; private set; } - public List Bencoded => List.Empty - .Add(new List(Delegators.Select(delegator => delegator.Bencoded))) - .Add(TotalDelegated.Serialize()) - .Add(TotalShares) - .Add(LastRewardPeriodStartHeight); + public List Bencoded + { + get + { + var bencoded = List.Empty + .Add(new List(Delegators.Select(delegator => delegator.Bencoded))) + .Add(TotalDelegated.Serialize()) + .Add(TotalShares); + + return LastRewardPeriodStartHeight is long lastRewardPeriodStartHeight + ? bencoded.Add(lastRewardPeriodStartHeight) + : bencoded; + } + } + IValue IBencodable.Bencoded => Bencoded; @@ -147,7 +157,7 @@ public BondResult Bond(T delegator, FungibleAssetValue fav, long height, Bond bo Delegators = Delegators.Add(delegator.Address); TotalShares += share; TotalDelegated += fav; - var lumpSumRewardsRecord = StartNewRewardPeriod(LastRewardPeriodStartHeight); + var lumpSumRewardsRecord = StartNewRewardPeriod(height, LastRewardPeriodStartHeight); LastRewardPeriodStartHeight = height; return new BondResult( @@ -171,7 +181,7 @@ public UnbondResult Unbond(T delegator, BigInteger share, long height, Bond bond TotalShares -= share; TotalDelegated -= fav; - var lumpSumRewardsRecord = StartNewRewardPeriod(LastRewardPeriodStartHeight); + var lumpSumRewardsRecord = StartNewRewardPeriod(height, LastRewardPeriodStartHeight); LastRewardPeriodStartHeight = height; return new UnbondResult( @@ -208,7 +218,7 @@ public RewardResult Reward( } } - var lumpSumRewardsRecord = StartNewRewardPeriod(LastRewardPeriodStartHeight); + var lumpSumRewardsRecord = StartNewRewardPeriod(height, LastRewardPeriodStartHeight); LastRewardPeriodStartHeight = height; return new RewardResult(reward, lumpSumRewardsRecord); @@ -264,13 +274,27 @@ protected Address DeriveAddress(byte[] typeId, IEnumerable? bytes = null) return new Address(hashed); } - private LumpSumRewardsRecord StartNewRewardPeriod(long lastStartHeight = -1) + private (LumpSumRewardsRecord New, LumpSumRewardsRecord? Saved) StartNewRewardPeriod( + long height, LumpSumRewardsRecord? currentRecord) { - return new LumpSumRewardsRecord( + currentRecord = currentRecord is null ? null : SaveRewardPeriod(currentRecord); + LumpSumRewardsRecord newRecord = new LumpSumRewardsRecord( LumpSumRewardsRecordAddress(), - RewardCurrency, + height, TotalShares, - lastStartHeight); + RewardCurrency, + currentRecord?.StartHeight); + return (newRecord, currentRecord); + } + + private LumpSumRewardsRecord SaveRewardPeriod(LumpSumRewardsRecord record) + { + return new LumpSumRewardsRecord( + LumpSumRewardsRecordAddress(record.StartHeight), + record.StartHeight, + record.TotalShares, + record.LumpSumRewards, + record.LastStartHeight); } } } diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index 822dc56c9d..130aad6ac4 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -184,7 +184,7 @@ public RedelegateResult Redelegate( { if (share.Sign <= 0) { - throw new ArgumentOutOfRangeException( + throw new ArgumentOutOfRangeException( nameof(share), share, "Share must be positive."); } diff --git a/Lib9c/Delegation/LumpSumRewardsRecord.cs b/Lib9c/Delegation/LumpSumRewardsRecord.cs index a38aedfe0c..88a42fd1b3 100644 --- a/Lib9c/Delegation/LumpSumRewardsRecord.cs +++ b/Lib9c/Delegation/LumpSumRewardsRecord.cs @@ -13,33 +13,35 @@ public class LumpSumRewardsRecord : IBencodable, IEquatable StartHeight is long; + public long? LastStartHeight { get; } public List Bencoded { get { var bencoded = List.Empty + .Add(StartHeight) .Add(TotalShares) - .Add(LastStartHeight) .Add(LumpSumRewards.Serialize()); - return StartHeight is long startHeight - ? bencoded.Add(startHeight) + return LastStartHeight is long lastStartHeight + ? bencoded.Add(lastStartHeight) : bencoded; } } IValue IBencodable.Bencoded => Bencoded; - public LumpSumRewardsRecord Start(long height) - => new LumpSumRewardsRecord( - Address, TotalShares, LastStartHeight, LumpSumRewards, height); - public LumpSumRewardsRecord AddLumpSumReward(FungibleAssetValue reward) => new LumpSumRewardsRecord( Address, + StartHeight, TotalShares, - LastStartHeight, LumpSumRewards + reward, - StartHeight); + LastStartHeight); public FungibleAssetValue RewardsDuringPeriod(BigInteger share) => (LumpSumRewards * share).DivRem(TotalShares, out _); @@ -108,8 +104,8 @@ public bool Equals(LumpSumRewardsRecord? other) => ReferenceEquals(this, other) || (other is LumpSumRewardsRecord lumpSumRewardRecord && Address == lumpSumRewardRecord.Address - && TotalShares == lumpSumRewardRecord.TotalShares && StartHeight == lumpSumRewardRecord.StartHeight + && TotalShares == lumpSumRewardRecord.TotalShares && LumpSumRewards.Equals(lumpSumRewardRecord.LumpSumRewards) && LastStartHeight == lumpSumRewardRecord.LastStartHeight); From e6ca28f50d7f4d475c48d74f59fb45c6b72a810e Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sun, 11 Aug 2024 15:01:16 +0900 Subject: [PATCH 017/206] refactor: Refactor delegation with repository pattern --- Lib9c/Delegation/Bond.cs | 2 + Lib9c/Delegation/BondResult.cs | 21 -- Lib9c/Delegation/ClaimRewardResult.cs | 24 -- Lib9c/Delegation/DelegateResult.cs | 31 --- Lib9c/Delegation/Delegatee.cs | 215 +++++++++-------- Lib9c/Delegation/DelegationRepository.cs | 129 +++++++++++ Lib9c/Delegation/Delegator.cs | 232 +++++++------------ Lib9c/Delegation/IClaimRewardResult.cs | 13 -- Lib9c/Delegation/IDelegateResult.cs | 16 -- Lib9c/Delegation/IDelegatee.cs | 13 +- Lib9c/Delegation/IDelegationRepository.cs | 36 +++ Lib9c/Delegation/IDelegator.cs | 25 +- Lib9c/Delegation/IRedelegateResult.cs | 22 -- Lib9c/Delegation/IUndelegateResult.cs | 16 -- Lib9c/Delegation/LumpSumRewardsRecord.cs | 8 + Lib9c/Delegation/RedelegateResult.cs | 47 ---- Lib9c/Delegation/RewardResult.cs | 17 -- Lib9c/Delegation/UnbondResult.cs | 21 -- Lib9c/Delegation/UnbondingSet.cs | 2 + Lib9c/Delegation/UndelegateResult.cs | 33 --- Lib9c/Module/Guild/GuildParticipantModule.cs | 2 +- 21 files changed, 391 insertions(+), 534 deletions(-) delete mode 100644 Lib9c/Delegation/BondResult.cs delete mode 100644 Lib9c/Delegation/ClaimRewardResult.cs delete mode 100644 Lib9c/Delegation/DelegateResult.cs create mode 100644 Lib9c/Delegation/DelegationRepository.cs delete mode 100644 Lib9c/Delegation/IClaimRewardResult.cs delete mode 100644 Lib9c/Delegation/IDelegateResult.cs create mode 100644 Lib9c/Delegation/IDelegationRepository.cs delete mode 100644 Lib9c/Delegation/IRedelegateResult.cs delete mode 100644 Lib9c/Delegation/IUndelegateResult.cs delete mode 100644 Lib9c/Delegation/RedelegateResult.cs delete mode 100644 Lib9c/Delegation/RewardResult.cs delete mode 100644 Lib9c/Delegation/UnbondResult.cs delete mode 100644 Lib9c/Delegation/UndelegateResult.cs diff --git a/Lib9c/Delegation/Bond.cs b/Lib9c/Delegation/Bond.cs index 71a38cc173..a2479c8c63 100644 --- a/Lib9c/Delegation/Bond.cs +++ b/Lib9c/Delegation/Bond.cs @@ -53,6 +53,8 @@ private Bond(Address address, BigInteger share, long lastDistributeHeight) public long LastDistributeHeight { get; } + public bool IsEmpty => Share.IsZero; + public List Bencoded => List.Empty .Add(Share) .Add(LastDistributeHeight); diff --git a/Lib9c/Delegation/BondResult.cs b/Lib9c/Delegation/BondResult.cs deleted file mode 100644 index 253e8ee88a..0000000000 --- a/Lib9c/Delegation/BondResult.cs +++ /dev/null @@ -1,21 +0,0 @@ -#nullable enable -using System.Numerics; - -namespace Nekoyume.Delegation -{ - public class BondResult - { - public BondResult(Bond bond, BigInteger bondedShare, LumpSumRewardsRecord lumpSumRewardsRecord) - { - Bond = bond; - BondedShare = bondedShare; - LumpSumRewardsRecord = lumpSumRewardsRecord; - } - - public Bond Bond { get; } - - public BigInteger BondedShare { get; } - - public LumpSumRewardsRecord LumpSumRewardsRecord { get; } - } -} diff --git a/Lib9c/Delegation/ClaimRewardResult.cs b/Lib9c/Delegation/ClaimRewardResult.cs deleted file mode 100644 index 78e3d37c50..0000000000 --- a/Lib9c/Delegation/ClaimRewardResult.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Libplanet.Types.Assets; - -namespace Nekoyume.Delegation -{ - public class ClaimRewardResult : IClaimRewardResult - where T: IDelegatee - { - public ClaimRewardResult( - T delegatee, FungibleAssetValue reward, LumpSumRewardsRecord lumpSumRewardsRecord) - { - Delegatee = delegatee; - Reward = reward; - LumpSumRewardsRecord = lumpSumRewardsRecord; - } - - public T Delegatee { get; } - - IDelegatee IClaimRewardResult.Delegatee => Delegatee; - - public FungibleAssetValue Reward { get; } - - public LumpSumRewardsRecord LumpSumRewardsRecord { get; } - } -} diff --git a/Lib9c/Delegation/DelegateResult.cs b/Lib9c/Delegation/DelegateResult.cs deleted file mode 100644 index e7d10efd1d..0000000000 --- a/Lib9c/Delegation/DelegateResult.cs +++ /dev/null @@ -1,31 +0,0 @@ -#nullable enable -using Libplanet.Types.Assets; - -namespace Nekoyume.Delegation -{ - public class DelegateResult : IDelegateResult - where T : IDelegatee - { - public DelegateResult( - T delegatee, - Bond bond, - FungibleAssetValue delegatedFAV, - LumpSumRewardsRecord lumpSumRewardsRecord) - { - Delegatee = delegatee; - Bond = bond; - DelegatedFAV = delegatedFAV; - LumpSumRewardsRecord = lumpSumRewardsRecord; - } - - public T Delegatee { get; } - - IDelegatee IDelegateResult.Delegatee => Delegatee; - - public Bond Bond { get; } - - public FungibleAssetValue DelegatedFAV { get; } - - public LumpSumRewardsRecord LumpSumRewardsRecord { get; } - } -} diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 4d04a605af..5682a36c96 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -23,27 +23,29 @@ public abstract class Delegatee : IDelegatee protected readonly byte[] RewardPoolId = new byte[] { 0x72 }; // `r` protected readonly byte[] PoolId = new byte[] { 0x70 }; // `p` - public Delegatee(Address address) + private readonly IDelegationRepository _repository; + + public Delegatee(Address address, IDelegationRepository repository) { Address = address; Delegators = ImmutableSortedSet
.Empty; TotalDelegated = Currency * 0; TotalShares = BigInteger.Zero; - LastRewardPeriodStartHeight = null; + _repository = repository; } - public Delegatee(Address address, IValue bencoded) - : this(address, (List)bencoded) + public Delegatee(Address address, IValue bencoded, IDelegationRepository repository) + : this(address, (List)bencoded, repository) { } - public Delegatee(Address address, List bencoded) + public Delegatee(Address address, List bencoded, IDelegationRepository repository) : this( address, ((List)bencoded[0]).Select(item => new Address(item)), new FungibleAssetValue(bencoded[1]), (Integer)bencoded[2], - (Integer?)bencoded.ElementAtOrDefault(3)) + repository) { } @@ -52,7 +54,7 @@ private Delegatee( IEnumerable
delegators, FungibleAssetValue totalDelegated, BigInteger totalShares, - long? lastRewardPeriodStartHeight) + IDelegationRepository repository) { if (!totalDelegated.Currency.Equals(Currency)) { @@ -79,7 +81,7 @@ private Delegatee( Delegators = delegators.ToImmutableSortedSet(); TotalDelegated = totalDelegated; TotalShares = totalShares; - LastRewardPeriodStartHeight = lastRewardPeriodStartHeight; + _repository = repository; } public Address Address { get; } @@ -106,23 +108,10 @@ private Delegatee( public BigInteger TotalShares { get; private set; } - public long? LastRewardPeriodStartHeight { get; private set; } - - public List Bencoded - { - get - { - var bencoded = List.Empty - .Add(new List(Delegators.Select(delegator => delegator.Bencoded))) - .Add(TotalDelegated.Serialize()) - .Add(TotalShares); - - return LastRewardPeriodStartHeight is long lastRewardPeriodStartHeight - ? bencoded.Add(lastRewardPeriodStartHeight) - : bencoded; - } - } - + public List Bencoded => List.Empty + .Add(new List(Delegators.Select(delegator => delegator.Bencoded))) + .Add(TotalDelegated.Serialize()) + .Add(TotalShares); IValue IBencodable.Bencoded => Bencoded; @@ -136,15 +125,18 @@ public FungibleAssetValue FAVToUnbond(BigInteger share) ? TotalDelegated : (TotalDelegated * share).DivRem(TotalShares, out _); - BondResult IDelegatee.Bond( - IDelegator delegator, FungibleAssetValue fav, long height, Bond bond) - => Bond((T)delegator, fav, height, bond); + BigInteger IDelegatee.Bond( + IDelegator delegator, FungibleAssetValue fav, long height) + => Bond((T)delegator, fav, height); - UnbondResult IDelegatee.Unbond( - IDelegator delegator, BigInteger share, long height, Bond bond) - => Unbond((T)delegator, share, height, bond); + FungibleAssetValue IDelegatee.Unbond( + IDelegator delegator, BigInteger share, long height) + => Unbond((T)delegator, share, height); - public BondResult Bond(T delegator, FungibleAssetValue fav, long height, Bond bond) + void IDelegatee.Reward(IDelegator delegator, long height) + => Reward((T)delegator, height); + + public BigInteger Bond(T delegator, FungibleAssetValue fav, long height) { if (!fav.Currency.Equals(Currency)) { @@ -152,19 +144,19 @@ public BondResult Bond(T delegator, FungibleAssetValue fav, long height, Bond bo "Cannot bond with invalid currency."); } + Bond bond = _repository.GetBond(this, delegator.Address); BigInteger share = ShareToBond(fav); bond = bond.AddShare(share); Delegators = Delegators.Add(delegator.Address); TotalShares += share; TotalDelegated += fav; - var lumpSumRewardsRecord = StartNewRewardPeriod(height, LastRewardPeriodStartHeight); - LastRewardPeriodStartHeight = height; - - return new BondResult( - bond, share, lumpSumRewardsRecord); + _repository.SetBond(bond); + Reward(delegator, height); + + return share; } - public UnbondResult Unbond(T delegator, BigInteger share, long height, Bond bond) + public FungibleAssetValue Unbond(T delegator, BigInteger share, long height) { if (TotalShares.IsZero || TotalDelegated.RawValue.IsZero) { @@ -172,6 +164,7 @@ public UnbondResult Unbond(T delegator, BigInteger share, long height, Bond bond "Cannot unbond without bonding."); } + Bond bond = _repository.GetBond(this, delegator.Address); FungibleAssetValue fav = FAVToUnbond(share); bond = bond.SubtractShare(share); if (bond.Share.IsZero) @@ -181,47 +174,21 @@ public UnbondResult Unbond(T delegator, BigInteger share, long height, Bond bond TotalShares -= share; TotalDelegated -= fav; - var lumpSumRewardsRecord = StartNewRewardPeriod(height, LastRewardPeriodStartHeight); - LastRewardPeriodStartHeight = height; + _repository.SetBond(bond); + Reward(delegator, height); - return new UnbondResult( - bond, fav, lumpSumRewardsRecord); + return fav; } - public RewardResult Reward( - BigInteger share, - long height, - IEnumerable lumpSumRewardsRecords) + public void Reward(T delegator, long height) { - FungibleAssetValue reward = RewardCurrency * 0; - long? linkedStartHeight = null; - - foreach (LumpSumRewardsRecord record in lumpSumRewardsRecords) - { - if (!(record.StartHeight is long startHeight)) - { - throw new ArgumentException("lump sum reward record wasn't started."); - } - - if (linkedStartHeight is long startHeightFromHigher - && startHeightFromHigher != startHeight) - { - throw new ArgumentException("lump sum reward record was started."); - } - - reward += record.RewardsDuringPeriod(share); - linkedStartHeight = record.LastStartHeight; - - if (linkedStartHeight == -1) - { - break; - } - } - - var lumpSumRewardsRecord = StartNewRewardPeriod(height, LastRewardPeriodStartHeight); - LastRewardPeriodStartHeight = height; - - return new RewardResult(reward, lumpSumRewardsRecord); + BigInteger share = _repository.GetBond(this, delegator.Address).Share; + IEnumerable lumpSumRewardsRecords = + GetLumpSumRewardsRecords(delegator.LastRewardHeight); + FungibleAssetValue reward = CalculateReward(share, lumpSumRewardsRecords); + _repository.TransferAsset(RewardPoolAddress, delegator.Address, reward); + StartNewRewardPeriod(height); + delegator.UpdateLastRewardHeight(height); } public Address BondAddress(Address delegatorAddress) @@ -233,10 +200,12 @@ public Address UnbondLockInAddress(Address delegatorAddress) public Address RebondGraceAddress(Address delegatorAddress) => DeriveAddress(RebondGraceId, delegatorAddress); - public Address LumpSumRewardsRecordAddress(long? height = null) - => height is long heightValue - ? DeriveAddress(LumpSumRewardsRecordId, BitConverter.GetBytes(heightValue)) - : DeriveAddress(LumpSumRewardsRecordId); + public Address CurrentLumpSumRewardsRecordAddress() + => DeriveAddress(LumpSumRewardsRecordId); + + public Address LumpSumRewardsRecordAddress(long height) + => DeriveAddress(LumpSumRewardsRecordId, BitConverter.GetBytes(height)); + public override bool Equals(object? obj) => obj is IDelegatee other && Equals(other); @@ -253,7 +222,6 @@ public bool Equals(IDelegatee? other) && Delegators.SequenceEqual(delegatee.Delegators) && TotalDelegated.Equals(delegatee.TotalDelegated) && TotalShares.Equals(delegatee.TotalShares) - && LastRewardPeriodStartHeight.Equals(delegatee.LastRewardPeriodStartHeight) && DelegateeId.SequenceEqual(delegatee.DelegateeId)); public override int GetHashCode() @@ -265,7 +233,7 @@ protected Address DeriveAddress(byte[] typeId, Address address) protected Address DeriveAddress(byte[] typeId, IEnumerable? bytes = null) { byte[] hashed; - using (var hmac = new HMACSHA1(DelegateeId.Concat(typeId).ToArray())) + using (HMACSHA1 hmac = new(DelegateeId.Concat(typeId).ToArray())) { hashed = hmac.ComputeHash( Address.ByteArray.Concat(bytes ?? Array.Empty()).ToArray()); @@ -274,27 +242,84 @@ protected Address DeriveAddress(byte[] typeId, IEnumerable? bytes = null) return new Address(hashed); } - private (LumpSumRewardsRecord New, LumpSumRewardsRecord? Saved) StartNewRewardPeriod( - long height, LumpSumRewardsRecord? currentRecord) + private void StartNewRewardPeriod(long height) { - currentRecord = currentRecord is null ? null : SaveRewardPeriod(currentRecord); - LumpSumRewardsRecord newRecord = new LumpSumRewardsRecord( - LumpSumRewardsRecordAddress(), + LumpSumRewardsRecord? currentRecord = _repository.GetCurrentLumpSumRewardsRecord(this); + long? lastStartHeight = null; + if (currentRecord is LumpSumRewardsRecord lastRecord) + { + _repository.SetLumpSumRewardsRecord( + lastRecord.MoveAddress( + CurrentLumpSumRewardsRecordAddress())); + lastStartHeight = lastRecord.StartHeight; + } + + LumpSumRewardsRecord newRecord = new( + CurrentLumpSumRewardsRecordAddress(), height, TotalShares, RewardCurrency, - currentRecord?.StartHeight); - return (newRecord, currentRecord); + lastStartHeight); + + _repository.SetLumpSumRewardsRecord(newRecord); } - private LumpSumRewardsRecord SaveRewardPeriod(LumpSumRewardsRecord record) + private FungibleAssetValue CalculateReward( + BigInteger share, + IEnumerable lumpSumRewardsRecords) { - return new LumpSumRewardsRecord( - LumpSumRewardsRecordAddress(record.StartHeight), - record.StartHeight, - record.TotalShares, - record.LumpSumRewards, - record.LastStartHeight); + FungibleAssetValue reward = RewardCurrency * 0; + long? linkedStartHeight = null; + + foreach (LumpSumRewardsRecord record in lumpSumRewardsRecords) + { + if (!(record.StartHeight is long startHeight)) + { + throw new ArgumentException("lump sum reward record wasn't started."); + } + + if (linkedStartHeight is long startHeightFromHigher + && startHeightFromHigher != startHeight) + { + throw new ArgumentException("lump sum reward record was started."); + } + + reward += record.RewardsDuringPeriod(share); + linkedStartHeight = record.LastStartHeight; + + if (linkedStartHeight == -1) + { + break; + } + } + + return reward; + } + + private List GetLumpSumRewardsRecords(long? lastRewardHeight) + { + List records = new(); + if (!(_repository.GetCurrentLumpSumRewardsRecord(this) is LumpSumRewardsRecord record)) + { + return records; + } + + do + { + records.Add(record); + + if (!(record.LastStartHeight is long lastStartHeight)) + { + break; + } + + record = _repository.GetLumpSumRewardsRecord(this, lastStartHeight) + ?? throw new InvalidOperationException( + $"Lump sum rewards record for #{lastStartHeight} is missing"); + } + while (record.StartHeight >= lastRewardHeight); + + return records; } } } diff --git a/Lib9c/Delegation/DelegationRepository.cs b/Lib9c/Delegation/DelegationRepository.cs new file mode 100644 index 0000000000..9b1d24b073 --- /dev/null +++ b/Lib9c/Delegation/DelegationRepository.cs @@ -0,0 +1,129 @@ +#nullable enable +using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public class DelegationRepository : IDelegationRepository + { + private readonly Address BondAddress = Addresses.Bond; + private readonly Address UnbondLockInAddress = Addresses.UnbondLockIn; + private readonly Address RebondGraceAddress = Addresses.RebondGrace; + private readonly Address UnbondingSetAddress = Addresses.UnbondingSet; + private readonly Address LumpSumRewardsRecordAddress = Addresses.LumpSumRewardsRecord; + + private IWorld _world; + private IActionContext _context; + private IAccount _bond; + private IAccount _unbondLockIn; + private IAccount _rebondGrace; + private IAccount _unbondingSet; + private IAccount _lumpSumRewardsRecord; + + public DelegationRepository(IWorld world, IActionContext context) + { + _world = world; + _context = context; + _bond = world.GetAccount(BondAddress); + _unbondLockIn = world.GetAccount(UnbondLockInAddress); + _rebondGrace = world.GetAccount(RebondGraceAddress); + _unbondingSet = world.GetAccount(UnbondingSetAddress); + _lumpSumRewardsRecord = world.GetAccount(LumpSumRewardsRecordAddress); + } + + public IWorld World => _world + .SetAccount(BondAddress, _bond) + .SetAccount(UnbondLockInAddress, _unbondLockIn) + .SetAccount(RebondGraceAddress, _rebondGrace) + .SetAccount(UnbondingSetAddress, _unbondingSet) + .SetAccount(LumpSumRewardsRecordAddress, _lumpSumRewardsRecord); + + public Bond GetBond(IDelegatee delegatee, Address delegatorAddress) + { + Address address = delegatee.BondAddress(delegatorAddress); + IValue? value = _bond.GetState(address); + return value is IValue bencoded + ? new Bond(address, bencoded) + : new Bond(address); + } + + public UnbondLockIn GetUnbondLockIn(IDelegatee delegatee, Address delegatorAddress) + { + Address address = delegatee.UnbondLockInAddress(delegatorAddress); + IValue? value = _unbondLockIn.GetState(address); + return value is IValue bencoded + ? new UnbondLockIn(address, delegatee.MaxUnbondLockInEntries, bencoded) + : new UnbondLockIn(address, delegatee.MaxUnbondLockInEntries); + } + + public RebondGrace GetRebondGrace(IDelegatee delegatee, Address delegatorAddress) + { + Address address = delegatee.RebondGraceAddress(delegatorAddress); + IValue? value = _rebondGrace.GetState(address); + return value is IValue bencoded + ? new RebondGrace(address, delegatee.MaxRebondGraceEntries, bencoded) + : new RebondGrace(address, delegatee.MaxUnbondLockInEntries); + } + + public UnbondingSet GetUnbondingSet() + => _unbondingSet.GetState(UnbondingSet.Address) is IValue bencoded + ? new UnbondingSet(bencoded) + : new UnbondingSet(); + + public LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height) + { + Address address = delegatee.LumpSumRewardsRecordAddress(height); + IValue? value = _lumpSumRewardsRecord.GetState(address); + return value is IValue bencoded + ? new LumpSumRewardsRecord(address, bencoded) + : null; + } + + public LumpSumRewardsRecord? GetCurrentLumpSumRewardsRecord(IDelegatee delegatee) + { + Address address = delegatee.CurrentLumpSumRewardsRecordAddress(); + IValue? value = _lumpSumRewardsRecord.GetState(address); + return value is IValue bencoded + ? new LumpSumRewardsRecord(address, bencoded) + : null; + } + + public void SetBond(Bond bond) + { + _bond = bond.IsEmpty + ? _bond.RemoveState(bond.Address) + : _bond.SetState(bond.Address, bond.Bencoded); + } + + public void SetUnbondLockIn(UnbondLockIn unbondLockIn) + { + _unbondLockIn = unbondLockIn.IsEmpty + ? _unbondLockIn.RemoveState(unbondLockIn.Address) + : _unbondLockIn.SetState(unbondLockIn.Address, unbondLockIn.Bencoded); + } + public void SetRebondGrace(RebondGrace rebondGrace) + { + _rebondGrace = rebondGrace.IsEmpty + ? _rebondGrace.RemoveState(rebondGrace.Address) + : _rebondGrace.SetState(rebondGrace.Address, rebondGrace.Bencoded); + } + + public void SetUnbondingSet(UnbondingSet unbondingSet) + { + _unbondingSet = unbondingSet.IsEmpty + ? _unbondingSet.RemoveState(UnbondingSet.Address) + : _unbondingSet.SetState(UnbondingSet.Address, unbondingSet.Bencoded); + } + public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) + { + _lumpSumRewardsRecord = _lumpSumRewardsRecord.SetState( + lumpSumRewardsRecord.Address, lumpSumRewardsRecord.Bencoded); + } + + public void TransferAsset(Address sender, Address recipient, FungibleAssetValue value) + => _world = _world.TransferAsset(_context, sender, recipient, value); + } +} diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index 130aad6ac4..1998f53223 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -1,6 +1,5 @@ #nullable enable using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Numerics; @@ -15,102 +14,70 @@ public abstract class Delegator : IDelegator where T : Delegatee where TSelf : Delegator { - public Delegator(Address address) - : this(address, ImmutableSortedSet
.Empty, 0L) + private readonly IDelegationRepository _repository; + + public Delegator(Address address, IDelegationRepository repository) + : this(address, ImmutableSortedSet
.Empty, 0L, repository) { } - public Delegator(Address address, IValue bencoded) - : this(address, (List)bencoded) + public Delegator(Address address, IValue bencoded, IDelegationRepository repository) + : this(address, (List)bencoded, repository) { } - public Delegator(Address address, List bencoded) + public Delegator(Address address, List bencoded, IDelegationRepository repository) : this( address, ((List)bencoded[0]).Select(item => new Address(item)).ToImmutableSortedSet(), - (Integer)bencoded[1]) + (Integer)bencoded[1], + repository) { } private Delegator( - Address address, ImmutableSortedSet
delegatees, long lastClaimRewardHeight) + Address address, + ImmutableSortedSet
delegatees, + long lastRewardHeight, + IDelegationRepository repository) { Address = address; Delegatees = delegatees; - LastClaimRewardHeight = lastClaimRewardHeight; + LastRewardHeight = lastRewardHeight; + _repository = repository; } public Address Address { get; } public ImmutableSortedSet
Delegatees { get; private set; } - public long LastClaimRewardHeight { get; private set; } + public long LastRewardHeight { get; private set; } public List Bencoded => List.Empty .Add(new List(Delegatees.Select(a => a.Bencoded))) - .Add(LastClaimRewardHeight); + .Add(LastRewardHeight); IValue IBencodable.Bencoded => Bencoded; - IDelegateResult IDelegator.Delegate( - IDelegatee delegatee, - FungibleAssetValue fav, - long height, - Bond bond) - => Delegate((T)delegatee, fav, height, bond); - - IUndelegateResult IDelegator.Undelegate( - IDelegatee delegatee, - BigInteger share, - long height, - Bond bond, - UnbondLockIn unbondLockIn, - UnbondingSet unbondingSet) - => Undelegate( - (T)delegatee, - share, - height, - bond, - unbondLockIn, - unbondingSet); - - IRedelegateResult IDelegator.Redelegate( - IDelegatee srcDelegatee, - IDelegatee dstDelegatee, - BigInteger share, - long height, - Bond srcBond, - Bond dstBond, - RebondGrace srcRebondGrace, - UnbondingSet unbondingSet) - => Redelegate( - (T)srcDelegatee, - (T)dstDelegatee, - share, - height, - srcBond, - dstBond, - srcRebondGrace, - unbondingSet); - - IClaimRewardResult IDelegator.ClaimReward( - IDelegatee delegatee, - IEnumerable lumpSumRewardRecords, - Bond bond, - long height) - => ClaimReward( - (T)delegatee, - lumpSumRewardRecords, - bond, - height); - - public DelegateResult Delegate( - T delegatee, - FungibleAssetValue fav, - long height, - Bond bond) + void IDelegator.Delegate( + IDelegatee delegatee, FungibleAssetValue fav, long height) + => Delegate((T)delegatee, fav, height); + + void IDelegator.Undelegate( + IDelegatee delegatee, BigInteger share, long height) + => Undelegate((T)delegatee, share, height); + + void IDelegator.Redelegate( + IDelegatee srcDelegatee, IDelegatee dstDelegatee, BigInteger share, long height) + => Redelegate((T)srcDelegatee, (T)dstDelegatee, share, height); + + void IDelegator.ClaimReward( + IDelegatee delegatee, long height) + => ClaimReward((T)delegatee, height); + + public void Delegate( + T delegatee, FungibleAssetValue fav, long height) { if (fav.Sign <= 0) { @@ -118,23 +85,13 @@ public DelegateResult Delegate( nameof(fav), fav, "Fungible asset value must be positive."); } - BondResult bondResult = delegatee.Bond((TSelf)this, fav, height, bond); + delegatee.Bond((TSelf)this, fav, height); Delegatees = Delegatees.Add(delegatee.Address); - - return new DelegateResult( - delegatee, - bondResult.Bond, - fav, - bondResult.LumpSumRewardsRecord); + _repository.TransferAsset(Address, delegatee.Address, fav); } - public UndelegateResult Undelegate( - T delegatee, - BigInteger share, - long height, - Bond bond, - UnbondLockIn unbondLockIn, - UnbondingSet unbondingSet) + public void Undelegate( + T delegatee, BigInteger share, long height) { if (share.Sign <= 0) { @@ -148,39 +105,29 @@ public UndelegateResult Undelegate( nameof(height), height, "Height must be positive."); } + UnbondLockIn unbondLockIn = _repository.GetUnbondLockIn(delegatee, Address); + if (unbondLockIn.IsFull) { throw new InvalidOperationException("Undelegation is full."); } - UnbondResult unbondResult = delegatee.Unbond((TSelf)this, share, height, bond); + FungibleAssetValue fav = delegatee.Unbond((TSelf)this, share, height); unbondLockIn = unbondLockIn.LockIn( - unbondResult.UnbondedFAV, height, height + delegatee.UnbondingPeriod); + fav, height, height + delegatee.UnbondingPeriod); - if (unbondResult.Bond.Share.IsZero) + if (!delegatee.Delegators.Contains(Address)) { Delegatees = Delegatees.Remove(delegatee.Address); } - unbondingSet = unbondingSet.SetUnbonding(unbondLockIn); - - return new UndelegateResult( - delegatee, - unbondResult.Bond, - unbondLockIn, - unbondingSet, - unbondResult.LumpSumRewardsRecord); + _repository.SetUnbondLockIn(unbondLockIn); + _repository.SetUnbondingSet( + _repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); } - public RedelegateResult Redelegate( - T srcDelegatee, - T dstDelegatee, - BigInteger share, - long height, - Bond srcBond, - Bond dstBond, - RebondGrace srcRebondGrace, - UnbondingSet unbondingSet) + public void Redelegate( + T srcDelegatee, T dstDelegatee, BigInteger share, long height) { if (share.Sign <= 0) { @@ -194,42 +141,30 @@ public RedelegateResult Redelegate( nameof(height), height, "Height must be positive."); } - UnbondResult srcUnbondResult = srcDelegatee.Unbond( - (TSelf)this, share, height, srcBond); - BondResult dstBondResult = dstDelegatee.Bond( - (TSelf)this, srcUnbondResult.UnbondedFAV, height, dstBond); - srcRebondGrace = srcRebondGrace.Grace( + FungibleAssetValue fav = srcDelegatee.Unbond( + (TSelf)this, share, height); + dstDelegatee.Bond( + (TSelf)this, fav, height); + RebondGrace srcRebondGrace = _repository.GetRebondGrace(srcDelegatee, Address).Grace( dstDelegatee.Address, - srcUnbondResult.UnbondedFAV, + fav, height, height + srcDelegatee.UnbondingPeriod); - if (srcUnbondResult.Bond.Share.IsZero) + if (!srcDelegatee.Delegators.Contains(Address)) { Delegatees = Delegatees.Remove(srcDelegatee.Address); } Delegatees = Delegatees.Add(dstDelegatee.Address); - unbondingSet.SetUnbonding(srcRebondGrace); - - return new RedelegateResult( - srcDelegatee, - dstDelegatee, - srcUnbondResult.Bond, - dstBondResult.Bond, - srcRebondGrace, - unbondingSet, - srcUnbondResult.LumpSumRewardsRecord, - dstBondResult.LumpSumRewardsRecord); + + _repository.SetRebondGrace(srcRebondGrace); + _repository.SetUnbondingSet( + _repository.GetUnbondingSet().SetUnbonding(srcRebondGrace)); } - public UndelegateResult CancelUndelegate( - T delegatee, - FungibleAssetValue fav, - long height, - Bond bond, - UnbondLockIn unbondLockIn, - UnbondingSet unbondingSet) + public void CancelUndelegate( + T delegatee, FungibleAssetValue fav, long height) { if (fav.Sign <= 0) { @@ -243,41 +178,34 @@ public UndelegateResult CancelUndelegate( nameof(height), height, "Height must be positive."); } + UnbondLockIn unbondLockIn = _repository.GetUnbondLockIn(delegatee, Address); + if (unbondLockIn.IsFull) { throw new InvalidOperationException("Undelegation is full."); } - BondResult bondResult = delegatee.Bond((TSelf)this, fav, height, bond); + delegatee.Bond((TSelf)this, fav, height); unbondLockIn = unbondLockIn.Cancel(fav, height); Delegatees = Delegatees.Add(delegatee.Address); - unbondingSet = unbondLockIn.IsEmpty - ? unbondingSet.RemoveUnbonding(unbondLockIn.Address) - : unbondingSet; - - return new UndelegateResult( - delegatee, - bondResult.Bond, - unbondLockIn, - unbondingSet, - bondResult.LumpSumRewardsRecord); + + _repository.SetUnbondLockIn(unbondLockIn); + UnbondingSet unbondingSet = _repository.GetUnbondingSet(); + if (unbondingSet.IsEmpty) + { + _repository.SetUnbondingSet(unbondingSet.RemoveUnbonding(unbondLockIn.Address)); + } } - public ClaimRewardResult ClaimReward( - T delegatee, - IEnumerable lumpSumRewardRecords, - Bond bond, - long height) + public void ClaimReward( + T delegatee, long height) { - RewardResult rewardResult = delegatee.Reward( - bond.Share, - height, - lumpSumRewardRecords); - LastClaimRewardHeight = height; - return new ClaimRewardResult( - delegatee, - rewardResult.Reward, - rewardResult.LumpSumRewardsRecord); + delegatee.Reward((TSelf)this, height); + } + + public void UpdateLastRewardHeight(long height) + { + LastRewardHeight = height; } public override bool Equals(object? obj) diff --git a/Lib9c/Delegation/IClaimRewardResult.cs b/Lib9c/Delegation/IClaimRewardResult.cs deleted file mode 100644 index 045a02bc02..0000000000 --- a/Lib9c/Delegation/IClaimRewardResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Libplanet.Types.Assets; - -namespace Nekoyume.Delegation -{ - public interface IClaimRewardResult - { - IDelegatee Delegatee { get; } - - FungibleAssetValue Reward { get; } - - LumpSumRewardsRecord LumpSumRewardsRecord { get; } - } -} diff --git a/Lib9c/Delegation/IDelegateResult.cs b/Lib9c/Delegation/IDelegateResult.cs deleted file mode 100644 index 5e521e122c..0000000000 --- a/Lib9c/Delegation/IDelegateResult.cs +++ /dev/null @@ -1,16 +0,0 @@ -#nullable enable -using Libplanet.Types.Assets; - -namespace Nekoyume.Delegation -{ - public interface IDelegateResult - { - IDelegatee Delegatee { get; } - - Bond Bond { get; } - - FungibleAssetValue DelegatedFAV { get; } - - LumpSumRewardsRecord LumpSumRewardsRecord { get; } - } -} diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index 223e6e9b93..b2e8df3ad9 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -33,14 +33,11 @@ public interface IDelegatee : IBencodable, IEquatable BigInteger TotalShares { get; } - BondResult Bond(IDelegator delegator, FungibleAssetValue fav, long height, Bond bond); + BigInteger Bond(IDelegator delegator, FungibleAssetValue fav, long height); - UnbondResult Unbond(IDelegator delegator, BigInteger share, long height, Bond bond); + FungibleAssetValue Unbond(IDelegator delegator, BigInteger share, long height); - RewardResult Reward( - BigInteger share, - long height, - IEnumerable lumpSumRewardsRecords); + void Reward(IDelegator delegator, long height); Address BondAddress(Address delegatorAddress); @@ -48,6 +45,8 @@ RewardResult Reward( Address RebondGraceAddress(Address delegatorAddress); - Address LumpSumRewardsRecordAddress(long? height = null); + Address CurrentLumpSumRewardsRecordAddress(); + + Address LumpSumRewardsRecordAddress(long height); } } diff --git a/Lib9c/Delegation/IDelegationRepository.cs b/Lib9c/Delegation/IDelegationRepository.cs new file mode 100644 index 0000000000..faceb520c5 --- /dev/null +++ b/Lib9c/Delegation/IDelegationRepository.cs @@ -0,0 +1,36 @@ +#nullable enable +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public interface IDelegationRepository + { + IWorld World { get; } + + Bond GetBond(IDelegatee delegatee, Address delegatorAddress); + + UnbondLockIn GetUnbondLockIn(IDelegatee delegatee, Address delegatorAddress); + + RebondGrace GetRebondGrace(IDelegatee delegatee, Address delegatorAddress); + + UnbondingSet GetUnbondingSet(); + + LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height); + + LumpSumRewardsRecord? GetCurrentLumpSumRewardsRecord(IDelegatee delegatee); + + void SetBond(Bond bond); + + void SetUnbondLockIn(UnbondLockIn unbondLockIn); + + void SetRebondGrace(RebondGrace rebondGrace); + + void SetUnbondingSet(UnbondingSet unbondingSet); + + void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord); + + void TransferAsset(Address sender, Address recipient, FungibleAssetValue value); + } +} diff --git a/Lib9c/Delegation/IDelegator.cs b/Lib9c/Delegation/IDelegator.cs index e1c7db0cf7..6d09a920b0 100644 --- a/Lib9c/Delegation/IDelegator.cs +++ b/Lib9c/Delegation/IDelegator.cs @@ -1,6 +1,5 @@ #nullable enable using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Numerics; using Bencodex; @@ -15,34 +14,24 @@ public interface IDelegator : IBencodable, IEquatable ImmutableSortedSet
Delegatees { get; } - IDelegateResult Delegate( + void Delegate( IDelegatee delegatee, FungibleAssetValue fav, - long height, - Bond bond); + long height); - IUndelegateResult Undelegate( + void Undelegate( IDelegatee delegatee, BigInteger share, - long height, - Bond bond, - UnbondLockIn unbondLockIn, - UnbondingSet unbondingSet); + long height); - IRedelegateResult Redelegate( + void Redelegate( IDelegatee srcDelegatee, IDelegatee dstDelegatee, BigInteger share, - long height, - Bond srcBond, - Bond dstBond, - RebondGrace srcRebondGrace, - UnbondingSet unbondingSet); + long height); - IClaimRewardResult ClaimReward( + void ClaimReward( IDelegatee delegatee, - IEnumerable lumpSumRewardRecords, - Bond bond, long height); } } diff --git a/Lib9c/Delegation/IRedelegateResult.cs b/Lib9c/Delegation/IRedelegateResult.cs deleted file mode 100644 index 462286716d..0000000000 --- a/Lib9c/Delegation/IRedelegateResult.cs +++ /dev/null @@ -1,22 +0,0 @@ -#nullable enable -namespace Nekoyume.Delegation -{ - public interface IRedelegateResult - { - IDelegatee SrcDelegatee { get; } - - IDelegatee DstDelegatee { get; } - - Bond SrcBond { get; } - - Bond DstBond { get; } - - RebondGrace RebondGrace { get; } - - UnbondingSet UnbondingSet { get; } - - LumpSumRewardsRecord SrcLumpSumRewardsRecord { get; } - - LumpSumRewardsRecord DstLumpSumRewardsRecord { get; } - } -} diff --git a/Lib9c/Delegation/IUndelegateResult.cs b/Lib9c/Delegation/IUndelegateResult.cs deleted file mode 100644 index 78d9067ff5..0000000000 --- a/Lib9c/Delegation/IUndelegateResult.cs +++ /dev/null @@ -1,16 +0,0 @@ -#nullable enable -namespace Nekoyume.Delegation -{ - public interface IUndelegateResult - { - IDelegatee Delegatee { get; } - - Bond Bond { get; } - - UnbondLockIn UnbondLockIn { get; } - - UnbondingSet UnbondingSet { get; } - - LumpSumRewardsRecord LumpSumRewardsRecord { get; } - } -} diff --git a/Lib9c/Delegation/LumpSumRewardsRecord.cs b/Lib9c/Delegation/LumpSumRewardsRecord.cs index 88a42fd1b3..d26efa261a 100644 --- a/Lib9c/Delegation/LumpSumRewardsRecord.cs +++ b/Lib9c/Delegation/LumpSumRewardsRecord.cs @@ -86,6 +86,14 @@ public List Bencoded IValue IBencodable.Bencoded => Bencoded; + public LumpSumRewardsRecord MoveAddress(Address address) + => new LumpSumRewardsRecord( + address, + StartHeight, + TotalShares, + LumpSumRewards, + LastStartHeight); + public LumpSumRewardsRecord AddLumpSumReward(FungibleAssetValue reward) => new LumpSumRewardsRecord( Address, diff --git a/Lib9c/Delegation/RedelegateResult.cs b/Lib9c/Delegation/RedelegateResult.cs deleted file mode 100644 index 3523291343..0000000000 --- a/Lib9c/Delegation/RedelegateResult.cs +++ /dev/null @@ -1,47 +0,0 @@ -#nullable enable -namespace Nekoyume.Delegation -{ - public class RedelegateResult : IRedelegateResult - where T : IDelegatee - { - public RedelegateResult( - T srcDelegatee, - T dstDelegatee, - Bond srcBond, - Bond dstBond, - RebondGrace rebondGrace, - UnbondingSet unbondingSet, - LumpSumRewardsRecord srcLumpSumRewardsRecord, - LumpSumRewardsRecord dstLumpSumRewardsRecord) - { - SrcDelegatee = srcDelegatee; - DstDelegatee = dstDelegatee; - SrcBond = srcBond; - DstBond = dstBond; - RebondGrace = rebondGrace; - UnbondingSet = unbondingSet; - SrcLumpSumRewardsRecord = srcLumpSumRewardsRecord; - DstLumpSumRewardsRecord = dstLumpSumRewardsRecord; - } - - public T SrcDelegatee { get; } - - IDelegatee IRedelegateResult.SrcDelegatee => SrcDelegatee; - - public T DstDelegatee { get; } - - IDelegatee IRedelegateResult.DstDelegatee => DstDelegatee; - - public Bond SrcBond { get; } - - public Bond DstBond { get; } - - public RebondGrace RebondGrace { get; } - - public UnbondingSet UnbondingSet { get; } - - public LumpSumRewardsRecord SrcLumpSumRewardsRecord { get; } - - public LumpSumRewardsRecord DstLumpSumRewardsRecord { get; } - } -} diff --git a/Lib9c/Delegation/RewardResult.cs b/Lib9c/Delegation/RewardResult.cs deleted file mode 100644 index 514affb60c..0000000000 --- a/Lib9c/Delegation/RewardResult.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Libplanet.Types.Assets; - -namespace Nekoyume.Delegation -{ - public class RewardResult - { - public RewardResult(FungibleAssetValue reward, LumpSumRewardsRecord lumpSumRewardsRecord) - { - Reward = reward; - LumpSumRewardsRecord = lumpSumRewardsRecord; - } - - public FungibleAssetValue Reward { get; } - - public LumpSumRewardsRecord LumpSumRewardsRecord { get; } - } -} diff --git a/Lib9c/Delegation/UnbondResult.cs b/Lib9c/Delegation/UnbondResult.cs deleted file mode 100644 index 4bc6fef506..0000000000 --- a/Lib9c/Delegation/UnbondResult.cs +++ /dev/null @@ -1,21 +0,0 @@ -#nullable enable -using Libplanet.Types.Assets; - -namespace Nekoyume.Delegation -{ - public class UnbondResult - { - public UnbondResult(Bond bond, FungibleAssetValue unbondedFAV, LumpSumRewardsRecord lumpSumRewardsRecord) - { - Bond = bond; - UnbondedFAV = unbondedFAV; - LumpSumRewardsRecord = lumpSumRewardsRecord; - } - - public Bond Bond { get; } - - public FungibleAssetValue UnbondedFAV { get; } - - public LumpSumRewardsRecord LumpSumRewardsRecord { get; } - } -} diff --git a/Lib9c/Delegation/UnbondingSet.cs b/Lib9c/Delegation/UnbondingSet.cs index 61a1dacbbd..98f1983a4b 100644 --- a/Lib9c/Delegation/UnbondingSet.cs +++ b/Lib9c/Delegation/UnbondingSet.cs @@ -87,6 +87,8 @@ public List Bencoded IValue IBencodable.Bencoded => Bencoded; + public bool IsEmpty => Unbondings.IsEmpty; + public UnbondingSet SetUnbondings(IEnumerable unbondings) { UnbondingSet result = this; diff --git a/Lib9c/Delegation/UndelegateResult.cs b/Lib9c/Delegation/UndelegateResult.cs deleted file mode 100644 index 0bbf0bbf71..0000000000 --- a/Lib9c/Delegation/UndelegateResult.cs +++ /dev/null @@ -1,33 +0,0 @@ -#nullable enable -namespace Nekoyume.Delegation -{ - public class UndelegateResult : IUndelegateResult - where T : IDelegatee - { - public UndelegateResult( - T delegatee, - Bond bond, - UnbondLockIn unbondLockIn, - UnbondingSet unbondingSet, - LumpSumRewardsRecord lumpSumRewardsRecord) - { - Delegatee = delegatee; - Bond = bond; - UnbondLockIn = unbondLockIn; - UnbondingSet = unbondingSet; - LumpSumRewardsRecord = lumpSumRewardsRecord; - } - - public T Delegatee { get; } - - IDelegatee IUndelegateResult.Delegatee => Delegatee; - - public Bond Bond { get; } - - public UnbondLockIn UnbondLockIn { get; } - - public UnbondingSet UnbondingSet { get; } - - public LumpSumRewardsRecord LumpSumRewardsRecord { get; } - } -} diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index 7e5e9fcaf8..5421ecc05a 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -197,7 +197,7 @@ private static IWorld ClaimReward( : throw new InvalidOperationException("The guild does not exist."); var bond = world.GetBond(guild, agentAddress); var rewardRecords = world.GetLumpSumRewardsRecords( - guild, context.BlockIndex, guildParticipant.LastClaimRewardHeight); + guild, context.BlockIndex, guildParticipant.LastRewardHeight); var claimRewardResult = guildParticipant.ClaimReward( guild, rewardRecords, bond, context.BlockIndex); From c4f37098fd61c2721770cb87a265daea6419735b Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sun, 11 Aug 2024 17:36:27 +0900 Subject: [PATCH 018/206] feat: Allow null repository delegator/ee --- Lib9c/Delegation/Delegatee.cs | 46 ++++++++++++++++++++++++++-------- Lib9c/Delegation/Delegator.cs | 44 +++++++++++++++++++++++++------- Lib9c/Delegation/IDelegatee.cs | 1 - 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 5682a36c96..2ba7059fd8 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -23,9 +23,9 @@ public abstract class Delegatee : IDelegatee protected readonly byte[] RewardPoolId = new byte[] { 0x72 }; // `r` protected readonly byte[] PoolId = new byte[] { 0x70 }; // `p` - private readonly IDelegationRepository _repository; + private readonly IDelegationRepository? _repository; - public Delegatee(Address address, IDelegationRepository repository) + public Delegatee(Address address, IDelegationRepository? repository = null) { Address = address; Delegators = ImmutableSortedSet
.Empty; @@ -34,12 +34,12 @@ public Delegatee(Address address, IDelegationRepository repository) _repository = repository; } - public Delegatee(Address address, IValue bencoded, IDelegationRepository repository) + public Delegatee(Address address, IValue bencoded, IDelegationRepository? repository = null) : this(address, (List)bencoded, repository) { } - public Delegatee(Address address, List bencoded, IDelegationRepository repository) + public Delegatee(Address address, List bencoded, IDelegationRepository? repository = null) : this( address, ((List)bencoded[0]).Select(item => new Address(item)), @@ -54,7 +54,7 @@ private Delegatee( IEnumerable
delegators, FungibleAssetValue totalDelegated, BigInteger totalShares, - IDelegationRepository repository) + IDelegationRepository? repository) { if (!totalDelegated.Currency.Equals(Currency)) { @@ -108,6 +108,8 @@ private Delegatee( public BigInteger TotalShares { get; private set; } + public IDelegationRepository? Repository => _repository; + public List Bencoded => List.Empty .Add(new List(Delegators.Select(delegator => delegator.Bencoded))) .Add(TotalDelegated.Serialize()) @@ -138,13 +140,14 @@ void IDelegatee.Reward(IDelegator delegator, long height) public BigInteger Bond(T delegator, FungibleAssetValue fav, long height) { + CannotMutateWithoutRepository(delegator); if (!fav.Currency.Equals(Currency)) { throw new InvalidOperationException( "Cannot bond with invalid currency."); } - Bond bond = _repository.GetBond(this, delegator.Address); + Bond bond = _repository!.GetBond(this, delegator.Address); BigInteger share = ShareToBond(fav); bond = bond.AddShare(share); Delegators = Delegators.Add(delegator.Address); @@ -158,13 +161,14 @@ public BigInteger Bond(T delegator, FungibleAssetValue fav, long height) public FungibleAssetValue Unbond(T delegator, BigInteger share, long height) { + CannotMutateWithoutRepository(delegator); if (TotalShares.IsZero || TotalDelegated.RawValue.IsZero) { throw new InvalidOperationException( "Cannot unbond without bonding."); } - Bond bond = _repository.GetBond(this, delegator.Address); + Bond bond = _repository!.GetBond(this, delegator.Address); FungibleAssetValue fav = FAVToUnbond(share); bond = bond.SubtractShare(share); if (bond.Share.IsZero) @@ -182,7 +186,8 @@ public FungibleAssetValue Unbond(T delegator, BigInteger share, long height) public void Reward(T delegator, long height) { - BigInteger share = _repository.GetBond(this, delegator.Address).Share; + CannotMutateWithoutRepository(delegator); + BigInteger share = _repository!.GetBond(this, delegator.Address).Share; IEnumerable lumpSumRewardsRecords = GetLumpSumRewardsRecords(delegator.LastRewardHeight); FungibleAssetValue reward = CalculateReward(share, lumpSumRewardsRecords); @@ -244,7 +249,8 @@ protected Address DeriveAddress(byte[] typeId, IEnumerable? bytes = null) private void StartNewRewardPeriod(long height) { - LumpSumRewardsRecord? currentRecord = _repository.GetCurrentLumpSumRewardsRecord(this); + CannotMutateWithoutRepository(); + LumpSumRewardsRecord? currentRecord = _repository!.GetCurrentLumpSumRewardsRecord(this); long? lastStartHeight = null; if (currentRecord is LumpSumRewardsRecord lastRecord) { @@ -298,8 +304,9 @@ private FungibleAssetValue CalculateReward( private List GetLumpSumRewardsRecords(long? lastRewardHeight) { + CannotMutateWithoutRepository(); List records = new(); - if (!(_repository.GetCurrentLumpSumRewardsRecord(this) is LumpSumRewardsRecord record)) + if (!(_repository!.GetCurrentLumpSumRewardsRecord(this) is LumpSumRewardsRecord record)) { return records; } @@ -321,5 +328,24 @@ record = _repository.GetLumpSumRewardsRecord(this, lastStartHeight) return records; } + + private void CannotMutateWithoutRepository(T delegator) + { + CannotMutateWithoutRepository(); + if (!_repository!.Equals(delegator.Repository)) + { + throw new InvalidOperationException( + "Cannot mutate with different repository."); + } + } + + private void CannotMutateWithoutRepository() + { + if (_repository is null) + { + throw new InvalidOperationException( + "Cannot mutate without repository."); + } + } } } diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index 1998f53223..065bf44426 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -14,19 +14,19 @@ public abstract class Delegator : IDelegator where T : Delegatee where TSelf : Delegator { - private readonly IDelegationRepository _repository; + private readonly IDelegationRepository? _repository; - public Delegator(Address address, IDelegationRepository repository) + public Delegator(Address address, IDelegationRepository? repository = null) : this(address, ImmutableSortedSet
.Empty, 0L, repository) { } - public Delegator(Address address, IValue bencoded, IDelegationRepository repository) + public Delegator(Address address, IValue bencoded, IDelegationRepository? repository = null) : this(address, (List)bencoded, repository) { } - public Delegator(Address address, List bencoded, IDelegationRepository repository) + public Delegator(Address address, List bencoded, IDelegationRepository? repository = null) : this( address, ((List)bencoded[0]).Select(item => new Address(item)).ToImmutableSortedSet(), @@ -39,7 +39,7 @@ private Delegator( Address address, ImmutableSortedSet
delegatees, long lastRewardHeight, - IDelegationRepository repository) + IDelegationRepository? repository) { Address = address; Delegatees = delegatees; @@ -53,6 +53,8 @@ private Delegator( public long LastRewardHeight { get; private set; } + public IDelegationRepository? Repository => _repository; + public List Bencoded => List.Empty .Add(new List(Delegatees.Select(a => a.Bencoded))) @@ -79,6 +81,7 @@ void IDelegator.ClaimReward( public void Delegate( T delegatee, FungibleAssetValue fav, long height) { + CannotMutateWithoutRepository(delegatee); if (fav.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -87,12 +90,13 @@ public void Delegate( delegatee.Bond((TSelf)this, fav, height); Delegatees = Delegatees.Add(delegatee.Address); - _repository.TransferAsset(Address, delegatee.Address, fav); + _repository!.TransferAsset(Address, delegatee.Address, fav); } public void Undelegate( T delegatee, BigInteger share, long height) { + CannotMutateWithoutRepository(delegatee); if (share.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -105,7 +109,7 @@ public void Undelegate( nameof(height), height, "Height must be positive."); } - UnbondLockIn unbondLockIn = _repository.GetUnbondLockIn(delegatee, Address); + UnbondLockIn unbondLockIn = _repository!.GetUnbondLockIn(delegatee, Address); if (unbondLockIn.IsFull) { @@ -129,6 +133,8 @@ public void Undelegate( public void Redelegate( T srcDelegatee, T dstDelegatee, BigInteger share, long height) { + CannotMutateWithoutRepository(srcDelegatee); + CannotMutateWithoutRepository(dstDelegatee); if (share.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -145,7 +151,7 @@ public void Redelegate( (TSelf)this, share, height); dstDelegatee.Bond( (TSelf)this, fav, height); - RebondGrace srcRebondGrace = _repository.GetRebondGrace(srcDelegatee, Address).Grace( + RebondGrace srcRebondGrace = _repository!.GetRebondGrace(srcDelegatee, Address).Grace( dstDelegatee.Address, fav, height, @@ -166,6 +172,7 @@ public void Redelegate( public void CancelUndelegate( T delegatee, FungibleAssetValue fav, long height) { + CannotMutateWithoutRepository(delegatee); if (fav.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -178,7 +185,7 @@ public void CancelUndelegate( nameof(height), height, "Height must be positive."); } - UnbondLockIn unbondLockIn = _repository.GetUnbondLockIn(delegatee, Address); + UnbondLockIn unbondLockIn = _repository!.GetUnbondLockIn(delegatee, Address); if (unbondLockIn.IsFull) { @@ -220,5 +227,24 @@ public bool Equals(IDelegator? other) public override int GetHashCode() => Address.GetHashCode(); + + private void CannotMutateWithoutRepository(T delegatee) + { + CannotMutateWithoutRepository(); + if (!_repository!.Equals(delegatee.Repository)) + { + throw new InvalidOperationException( + "Cannot mutate with different repository."); + } + } + + private void CannotMutateWithoutRepository() + { + if (_repository is null) + { + throw new InvalidOperationException( + "Cannot mutate without repository."); + } + } } } diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index b2e8df3ad9..85ecd426aa 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -1,6 +1,5 @@ #nullable enable using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Numerics; using Bencodex; From 35d692c12e7d167e28678ecacb1ad0fa76f7306d Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 12 Aug 2024 00:28:20 +0900 Subject: [PATCH 019/206] refactor: Apply changes from repository on modules --- Lib9c/Model/Guild/Guild.cs | 21 ++- Lib9c/Model/Guild/GuildParticipant.cs | 20 ++- Lib9c/Module/Delegation/BondModule.cs | 8 -- .../Delegation/LumpSumRewardsRecordModule.cs | 24 +--- Lib9c/Module/Delegation/RebondGraceModule.cs | 9 -- Lib9c/Module/Delegation/UnbondLockInModule.cs | 9 -- Lib9c/Module/Delegation/UnbondingSetModule.cs | 29 +++- Lib9c/Module/Guild/GuildModule.cs | 31 ++++ Lib9c/Module/Guild/GuildParticipantModule.cs | 133 ++++++++++++------ 9 files changed, 182 insertions(+), 102 deletions(-) diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index 82110fb0f6..e5a5b76aa8 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -17,15 +17,27 @@ public class Guild : Delegatee, IEquatable, IBen public readonly AgentAddress GuildMasterAddress; - public Guild(AgentAddress guildMasterAddress, Currency rewardCurrency) - : base(guildMasterAddress) + public Guild( + AgentAddress guildMasterAddress, Currency rewardCurrency) + : this(guildMasterAddress, rewardCurrency, null) + { + } + + public Guild(List list, Currency rewardCurrency) + : this(list, rewardCurrency, null) + { + } + + public Guild( + AgentAddress guildMasterAddress, Currency rewardCurrency, IDelegationRepository repository) + : base(guildMasterAddress, repository) { GuildMasterAddress = guildMasterAddress; RewardCurrency = rewardCurrency; } - public Guild(List list, Currency rewardCurrency) - : base(new Address(list[2]), list[3]) + public Guild(List list, Currency rewardCurrency, IDelegationRepository repository) + : base(new Address(list[2]), list[3], repository) { GuildMasterAddress = new AgentAddress(list[2]); RewardCurrency = rewardCurrency; @@ -39,7 +51,6 @@ public Guild(List list, Currency rewardCurrency) { throw new FailedLoadStateException("Un-deserializable state."); } - } public override Currency Currency => Currencies.GuildGold; diff --git a/Lib9c/Model/Guild/GuildParticipant.cs b/Lib9c/Model/Guild/GuildParticipant.cs index 89737c019a..dc97f49411 100644 --- a/Lib9c/Model/Guild/GuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildParticipant.cs @@ -15,14 +15,26 @@ public class GuildParticipant : Delegator, IBencodable, public readonly GuildAddress GuildAddress; - public GuildParticipant(AgentAddress agentAddress, GuildAddress guildAddress) - : base(agentAddress) + public GuildParticipant( + AgentAddress agentAddress, GuildAddress guildAddress) + : this(agentAddress, guildAddress, null) { - GuildAddress = guildAddress; } public GuildParticipant(AgentAddress agentAddress, List list) - : base(agentAddress, list[3]) + : this(agentAddress, list, null) + { + } + + public GuildParticipant( + AgentAddress agentAddress, GuildAddress guildAddress, IDelegationRepository repository) + : base(agentAddress, repository) + { + GuildAddress = guildAddress; + } + + public GuildParticipant(AgentAddress agentAddress, List list, IDelegationRepository repository) + : base(agentAddress, list[3], repository) { GuildAddress = new GuildAddress(list[2]); diff --git a/Lib9c/Module/Delegation/BondModule.cs b/Lib9c/Module/Delegation/BondModule.cs index da804049d7..63b6b7fa9c 100644 --- a/Lib9c/Module/Delegation/BondModule.cs +++ b/Lib9c/Module/Delegation/BondModule.cs @@ -3,7 +3,6 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Nekoyume.Delegation; -using Nekoyume.Extensions; namespace Nekoyume.Module.Delegation { @@ -39,12 +38,5 @@ public static bool TryGetBond( return false; } } - - public static IWorld SetBond(this IWorld world, Bond bond) - => world.MutateAccount( - Addresses.Bond, - account => bond.Share.IsZero - ? account.RemoveState(bond.Address) - : account.SetState(bond.Address, bond.Bencoded)); } } diff --git a/Lib9c/Module/Delegation/LumpSumRewardsRecordModule.cs b/Lib9c/Module/Delegation/LumpSumRewardsRecordModule.cs index a3f00b7d49..2e6275e1d4 100644 --- a/Lib9c/Module/Delegation/LumpSumRewardsRecordModule.cs +++ b/Lib9c/Module/Delegation/LumpSumRewardsRecordModule.cs @@ -1,30 +1,17 @@ #nullable enable using System; -using System.Collections.Generic; using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; using Nekoyume.Delegation; -using Nekoyume.Extensions; namespace Nekoyume.Module.Delegation { public static class LumpSumRewardsRecordModule { - public static List GetLumpSumRewardsRecords( - this IWorldState world, IDelegatee delegatee, long height, long startHeight) - { - var records = new List(); - LumpSumRewardsRecord record; - while (height >= startHeight) - { - record = world.GetLumpSumRewardsRecord(delegatee, height); - records.Add(record); - height = record.LastStartHeight; - } - - return records; - } + public static LumpSumRewardsRecord GetCurrentLumpSumRewardsRecord( + this IWorldState world, IDelegatee delegatee) + => GetLumpSumRewardsRecord(world, delegatee.CurrentLumpSumRewardsRecordAddress()); public static LumpSumRewardsRecord GetLumpSumRewardsRecord( this IWorldState world, IDelegatee delegatee, long height) @@ -57,10 +44,5 @@ public static bool TryGetLumpSumRewardsRecord( return false; } } - - public static IWorld SetLumpSumRewardsRecord(this IWorld world, LumpSumRewardsRecord lumpSumRewardsRecord) - => world.MutateAccount( - Addresses.LumpSumRewardsRecord, - account => account.SetState(lumpSumRewardsRecord.Address, lumpSumRewardsRecord.Bencoded)); } } diff --git a/Lib9c/Module/Delegation/RebondGraceModule.cs b/Lib9c/Module/Delegation/RebondGraceModule.cs index 6bb3d7d461..c49af5661d 100644 --- a/Lib9c/Module/Delegation/RebondGraceModule.cs +++ b/Lib9c/Module/Delegation/RebondGraceModule.cs @@ -2,7 +2,6 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Nekoyume.Delegation; -using Nekoyume.Extensions; namespace Nekoyume.Module.Delegation { @@ -45,13 +44,5 @@ public static bool TryGetRebondGrace( return false; } } - - public static IWorld SetRebondGrace( - this IWorld world, RebondGrace rebondGrace) - => world.MutateAccount( - Addresses.RebondGrace, - account => rebondGrace.IsEmpty - ? account.RemoveState(rebondGrace.Address) - : account.SetState(rebondGrace.Address, rebondGrace.Bencoded)); } } diff --git a/Lib9c/Module/Delegation/UnbondLockInModule.cs b/Lib9c/Module/Delegation/UnbondLockInModule.cs index 03cef17ac8..c64f00c94f 100644 --- a/Lib9c/Module/Delegation/UnbondLockInModule.cs +++ b/Lib9c/Module/Delegation/UnbondLockInModule.cs @@ -3,7 +3,6 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Nekoyume.Delegation; -using Nekoyume.Extensions; namespace Nekoyume.Module.Delegation { @@ -46,13 +45,5 @@ public static bool TryGetUnbondLockIn( return false; } } - - public static IWorld SetUnbondLockIn( - this IWorld world, UnbondLockIn unbondLockIn) - => world.MutateAccount( - Addresses.UnbondLockIn, - account => unbondLockIn.IsEmpty - ? account.RemoveState(unbondLockIn.Address) - : account.SetState(unbondLockIn.Address, unbondLockIn.Bencoded)); } } diff --git a/Lib9c/Module/Delegation/UnbondingSetModule.cs b/Lib9c/Module/Delegation/UnbondingSetModule.cs index 80a1ee45ff..bb52c77a2c 100644 --- a/Lib9c/Module/Delegation/UnbondingSetModule.cs +++ b/Lib9c/Module/Delegation/UnbondingSetModule.cs @@ -40,11 +40,6 @@ public static bool TryGetUnbondingSet( } } - public static IWorld SetUnbondingSet(this IWorld world, UnbondingSet unbondingSet) - => world.MutateAccount( - Addresses.UnbondingSet, - account => account.SetState(UnbondingSet.Address, unbondingSet.Bencoded)); - public static IWorld Release(this IWorld world, IActionContext context) { var unbondingSet = world.GetUnbondingSet(); @@ -64,13 +59,35 @@ public static IWorld Release(this IWorld world, IActionContext context) return world.SetUnbondingSet(unbondingSet); } - public static IUnbonding[] ReleaseUnbondings(this IWorld world, IActionContext context, UnbondingSet unbondingSet) + private static IUnbonding[] ReleaseUnbondings( + this IWorld world, IActionContext context, UnbondingSet unbondingSet) => unbondingSet.ReleaseUnbondings( context.BlockIndex, (address, type) => world.GetAccount(AccountAddress(type)).GetState(address) ?? throw new FailedLoadStateException( $"Tried to release unbonding on {address}, but unbonding does not exist.")); + private static IWorld SetUnbondingSet(this IWorld world, UnbondingSet unbondingSet) + => world.MutateAccount( + Addresses.UnbondingSet, + account => account.SetState(UnbondingSet.Address, unbondingSet.Bencoded)); + + private static IWorld SetUnbondLockIn( + this IWorld world, UnbondLockIn unbondLockIn) + => world.MutateAccount( + Addresses.UnbondLockIn, + account => unbondLockIn.IsEmpty + ? account.RemoveState(unbondLockIn.Address) + : account.SetState(unbondLockIn.Address, unbondLockIn.Bencoded)); + + public static IWorld SetRebondGrace( + this IWorld world, RebondGrace rebondGrace) + => world.MutateAccount( + Addresses.RebondGrace, + account => rebondGrace.IsEmpty + ? account.RemoveState(rebondGrace.Address) + : account.SetState(rebondGrace.Address, rebondGrace.Bencoded)); + private static Address AccountAddress(Type type) => type switch { var t when t == typeof(UnbondLockIn) => Addresses.UnbondLockIn, diff --git a/Lib9c/Module/Guild/GuildModule.cs b/Lib9c/Module/Guild/GuildModule.cs index 30c0e1cefc..9968c14639 100644 --- a/Lib9c/Module/Guild/GuildModule.cs +++ b/Lib9c/Module/Guild/GuildModule.cs @@ -4,6 +4,7 @@ using Bencodex.Types; using Libplanet.Action.State; using Nekoyume.Action; +using Nekoyume.Delegation; using Nekoyume.Extensions; using Nekoyume.TypedAddress; @@ -22,6 +23,18 @@ public static Model.Guild.Guild GetGuild(this IWorldState worldState, GuildAddre throw new FailedLoadStateException("There is no such guild."); } + public static Model.Guild.Guild GetGuild( + this IWorldState worldState, GuildAddress guildAddress, IDelegationRepository repository) + { + var value = worldState.GetAccountState(Addresses.Guild).GetState(guildAddress); + if (value is List list) + { + return new Model.Guild.Guild(list, worldState.GetGoldCurrency(), repository); + } + + throw new FailedLoadStateException("There is no such guild."); + } + public static bool TryGetGuild(this IWorldState worldState, GuildAddress guildAddress, [NotNullWhen(true)] out Model.Guild.Guild? guild) { @@ -37,6 +50,24 @@ public static bool TryGetGuild(this IWorldState worldState, } } + public static bool TryGetGuild( + this IWorldState worldState, + GuildAddress guildAddress, + IDelegationRepository repository, + [NotNullWhen(true)] out Model.Guild.Guild? guild) + { + try + { + guild = GetGuild(worldState, guildAddress, repository); + return true; + } + catch + { + guild = null; + return false; + } + } + public static IWorld MakeGuild(this IWorld world, GuildAddress guildAddress, AgentAddress signer) { if (world.GetJoinedGuild(signer) is not null) diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index 5421ecc05a..e8244ac30e 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -8,6 +8,7 @@ using Libplanet.Action.State; using Libplanet.Types.Assets; using Nekoyume.Action; +using Nekoyume.Delegation; using Nekoyume.Extensions; using Nekoyume.Model.Guild; using Nekoyume.Module.Delegation; @@ -47,7 +48,23 @@ public static IWorld LeaveGuildWithUndelegate( .LeaveGuild(new AgentAddress(context.Signer)); } - // TODO: Implement `MoveGuild()`, `MoveGuildWithRedelegate()`, `Redelegate()` method. + public static IWorld MoveGuildWithRedelegate( + this IWorld world, + IActionContext context, + GuildAddress srcGuildAddress, + GuildAddress dstGuildAddress) + { + var agentAddress = new AgentAddress(context.Signer); + var srcGuild = world.GetGuild(srcGuildAddress); + return world + .Redelegate( + context, + srcGuildAddress, + dstGuildAddress, + world.GetBond(srcGuild, context.Signer).Share) + .LeaveGuild(agentAddress) + .JoinGuild(dstGuildAddress, agentAddress); + } public static IWorld JoinGuild( this IWorld world, @@ -95,7 +112,8 @@ public static IWorld RawLeaveGuild(this IWorld world, AgentAddress target) .DecreaseGuildMemberCount(guildParticipant.GuildAddress); } - private static Model.Guild.GuildParticipant GetGuildParticipant(this IWorldState worldState, AgentAddress agentAddress) + private static Model.Guild.GuildParticipant GetGuildParticipant( + this IWorldState worldState, AgentAddress agentAddress) { var value = worldState.GetAccountState(Addresses.GuildParticipant) .GetState(agentAddress); @@ -107,6 +125,21 @@ private static Model.Guild.GuildParticipant GetGuildParticipant(this IWorldState throw new FailedLoadStateException("It may not join any guild."); } + private static Model.Guild.GuildParticipant GetGuildParticipant( + this IWorldState worldState, + AgentAddress agentAddress, + IDelegationRepository repository) + { + var value = worldState.GetAccountState(Addresses.GuildParticipant) + .GetState(agentAddress); + if (value is List list) + { + return new Model.Guild.GuildParticipant(agentAddress, list, repository); + } + + throw new FailedLoadStateException("It may not join any guild."); + } + private static bool TryGetGuildParticipant(this IWorldState worldState, AgentAddress agentAddress, [NotNullWhen(true)] out Model.Guild.GuildParticipant? guildParticipant) @@ -123,6 +156,23 @@ private static bool TryGetGuildParticipant(this IWorldState worldState, } } + private static bool TryGetGuildParticipant(this IWorldState worldState, + AgentAddress agentAddress, + IDelegationRepository repository, + [NotNullWhen(true)] out Model.Guild.GuildParticipant? guildParticipant) + { + try + { + guildParticipant = GetGuildParticipant(worldState, agentAddress, repository); + return true; + } + catch + { + guildParticipant = null; + return false; + } + } + private static IWorld RemoveGuildParticipant(this IWorld world, AgentAddress agentAddress) { return world.MutateAccount(Addresses.GuildParticipant, @@ -135,23 +185,18 @@ private static IWorld Delegate( GuildAddress guildAddress, FungibleAssetValue fav) { - world = world.ClaimReward(context, guildAddress); + var repo = new DelegationRepository(world, context); var agentAddress = new AgentAddress(context.Signer); - var guildParticipant = world.TryGetGuildParticipant(agentAddress, out var p) + var guildParticipant = world.TryGetGuildParticipant(agentAddress, repo, out var p) ? p - : new GuildParticipant(agentAddress, guildAddress); - var guild = world.TryGetGuild(guildAddress, out var g) + : throw new InvalidOperationException("The signer was not joined to any guild."); + var guild = world.TryGetGuild(guildAddress, repo, out var g) ? g : throw new InvalidOperationException("The guild does not exist."); - var bond = world.GetBond(guild, agentAddress); - var result = guildParticipant.Delegate(guild, fav, context.BlockIndex, bond); + guildParticipant.Delegate(guild, fav, context.BlockIndex); - return world - .SetBond(result.Bond) - .SetGuild(result.Delegatee) - .SetGuildParticipant(guildParticipant) - .TransferAsset(context, agentAddress, guildAddress, result.DelegatedFAV); + return repo.World.SetGuild(guild).SetGuildParticipant(guildParticipant); } private static IWorld Undelegate( @@ -160,27 +205,42 @@ private static IWorld Undelegate( GuildAddress guildAddress, BigInteger share) { - world = world.ClaimReward(context, guildAddress); + var repo = new DelegationRepository(world, context); var agentAddress = new AgentAddress(context.Signer); - var guildParticipant = world.TryGetGuildParticipant(agentAddress, out var p) + var guildParticipant = world.TryGetGuildParticipant(agentAddress, repo, out var p) ? p - : new GuildParticipant(agentAddress, guildAddress); - var guild = world.TryGetGuild(guildAddress, out var g) + : throw new InvalidOperationException("The signer was not joined to any guild."); + var guild = world.TryGetGuild(guildAddress, repo, out var g) ? g : throw new InvalidOperationException("The guild does not exist."); - var bond = world.GetBond(guild, agentAddress); - var unbondLockIn = world.GetUnbondLockIn(guild, agentAddress); - var unbondingSet = world.GetUnbondingSet(); - var result = guildParticipant.Undelegate( - guild, share, context.BlockIndex, bond, unbondLockIn, unbondingSet); + guildParticipant.Undelegate(guild, share, context.BlockIndex); - return world - .SetBond(result.Bond) - .SetGuild(result.Delegatee) - .SetGuildParticipant(guildParticipant) - .SetUnbondLockIn(result.UnbondLockIn) - .SetUnbondingSet(result.UnbondingSet); + return repo.World.SetGuild(guild).SetGuildParticipant(guildParticipant); + } + + public static IWorld Redelegate( + this IWorld world, + IActionContext context, + GuildAddress srcGuildAddress, + GuildAddress dstGuildAddress, + BigInteger share) + { + var repo = new DelegationRepository(world, context); + + var agentAddress = new AgentAddress(context.Signer); + var guildParticipant = world.TryGetGuildParticipant(agentAddress, repo, out var p) + ? p + : throw new InvalidOperationException("The signer was not joined to any guild."); + var srcGuild = world.TryGetGuild(srcGuildAddress, repo, out var s) + ? s + : throw new InvalidOperationException("The guild does not exist."); + var dstGuild = world.TryGetGuild(srcGuildAddress, repo, out var d) + ? d + : throw new InvalidOperationException("The guild does not exist."); + guildParticipant.Redelegate(srcGuild, dstGuild, share, context.BlockIndex); + + return repo.World.SetGuild(srcGuild).SetGuild(dstGuild).SetGuildParticipant(guildParticipant); } private static IWorld ClaimReward( @@ -188,25 +248,18 @@ private static IWorld ClaimReward( IActionContext context, GuildAddress guildAddress) { + var repo = new DelegationRepository(world, context); + var agentAddress = new AgentAddress(context.Signer); var guildParticipant = world.TryGetGuildParticipant(agentAddress, out var p) ? p - : new GuildParticipant(agentAddress, guildAddress); + : throw new InvalidOperationException("The signer was not joined to any guild."); var guild = world.TryGetGuild(guildAddress, out var g) ? g : throw new InvalidOperationException("The guild does not exist."); - var bond = world.GetBond(guild, agentAddress); - var rewardRecords = world.GetLumpSumRewardsRecords( - guild, context.BlockIndex, guildParticipant.LastRewardHeight); - - var claimRewardResult = guildParticipant.ClaimReward( - guild, rewardRecords, bond, context.BlockIndex); + guildParticipant.ClaimReward(guild, context.BlockIndex); - return world - .SetLumpSumRewardsRecord(claimRewardResult.LumpSumRewardsRecord) - .SetGuild(claimRewardResult.Delegatee) - .SetGuildParticipant(guildParticipant) - .TransferAsset(context, guild.RewardPoolAddress, agentAddress, claimRewardResult.Reward); + return repo.World.SetGuild(guild).SetGuildParticipant(guildParticipant); } private static IWorld SetGuildParticipant( From 224242a0b91605bd35d85ea7fc915f6a124b784e Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 15 Aug 2024 22:57:50 +0900 Subject: [PATCH 020/206] refactor: Refactor unbonding handling --- .Lib9c.Tests/Delegation/DelegateeTest.cs | 67 +++-- .Lib9c.Tests/Delegation/DelegationFixture.cs | 62 ++--- .Lib9c.Tests/Delegation/DelegatorTest.cs | 248 ++++++++++++------ .Lib9c.Tests/Delegation/DummyDelegatee.cs | 6 +- .Lib9c.Tests/Delegation/DummyDelegator.cs | 4 +- .Lib9c.Tests/Delegation/TestDelegatee.cs | 10 +- .Lib9c.Tests/Delegation/TestDelegationRepo.cs | 151 +++++++++++ .Lib9c.Tests/Delegation/TestDelegator.cs | 8 +- Lib9c/Action/Delegate/ReleaseUnbondings.cs | 2 +- Lib9c/Delegation/Delegatee.cs | 38 +-- Lib9c/Delegation/DelegationRepository.cs | 61 +++-- Lib9c/Delegation/Delegator.cs | 39 ++- Lib9c/Delegation/IDelegatee.cs | 2 +- Lib9c/Delegation/IDelegationRepository.cs | 4 + Lib9c/Delegation/RebondGrace.cs | 41 ++- Lib9c/Delegation/UnbondLockIn.cs | 109 +++++--- Lib9c/Delegation/UnbondingSet.cs | 73 +++--- Lib9c/Model/Guild/Guild.cs | 2 +- Lib9c/Module/Delegation/BondModule.cs | 3 +- Lib9c/Module/Delegation/RebondGraceModule.cs | 3 +- Lib9c/Module/Delegation/UnbondLockInModule.cs | 3 +- Lib9c/Module/Delegation/UnbondingSetModule.cs | 96 ++----- 22 files changed, 647 insertions(+), 385 deletions(-) create mode 100644 .Lib9c.Tests/Delegation/TestDelegationRepo.cs diff --git a/.Lib9c.Tests/Delegation/DelegateeTest.cs b/.Lib9c.Tests/Delegation/DelegateeTest.cs index 50121bba57..a09902ae5e 100644 --- a/.Lib9c.Tests/Delegation/DelegateeTest.cs +++ b/.Lib9c.Tests/Delegation/DelegateeTest.cs @@ -20,7 +20,7 @@ public DelegateeTest() public void Ctor() { var address = new Address("0xe8327129891e1A0B2E3F0bfa295777912295942a"); - var delegatee = new TestDelegatee(address); + var delegatee = new TestDelegatee(address, _fixture.Repository); Assert.Equal(address, delegatee.Address); Assert.Equal(DelegationFixture.TestCurrency, delegatee.Currency); Assert.Equal(3, delegatee.UnbondingPeriod); @@ -33,10 +33,9 @@ public void CtorWithBencoded() var address = new Address("0xe8327129891e1A0B2E3F0bfa295777912295942a"); var delegatee = _fixture.TestDelegatee1; var delegator = _fixture.TestDelegator1; - var bond = _fixture.Bond1To1; - delegatee.Bond(delegator, delegatee.Currency * 10, 10L, bond); + delegatee.Bond(delegator, delegatee.Currency * 10, 10L); - var delegateeRecon = new TestDelegatee(address, delegatee.Bencoded); + var delegateeRecon = new TestDelegatee(address, delegatee.Bencoded, delegatee.Repository); Assert.Equal(address, delegateeRecon.Address); Assert.Equal(delegator.Address, Assert.Single(delegateeRecon.Delegators)); Assert.Equal(delegatee.TotalDelegated, delegateeRecon.TotalDelegated); @@ -56,8 +55,6 @@ public void Bond() var testDelegatee = _fixture.TestDelegatee1; var testDelegator1 = _fixture.TestDelegator1; var testDelegator2 = _fixture.TestDelegator2; - var bond1To1 = _fixture.Bond1To1; - var bond2To1 = _fixture.Bond2To1; var share1 = BigInteger.Zero; var share2 = BigInteger.Zero; @@ -70,11 +67,11 @@ public void Bond() totalShare += share; totalBonding += bonding; - var bondResult = testDelegatee.Bond(testDelegator1, bonding, 10L, bond1To1); - bond1To1 = bondResult.Bond; + var bondedShare = testDelegatee.Bond(testDelegator1, bonding, 10L); + var bondedShare1 = _fixture.Repository.GetBond(testDelegatee, testDelegator1.Address).Share; Assert.Equal(testDelegator1.Address, Assert.Single(testDelegatee.Delegators)); - Assert.Equal(share, bondResult.BondedShare); - Assert.Equal(share1, bondResult.Bond.Share); + Assert.Equal(share, bondedShare); + Assert.Equal(share1, bondedShare1); Assert.Equal(totalShare, testDelegatee.TotalShares); Assert.Equal(totalBonding, testDelegatee.TotalDelegated); @@ -83,10 +80,11 @@ public void Bond() share1 += share; totalShare += share; totalBonding += bonding; - bondResult = testDelegatee.Bond(testDelegator1, bonding, 20L, bond1To1); + bondedShare = testDelegatee.Bond(testDelegator1, bonding, 20L); + bondedShare1 = _fixture.Repository.GetBond(testDelegatee, testDelegator1.Address).Share; Assert.Equal(testDelegator1.Address, Assert.Single(testDelegatee.Delegators)); - Assert.Equal(share, bondResult.BondedShare); - Assert.Equal(share1, bondResult.Bond.Share); + Assert.Equal(share, bondedShare); + Assert.Equal(share1, bondedShare1); Assert.Equal(totalShare, testDelegatee.TotalShares); Assert.Equal(totalBonding, testDelegatee.TotalDelegated); @@ -95,12 +93,13 @@ public void Bond() share2 += share; totalShare += share; totalBonding += bonding; - bondResult = testDelegatee.Bond(testDelegator2, bonding, 30L, bond2To1); + bondedShare = testDelegatee.Bond(testDelegator2, bonding, 30L); + var bondedShare2 = _fixture.Repository.GetBond(testDelegatee, testDelegator2.Address).Share; Assert.Equal(2, testDelegatee.Delegators.Count); Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); Assert.Contains(testDelegator2.Address, testDelegatee.Delegators); - Assert.Equal(share, bondResult.BondedShare); - Assert.Equal(share2, bondResult.Bond.Share); + Assert.Equal(share, bondedShare); + Assert.Equal(share2, bondedShare2); Assert.Equal(totalShare, testDelegatee.TotalShares); Assert.Equal(totalBonding, testDelegatee.TotalDelegated); } @@ -111,11 +110,10 @@ public void CannotBondInvalidDelegator() IDelegatee testDelegatee = _fixture.TestDelegatee1; var testDelegator = _fixture.TestDelegator1; var dummyDelegator = _fixture.DummyDelegator1; - var bond = _fixture.Bond1To1; Assert.Throws( () => testDelegatee.Bond( - dummyDelegator, testDelegatee.Currency * 10, 10L, bond)); + dummyDelegator, testDelegatee.Currency * 10, 10L)); } [Fact] @@ -124,12 +122,11 @@ public void CannotBondInvalidCurrency() var testDelegatee = _fixture.TestDelegatee1; var testDelegator = _fixture.TestDelegator1; var dummyDelegator = _fixture.DummyDelegator1; - var bond = _fixture.Bond1To1; var invalidCurrency = Currency.Uncapped("invalid", 3, null); Assert.Throws( () => testDelegatee.Bond( - testDelegator, invalidCurrency * 10, 10L, bond)); + testDelegator, invalidCurrency * 10, 10L)); } [Fact] @@ -138,8 +135,6 @@ public void Unbond() var testDelegatee = _fixture.TestDelegatee1; var testDelegator1 = _fixture.TestDelegator1; var testDelegator2 = _fixture.TestDelegator2; - var bond1To1 = _fixture.Bond1To1; - var bond2To1 = _fixture.Bond2To1; var share1 = BigInteger.Zero; var share2 = BigInteger.Zero; @@ -151,27 +146,27 @@ public void Unbond() share1 += share; totalShares += share; totalDelegated += bonding; - bond1To1 = testDelegatee.Bond(testDelegator1, bonding, 1L, bond1To1).Bond; + testDelegatee.Bond(testDelegator1, bonding, 1L); bonding = testDelegatee.Currency * 50; share = testDelegatee.ShareToBond(bonding); share2 += share; totalShares += share; totalDelegated += bonding; - bond2To1 = testDelegatee.Bond(testDelegator2, bonding, 2L, bond2To1).Bond; + testDelegatee.Bond(testDelegator2, bonding, 2L); var unbonding = share1 / 2; share1 -= unbonding; totalShares -= unbonding; var unbondingFAV = testDelegatee.FAVToUnbond(unbonding); totalDelegated -= unbondingFAV; - var unbondResult = testDelegatee.Unbond(testDelegator1, unbonding, 3L, bond1To1); - bond1To1 = unbondResult.Bond; + var unbondedFAV = testDelegatee.Unbond(testDelegator1, unbonding, 3L); + var shareAfterUnbond = _fixture.Repository.GetBond(testDelegatee, testDelegator1.Address).Share; Assert.Equal(2, testDelegatee.Delegators.Count); Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); Assert.Contains(testDelegator2.Address, testDelegatee.Delegators); - Assert.Equal(unbondingFAV, unbondResult.UnbondedFAV); - Assert.Equal(share1, unbondResult.Bond.Share); + Assert.Equal(unbondingFAV, unbondedFAV); + Assert.Equal(share1, shareAfterUnbond); Assert.Equal(totalShares, testDelegatee.TotalShares); Assert.Equal(totalDelegated, testDelegatee.TotalDelegated); @@ -180,22 +175,24 @@ public void Unbond() totalShares -= unbonding; unbondingFAV = testDelegatee.FAVToUnbond(unbonding); totalDelegated -= unbondingFAV; - unbondResult = testDelegatee.Unbond(testDelegator2, unbonding, 4L, bond2To1); + unbondedFAV = testDelegatee.Unbond(testDelegator2, unbonding, 4L); + shareAfterUnbond = _fixture.Repository.GetBond(testDelegatee, testDelegator2.Address).Share; Assert.Equal(2, testDelegatee.Delegators.Count); Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); Assert.Contains(testDelegator2.Address, testDelegatee.Delegators); - Assert.Equal(unbondingFAV, unbondResult.UnbondedFAV); - Assert.Equal(share2, unbondResult.Bond.Share); + Assert.Equal(unbondingFAV, unbondedFAV); + Assert.Equal(share2, shareAfterUnbond); Assert.Equal(totalShares, testDelegatee.TotalShares); Assert.Equal(totalDelegated, testDelegatee.TotalDelegated); totalShares -= share1; unbondingFAV = testDelegatee.FAVToUnbond(share1); totalDelegated -= unbondingFAV; - unbondResult = testDelegatee.Unbond(testDelegator1, share1, 5L, bond1To1); + unbondedFAV = testDelegatee.Unbond(testDelegator1, share1, 5L); + shareAfterUnbond = _fixture.Repository.GetBond(testDelegatee, testDelegator1.Address).Share; Assert.Equal(testDelegator2.Address, Assert.Single(testDelegatee.Delegators)); - Assert.Equal(unbondingFAV, unbondResult.UnbondedFAV); - Assert.Equal(BigInteger.Zero, unbondResult.Bond.Share); + Assert.Equal(unbondingFAV, unbondedFAV); + Assert.Equal(BigInteger.Zero, shareAfterUnbond); Assert.Equal(totalShares, testDelegatee.TotalShares); Assert.Equal(totalDelegated, testDelegatee.TotalDelegated); } @@ -206,7 +203,7 @@ public void CannotUnbondInvalidDelegator() IDelegatee delegatee = _fixture.TestDelegatee1; Assert.Throws( () => delegatee.Unbond( - _fixture.DummyDelegator1, BigInteger.One, 10L, _fixture.Bond1To1)); + _fixture.DummyDelegator1, BigInteger.One, 10L)); } [Fact] diff --git a/.Lib9c.Tests/Delegation/DelegationFixture.cs b/.Lib9c.Tests/Delegation/DelegationFixture.cs index 669517c752..23ececeaeb 100644 --- a/.Lib9c.Tests/Delegation/DelegationFixture.cs +++ b/.Lib9c.Tests/Delegation/DelegationFixture.cs @@ -1,7 +1,12 @@ namespace Lib9c.Tests.Delegation { + using Lib9c.Tests.Action; + using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Store; + using Libplanet.Store.Trie; using Libplanet.Types.Assets; + using Libplanet.Types.Blocks; using Nekoyume.Delegation; public class DelegationFixture @@ -11,24 +16,29 @@ public class DelegationFixture public DelegationFixture() { - TestDelegator1 = new TestDelegator(new Address("0x0054E98312C47E7Fa0ABed45C23Fa187e31C373a")); - TestDelegator2 = new TestDelegator(new Address("0x327CCff388255E9399207C3d5a09357D0BBc73dF")); - TestDelegatee1 = new TestDelegatee(new Address("0x67A44E11506b8f0Bb625fEECccb205b33265Bb48")); - TestDelegatee2 = new TestDelegatee(new Address("0xea1C4eedEfC99691DEfc6eF2753FAfa8C17F4584")); - DummyDelegatee1 = new DummyDelegatee(new Address("0x67A44E11506b8f0Bb625fEECccb205b33265Bb48")); - DummyDelegator1 = new DummyDelegator(new Address("0x0054E98312C47E7Fa0ABed45C23Fa187e31C373a")); - Bond1To1 = new Bond(TestDelegatee1.BondAddress(TestDelegator1.Address)); - Bond2To1 = new Bond(TestDelegatee1.BondAddress(TestDelegator2.Address)); - Bond1To2 = new Bond(TestDelegatee2.BondAddress(TestDelegator1.Address)); - Unbond1To1 = new UnbondLockIn(TestDelegatee1.BondAddress(TestDelegator1.Address), 10); - Unbond2To1 = new UnbondLockIn(TestDelegatee1.BondAddress(TestDelegator2.Address), 10); - Unbond1To2 = new UnbondLockIn(TestDelegatee2.BondAddress(TestDelegator1.Address), 10); - Rebond1To1 = new RebondGrace(TestDelegatee1.BondAddress(TestDelegator1.Address), 10); - Rebond2To1 = new RebondGrace(TestDelegatee1.BondAddress(TestDelegator2.Address), 10); - Rebond1To2 = new RebondGrace(TestDelegatee2.BondAddress(TestDelegator1.Address), 10); - UnbondingSet = new UnbondingSet(); + var stateStore = new TrieStateStore(new MemoryKeyValueStore()); + var world = new World( + new WorldBaseState( + stateStore.Commit( + stateStore.GetStateRoot(null).SetMetadata( + new TrieMetadata(BlockMetadata.CurrentProtocolVersion))), + stateStore)); + var context = new ActionContext() + { + BlockProtocolVersion = BlockMetadata.CurrentProtocolVersion, + }; + + Repository = new TestDelegationRepo(world, context); + TestDelegator1 = new TestDelegator(new Address("0x0054E98312C47E7Fa0ABed45C23Fa187e31C373a"), Repository); + TestDelegator2 = new TestDelegator(new Address("0x327CCff388255E9399207C3d5a09357D0BBc73dF"), Repository); + TestDelegatee1 = new TestDelegatee(new Address("0x67A44E11506b8f0Bb625fEECccb205b33265Bb48"), Repository); + TestDelegatee2 = new TestDelegatee(new Address("0xea1C4eedEfC99691DEfc6eF2753FAfa8C17F4584"), Repository); + DummyDelegatee1 = new DummyDelegatee(new Address("0x67A44E11506b8f0Bb625fEECccb205b33265Bb48"), Repository); + DummyDelegator1 = new DummyDelegator(new Address("0x0054E98312C47E7Fa0ABed45C23Fa187e31C373a"), Repository); } + public TestDelegationRepo Repository { get; } + public TestDelegator TestDelegator1 { get; } public TestDelegator TestDelegator2 { get; } @@ -40,25 +50,5 @@ public DelegationFixture() public DummyDelegatee DummyDelegatee1 { get; } public DummyDelegator DummyDelegator1 { get; } - - public Bond Bond1To1 { get; } - - public Bond Bond2To1 { get; } - - public Bond Bond1To2 { get; } - - public UnbondLockIn Unbond1To1 { get; } - - public UnbondLockIn Unbond2To1 { get; } - - public UnbondLockIn Unbond1To2 { get; } - - public RebondGrace Rebond1To1 { get; } - - public RebondGrace Rebond2To1 { get; } - - public RebondGrace Rebond1To2 { get; } - - public UnbondingSet UnbondingSet { get; } } } diff --git a/.Lib9c.Tests/Delegation/DelegatorTest.cs b/.Lib9c.Tests/Delegation/DelegatorTest.cs index 4b319132fd..75d3b9f41c 100644 --- a/.Lib9c.Tests/Delegation/DelegatorTest.cs +++ b/.Lib9c.Tests/Delegation/DelegatorTest.cs @@ -1,5 +1,7 @@ namespace Lib9c.Tests.Delegation { + using System.Linq; + using Libplanet.Action.State; using Libplanet.Crypto; using Xunit; @@ -16,7 +18,7 @@ public DelegatorTest() public void Ctor() { var address = new Address("0x070e5719767CfB86712C31F5AB0072c48959d862"); - var delegator = new TestDelegator(address); + var delegator = new TestDelegator(address, _fixture.Repository); Assert.Equal(address, delegator.Address); Assert.Empty(delegator.Delegatees); } @@ -24,12 +26,13 @@ public void Ctor() [Fact] public void CtorWithBencoded() { + var repo = _fixture.Repository; var delegator = _fixture.TestDelegator1; var delegatee = _fixture.TestDelegatee1; - var bond = _fixture.Bond1To1; - delegator.Delegate(delegatee, delegatee.Currency * 10, 10L, bond); + repo.MintAsset(delegator.Address, delegatee.Currency * 100); + delegator.Delegate(delegatee, delegatee.Currency * 10, 10L); - var delegatorRecon = new TestDelegator(delegator.Address, delegator.Bencoded); + var delegatorRecon = new TestDelegator(delegator.Address, delegator.Bencoded, delegator.Repository); Assert.Equal(delegator.Address, delegatorRecon.Address); Assert.Equal(delegatee.Address, Assert.Single(delegatorRecon.Delegatees)); } @@ -37,18 +40,22 @@ public void CtorWithBencoded() [Fact] public void Delegate() { + var repo = _fixture.Repository; var delegator = _fixture.TestDelegator1; var delegatee1 = _fixture.TestDelegatee1; var delegatee2 = _fixture.TestDelegatee2; - var bond1 = _fixture.Bond1To1; - var bond2 = _fixture.Bond1To2; + var delegatorInitialBalance = delegatee1.Currency * 100; + repo.MintAsset(delegator.Address, delegatorInitialBalance); + var delegateFAV = delegatee1.Currency * 10; var delegateShare = delegatee1.ShareToBond(delegateFAV); - var result = delegator.Delegate(delegatee1, delegateFAV, 1L, bond1); - bond1 = result.Bond; - delegatee1 = result.Delegatee; - Assert.Equal(delegateFAV, result.DelegatedFAV); - Assert.Equal(delegateShare, result.Bond.Share); + delegator.Delegate(delegatee1, delegateFAV, 1L); + var delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.Currency); + var delegateeBalance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.Currency); + var share = repo.GetBond(delegatee1, delegator.Address).Share; + Assert.Equal(delegatorInitialBalance - delegateFAV, delegatorBalance); + Assert.Equal(delegateFAV, delegateeBalance); + Assert.Equal(delegateShare, share); Assert.Equal(delegateFAV, delegatee1.TotalDelegated); Assert.Equal(delegateShare, delegatee1.TotalShares); Assert.Equal(delegator.Address, Assert.Single(delegatee1.Delegators)); @@ -56,17 +63,25 @@ public void Delegate() var delegateFAV2 = delegatee1.Currency * 20; var delegateShare2 = delegatee1.ShareToBond(delegateFAV2); - result = delegator.Delegate(delegatee1, delegateFAV2, 2L, bond1); - Assert.Equal(delegateFAV2, result.DelegatedFAV); - Assert.Equal(delegateShare + delegateShare2, result.Bond.Share); + delegator.Delegate(delegatee1, delegateFAV2, 2L); + delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.Currency); + delegateeBalance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.Currency); + share = repo.GetBond(delegatee1, delegator.Address).Share; + Assert.Equal(delegatorInitialBalance - delegateFAV - delegateFAV2, delegatorBalance); + Assert.Equal(delegateFAV + delegateFAV2, delegateeBalance); + Assert.Equal(delegateShare + delegateShare2, share); Assert.Equal(delegateFAV + delegateFAV2, delegatee1.TotalDelegated); Assert.Equal(delegateShare + delegateShare2, delegatee1.TotalShares); Assert.Equal(delegator.Address, Assert.Single(delegatee1.Delegators)); Assert.Equal(delegatee1.Address, Assert.Single(delegator.Delegatees)); - result = delegator.Delegate(delegatee2, delegateFAV, 3L, bond2); - Assert.Equal(delegateFAV, result.DelegatedFAV); - Assert.Equal(delegateShare, result.Bond.Share); + delegator.Delegate(delegatee2, delegateFAV, 3L); + delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee2.Currency); + delegateeBalance = repo.World.GetBalance(delegatee2.DelegationPoolAddress, delegatee2.Currency); + share = repo.GetBond(delegatee2, delegator.Address).Share; + Assert.Equal(delegatorInitialBalance - delegateFAV * 2 - delegateFAV2, delegatorBalance); + Assert.Equal(delegateFAV, delegateeBalance); + Assert.Equal(delegateShare, share); Assert.Equal(delegateFAV, delegatee2.TotalDelegated); Assert.Equal(delegateShare, delegatee2.TotalShares); Assert.Equal(2, delegator.Delegatees.Count); @@ -77,26 +92,30 @@ public void Delegate() [Fact] public void Undelegate() { + var repo = _fixture.Repository; var delegator = _fixture.TestDelegator1; var delegatee = _fixture.TestDelegatee1; - var bond = _fixture.Bond1To1; - var unbondLockIn = _fixture.Unbond1To1; - var unbondingSet = _fixture.UnbondingSet; - var delegateResult = delegator.Delegate( - delegatee, delegatee.Currency * 10, 10L, bond); - delegatee = delegateResult.Delegatee; - bond = delegateResult.Bond; - - var undelegatingShare = delegateResult.Bond.Share / 2; + var delegatorInitialBalance = delegatee.Currency * 100; + repo.MintAsset(delegator.Address, delegatorInitialBalance); + var delegatingFAV = delegatee.Currency * 10; + delegator.Delegate(delegatee, delegatingFAV, 10L); + var initialShare = repo.GetBond(delegatee, delegator.Address).Share; + var undelegatingShare = initialShare / 3; var undelegatingFAV = delegatee.FAVToUnbond(undelegatingShare); - var undelegateResult = delegator.Undelegate( - delegatee, undelegatingShare, 10L, bond, unbondLockIn, unbondingSet); - delegatee = undelegateResult.Delegatee; - bond = undelegateResult.Bond; - unbondLockIn = undelegateResult.UnbondLockIn; - unbondingSet = undelegateResult.UnbondingSet; + delegator.Undelegate(delegatee, undelegatingShare, 10L); + var delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.Currency); + var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var share1 = repo.GetBond(delegatee, delegator.Address).Share; + var unbondLockIn = repo.GetUnbondLockIn(delegatee, delegator.Address); + var unbondingSet = repo.GetUnbondingSet(); + Assert.Equal(delegatorInitialBalance - delegatingFAV, delegatorBalance); + Assert.Equal(delegatingFAV, delegateeBalance); + Assert.Equal(initialShare - undelegatingShare, share1); + Assert.Equal(delegatingFAV - undelegatingFAV, delegatee.TotalDelegated); + Assert.Equal(initialShare - undelegatingShare, delegatee.TotalShares); + Assert.Equal(delegator.Address, Assert.Single(delegatee.Delegators)); Assert.Equal(delegatee.Address, Assert.Single(delegator.Delegatees)); - Assert.Single(unbondingSet.Unbondings); + Assert.Equal(unbondLockIn.Address, Assert.Single(unbondingSet.FlattenedUnbondings)); var entriesByExpireHeight = Assert.Single(unbondLockIn.Entries); Assert.Equal(10L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); var entry = Assert.Single(entriesByExpireHeight.Value); @@ -105,66 +124,102 @@ public void Undelegate() Assert.Equal(10L, entry.CreationHeight); Assert.Equal(10L + delegatee.UnbondingPeriod, entry.ExpireHeight); - undelegatingShare = bond.Share; - undelegateResult = delegator.Undelegate( - delegatee, undelegatingShare, 12L, bond, unbondLockIn, unbondingSet); - delegatee = undelegateResult.Delegatee; - bond = undelegateResult.Bond; - unbondLockIn = undelegateResult.UnbondLockIn; - unbondingSet = undelegateResult.UnbondingSet; + undelegatingShare = repo.GetBond(delegatee, delegator.Address).Share; + var undelegatingFAV2 = delegatee.FAVToUnbond(undelegatingShare); + delegator.Undelegate(delegatee, undelegatingShare, 12L); + delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.Currency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var share2 = repo.GetBond(delegatee, delegator.Address).Share; + unbondLockIn = repo.GetUnbondLockIn(delegatee, delegator.Address); + unbondingSet = repo.GetUnbondingSet(); + Assert.Equal(delegatorInitialBalance - delegatingFAV, delegatorBalance); + Assert.Equal(delegatingFAV, delegateeBalance); + Assert.Equal(share1 - undelegatingShare, share2); + Assert.Equal(delegatee.Currency * 0, delegatee.TotalDelegated); + Assert.Equal(System.Numerics.BigInteger.Zero, delegatee.TotalShares); Assert.Empty(delegator.Delegatees); + Assert.Empty(delegatee.Delegators); + Assert.Equal(unbondLockIn.Address, Assert.Single(unbondingSet.FlattenedUnbondings)); Assert.Equal(2, unbondLockIn.Entries.Count); unbondLockIn = unbondLockIn.Release(10L + delegatee.UnbondingPeriod - 1); + delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.Currency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); Assert.Equal(2, unbondLockIn.Entries.Count); + entriesByExpireHeight = unbondLockIn.Entries.ElementAt(0); + Assert.Equal(10L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); + entry = Assert.Single(entriesByExpireHeight.Value); + Assert.Equal(undelegatingFAV, entry.InitialLockInFAV); + Assert.Equal(undelegatingFAV, entry.LockInFAV); + Assert.Equal(10L, entry.CreationHeight); + Assert.Equal(10L + delegatee.UnbondingPeriod, entry.ExpireHeight); + entriesByExpireHeight = unbondLockIn.Entries.ElementAt(1); + Assert.Equal(12L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); + entry = Assert.Single(entriesByExpireHeight.Value); + Assert.Equal(undelegatingFAV2, entry.InitialLockInFAV); + Assert.Equal(undelegatingFAV2, entry.LockInFAV); + Assert.Equal(12L, entry.CreationHeight); + Assert.Equal(12L + delegatee.UnbondingPeriod, entry.ExpireHeight); + Assert.Equal(delegatorInitialBalance - delegatingFAV, delegatorBalance); + Assert.Equal(delegatingFAV, delegateeBalance); unbondLockIn = unbondLockIn.Release(10L + delegatee.UnbondingPeriod); + delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.Currency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); entriesByExpireHeight = Assert.Single(unbondLockIn.Entries); Assert.Equal(12L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); entry = Assert.Single(entriesByExpireHeight.Value); - Assert.Equal(undelegatingFAV, entry.InitialLockInFAV); - Assert.Equal(undelegatingFAV, entry.LockInFAV); + Assert.Equal(undelegatingFAV2, entry.InitialLockInFAV); + Assert.Equal(undelegatingFAV2, entry.LockInFAV); Assert.Equal(12L, entry.CreationHeight); Assert.Equal(12L + delegatee.UnbondingPeriod, entry.ExpireHeight); + Assert.Equal(delegatorInitialBalance - delegatingFAV + undelegatingFAV, delegatorBalance); + Assert.Equal(delegatingFAV - undelegatingFAV, delegateeBalance); unbondLockIn = unbondLockIn.Release(12L + delegatee.UnbondingPeriod); + delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.Currency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); Assert.Empty(unbondLockIn.Entries); + Assert.Equal(delegatorInitialBalance, delegatorBalance); + Assert.Equal(delegatee.Currency * 0, delegateeBalance); } [Fact] public void Redelegate() { + var repo = _fixture.Repository; var delegator = _fixture.TestDelegator1; var delegatee1 = _fixture.TestDelegatee1; var delegatee2 = _fixture.TestDelegatee2; - var bond1 = _fixture.Bond1To1; - var bond2 = _fixture.Bond1To2; - var rebondGrace = _fixture.Rebond1To1; - var unbondingSet = _fixture.UnbondingSet; - var delegateResult = delegator.Delegate( - delegatee1, delegatee1.Currency * 10, 1L, bond1); - delegatee1 = delegateResult.Delegatee; - bond1 = delegateResult.Bond; + var delegatorInitialBalance = delegatee1.Currency * 100; + repo.MintAsset(delegator.Address, delegatorInitialBalance); + var delegatingFAV = delegatee1.Currency * 10; + delegator.Delegate(delegatee1, delegatingFAV, 1L); Assert.Equal(delegatee1.Address, Assert.Single(delegator.Delegatees)); - - var redelegatingShare = bond1.Share / 2; + var initialShare = repo.GetBond(delegatee1, delegator.Address).Share; + var redelegatingShare = initialShare / 3; var redelegatingFAV = delegatee1.FAVToUnbond(redelegatingShare); - var redelegateResult = delegator.Redelegate( - delegatee1, - delegatee2, - redelegatingShare, - 10L, - bond1, - bond2, - rebondGrace, - unbondingSet); - delegatee1 = redelegateResult.SrcDelegatee; - delegatee2 = redelegateResult.DstDelegatee; - bond1 = redelegateResult.SrcBond; - bond2 = redelegateResult.DstBond; - rebondGrace = redelegateResult.RebondGrace; - unbondingSet = redelegateResult.UnbondingSet; + var redelegatedDstShare = delegatee2.ShareToBond(redelegatingFAV); + delegator.Redelegate(delegatee1, delegatee2, redelegatingShare, 10L); + var delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.Currency); + var delegatee1Balance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.Currency); + var delegatee2Balance = repo.World.GetBalance(delegatee2.DelegationPoolAddress, delegatee2.Currency); + var share1 = repo.GetBond(delegatee1, delegator.Address).Share; + var share2 = repo.GetBond(delegatee2, delegator.Address).Share; + var rebondGrace = repo.GetRebondGrace(delegatee1, delegator.Address); + var unbondingSet = repo.GetUnbondingSet(); + Assert.Equal(delegatorInitialBalance - delegatingFAV, delegatorBalance); + Assert.Equal(delegatingFAV, delegatee1Balance); + Assert.Equal(initialShare - redelegatingShare, share1); + Assert.Equal(initialShare - redelegatingShare, delegatee1.TotalShares); + Assert.Equal(redelegatedDstShare, share2); + Assert.Equal(redelegatedDstShare, delegatee2.TotalShares); + Assert.Equal(delegatingFAV - redelegatingFAV, delegatee1.TotalDelegated); + Assert.Equal(redelegatingFAV, delegatee2.TotalDelegated); + Assert.Equal(delegator.Address, Assert.Single(delegatee1.Delegators)); + Assert.Equal(delegator.Address, Assert.Single(delegatee2.Delegators)); Assert.Equal(2, delegator.Delegatees.Count); + Assert.Equal(rebondGrace.Address, Assert.Single(unbondingSet.FlattenedUnbondings)); var entriesByExpireHeight = Assert.Single(rebondGrace.Entries); Assert.Equal(10L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); var entry = Assert.Single(entriesByExpireHeight.Value); @@ -174,36 +229,57 @@ public void Redelegate() Assert.Equal(10L, entry.CreationHeight); Assert.Equal(10L + delegatee1.UnbondingPeriod, entry.ExpireHeight); - redelegatingShare = bond1.Share; - redelegatingFAV = delegatee1.FAVToUnbond(redelegatingShare); - redelegateResult = delegator.Redelegate( - delegatee1, - delegatee2, - redelegatingShare, - 12L, - bond1, - bond2, - rebondGrace, - unbondingSet); - delegatee1 = redelegateResult.SrcDelegatee; - delegatee2 = redelegateResult.DstDelegatee; - bond1 = redelegateResult.SrcBond; - bond2 = redelegateResult.DstBond; - rebondGrace = redelegateResult.RebondGrace; - unbondingSet = redelegateResult.UnbondingSet; + var redelegatingShare2 = repo.GetBond(delegatee1, delegator.Address).Share; + var redelegatingFAV2 = delegatee1.FAVToUnbond(redelegatingShare2); + var redelegatedDstShare2 = delegatee2.ShareToBond(redelegatingFAV2); + delegator.Redelegate(delegatee1, delegatee2, redelegatingShare2, 12L); + delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.Currency); + delegatee1Balance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.Currency); + delegatee2Balance = repo.World.GetBalance(delegatee2.DelegationPoolAddress, delegatee2.Currency); + share1 = repo.GetBond(delegatee1, delegator.Address).Share; + share2 = repo.GetBond(delegatee2, delegator.Address).Share; + rebondGrace = repo.GetRebondGrace(delegatee1, delegator.Address); + unbondingSet = repo.GetUnbondingSet(); + Assert.Equal(delegatorInitialBalance - delegatingFAV, delegatorBalance); + Assert.Equal(delegatingFAV, delegatee1Balance); + Assert.Equal(initialShare - redelegatingShare - redelegatingShare2, share1); + Assert.Equal(initialShare - redelegatingShare - redelegatingShare2, delegatee1.TotalShares); + Assert.Equal(redelegatedDstShare + redelegatedDstShare2, share2); + Assert.Equal(redelegatedDstShare + redelegatedDstShare2, delegatee2.TotalShares); + Assert.Equal(delegatingFAV - redelegatingFAV - redelegatingFAV2, delegatee1.TotalDelegated); + Assert.Equal(redelegatingFAV + redelegatingFAV2, delegatee2.TotalDelegated); + Assert.Empty(delegatee1.Delegators); + Assert.Equal(delegator.Address, Assert.Single(delegatee2.Delegators)); Assert.Equal(delegatee2.Address, Assert.Single(delegator.Delegatees)); + Assert.Equal(rebondGrace.Address, Assert.Single(unbondingSet.FlattenedUnbondings)); Assert.Equal(2, rebondGrace.Entries.Count); rebondGrace = rebondGrace.Release(10L + delegatee1.UnbondingPeriod - 1); Assert.Equal(2, rebondGrace.Entries.Count); + entriesByExpireHeight = rebondGrace.Entries.ElementAt(0); + Assert.Equal(10L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); + entry = Assert.Single(entriesByExpireHeight.Value); + Assert.Equal(delegatee2.Address, entry.RebondeeAddress); + Assert.Equal(redelegatingFAV, entry.InitialGraceFAV); + Assert.Equal(redelegatingFAV, entry.GraceFAV); + Assert.Equal(10L, entry.CreationHeight); + Assert.Equal(10L + delegatee1.UnbondingPeriod, entry.ExpireHeight); + entriesByExpireHeight = rebondGrace.Entries.ElementAt(1); + Assert.Equal(12L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); + entry = Assert.Single(entriesByExpireHeight.Value); + Assert.Equal(delegatee2.Address, entry.RebondeeAddress); + Assert.Equal(redelegatingFAV2, entry.InitialGraceFAV); + Assert.Equal(redelegatingFAV2, entry.GraceFAV); + Assert.Equal(12L, entry.CreationHeight); + Assert.Equal(12L + delegatee1.UnbondingPeriod, entry.ExpireHeight); rebondGrace = rebondGrace.Release(10L + delegatee1.UnbondingPeriod); entriesByExpireHeight = Assert.Single(rebondGrace.Entries); Assert.Equal(12L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); entry = Assert.Single(entriesByExpireHeight.Value); Assert.Equal(delegatee2.Address, entry.RebondeeAddress); - Assert.Equal(redelegatingFAV, entry.InitialGraceFAV); - Assert.Equal(redelegatingFAV, entry.GraceFAV); + Assert.Equal(redelegatingFAV2, entry.InitialGraceFAV); + Assert.Equal(redelegatingFAV2, entry.GraceFAV); Assert.Equal(12L, entry.CreationHeight); Assert.Equal(12L + delegatee1.UnbondingPeriod, entry.ExpireHeight); diff --git a/.Lib9c.Tests/Delegation/DummyDelegatee.cs b/.Lib9c.Tests/Delegation/DummyDelegatee.cs index 57642d00d1..26551a34d0 100644 --- a/.Lib9c.Tests/Delegation/DummyDelegatee.cs +++ b/.Lib9c.Tests/Delegation/DummyDelegatee.cs @@ -5,8 +5,8 @@ public sealed class DummyDelegatee : Delegatee { - public DummyDelegatee(Address address) - : base(address) + public DummyDelegatee(Address address, IDelegationRepository repository) + : base(address, repository) { } @@ -14,7 +14,7 @@ public DummyDelegatee(Address address) public override Currency RewardCurrency => DelegationFixture.TestCurrency; - public override Address PoolAddress => DelegationFixture.FixedPoolAddress; + public override Address DelegationPoolAddress => DelegationFixture.FixedPoolAddress; public override long UnbondingPeriod => 3; diff --git a/.Lib9c.Tests/Delegation/DummyDelegator.cs b/.Lib9c.Tests/Delegation/DummyDelegator.cs index 12b107d3b3..19da999654 100644 --- a/.Lib9c.Tests/Delegation/DummyDelegator.cs +++ b/.Lib9c.Tests/Delegation/DummyDelegator.cs @@ -3,8 +3,8 @@ public sealed class DummyDelegator : Delegator { - public DummyDelegator(Address address) - : base(address) + public DummyDelegator(Address address, IDelegationRepository repository) + : base(address, repository) { } } diff --git a/.Lib9c.Tests/Delegation/TestDelegatee.cs b/.Lib9c.Tests/Delegation/TestDelegatee.cs index adc6e0682e..d4c3594fe3 100644 --- a/.Lib9c.Tests/Delegation/TestDelegatee.cs +++ b/.Lib9c.Tests/Delegation/TestDelegatee.cs @@ -7,13 +7,13 @@ namespace Lib9c.Tests.Delegation public sealed class TestDelegatee : Delegatee { - public TestDelegatee(Address address) - : base(address) + public TestDelegatee(Address address, IDelegationRepository repository) + : base(address, repository) { } - public TestDelegatee(Address address, IValue bencoded) - : base(address, bencoded) + public TestDelegatee(Address address, IValue bencoded, IDelegationRepository repository) + : base(address, bencoded, repository) { } @@ -23,7 +23,7 @@ public TestDelegatee(Address address, IValue bencoded) public override long UnbondingPeriod => 3; - public override Address PoolAddress => DeriveAddress(PoolId); + public override Address DelegationPoolAddress => DeriveAddress(PoolId); public override byte[] DelegateeId => new byte[] { 0x01 }; diff --git a/.Lib9c.Tests/Delegation/TestDelegationRepo.cs b/.Lib9c.Tests/Delegation/TestDelegationRepo.cs new file mode 100644 index 0000000000..517dc86817 --- /dev/null +++ b/.Lib9c.Tests/Delegation/TestDelegationRepo.cs @@ -0,0 +1,151 @@ +#nullable enable +namespace Nekoyume.Delegation +{ + using System; + using Bencodex.Types; + using Libplanet.Action; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Types.Assets; + + public class TestDelegationRepo : IDelegationRepository + { + private readonly Address bondAddress = Addresses.Bond; + private readonly Address unbondLockInAddress = Addresses.UnbondLockIn; + private readonly Address rebondGraceAddress = Addresses.RebondGrace; + private readonly Address unbondingSetAddress = Addresses.UnbondingSet; + private readonly Address lumpSumRewardsRecordAddress = Addresses.LumpSumRewardsRecord; + + private IWorld _world; + private IActionContext _context; + private IAccount _bond; + private IAccount _unbondLockIn; + private IAccount _rebondGrace; + private IAccount _unbondingSet; + private IAccount _lumpSumRewardsRecord; + + public TestDelegationRepo(IWorld world, IActionContext context) + { + _world = world; + _context = context; + _bond = world.GetAccount(bondAddress); + _unbondLockIn = world.GetAccount(unbondLockInAddress); + _rebondGrace = world.GetAccount(rebondGraceAddress); + _unbondingSet = world.GetAccount(unbondingSetAddress); + _lumpSumRewardsRecord = world.GetAccount(lumpSumRewardsRecordAddress); + } + + public IWorld World => _world + .SetAccount(bondAddress, _bond) + .SetAccount(unbondLockInAddress, _unbondLockIn) + .SetAccount(rebondGraceAddress, _rebondGrace) + .SetAccount(unbondingSetAddress, _unbondingSet) + .SetAccount(lumpSumRewardsRecordAddress, _lumpSumRewardsRecord); + + public Bond GetBond(IDelegatee delegatee, Address delegatorAddress) + { + Address address = delegatee.BondAddress(delegatorAddress); + IValue? value = _bond.GetState(address); + return value is IValue bencoded + ? new Bond(address, bencoded) + : new Bond(address); + } + + public UnbondLockIn GetUnbondLockIn(IDelegatee delegatee, Address delegatorAddress) + { + Address address = delegatee.UnbondLockInAddress(delegatorAddress); + IValue? value = _unbondLockIn.GetState(address); + return value is IValue bencoded + ? new UnbondLockIn(address, delegatee.MaxUnbondLockInEntries, bencoded, this) + : new UnbondLockIn(address, delegatee.MaxUnbondLockInEntries, delegatee.DelegationPoolAddress, delegatorAddress, this); + } + + public UnbondLockIn GetUnlimitedUnbondLockIn(Address address) + { + IValue? value = _unbondLockIn.GetState(address); + return value is IValue bencoded + ? new UnbondLockIn(address, int.MaxValue, bencoded, this) + : throw new InvalidOperationException("UnbondLockIn not found."); + } + + public RebondGrace GetRebondGrace(IDelegatee delegatee, Address delegatorAddress) + { + Address address = delegatee.RebondGraceAddress(delegatorAddress); + IValue? value = _rebondGrace.GetState(address); + return value is IValue bencoded + ? new RebondGrace(address, delegatee.MaxRebondGraceEntries, bencoded, this) + : new RebondGrace(address, delegatee.MaxRebondGraceEntries, this); + } + + public RebondGrace GetUnlimitedRebondGrace(Address address) + { + IValue? value = _rebondGrace.GetState(address); + return value is IValue bencoded + ? new RebondGrace(address, int.MaxValue, bencoded, this) + : throw new InvalidOperationException("RebondGrace not found."); + } + + public UnbondingSet GetUnbondingSet() + => _unbondingSet.GetState(UnbondingSet.Address) is IValue bencoded + ? new UnbondingSet(bencoded, this) + : new UnbondingSet(this); + + public LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height) + { + Address address = delegatee.LumpSumRewardsRecordAddress(height); + IValue? value = _lumpSumRewardsRecord.GetState(address); + return value is IValue bencoded + ? new LumpSumRewardsRecord(address, bencoded) + : null; + } + + public LumpSumRewardsRecord? GetCurrentLumpSumRewardsRecord(IDelegatee delegatee) + { + Address address = delegatee.CurrentLumpSumRewardsRecordAddress(); + IValue? value = _lumpSumRewardsRecord.GetState(address); + return value is IValue bencoded + ? new LumpSumRewardsRecord(address, bencoded) + : null; + } + + public void SetBond(Bond bond) + { + _bond = bond.IsEmpty + ? _bond.RemoveState(bond.Address) + : _bond.SetState(bond.Address, bond.Bencoded); + } + + public void SetUnbondLockIn(UnbondLockIn unbondLockIn) + { + _unbondLockIn = unbondLockIn.IsEmpty + ? _unbondLockIn.RemoveState(unbondLockIn.Address) + : _unbondLockIn.SetState(unbondLockIn.Address, unbondLockIn.Bencoded); + } + + public void SetRebondGrace(RebondGrace rebondGrace) + { + _rebondGrace = rebondGrace.IsEmpty + ? _rebondGrace.RemoveState(rebondGrace.Address) + : _rebondGrace.SetState(rebondGrace.Address, rebondGrace.Bencoded); + } + + public void SetUnbondingSet(UnbondingSet unbondingSet) + { + _unbondingSet = unbondingSet.IsEmpty + ? _unbondingSet.RemoveState(UnbondingSet.Address) + : _unbondingSet.SetState(UnbondingSet.Address, unbondingSet.Bencoded); + } + + public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) + { + _lumpSumRewardsRecord = _lumpSumRewardsRecord.SetState( + lumpSumRewardsRecord.Address, lumpSumRewardsRecord.Bencoded); + } + + public void TransferAsset(Address sender, Address recipient, FungibleAssetValue value) + => _world = _world.TransferAsset(_context, sender, recipient, value); + + public void MintAsset(Address recipient, FungibleAssetValue value) + => _world = _world.MintAsset(_context, recipient, value); + } +} diff --git a/.Lib9c.Tests/Delegation/TestDelegator.cs b/.Lib9c.Tests/Delegation/TestDelegator.cs index 6c1ef86ae3..0dfef5a92f 100644 --- a/.Lib9c.Tests/Delegation/TestDelegator.cs +++ b/.Lib9c.Tests/Delegation/TestDelegator.cs @@ -6,13 +6,13 @@ namespace Lib9c.Tests.Delegation public sealed class TestDelegator : Delegator { - public TestDelegator(Address address) - : base(address) + public TestDelegator(Address address, IDelegationRepository repo) + : base(address, repo) { } - public TestDelegator(Address address, IValue bencoded) - : base(address, bencoded) + public TestDelegator(Address address, IValue bencoded, IDelegationRepository repo) + : base(address, bencoded, repo) { } } diff --git a/Lib9c/Action/Delegate/ReleaseUnbondings.cs b/Lib9c/Action/Delegate/ReleaseUnbondings.cs index 9210165ffd..449e32ed38 100644 --- a/Lib9c/Action/Delegate/ReleaseUnbondings.cs +++ b/Lib9c/Action/Delegate/ReleaseUnbondings.cs @@ -25,7 +25,7 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; - return world.Release(context); + return world.ReleaseUnbondings(context); } } } diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 2ba7059fd8..e00e40ebbd 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -90,7 +90,7 @@ private Delegatee( public abstract Currency RewardCurrency { get; } - public abstract Address PoolAddress { get; } + public abstract Address DelegationPoolAddress { get; } public abstract long UnbondingPeriod { get; } @@ -140,7 +140,9 @@ void IDelegatee.Reward(IDelegator delegator, long height) public BigInteger Bond(T delegator, FungibleAssetValue fav, long height) { - CannotMutateWithoutRepository(delegator); + CannotMutateReleationsWithoutRepository(delegator); + Reward(delegator, height); + if (!fav.Currency.Equals(Currency)) { throw new InvalidOperationException( @@ -154,14 +156,14 @@ public BigInteger Bond(T delegator, FungibleAssetValue fav, long height) TotalShares += share; TotalDelegated += fav; _repository.SetBond(bond); - Reward(delegator, height); return share; } public FungibleAssetValue Unbond(T delegator, BigInteger share, long height) { - CannotMutateWithoutRepository(delegator); + CannotMutateReleationsWithoutRepository(delegator); + Reward(delegator, height); if (TotalShares.IsZero || TotalDelegated.RawValue.IsZero) { throw new InvalidOperationException( @@ -179,18 +181,22 @@ public FungibleAssetValue Unbond(T delegator, BigInteger share, long height) TotalShares -= share; TotalDelegated -= fav; _repository.SetBond(bond); - Reward(delegator, height); - + return fav; } public void Reward(T delegator, long height) { - CannotMutateWithoutRepository(delegator); + CannotMutateReleationsWithoutRepository(delegator); BigInteger share = _repository!.GetBond(this, delegator.Address).Share; IEnumerable lumpSumRewardsRecords = GetLumpSumRewardsRecords(delegator.LastRewardHeight); FungibleAssetValue reward = CalculateReward(share, lumpSumRewardsRecords); + if (reward.Sign <= 0) + { + return; + } + _repository.TransferAsset(RewardPoolAddress, delegator.Address, reward); StartNewRewardPeriod(height); delegator.UpdateLastRewardHeight(height); @@ -221,7 +227,7 @@ public bool Equals(IDelegatee? other) && (GetType() != delegatee.GetType()) && Address.Equals(delegatee.Address) && Currency.Equals(delegatee.Currency) - && PoolAddress.Equals(delegatee.PoolAddress) + && DelegationPoolAddress.Equals(delegatee.DelegationPoolAddress) && UnbondingPeriod == delegatee.UnbondingPeriod && RewardPoolAddress.Equals(delegatee.RewardPoolAddress) && Delegators.SequenceEqual(delegatee.Delegators) @@ -249,7 +255,7 @@ protected Address DeriveAddress(byte[] typeId, IEnumerable? bytes = null) private void StartNewRewardPeriod(long height) { - CannotMutateWithoutRepository(); + CannotMutateRelationsWithoutRepository(); LumpSumRewardsRecord? currentRecord = _repository!.GetCurrentLumpSumRewardsRecord(this); long? lastStartHeight = null; if (currentRecord is LumpSumRewardsRecord lastRecord) @@ -304,14 +310,15 @@ private FungibleAssetValue CalculateReward( private List GetLumpSumRewardsRecords(long? lastRewardHeight) { - CannotMutateWithoutRepository(); + CannotMutateRelationsWithoutRepository(); List records = new(); - if (!(_repository!.GetCurrentLumpSumRewardsRecord(this) is LumpSumRewardsRecord record)) + if (lastRewardHeight is null + || !(_repository!.GetCurrentLumpSumRewardsRecord(this) is LumpSumRewardsRecord record)) { return records; } - do + while (record.StartHeight >= lastRewardHeight) { records.Add(record); @@ -324,14 +331,13 @@ record = _repository.GetLumpSumRewardsRecord(this, lastStartHeight) ?? throw new InvalidOperationException( $"Lump sum rewards record for #{lastStartHeight} is missing"); } - while (record.StartHeight >= lastRewardHeight); return records; } - private void CannotMutateWithoutRepository(T delegator) + private void CannotMutateReleationsWithoutRepository(T delegator) { - CannotMutateWithoutRepository(); + CannotMutateRelationsWithoutRepository(); if (!_repository!.Equals(delegator.Repository)) { throw new InvalidOperationException( @@ -339,7 +345,7 @@ private void CannotMutateWithoutRepository(T delegator) } } - private void CannotMutateWithoutRepository() + private void CannotMutateRelationsWithoutRepository() { if (_repository is null) { diff --git a/Lib9c/Delegation/DelegationRepository.cs b/Lib9c/Delegation/DelegationRepository.cs index 9b1d24b073..4983f9f172 100644 --- a/Lib9c/Delegation/DelegationRepository.cs +++ b/Lib9c/Delegation/DelegationRepository.cs @@ -1,4 +1,5 @@ #nullable enable +using System; using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.State; @@ -9,11 +10,11 @@ namespace Nekoyume.Delegation { public class DelegationRepository : IDelegationRepository { - private readonly Address BondAddress = Addresses.Bond; - private readonly Address UnbondLockInAddress = Addresses.UnbondLockIn; - private readonly Address RebondGraceAddress = Addresses.RebondGrace; - private readonly Address UnbondingSetAddress = Addresses.UnbondingSet; - private readonly Address LumpSumRewardsRecordAddress = Addresses.LumpSumRewardsRecord; + private readonly Address bondAddress = Addresses.Bond; + private readonly Address unbondLockInAddress = Addresses.UnbondLockIn; + private readonly Address rebondGraceAddress = Addresses.RebondGrace; + private readonly Address unbondingSetAddress = Addresses.UnbondingSet; + private readonly Address lumpSumRewardsRecordAddress = Addresses.LumpSumRewardsRecord; private IWorld _world; private IActionContext _context; @@ -27,19 +28,19 @@ public DelegationRepository(IWorld world, IActionContext context) { _world = world; _context = context; - _bond = world.GetAccount(BondAddress); - _unbondLockIn = world.GetAccount(UnbondLockInAddress); - _rebondGrace = world.GetAccount(RebondGraceAddress); - _unbondingSet = world.GetAccount(UnbondingSetAddress); - _lumpSumRewardsRecord = world.GetAccount(LumpSumRewardsRecordAddress); + _bond = world.GetAccount(bondAddress); + _unbondLockIn = world.GetAccount(unbondLockInAddress); + _rebondGrace = world.GetAccount(rebondGraceAddress); + _unbondingSet = world.GetAccount(unbondingSetAddress); + _lumpSumRewardsRecord = world.GetAccount(lumpSumRewardsRecordAddress); } public IWorld World => _world - .SetAccount(BondAddress, _bond) - .SetAccount(UnbondLockInAddress, _unbondLockIn) - .SetAccount(RebondGraceAddress, _rebondGrace) - .SetAccount(UnbondingSetAddress, _unbondingSet) - .SetAccount(LumpSumRewardsRecordAddress, _lumpSumRewardsRecord); + .SetAccount(bondAddress, _bond) + .SetAccount(unbondLockInAddress, _unbondLockIn) + .SetAccount(rebondGraceAddress, _rebondGrace) + .SetAccount(unbondingSetAddress, _unbondingSet) + .SetAccount(lumpSumRewardsRecordAddress, _lumpSumRewardsRecord); public Bond GetBond(IDelegatee delegatee, Address delegatorAddress) { @@ -55,8 +56,16 @@ public UnbondLockIn GetUnbondLockIn(IDelegatee delegatee, Address delegatorAddre Address address = delegatee.UnbondLockInAddress(delegatorAddress); IValue? value = _unbondLockIn.GetState(address); return value is IValue bencoded - ? new UnbondLockIn(address, delegatee.MaxUnbondLockInEntries, bencoded) - : new UnbondLockIn(address, delegatee.MaxUnbondLockInEntries); + ? new UnbondLockIn(address, delegatee.MaxUnbondLockInEntries, bencoded, this) + : new UnbondLockIn(address, delegatee.MaxUnbondLockInEntries, delegatee.DelegationPoolAddress, delegatorAddress, this); + } + + public UnbondLockIn GetUnlimitedUnbondLockIn(Address address) + { + IValue? value = _unbondLockIn.GetState(address); + return value is IValue bencoded + ? new UnbondLockIn(address, int.MaxValue, bencoded, this) + : throw new InvalidOperationException("UnbondLockIn not found."); } public RebondGrace GetRebondGrace(IDelegatee delegatee, Address delegatorAddress) @@ -64,14 +73,22 @@ public RebondGrace GetRebondGrace(IDelegatee delegatee, Address delegatorAddress Address address = delegatee.RebondGraceAddress(delegatorAddress); IValue? value = _rebondGrace.GetState(address); return value is IValue bencoded - ? new RebondGrace(address, delegatee.MaxRebondGraceEntries, bencoded) - : new RebondGrace(address, delegatee.MaxUnbondLockInEntries); + ? new RebondGrace(address, delegatee.MaxRebondGraceEntries, bencoded, this) + : new RebondGrace(address, delegatee.MaxRebondGraceEntries, this); + } + + public RebondGrace GetUnlimitedRebondGrace(Address address) + { + IValue? value = _rebondGrace.GetState(address); + return value is IValue bencoded + ? new RebondGrace(address, int.MaxValue, bencoded, this) + : throw new InvalidOperationException("RebondGrace not found."); } public UnbondingSet GetUnbondingSet() => _unbondingSet.GetState(UnbondingSet.Address) is IValue bencoded - ? new UnbondingSet(bencoded) - : new UnbondingSet(); + ? new UnbondingSet(bencoded, this) + : new UnbondingSet(this); public LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height) { @@ -104,6 +121,7 @@ public void SetUnbondLockIn(UnbondLockIn unbondLockIn) ? _unbondLockIn.RemoveState(unbondLockIn.Address) : _unbondLockIn.SetState(unbondLockIn.Address, unbondLockIn.Bencoded); } + public void SetRebondGrace(RebondGrace rebondGrace) { _rebondGrace = rebondGrace.IsEmpty @@ -117,6 +135,7 @@ public void SetUnbondingSet(UnbondingSet unbondingSet) ? _unbondingSet.RemoveState(UnbondingSet.Address) : _unbondingSet.SetState(UnbondingSet.Address, unbondingSet.Bencoded); } + public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) { _lumpSumRewardsRecord = _lumpSumRewardsRecord.SetState( diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index 065bf44426..a9a15670f2 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -17,7 +17,7 @@ public abstract class Delegator : IDelegator private readonly IDelegationRepository? _repository; public Delegator(Address address, IDelegationRepository? repository = null) - : this(address, ImmutableSortedSet
.Empty, 0L, repository) + : this(address, ImmutableSortedSet
.Empty, null, repository) { } @@ -30,7 +30,7 @@ public Delegator(Address address, List bencoded, IDelegationRepository? reposito : this( address, ((List)bencoded[0]).Select(item => new Address(item)).ToImmutableSortedSet(), - (Integer)bencoded[1], + bencoded[1] is Integer lastRewardHeight ? lastRewardHeight : null, repository) { } @@ -38,7 +38,7 @@ public Delegator(Address address, List bencoded, IDelegationRepository? reposito private Delegator( Address address, ImmutableSortedSet
delegatees, - long lastRewardHeight, + long? lastRewardHeight, IDelegationRepository? repository) { Address = address; @@ -51,14 +51,14 @@ private Delegator( public ImmutableSortedSet
Delegatees { get; private set; } - public long LastRewardHeight { get; private set; } + public long? LastRewardHeight { get; private set; } public IDelegationRepository? Repository => _repository; public List Bencoded => List.Empty .Add(new List(Delegatees.Select(a => a.Bencoded))) - .Add(LastRewardHeight); + .Add(Null.Value); IValue IBencodable.Bencoded => Bencoded; @@ -81,7 +81,7 @@ void IDelegator.ClaimReward( public void Delegate( T delegatee, FungibleAssetValue fav, long height) { - CannotMutateWithoutRepository(delegatee); + CannotMutateRelationsWithoutRepository(delegatee); if (fav.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -90,13 +90,13 @@ public void Delegate( delegatee.Bond((TSelf)this, fav, height); Delegatees = Delegatees.Add(delegatee.Address); - _repository!.TransferAsset(Address, delegatee.Address, fav); + _repository!.TransferAsset(Address, delegatee.DelegationPoolAddress, fav); } public void Undelegate( T delegatee, BigInteger share, long height) { - CannotMutateWithoutRepository(delegatee); + CannotMutateRelationsWithoutRepository(delegatee); if (share.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -133,8 +133,8 @@ public void Undelegate( public void Redelegate( T srcDelegatee, T dstDelegatee, BigInteger share, long height) { - CannotMutateWithoutRepository(srcDelegatee); - CannotMutateWithoutRepository(dstDelegatee); + CannotMutateRelationsWithoutRepository(srcDelegatee); + CannotMutateRelationsWithoutRepository(dstDelegatee); if (share.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -172,7 +172,7 @@ public void Redelegate( public void CancelUndelegate( T delegatee, FungibleAssetValue fav, long height) { - CannotMutateWithoutRepository(delegatee); + CannotMutateRelationsWithoutRepository(delegatee); if (fav.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -197,16 +197,14 @@ public void CancelUndelegate( Delegatees = Delegatees.Add(delegatee.Address); _repository.SetUnbondLockIn(unbondLockIn); - UnbondingSet unbondingSet = _repository.GetUnbondingSet(); - if (unbondingSet.IsEmpty) - { - _repository.SetUnbondingSet(unbondingSet.RemoveUnbonding(unbondLockIn.Address)); - } + _repository.SetUnbondingSet( + _repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); } public void ClaimReward( T delegatee, long height) { + CannotMutateRelationsWithoutRepository(delegatee); delegatee.Reward((TSelf)this, height); } @@ -223,14 +221,15 @@ public bool Equals(IDelegator? other) || (other is Delegator delegator && GetType() != delegator.GetType() && Address.Equals(delegator.Address) - && Delegatees.SequenceEqual(delegator.Delegatees)); + && Delegatees.SequenceEqual(delegator.Delegatees) + && LastRewardHeight == delegator.LastRewardHeight); public override int GetHashCode() => Address.GetHashCode(); - private void CannotMutateWithoutRepository(T delegatee) + private void CannotMutateRelationsWithoutRepository(T delegatee) { - CannotMutateWithoutRepository(); + CannotMutateRelationsWithoutRepository(); if (!_repository!.Equals(delegatee.Repository)) { throw new InvalidOperationException( @@ -238,7 +237,7 @@ private void CannotMutateWithoutRepository(T delegatee) } } - private void CannotMutateWithoutRepository() + private void CannotMutateRelationsWithoutRepository() { if (_repository is null) { diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index 85ecd426aa..3687851379 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -16,7 +16,7 @@ public interface IDelegatee : IBencodable, IEquatable Currency RewardCurrency { get; } - Address PoolAddress { get; } + Address DelegationPoolAddress { get; } long UnbondingPeriod { get; } diff --git a/Lib9c/Delegation/IDelegationRepository.cs b/Lib9c/Delegation/IDelegationRepository.cs index faceb520c5..ed484be651 100644 --- a/Lib9c/Delegation/IDelegationRepository.cs +++ b/Lib9c/Delegation/IDelegationRepository.cs @@ -13,8 +13,12 @@ public interface IDelegationRepository UnbondLockIn GetUnbondLockIn(IDelegatee delegatee, Address delegatorAddress); + UnbondLockIn GetUnlimitedUnbondLockIn(Address address); + RebondGrace GetRebondGrace(IDelegatee delegatee, Address delegatorAddress); + RebondGrace GetUnlimitedRebondGrace(Address address); + UnbondingSet GetUnbondingSet(); LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height); diff --git a/Lib9c/Delegation/RebondGrace.cs b/Lib9c/Delegation/RebondGrace.cs index d19c761edf..47084b041d 100644 --- a/Lib9c/Delegation/RebondGrace.cs +++ b/Lib9c/Delegation/RebondGrace.cs @@ -15,20 +15,23 @@ public sealed class RebondGrace : IUnbonding, IBencodable, IEquatable _entryComparer = new RebondGraceEntryComparer(); - public RebondGrace(Address address, int maxEntries) + private readonly IDelegationRepository? _repository; + + public RebondGrace(Address address, int maxEntries, IDelegationRepository? repository = null) : this( address, maxEntries, - ImmutableSortedDictionary>.Empty) + ImmutableSortedDictionary>.Empty, + repository) { } - public RebondGrace(Address address, int maxEntries, IValue bencoded) - : this(address, maxEntries, (List)bencoded) + public RebondGrace(Address address, int maxEntries, IValue bencoded, IDelegationRepository? repository = null) + : this(address, maxEntries, (List)bencoded, repository) { } - public RebondGrace(Address address, int maxEntries, List bencoded) + public RebondGrace(Address address, int maxEntries, List bencoded, IDelegationRepository? repository = null) : this( address, maxEntries, @@ -38,12 +41,17 @@ public RebondGrace(Address address, int maxEntries, List bencoded) ((List)list[1]).Select(e => new RebondGraceEntry(e)).ToImmutableList()) : throw new InvalidCastException( $"Unable to cast object of type '{kv.GetType()}' to type '{typeof(List)}'.")) - .ToImmutableSortedDictionary()) + .ToImmutableSortedDictionary(), + repository) { } - public RebondGrace(Address address, int maxEntries, IEnumerable entries) - : this(address, maxEntries) + public RebondGrace( + Address address, + int maxEntries, + IEnumerable entries, + IDelegationRepository? repository = null) + : this(address, maxEntries, repository) { foreach (var entry in entries) { @@ -54,7 +62,8 @@ public RebondGrace(Address address, int maxEntries, IEnumerable> entries) + ImmutableSortedDictionary> entries, + IDelegationRepository? repository) { if (maxEntries < 0) { @@ -67,12 +76,15 @@ private RebondGrace( Address = address; MaxEntries = maxEntries; Entries = entries; + _repository = repository; } public Address Address { get; } public int MaxEntries { get; } + public IDelegationRepository? Repository => _repository; + public long LowestExpireHeight => Entries.First().Key; public bool IsFull => Entries.Values.Sum(e => e.Count) >= MaxEntries; @@ -180,7 +192,16 @@ private RebondGrace AddEntry(RebondGraceEntry entry) private RebondGrace UpdateEntries( ImmutableSortedDictionary> entries) - => new RebondGrace(Address, MaxEntries, entries); + => new RebondGrace(Address, MaxEntries, entries, _repository); + + private void CannotMutateRelationsWithoutRepository() + { + if (_repository is null) + { + throw new InvalidOperationException( + "Cannot mutate without repository."); + } + } public class RebondGraceEntry : IUnbondingEntry, IBencodable, IEquatable { diff --git a/Lib9c/Delegation/UnbondLockIn.cs b/Lib9c/Delegation/UnbondLockIn.cs index 7647b3c698..4eee12f8da 100644 --- a/Lib9c/Delegation/UnbondLockIn.cs +++ b/Lib9c/Delegation/UnbondLockIn.cs @@ -15,39 +15,58 @@ public sealed class UnbondLockIn : IUnbonding, IBencodable, IEquatable _entryComparer = new UnbondLockInEntryComparer(); - private FungibleAssetValue? _releasedFAV; + private readonly IDelegationRepository? _repository; - public UnbondLockIn(Address address, int maxEntries) + public UnbondLockIn( + Address address, + int maxEntries, + Address sender, + Address recipient, + IDelegationRepository? repository) : this( address, maxEntries, - ImmutableSortedDictionary>.Empty) + sender, + recipient, + ImmutableSortedDictionary>.Empty, + repository) { + _repository = repository; } - public UnbondLockIn(Address address, int maxEntries, IValue bencoded) - : this(address, maxEntries, (List)bencoded) + public UnbondLockIn( + Address address, int maxEntries, IValue bencoded, IDelegationRepository? repository = null) + : this(address, maxEntries, (List)bencoded, repository) { } - public UnbondLockIn(Address address, int maxEntries, List bencoded) + public UnbondLockIn( + Address address, int maxEntries, List bencoded, IDelegationRepository? repository = null) : this( address, maxEntries, - bencoded.Select(kv => kv is List list + new Address(bencoded[0]), + new Address(bencoded[1]), + ((List)bencoded[2]).Select(kv => kv is List list ? new KeyValuePair>( (Integer)list[0], ((List)list[1]).Select(e => new UnbondLockInEntry(e)).ToImmutableList()) : throw new InvalidCastException( $"Unable to cast object of type '{kv.GetType()}' " + $"to type '{typeof(List)}'.")) - .ToImmutableSortedDictionary()) + .ToImmutableSortedDictionary(), + repository) { } public UnbondLockIn( - Address address, int maxEntries, IEnumerable entries) - : this(address, maxEntries) + Address address, + int maxEntries, + Address sender, + Address recipient, + IEnumerable entries, + IDelegationRepository? repository = null) + : this(address, maxEntries, sender, recipient, repository) { foreach (var entry in entries) { @@ -58,8 +77,10 @@ public UnbondLockIn( private UnbondLockIn( Address address, int maxEntries, + Address sender, + Address recipient, ImmutableSortedDictionary> entries, - FungibleAssetValue? releasedFAV = null) + IDelegationRepository? repository) { if (maxEntries < 0) { @@ -72,36 +93,46 @@ private UnbondLockIn( Address = address; MaxEntries = maxEntries; Entries = entries; - _releasedFAV = releasedFAV; + Sender = sender; + Recipient = recipient; + _repository = repository; } public Address Address { get; } public int MaxEntries { get; } + public Address Sender { get; } + + public Address Recipient { get; } + + // TODO: Use better custom collection type + public ImmutableSortedDictionary> Entries { get; } + public long LowestExpireHeight => Entries.First().Key; public bool IsFull => Entries.Values.Sum(e => e.Count) >= MaxEntries; public bool IsEmpty => Entries.IsEmpty; - // TODO: Use better custom collection type - public ImmutableSortedDictionary> Entries { get; } - public ImmutableArray FlattenedEntries => Entries.Values.SelectMany(e => e).ToImmutableArray(); public List Bencoded - => new List( - Entries.Select( - sortedDict => new List( - (Integer)sortedDict.Key, - new List(sortedDict.Value.Select(e => e.Bencoded))))); + => List.Empty + .Add(Sender.Bencoded) + .Add(Recipient.Bencoded) + .Add(new List( + Entries.Select( + sortedDict => new List( + (Integer)sortedDict.Key, + new List(sortedDict.Value.Select(e => e.Bencoded)))))); IValue IBencodable.Bencoded => Bencoded; public UnbondLockIn Release(long height) { + CannotMutateRelationsWithoutRepository(); if (height <= 0) { throw new ArgumentOutOfRangeException( @@ -111,7 +142,7 @@ public UnbondLockIn Release(long height) } var updatedEntries = Entries; - FungibleAssetValue? releasedFAV = null; + FungibleAssetValue? releasingFAV = null; foreach (var (expireHeight, entries) in updatedEntries) { if (expireHeight <= height) @@ -119,10 +150,11 @@ public UnbondLockIn Release(long height) FungibleAssetValue entriesFAV = entries .Select(e => e.LockInFAV) .Aggregate((accum, next) => accum + next); - releasedFAV = _releasedFAV is null - ? entriesFAV - : _releasedFAV + entriesFAV; + releasingFAV = releasingFAV.HasValue + ? releasingFAV.Value + entriesFAV + : entriesFAV; updatedEntries = updatedEntries.Remove(expireHeight); + } else { @@ -130,7 +162,12 @@ public UnbondLockIn Release(long height) } } - return UpdateEntries(updatedEntries, releasedFAV); + if (releasingFAV.HasValue) + { + _repository!.TransferAsset(Sender, Recipient, releasingFAV.Value); + } + + return UpdateEntries(updatedEntries); } IUnbonding IUnbonding.Release(long height) => Release(height); @@ -141,12 +178,6 @@ public UnbondLockIn Slash() IUnbonding IUnbonding.Slash() => Slash(); - public FungibleAssetValue? FlushReleasedFAV() - { - var releasedFAV = _releasedFAV; - _releasedFAV = null; - return releasedFAV; - } public override bool Equals(object? obj) => obj is UnbondLockIn other && Equals(other); @@ -156,6 +187,8 @@ public bool Equals(UnbondLockIn? other) || (other is UnbondLockIn unbondLockIn && Address.Equals(unbondLockIn.Address) && MaxEntries == unbondLockIn.MaxEntries + && Sender.Equals(unbondLockIn.Sender) + && Recipient.Equals(unbondLockIn.Recipient) && FlattenedEntries.SequenceEqual(unbondLockIn.FlattenedEntries)); public override int GetHashCode() @@ -244,9 +277,8 @@ internal FungibleAssetValue Cancellable(long height) .Aggregate((accum, next) => accum + next); private UnbondLockIn UpdateEntries( - ImmutableSortedDictionary> entries, - FungibleAssetValue? releasedFAV=null) - => new UnbondLockIn(Address, MaxEntries, entries, releasedFAV); + ImmutableSortedDictionary> entries) + => new UnbondLockIn(Address, MaxEntries, Sender, Recipient, entries, _repository); private UnbondLockIn AddEntry(UnbondLockInEntry entry) { @@ -269,6 +301,15 @@ private UnbondLockIn AddEntry(UnbondLockInEntry entry) entry.ExpireHeight, ImmutableList.Empty.Add(entry))); } + private void CannotMutateRelationsWithoutRepository() + { + if (_repository is null) + { + throw new InvalidOperationException( + "Cannot mutate without repository."); + } + } + public class UnbondLockInEntry : IUnbondingEntry, IBencodable, IEquatable { private int? _cachedHashCode; diff --git a/Lib9c/Delegation/UnbondingSet.cs b/Lib9c/Delegation/UnbondingSet.cs index 98f1983a4b..8823081b6e 100644 --- a/Lib9c/Delegation/UnbondingSet.cs +++ b/Lib9c/Delegation/UnbondingSet.cs @@ -14,30 +14,32 @@ public sealed class UnbondingSet : IBencodable private static readonly byte[] _unbondLockInTypeBytes = new byte[] { 0x75 }; // 'u' private static readonly byte[] _rebondGraceTypeBytes = new byte[] { 0x72 }; // 'r' + private readonly IDelegationRepository _repository; private ImmutableSortedDictionary _lowestExpireHeights; private ImmutableSortedDictionary _typeDict; - public UnbondingSet() + public UnbondingSet(IDelegationRepository repository) : this( ImmutableSortedDictionary>.Empty, ImmutableSortedDictionary.Empty, - ImmutableSortedDictionary.Empty) + ImmutableSortedDictionary.Empty, + repository) { } - public UnbondingSet(IValue bencoded) - : this((List)bencoded) + public UnbondingSet(IValue bencoded, IDelegationRepository repository) + : this((List)bencoded, repository) { } - public UnbondingSet(List bencoded) + public UnbondingSet(List bencoded, IDelegationRepository repository) : this( - ((List)bencoded[1]).Select( + ((List)bencoded[0]).Select( kv => new KeyValuePair>( (Integer)((List)kv)[0], ((List)((List)kv)[1]).Select(a => new Address(a)).ToImmutableSortedSet())) .ToImmutableSortedDictionary(), - ((List)bencoded[2]).Select( + ((List)bencoded[1]).Select( kv => new KeyValuePair( new Address(((List)kv)[0]), (Integer)((List)kv)[1])) @@ -46,18 +48,21 @@ public UnbondingSet(List bencoded) kv => new KeyValuePair( new Address(((List)kv)[0]), ((Binary)((List)kv)[1]).ToArray())) - .ToImmutableSortedDictionary()) + .ToImmutableSortedDictionary(), + repository) { } private UnbondingSet( ImmutableSortedDictionary> unbondings, ImmutableSortedDictionary lowestExpireHeights, - ImmutableSortedDictionary typeDict) + ImmutableSortedDictionary typeDict, + IDelegationRepository repository) { Unbondings = unbondings; _lowestExpireHeights = lowestExpireHeights; _typeDict = typeDict; + _repository = repository; } public static Address Address => new Address( @@ -67,6 +72,11 @@ private UnbondingSet( public ImmutableSortedDictionary> Unbondings { get; } + public ImmutableArray
FlattenedUnbondings + => Unbondings.Values.SelectMany(e => e).ToImmutableArray(); + + public IDelegationRepository Repository => _repository; + public List Bencoded => List.Empty .Add(new List( @@ -89,6 +99,18 @@ public List Bencoded public bool IsEmpty => Unbondings.IsEmpty; + public ImmutableArray UnbondingsToRelease(long height) + => Unbondings + .TakeWhile(kv => kv.Key <= height) + .SelectMany(kv => kv.Value) + .Select(address => ( + Address: address, + Type: ToUnbondingType(_typeDict[address]))) + .Select(tuple => LoadUnbonding( + tuple.Address, + tuple.Type)) + .ToImmutableArray(); + public UnbondingSet SetUnbondings(IEnumerable unbondings) { UnbondingSet result = this; @@ -122,7 +144,8 @@ public UnbondingSet SetUnbonding(IUnbonding unbonding) _lowestExpireHeights.SetItem( unbonding.Address, unbonding.LowestExpireHeight), _typeDict.SetItem( - unbonding.Address, ToTypeBytes(unbonding))); + unbonding.Address, ToTypeBytes(unbonding)), + _repository); } return new UnbondingSet( @@ -132,10 +155,12 @@ public UnbondingSet SetUnbonding(IUnbonding unbonding) _lowestExpireHeights.SetItem( unbonding.Address, unbonding.LowestExpireHeight), _typeDict.SetItem( - unbonding.Address, ToTypeBytes(unbonding))); + unbonding.Address, ToTypeBytes(unbonding)), + _repository); } - public UnbondingSet RemoveUnbonding(Address address) + + private UnbondingSet RemoveUnbonding(Address address) { if (_lowestExpireHeights.TryGetValue(address, out var expireHeight) && Unbondings.TryGetValue(expireHeight, out var addresses)) @@ -143,7 +168,8 @@ public UnbondingSet RemoveUnbonding(Address address) return new UnbondingSet( Unbondings.SetItem(expireHeight, addresses.Remove(address)), _lowestExpireHeights.Remove(address), - _typeDict.Remove(address)); + _typeDict.Remove(address), + _repository); } else { @@ -151,21 +177,6 @@ public UnbondingSet RemoveUnbonding(Address address) } } - public IUnbonding[] ReleaseUnbondings(long height, Func bencodedGetter) - { - return Unbondings - .TakeWhile(kv => kv.Key <= height) - .SelectMany(kv => kv.Value) - .Select(address => ( - Address: address, - Type: ToUnbondingType(_typeDict[address]))) - .Select(tuple => LoadUnbonding( - tuple.Address, - tuple.Type, - bencodedGetter(tuple.Address, tuple.Type))) - .Select(u => u.Release(height)).ToArray(); - } - private static byte[] ToTypeBytes(IUnbonding unbonding) => unbonding switch { @@ -183,11 +194,11 @@ _ when typeBytes.SequenceEqual(_rebondGraceTypeBytes) _ => throw new ArgumentException("Invalid type bytes.") }; - private static IUnbonding LoadUnbonding(Address address, Type type, IValue bencoded) + private IUnbonding LoadUnbonding(Address address, Type type) => type switch { - var t when t == typeof(UnbondLockIn) => new UnbondLockIn(address, int.MaxValue, bencoded), - var t when t == typeof(RebondGrace) => new RebondGrace(address, int.MaxValue, bencoded), + var t when t == typeof(UnbondLockIn) => _repository.GetUnlimitedUnbondLockIn(address), + var t when t == typeof(RebondGrace) => _repository.GetUnlimitedRebondGrace(address), _ => throw new ArgumentException("Invalid unbonding type.") }; } diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index e5a5b76aa8..a174086537 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -57,7 +57,7 @@ public Guild(List list, Currency rewardCurrency, IDelegationRepository repositor public override Currency RewardCurrency { get; } - public override Address PoolAddress => DeriveAddress(PoolId); + public override Address DelegationPoolAddress => DeriveAddress(PoolId); public override long UnbondingPeriod => 75600L; diff --git a/Lib9c/Module/Delegation/BondModule.cs b/Lib9c/Module/Delegation/BondModule.cs index 63b6b7fa9c..122512fa71 100644 --- a/Lib9c/Module/Delegation/BondModule.cs +++ b/Lib9c/Module/Delegation/BondModule.cs @@ -1,4 +1,5 @@ #nullable enable +using System; using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; @@ -15,7 +16,7 @@ public static Bond GetBond( public static Bond GetBond(this IWorldState world, Address address) => TryGetBond(world, address, out var bond) ? bond! - : new Bond(address); + : throw new InvalidOperationException("Bond not found"); public static bool TryGetBond( this IWorldState world, Address address, out Bond? bond) diff --git a/Lib9c/Module/Delegation/RebondGraceModule.cs b/Lib9c/Module/Delegation/RebondGraceModule.cs index c49af5661d..adda5816ff 100644 --- a/Lib9c/Module/Delegation/RebondGraceModule.cs +++ b/Lib9c/Module/Delegation/RebondGraceModule.cs @@ -1,4 +1,5 @@ #nullable enable +using System; using Libplanet.Action.State; using Libplanet.Crypto; using Nekoyume.Delegation; @@ -18,7 +19,7 @@ public static RebondGrace GetRebondGrace( this IWorldState world, Address address, int maxEntries) => TryGetRebondGrace(world, address, maxEntries, out var rebondGrace) ? rebondGrace! - : new RebondGrace(address, maxEntries); + : throw new InvalidOperationException("RebondGrace not found"); public static bool TryGetRebondGrace( this IWorldState world, diff --git a/Lib9c/Module/Delegation/UnbondLockInModule.cs b/Lib9c/Module/Delegation/UnbondLockInModule.cs index c64f00c94f..0b6a3a950a 100644 --- a/Lib9c/Module/Delegation/UnbondLockInModule.cs +++ b/Lib9c/Module/Delegation/UnbondLockInModule.cs @@ -1,4 +1,5 @@ #nullable enable +using System; using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; @@ -19,7 +20,7 @@ public static UnbondLockIn GetUnbondLockIn( this IWorldState world, Address address, int maxEntries) => TryGetUnbondLockIn(world, address, maxEntries, out var unbondLockIn) ? unbondLockIn! - : new UnbondLockIn(address, maxEntries); + : throw new InvalidOperationException("UnbondLockIn not found"); public static bool TryGetUnbondLockIn( this IWorldState world, diff --git a/Lib9c/Module/Delegation/UnbondingSetModule.cs b/Lib9c/Module/Delegation/UnbondingSetModule.cs index bb52c77a2c..e0d9027d0e 100644 --- a/Lib9c/Module/Delegation/UnbondingSetModule.cs +++ b/Lib9c/Module/Delegation/UnbondingSetModule.cs @@ -1,98 +1,42 @@ #nullable enable using System; -using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Action; using Nekoyume.Delegation; -using Nekoyume.Extensions; namespace Nekoyume.Module.Delegation { public static class UnbondingSetModule { - public static UnbondingSet GetUnbondingSet(this IWorldState world) - => TryGetUnbondingSet(world, out var unbondingSet) - ? unbondingSet! - : new UnbondingSet(); - - public static bool TryGetUnbondingSet( - this IWorldState world, out UnbondingSet? unbondingSet) + public static IWorld ReleaseUnbondings(this IWorld world, IActionContext context) { - try - { - var value = world.GetAccountState(Addresses.UnbondingSet) - .GetState(UnbondingSet.Address); - if (!(value is List list)) - { - unbondingSet = null; - return false; - } + var repository = new DelegationRepository(world, context); + var unbondingSet = repository.GetUnbondingSet(); + var unbondings = unbondingSet.UnbondingsToRelease(context.BlockIndex); - unbondingSet = new UnbondingSet(list); - return true; - } - catch + IUnbonding released; + foreach (var unbonding in unbondings) { - unbondingSet = null; - return false; - } - } + released = unbonding.Release(context.BlockIndex); - public static IWorld Release(this IWorld world, IActionContext context) - { - var unbondingSet = world.GetUnbondingSet(); - var releaseds = world.ReleaseUnbondings(context, unbondingSet); - foreach (var released in releaseds) - { - world = released switch + switch (released) { - UnbondLockIn unbondLockIn => world.SetUnbondLockIn(unbondLockIn), - RebondGrace rebondGrace => world.SetRebondGrace(rebondGrace), - _ => throw new ArgumentException("Invalid unbonding type.") - }; + case UnbondLockIn unbondLockIn: + repository.SetUnbondLockIn(unbondLockIn); + break; + case RebondGrace rebondGrace: + repository.SetRebondGrace(rebondGrace); + break; + default: + throw new ArgumentException("Invalid unbonding type."); + } unbondingSet = unbondingSet.SetUnbonding(released); } - return world.SetUnbondingSet(unbondingSet); - } - - private static IUnbonding[] ReleaseUnbondings( - this IWorld world, IActionContext context, UnbondingSet unbondingSet) - => unbondingSet.ReleaseUnbondings( - context.BlockIndex, - (address, type) => world.GetAccount(AccountAddress(type)).GetState(address) - ?? throw new FailedLoadStateException( - $"Tried to release unbonding on {address}, but unbonding does not exist.")); - - private static IWorld SetUnbondingSet(this IWorld world, UnbondingSet unbondingSet) - => world.MutateAccount( - Addresses.UnbondingSet, - account => account.SetState(UnbondingSet.Address, unbondingSet.Bencoded)); + repository.SetUnbondingSet(unbondingSet); - private static IWorld SetUnbondLockIn( - this IWorld world, UnbondLockIn unbondLockIn) - => world.MutateAccount( - Addresses.UnbondLockIn, - account => unbondLockIn.IsEmpty - ? account.RemoveState(unbondLockIn.Address) - : account.SetState(unbondLockIn.Address, unbondLockIn.Bencoded)); - - public static IWorld SetRebondGrace( - this IWorld world, RebondGrace rebondGrace) - => world.MutateAccount( - Addresses.RebondGrace, - account => rebondGrace.IsEmpty - ? account.RemoveState(rebondGrace.Address) - : account.SetState(rebondGrace.Address, rebondGrace.Bencoded)); - - private static Address AccountAddress(Type type) => type switch - { - var t when t == typeof(UnbondLockIn) => Addresses.UnbondLockIn, - var t when t == typeof(RebondGrace) => Addresses.RebondGrace, - _ => throw new ArgumentException("Invalid unbonding type.") - }; + return repository.World; + } } } From 2bc3e7fe95d633585df2ffcc6560b55983e85868 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 16 Aug 2024 00:31:54 +0900 Subject: [PATCH 021/206] fix: Fix reward functionalities with test --- .Lib9c.Tests/Delegation/DelegatorTest.cs | 229 ++++++++++++++++++ .Lib9c.Tests/Delegation/TestDelegationRepo.cs | 12 + Lib9c/Delegation/Delegatee.cs | 27 ++- Lib9c/Delegation/DelegationRepository.cs | 12 + Lib9c/Delegation/IDelegationRepository.cs | 2 + 5 files changed, 275 insertions(+), 7 deletions(-) diff --git a/.Lib9c.Tests/Delegation/DelegatorTest.cs b/.Lib9c.Tests/Delegation/DelegatorTest.cs index 75d3b9f41c..283563417a 100644 --- a/.Lib9c.Tests/Delegation/DelegatorTest.cs +++ b/.Lib9c.Tests/Delegation/DelegatorTest.cs @@ -286,5 +286,234 @@ public void Redelegate() rebondGrace = rebondGrace.Release(12L + delegatee1.UnbondingPeriod); Assert.Empty(rebondGrace.Entries); } + + [Fact] + public void RewardOnDelegate() + { + var repo = _fixture.Repository; + var delegator1 = _fixture.TestDelegator1; + var delegator2 = _fixture.TestDelegator2; + var delegatee = _fixture.TestDelegatee1; + var delegatorInitialBalance = delegatee.Currency * 100; + repo.MintAsset(delegator1.Address, delegatorInitialBalance); + repo.MintAsset(delegator2.Address, delegatorInitialBalance); + + var reward = delegatee.Currency * 100; + repo.MintAsset(delegatee.RewardPoolAddress, reward); + // EndBlock after delegatee's reward + repo.Reward(delegatee, 10L, reward); + + var delegatingFAV1 = delegatee.Currency * 10; + delegator1.Delegate(delegatee, delegatingFAV1, 10L); + var delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); + var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var share1 = repo.GetBond(delegatee, delegator1.Address).Share; + Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); + + var delegatingFAV2 = delegatee.Currency * 20; + delegator2.Delegate(delegatee, delegatingFAV2, 10L); + var delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var share2 = repo.GetBond(delegatee, delegator2.Address).Share; + Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); + + var totalShares = delegatee.TotalShares; + + repo.MintAsset(delegatee.RewardPoolAddress, reward); + // EndBlock after delegatee's reward + repo.Reward(delegatee, 10L, reward); + + delegatingFAV1 = delegatee.Currency * 10; + delegator1.Delegate(delegatee, delegatingFAV1, 11L); + delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); + var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + + var reward1 = (reward * share1).DivRem(totalShares, out _); + Assert.Equal(delegatorInitialBalance - delegatingFAV1 * 2 + reward1, delegator1Balance); + Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); + Assert.Equal(reward * 2 - reward1, rewardPoolBalance); + + delegator2.Delegate(delegatee, delegatingFAV2, 11L); + delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); + rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + + var reward2 = (reward * share2).DivRem(totalShares, out _); + Assert.Equal(delegatorInitialBalance - delegatingFAV1 * 2 + reward1, delegator1Balance); + Assert.Equal(delegatorInitialBalance - delegatingFAV2 * 2 + reward2, delegator2Balance); + Assert.Equal(reward * 2 - reward1 - reward2, rewardPoolBalance); + } + + [Fact] + public void RewardOnUndelegate() + { + var repo = _fixture.Repository; + var delegator1 = _fixture.TestDelegator1; + var delegator2 = _fixture.TestDelegator2; + var delegatee = _fixture.TestDelegatee1; + var delegatorInitialBalance = delegatee.Currency * 100; + repo.MintAsset(delegator1.Address, delegatorInitialBalance); + repo.MintAsset(delegator2.Address, delegatorInitialBalance); + + var reward = delegatee.Currency * 100; + repo.MintAsset(delegatee.RewardPoolAddress, reward); + // EndBlock after delegatee's reward + repo.Reward(delegatee, 10L, reward); + + var delegatingFAV1 = delegatee.Currency * 10; + delegator1.Delegate(delegatee, delegatingFAV1, 10L); + var delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); + var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var share1 = repo.GetBond(delegatee, delegator1.Address).Share; + Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); + + var delegatingFAV2 = delegatee.Currency * 20; + delegator2.Delegate(delegatee, delegatingFAV2, 10L); + var delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var share2 = repo.GetBond(delegatee, delegator2.Address).Share; + Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); + + var totalShares = delegatee.TotalShares; + + repo.MintAsset(delegatee.RewardPoolAddress, reward); + // EndBlock after delegatee's reward + repo.Reward(delegatee, 10L, reward); + + var shareToUndelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; + delegator1.Undelegate(delegatee, shareToUndelegate, 11L); + delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); + var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + + var reward1 = (reward * share1).DivRem(totalShares, out _); + Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); + Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); + Assert.Equal(reward * 2 - reward1, rewardPoolBalance); + + shareToUndelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; + delegator2.Undelegate(delegatee, shareToUndelegate, 11L); + delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); + rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + + var reward2 = (reward * share2).DivRem(totalShares, out _); + Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); + Assert.Equal(delegatorInitialBalance - delegatingFAV2 + reward2, delegator2Balance); + Assert.Equal(reward * 2 - reward1 - reward2, rewardPoolBalance); + } + + [Fact] + public void RewardOnRedelegate() + { + var repo = _fixture.Repository; + var delegator1 = _fixture.TestDelegator1; + var delegator2 = _fixture.TestDelegator2; + var delegatee = _fixture.TestDelegatee1; + var dstDelegatee = _fixture.TestDelegatee2; + var delegatorInitialBalance = delegatee.Currency * 100; + repo.MintAsset(delegator1.Address, delegatorInitialBalance); + repo.MintAsset(delegator2.Address, delegatorInitialBalance); + + var reward = delegatee.Currency * 100; + repo.MintAsset(delegatee.RewardPoolAddress, reward); + // EndBlock after delegatee's reward + repo.Reward(delegatee, 10L, reward); + + var delegatingFAV1 = delegatee.Currency * 10; + delegator1.Delegate(delegatee, delegatingFAV1, 10L); + var delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); + var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var share1 = repo.GetBond(delegatee, delegator1.Address).Share; + Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); + + var delegatingFAV2 = delegatee.Currency * 20; + delegator2.Delegate(delegatee, delegatingFAV2, 10L); + var delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var share2 = repo.GetBond(delegatee, delegator2.Address).Share; + Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); + + var totalShares = delegatee.TotalShares; + + repo.MintAsset(delegatee.RewardPoolAddress, reward); + // EndBlock after delegatee's reward + repo.Reward(delegatee, 10L, reward); + + var shareToRedelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; + delegator1.Redelegate(delegatee, dstDelegatee, shareToRedelegate, 11L); + delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); + var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + + var reward1 = (reward * share1).DivRem(totalShares, out _); + Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); + Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); + Assert.Equal(reward * 2 - reward1, rewardPoolBalance); + + shareToRedelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; + delegator2.Redelegate(delegatee, dstDelegatee, shareToRedelegate, 11L); + delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); + rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + + var reward2 = (reward * share2).DivRem(totalShares, out _); + Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); + Assert.Equal(delegatorInitialBalance - delegatingFAV2 + reward2, delegator2Balance); + Assert.Equal(reward * 2 - reward1 - reward2, rewardPoolBalance); + } + + [Fact] + public void RewardOnClaim() + { + var repo = _fixture.Repository; + var delegator1 = _fixture.TestDelegator1; + var delegator2 = _fixture.TestDelegator2; + var delegatee = _fixture.TestDelegatee1; + var dstDelegatee = _fixture.TestDelegatee2; + var delegatorInitialBalance = delegatee.Currency * 100; + repo.MintAsset(delegator1.Address, delegatorInitialBalance); + repo.MintAsset(delegator2.Address, delegatorInitialBalance); + + var reward = delegatee.Currency * 100; + repo.MintAsset(delegatee.RewardPoolAddress, reward); + // EndBlock after delegatee's reward + repo.Reward(delegatee, 10L, reward); + + var delegatingFAV1 = delegatee.Currency * 10; + delegator1.Delegate(delegatee, delegatingFAV1, 10L); + var delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); + var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var share1 = repo.GetBond(delegatee, delegator1.Address).Share; + Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); + + var delegatingFAV2 = delegatee.Currency * 20; + delegator2.Delegate(delegatee, delegatingFAV2, 10L); + var delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var share2 = repo.GetBond(delegatee, delegator2.Address).Share; + Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); + + var totalShares = delegatee.TotalShares; + + repo.MintAsset(delegatee.RewardPoolAddress, reward); + // EndBlock after delegatee's reward + repo.Reward(delegatee, 10L, reward); + + var shareToRedelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; + delegator1.ClaimReward(delegatee, 11L); + delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); + var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + + var reward1 = (reward * share1).DivRem(totalShares, out _); + Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); + Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); + Assert.Equal(reward * 2 - reward1, rewardPoolBalance); + + shareToRedelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; + delegator2.ClaimReward(delegatee, 11L); + delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); + rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + + var reward2 = (reward * share2).DivRem(totalShares, out _); + Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); + Assert.Equal(delegatorInitialBalance - delegatingFAV2 + reward2, delegator2Balance); + Assert.Equal(reward * 2 - reward1 - reward2, rewardPoolBalance); + } } } diff --git a/.Lib9c.Tests/Delegation/TestDelegationRepo.cs b/.Lib9c.Tests/Delegation/TestDelegationRepo.cs index 517dc86817..ba623de30c 100644 --- a/.Lib9c.Tests/Delegation/TestDelegationRepo.cs +++ b/.Lib9c.Tests/Delegation/TestDelegationRepo.cs @@ -142,6 +142,18 @@ public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) lumpSumRewardsRecord.Address, lumpSumRewardsRecord.Bencoded); } + public void Reward(IDelegatee delegatee, long height, FungibleAssetValue reward) + { + LumpSumRewardsRecord record = GetCurrentLumpSumRewardsRecord(delegatee) + ?? new LumpSumRewardsRecord( + delegatee.CurrentLumpSumRewardsRecordAddress(), + height, + delegatee.TotalShares, + delegatee.RewardCurrency); + record = record.AddLumpSumReward(reward); + SetLumpSumRewardsRecord(record); + } + public void TransferAsset(Address sender, Address recipient, FungibleAssetValue value) => _world = _world.TransferAsset(_context, sender, recipient, value); diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index e00e40ebbd..74ca41d016 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -156,6 +156,7 @@ public BigInteger Bond(T delegator, FungibleAssetValue fav, long height) TotalShares += share; TotalDelegated += fav; _repository.SetBond(bond); + StartNewRewardPeriod(height); return share; } @@ -181,7 +182,8 @@ public FungibleAssetValue Unbond(T delegator, BigInteger share, long height) TotalShares -= share; TotalDelegated -= fav; _repository.SetBond(bond); - + StartNewRewardPeriod(height); + return fav; } @@ -192,13 +194,11 @@ public void Reward(T delegator, long height) IEnumerable lumpSumRewardsRecords = GetLumpSumRewardsRecords(delegator.LastRewardHeight); FungibleAssetValue reward = CalculateReward(share, lumpSumRewardsRecords); - if (reward.Sign <= 0) + if (reward.Sign > 0) { - return; + _repository.TransferAsset(RewardPoolAddress, delegator.Address, reward); } - _repository.TransferAsset(RewardPoolAddress, delegator.Address, reward); - StartNewRewardPeriod(height); delegator.UpdateLastRewardHeight(height); } @@ -260,10 +260,23 @@ private void StartNewRewardPeriod(long height) long? lastStartHeight = null; if (currentRecord is LumpSumRewardsRecord lastRecord) { + lastStartHeight = lastRecord.StartHeight; + if (lastStartHeight == height) + { + currentRecord = new( + currentRecord.Address, + currentRecord.StartHeight, + TotalShares, + RewardCurrency, + currentRecord.LastStartHeight); + + _repository.SetLumpSumRewardsRecord(currentRecord); + return; + } + _repository.SetLumpSumRewardsRecord( lastRecord.MoveAddress( - CurrentLumpSumRewardsRecordAddress())); - lastStartHeight = lastRecord.StartHeight; + LumpSumRewardsRecordAddress(lastRecord.StartHeight))); } LumpSumRewardsRecord newRecord = new( diff --git a/Lib9c/Delegation/DelegationRepository.cs b/Lib9c/Delegation/DelegationRepository.cs index 4983f9f172..46adb50299 100644 --- a/Lib9c/Delegation/DelegationRepository.cs +++ b/Lib9c/Delegation/DelegationRepository.cs @@ -142,6 +142,18 @@ public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) lumpSumRewardsRecord.Address, lumpSumRewardsRecord.Bencoded); } + public void Reward(IDelegatee delegatee, long height, FungibleAssetValue reward) + { + LumpSumRewardsRecord record = GetCurrentLumpSumRewardsRecord(delegatee) + ?? new LumpSumRewardsRecord( + delegatee.CurrentLumpSumRewardsRecordAddress(), + height, + delegatee.TotalShares, + delegatee.RewardCurrency); + record = record.AddLumpSumReward(reward); + SetLumpSumRewardsRecord(record); + } + public void TransferAsset(Address sender, Address recipient, FungibleAssetValue value) => _world = _world.TransferAsset(_context, sender, recipient, value); } diff --git a/Lib9c/Delegation/IDelegationRepository.cs b/Lib9c/Delegation/IDelegationRepository.cs index ed484be651..8498a9ffa5 100644 --- a/Lib9c/Delegation/IDelegationRepository.cs +++ b/Lib9c/Delegation/IDelegationRepository.cs @@ -35,6 +35,8 @@ public interface IDelegationRepository void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord); + void Reward(IDelegatee delegatee, long height, FungibleAssetValue reward); + void TransferAsset(Address sender, Address recipient, FungibleAssetValue value); } } From 59e4df172c7be02c42f909057b8c68f8642e4359 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 19 Aug 2024 08:42:45 +0900 Subject: [PATCH 022/206] chore: Fix typo --- Lib9c/Delegation/Delegatee.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 74ca41d016..58805046fc 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -140,7 +140,7 @@ void IDelegatee.Reward(IDelegator delegator, long height) public BigInteger Bond(T delegator, FungibleAssetValue fav, long height) { - CannotMutateReleationsWithoutRepository(delegator); + CannotMutateRelationsWithoutRepository(delegator); Reward(delegator, height); if (!fav.Currency.Equals(Currency)) @@ -163,7 +163,7 @@ public BigInteger Bond(T delegator, FungibleAssetValue fav, long height) public FungibleAssetValue Unbond(T delegator, BigInteger share, long height) { - CannotMutateReleationsWithoutRepository(delegator); + CannotMutateRelationsWithoutRepository(delegator); Reward(delegator, height); if (TotalShares.IsZero || TotalDelegated.RawValue.IsZero) { @@ -189,7 +189,7 @@ public FungibleAssetValue Unbond(T delegator, BigInteger share, long height) public void Reward(T delegator, long height) { - CannotMutateReleationsWithoutRepository(delegator); + CannotMutateRelationsWithoutRepository(delegator); BigInteger share = _repository!.GetBond(this, delegator.Address).Share; IEnumerable lumpSumRewardsRecords = GetLumpSumRewardsRecords(delegator.LastRewardHeight); @@ -348,7 +348,7 @@ record = _repository.GetLumpSumRewardsRecord(this, lastStartHeight) return records; } - private void CannotMutateReleationsWithoutRepository(T delegator) + private void CannotMutateRelationsWithoutRepository(T delegator) { CannotMutateRelationsWithoutRepository(); if (!_repository!.Equals(delegator.Repository)) From 4fc97bee59d28b784fda0a4dd1eabb3e7526d293 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 19 Aug 2024 10:25:57 +0900 Subject: [PATCH 023/206] feat: Add reward collection --- Lib9c/Delegation/Delegatee.cs | 31 +++++++++++++++-------- Lib9c/Delegation/DelegationRepository.cs | 7 +++-- Lib9c/Delegation/Delegator.cs | 2 +- Lib9c/Delegation/IDelegatee.cs | 8 ++++-- Lib9c/Delegation/IDelegationRepository.cs | 4 ++- Lib9c/Delegation/LumpSumRewardsRecord.cs | 4 +-- Lib9c/Module/Guild/GuildModule.cs | 17 +++++++++++++ 7 files changed, 55 insertions(+), 18 deletions(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 58805046fc..49339ef8ba 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -16,11 +16,12 @@ public abstract class Delegatee : IDelegatee where T : Delegator where TSelf : Delegatee { - protected readonly byte[] BondId = new byte[] { 0x44 }; // `D` + protected readonly byte[] BondId = new byte[] { 0x42 }; // `B` protected readonly byte[] UnbondLockInId = new byte[] { 0x55 }; // `U` protected readonly byte[] RebondGraceId = new byte[] { 0x52 }; // `R` protected readonly byte[] LumpSumRewardsRecordId = new byte[] { 0x4c }; // `L` - protected readonly byte[] RewardPoolId = new byte[] { 0x72 }; // `r` + protected readonly byte[] RewardCollectorId = new byte[] { 0x63 }; // `c` + protected readonly byte[] RewardDistributorId = new byte[] { 0x64 }; // `d` protected readonly byte[] PoolId = new byte[] { 0x70 }; // `p` private readonly IDelegationRepository? _repository; @@ -100,7 +101,9 @@ private Delegatee( public abstract int MaxRebondGraceEntries { get; } - public Address RewardPoolAddress => DeriveAddress(RewardPoolId); + public Address RewardCollectorAddress => DeriveAddress(RewardCollectorId); + + public Address RewardDistributorAddress => DeriveAddress(RewardDistributorId); public ImmutableSortedSet
Delegators { get; private set; } @@ -135,13 +138,13 @@ FungibleAssetValue IDelegatee.Unbond( IDelegator delegator, BigInteger share, long height) => Unbond((T)delegator, share, height); - void IDelegatee.Reward(IDelegator delegator, long height) - => Reward((T)delegator, height); + void IDelegatee.DistributeReward(IDelegator delegator, long height) + => DistributeReward((T)delegator, height); public BigInteger Bond(T delegator, FungibleAssetValue fav, long height) { CannotMutateRelationsWithoutRepository(delegator); - Reward(delegator, height); + DistributeReward(delegator, height); if (!fav.Currency.Equals(Currency)) { @@ -164,7 +167,7 @@ public BigInteger Bond(T delegator, FungibleAssetValue fav, long height) public FungibleAssetValue Unbond(T delegator, BigInteger share, long height) { CannotMutateRelationsWithoutRepository(delegator); - Reward(delegator, height); + DistributeReward(delegator, height); if (TotalShares.IsZero || TotalDelegated.RawValue.IsZero) { throw new InvalidOperationException( @@ -187,7 +190,7 @@ public FungibleAssetValue Unbond(T delegator, BigInteger share, long height) return fav; } - public void Reward(T delegator, long height) + public void DistributeReward(T delegator, long height) { CannotMutateRelationsWithoutRepository(delegator); BigInteger share = _repository!.GetBond(this, delegator.Address).Share; @@ -196,12 +199,20 @@ public void Reward(T delegator, long height) FungibleAssetValue reward = CalculateReward(share, lumpSumRewardsRecords); if (reward.Sign > 0) { - _repository.TransferAsset(RewardPoolAddress, delegator.Address, reward); + _repository.TransferAsset(RewardDistributorAddress, delegator.Address, reward); } delegator.UpdateLastRewardHeight(height); } + public void CollectRewards(long height) + { + CannotMutateRelationsWithoutRepository(); + FungibleAssetValue rewards = _repository!.GetBalance(RewardCollectorAddress, RewardCurrency); + _repository!.AddLumpSumRewards(this, height, rewards); + _repository!.TransferAsset(RewardCollectorAddress, RewardDistributorAddress, rewards); + } + public Address BondAddress(Address delegatorAddress) => DeriveAddress(BondId, delegatorAddress); @@ -229,7 +240,7 @@ public bool Equals(IDelegatee? other) && Currency.Equals(delegatee.Currency) && DelegationPoolAddress.Equals(delegatee.DelegationPoolAddress) && UnbondingPeriod == delegatee.UnbondingPeriod - && RewardPoolAddress.Equals(delegatee.RewardPoolAddress) + && RewardDistributorAddress.Equals(delegatee.RewardDistributorAddress) && Delegators.SequenceEqual(delegatee.Delegators) && TotalDelegated.Equals(delegatee.TotalDelegated) && TotalShares.Equals(delegatee.TotalShares) diff --git a/Lib9c/Delegation/DelegationRepository.cs b/Lib9c/Delegation/DelegationRepository.cs index 46adb50299..85ed1168a1 100644 --- a/Lib9c/Delegation/DelegationRepository.cs +++ b/Lib9c/Delegation/DelegationRepository.cs @@ -108,6 +108,9 @@ public UnbondingSet GetUnbondingSet() : null; } + public FungibleAssetValue GetBalance(Address address, Currency currency) + => _world.GetBalance(address, currency); + public void SetBond(Bond bond) { _bond = bond.IsEmpty @@ -142,7 +145,7 @@ public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) lumpSumRewardsRecord.Address, lumpSumRewardsRecord.Bencoded); } - public void Reward(IDelegatee delegatee, long height, FungibleAssetValue reward) + public void AddLumpSumRewards(IDelegatee delegatee, long height, FungibleAssetValue rewards) { LumpSumRewardsRecord record = GetCurrentLumpSumRewardsRecord(delegatee) ?? new LumpSumRewardsRecord( @@ -150,7 +153,7 @@ public void Reward(IDelegatee delegatee, long height, FungibleAssetValue reward) height, delegatee.TotalShares, delegatee.RewardCurrency); - record = record.AddLumpSumReward(reward); + record = record.AddLumpSumRewards(rewards); SetLumpSumRewardsRecord(record); } diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index a9a15670f2..ccabfdc0d3 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -205,7 +205,7 @@ public void ClaimReward( T delegatee, long height) { CannotMutateRelationsWithoutRepository(delegatee); - delegatee.Reward((TSelf)this, height); + delegatee.DistributeReward((TSelf)this, height); } public void UpdateLastRewardHeight(long height) diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index 3687851379..6312c22fc1 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -24,7 +24,9 @@ public interface IDelegatee : IBencodable, IEquatable int MaxRebondGraceEntries { get; } - Address RewardPoolAddress { get; } + Address RewardCollectorAddress { get; } + + Address RewardDistributorAddress { get; } ImmutableSortedSet
Delegators { get; } @@ -36,7 +38,9 @@ public interface IDelegatee : IBencodable, IEquatable FungibleAssetValue Unbond(IDelegator delegator, BigInteger share, long height); - void Reward(IDelegator delegator, long height); + void DistributeReward(IDelegator delegator, long height); + + void CollectRewards(long height); Address BondAddress(Address delegatorAddress); diff --git a/Lib9c/Delegation/IDelegationRepository.cs b/Lib9c/Delegation/IDelegationRepository.cs index 8498a9ffa5..b71df158bb 100644 --- a/Lib9c/Delegation/IDelegationRepository.cs +++ b/Lib9c/Delegation/IDelegationRepository.cs @@ -25,6 +25,8 @@ public interface IDelegationRepository LumpSumRewardsRecord? GetCurrentLumpSumRewardsRecord(IDelegatee delegatee); + FungibleAssetValue GetBalance(Address address, Currency currency); + void SetBond(Bond bond); void SetUnbondLockIn(UnbondLockIn unbondLockIn); @@ -35,7 +37,7 @@ public interface IDelegationRepository void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord); - void Reward(IDelegatee delegatee, long height, FungibleAssetValue reward); + void AddLumpSumRewards(IDelegatee delegatee, long height, FungibleAssetValue rewards); void TransferAsset(Address sender, Address recipient, FungibleAssetValue value); } diff --git a/Lib9c/Delegation/LumpSumRewardsRecord.cs b/Lib9c/Delegation/LumpSumRewardsRecord.cs index d26efa261a..753aecfba0 100644 --- a/Lib9c/Delegation/LumpSumRewardsRecord.cs +++ b/Lib9c/Delegation/LumpSumRewardsRecord.cs @@ -94,12 +94,12 @@ public LumpSumRewardsRecord MoveAddress(Address address) LumpSumRewards, LastStartHeight); - public LumpSumRewardsRecord AddLumpSumReward(FungibleAssetValue reward) + public LumpSumRewardsRecord AddLumpSumRewards(FungibleAssetValue rewards) => new LumpSumRewardsRecord( Address, StartHeight, TotalShares, - LumpSumRewards + reward, + LumpSumRewards + rewards, LastStartHeight); public FungibleAssetValue RewardsDuringPeriod(BigInteger share) diff --git a/Lib9c/Module/Guild/GuildModule.cs b/Lib9c/Module/Guild/GuildModule.cs index 9968c14639..f3228e4aed 100644 --- a/Lib9c/Module/Guild/GuildModule.cs +++ b/Lib9c/Module/Guild/GuildModule.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics.CodeAnalysis; using Bencodex.Types; +using Libplanet.Action; using Libplanet.Action.State; using Nekoyume.Action; using Nekoyume.Delegation; @@ -119,5 +120,21 @@ public static IWorld SetGuild(this IWorld world, Model.Guild.Guild guild) => world.MutateAccount( Addresses.Guild, account => account.SetState(guild.Address, guild.Bencoded)); + + public static IWorld CollectRewardGuild( + this IWorld world, + IActionContext context, + GuildAddress guildAddress) + { + var repo = new DelegationRepository(world, context); + + var guild = world.TryGetGuild(guildAddress, repo, out var g) + ? g + : throw new InvalidOperationException("The guild does not exist."); + + guild.CollectRewards(context.BlockIndex); + + return repo.World; + } } } From 4fb882d81387ec1672669a654d22c22feece67ee Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 19 Aug 2024 10:26:11 +0900 Subject: [PATCH 024/206] test: Fix tests --- .../Guild/Migration/GuildMigrationCtrlTest.cs | 25 ++++++++-- .../Migration/MigratePledgeToGuildTest.cs | 19 ++++++-- .Lib9c.Tests/Delegation/DelegateeTest.cs | 4 +- .Lib9c.Tests/Delegation/DelegatorTest.cs | 48 +++++++++---------- .Lib9c.Tests/Delegation/TestDelegationRepo.cs | 7 ++- ...GuildParticipantTest.Snapshot.verified.txt | 8 +++- .../Guild/GuildTest.Snapshot.verified.txt | 33 ++++++++++++- 7 files changed, 107 insertions(+), 37 deletions(-) diff --git a/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs b/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs index 46c937548d..1a78310da1 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action.Guild.Migration using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Mocks; + using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; using Nekoyume.Action.Guild; @@ -25,7 +26,11 @@ public void MigratePlanetariumPledgeToGuild_When_WithUnapprovedPledgeContract() var guildAddress = AddressUtil.CreateGuildAddress(); var target = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); - var world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .MakeGuild(guildAddress, guildMasterAddress) .JoinGuild(guildAddress, guildMasterAddress) .SetLegacyState(pledgeAddress, new List( @@ -45,7 +50,11 @@ public void MigratePlanetariumPledgeToGuild_When_WithPledgeContract() var guildAddress = AddressUtil.CreateGuildAddress(); var target = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); - var world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .MakeGuild(guildAddress, guildMasterAddress) .JoinGuild(guildAddress, guildMasterAddress) .SetLegacyState(pledgeAddress, new List( @@ -67,7 +76,11 @@ public void MigratePlanetariumPledgeToGuild_When_WithoutPledgeContract() var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); var target = AddressUtil.CreateAgentAddress(); - var world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .MakeGuild(guildAddress, guildMasterAddress) .JoinGuild(guildAddress, guildMasterAddress); @@ -81,7 +94,11 @@ public void MigratePlanetariumPledgeToGuild_When_WithoutGuildYet() { var target = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); - var world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .SetLegacyState(pledgeAddress, new List( MeadConfig.PatronAddress.Serialize(), true.Serialize(), diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs index 70c8850a2d..06b5ce90cf 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action.Guild.Migration using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Mocks; + using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; using Nekoyume.Action.Guild; @@ -42,7 +43,11 @@ public void Execute_When_WithPledgeContract() var target = AddressUtil.CreateAgentAddress(); var caller = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); - var world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .MakeGuild(guildAddress, guildMasterAddress) .JoinGuild(guildAddress, guildMasterAddress) .SetLegacyState(pledgeAddress, new List( @@ -84,7 +89,11 @@ public void Execute_When_WithUnapprovedPledgeContract() var target = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); var caller = AddressUtil.CreateAgentAddress(); - var world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .MakeGuild(guildAddress, guildMasterAddress) .JoinGuild(guildAddress, guildMasterAddress) .SetLegacyState(pledgeAddress, new List( @@ -117,7 +126,11 @@ public void Execute_When_WithoutPledgeContract() var guildAddress = AddressUtil.CreateGuildAddress(); var target = AddressUtil.CreateAgentAddress(); var caller = AddressUtil.CreateAgentAddress(); - var world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .MakeGuild(guildAddress, guildMasterAddress) .JoinGuild(guildAddress, guildMasterAddress); diff --git a/.Lib9c.Tests/Delegation/DelegateeTest.cs b/.Lib9c.Tests/Delegation/DelegateeTest.cs index a09902ae5e..abf4739499 100644 --- a/.Lib9c.Tests/Delegation/DelegateeTest.cs +++ b/.Lib9c.Tests/Delegation/DelegateeTest.cs @@ -247,8 +247,8 @@ public void AddressConsistency() Assert.Equal(testDelegatee1.Address, dummyDelegatee1.Address); Assert.NotEqual( - testDelegatee1.RewardPoolAddress, - dummyDelegatee1.RewardPoolAddress); + testDelegatee1.RewardDistributorAddress, + dummyDelegatee1.RewardDistributorAddress); Assert.NotEqual( testDelegatee1.BondAddress(testDelegator1.Address), dummyDelegatee1.BondAddress(testDelegator1.Address)); diff --git a/.Lib9c.Tests/Delegation/DelegatorTest.cs b/.Lib9c.Tests/Delegation/DelegatorTest.cs index 283563417a..01c37f40b2 100644 --- a/.Lib9c.Tests/Delegation/DelegatorTest.cs +++ b/.Lib9c.Tests/Delegation/DelegatorTest.cs @@ -299,9 +299,9 @@ public void RewardOnDelegate() repo.MintAsset(delegator2.Address, delegatorInitialBalance); var reward = delegatee.Currency * 100; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + repo.MintAsset(delegatee.RewardDistributorAddress, reward); // EndBlock after delegatee's reward - repo.Reward(delegatee, 10L, reward); + repo.AddLumpSumRewards(delegatee, 10L, reward); var delegatingFAV1 = delegatee.Currency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 10L); @@ -319,14 +319,14 @@ public void RewardOnDelegate() var totalShares = delegatee.TotalShares; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + repo.MintAsset(delegatee.RewardDistributorAddress, reward); // EndBlock after delegatee's reward - repo.Reward(delegatee, 10L, reward); + repo.AddLumpSumRewards(delegatee, 10L, reward); delegatingFAV1 = delegatee.Currency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 11L); delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); - var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); var reward1 = (reward * share1).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 * 2 + reward1, delegator1Balance); @@ -335,7 +335,7 @@ public void RewardOnDelegate() delegator2.Delegate(delegatee, delegatingFAV2, 11L); delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); - rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); var reward2 = (reward * share2).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 * 2 + reward1, delegator1Balance); @@ -355,9 +355,9 @@ public void RewardOnUndelegate() repo.MintAsset(delegator2.Address, delegatorInitialBalance); var reward = delegatee.Currency * 100; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + repo.MintAsset(delegatee.RewardDistributorAddress, reward); // EndBlock after delegatee's reward - repo.Reward(delegatee, 10L, reward); + repo.AddLumpSumRewards(delegatee, 10L, reward); var delegatingFAV1 = delegatee.Currency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 10L); @@ -375,14 +375,14 @@ public void RewardOnUndelegate() var totalShares = delegatee.TotalShares; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + repo.MintAsset(delegatee.RewardDistributorAddress, reward); // EndBlock after delegatee's reward - repo.Reward(delegatee, 10L, reward); + repo.AddLumpSumRewards(delegatee, 10L, reward); var shareToUndelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; delegator1.Undelegate(delegatee, shareToUndelegate, 11L); delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); - var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); var reward1 = (reward * share1).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); @@ -392,7 +392,7 @@ public void RewardOnUndelegate() shareToUndelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.Undelegate(delegatee, shareToUndelegate, 11L); delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); - rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); var reward2 = (reward * share2).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); @@ -413,9 +413,9 @@ public void RewardOnRedelegate() repo.MintAsset(delegator2.Address, delegatorInitialBalance); var reward = delegatee.Currency * 100; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + repo.MintAsset(delegatee.RewardDistributorAddress, reward); // EndBlock after delegatee's reward - repo.Reward(delegatee, 10L, reward); + repo.AddLumpSumRewards(delegatee, 10L, reward); var delegatingFAV1 = delegatee.Currency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 10L); @@ -433,14 +433,14 @@ public void RewardOnRedelegate() var totalShares = delegatee.TotalShares; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + repo.MintAsset(delegatee.RewardDistributorAddress, reward); // EndBlock after delegatee's reward - repo.Reward(delegatee, 10L, reward); + repo.AddLumpSumRewards(delegatee, 10L, reward); var shareToRedelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; delegator1.Redelegate(delegatee, dstDelegatee, shareToRedelegate, 11L); delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); - var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); var reward1 = (reward * share1).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); @@ -450,7 +450,7 @@ public void RewardOnRedelegate() shareToRedelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.Redelegate(delegatee, dstDelegatee, shareToRedelegate, 11L); delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); - rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); var reward2 = (reward * share2).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); @@ -471,9 +471,9 @@ public void RewardOnClaim() repo.MintAsset(delegator2.Address, delegatorInitialBalance); var reward = delegatee.Currency * 100; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + repo.MintAsset(delegatee.RewardDistributorAddress, reward); // EndBlock after delegatee's reward - repo.Reward(delegatee, 10L, reward); + repo.AddLumpSumRewards(delegatee, 10L, reward); var delegatingFAV1 = delegatee.Currency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 10L); @@ -491,14 +491,14 @@ public void RewardOnClaim() var totalShares = delegatee.TotalShares; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + repo.MintAsset(delegatee.RewardDistributorAddress, reward); // EndBlock after delegatee's reward - repo.Reward(delegatee, 10L, reward); + repo.AddLumpSumRewards(delegatee, 10L, reward); var shareToRedelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; delegator1.ClaimReward(delegatee, 11L); delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); - var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); var reward1 = (reward * share1).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); @@ -508,7 +508,7 @@ public void RewardOnClaim() shareToRedelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.ClaimReward(delegatee, 11L); delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); - rewardPoolBalance = repo.World.GetBalance(delegatee.RewardPoolAddress, delegatee.Currency); + rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); var reward2 = (reward * share2).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); diff --git a/.Lib9c.Tests/Delegation/TestDelegationRepo.cs b/.Lib9c.Tests/Delegation/TestDelegationRepo.cs index ba623de30c..3458a47409 100644 --- a/.Lib9c.Tests/Delegation/TestDelegationRepo.cs +++ b/.Lib9c.Tests/Delegation/TestDelegationRepo.cs @@ -108,6 +108,9 @@ public UnbondingSet GetUnbondingSet() : null; } + public FungibleAssetValue GetBalance(Address address, Currency currency) + => _world.GetBalance(address, currency); + public void SetBond(Bond bond) { _bond = bond.IsEmpty @@ -142,7 +145,7 @@ public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) lumpSumRewardsRecord.Address, lumpSumRewardsRecord.Bencoded); } - public void Reward(IDelegatee delegatee, long height, FungibleAssetValue reward) + public void AddLumpSumRewards(IDelegatee delegatee, long height, FungibleAssetValue reward) { LumpSumRewardsRecord record = GetCurrentLumpSumRewardsRecord(delegatee) ?? new LumpSumRewardsRecord( @@ -150,7 +153,7 @@ public void Reward(IDelegatee delegatee, long height, FungibleAssetValue reward) height, delegatee.TotalShares, delegatee.RewardCurrency); - record = record.AddLumpSumReward(reward); + record = record.AddLumpSumRewards(reward); SetLumpSumRewardsRecord(record); } diff --git a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.verified.txt b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.verified.txt index f135b22392..9e3bf7e99a 100644 --- a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.verified.txt +++ b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.verified.txt @@ -1,4 +1,4 @@ -[ +[ { EncodingLength: 21, Kind: Text, @@ -30,5 +30,11 @@ 248, 146, 242 + ], + [ + [], + { + EncodingLength: 1 + } ] ] diff --git a/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt b/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt index 0267c82cfe..5617d65487 100644 --- a/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt +++ b/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt @@ -1,4 +1,4 @@ -[ +[ { EncodingLength: 8, Kind: Text, @@ -30,5 +30,36 @@ 248, 146, 242 + ], + [ + [], + [ + { + Bencodex.Types.Text "decimalPlaces": [ + 18 + ], + Bencodex.Types.Text "minters": { + EncodingLength: 1 + }, + Bencodex.Types.Text "ticker": { + EncodingLength: 14, + Kind: Text, + Value: GUILD_GOLD + }, + Bencodex.Types.Text "totalSupplyTrackable": { + EncodingLength: 1, + Kind: Boolean, + Value: true + } + }, + { + EncodingLength: 3, + Kind: Integer + } + ], + { + EncodingLength: 3, + Kind: Integer + } ] ] From 156c6850968337b0fc3dcf1a06dce95799846d3b Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 19 Aug 2024 10:48:28 +0900 Subject: [PATCH 025/206] test: Set GoldCurrency on tests --- .../Guild/AcceptGuildApplicationTest.cs | 10 +++++- .Lib9c.Tests/Action/Guild/ApplyGuildTest.cs | 10 +++++- .../Action/Guild/BanGuildMemberTest.cs | 32 +++++++++++++---- .../Guild/CancelGuildApplicationTest.cs | 10 +++++- .Lib9c.Tests/Action/Guild/MakeGuildTest.cs | 7 ++++ .Lib9c.Tests/Action/Guild/QuitGuildTest.cs | 10 +++++- .../Guild/RejectGuildApplicationTest.cs | 11 ++++-- .Lib9c.Tests/Action/Guild/RemoveGuildTest.cs | 36 ++++++++++++++----- .../Action/Guild/UnbanGuildMemberTest.cs | 19 +++++++--- .../Tx/Begin/AutoJoinGuildTest.cs | 13 +++++-- 10 files changed, 132 insertions(+), 26 deletions(-) diff --git a/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs b/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs index 8b6215d070..5ef4576d2f 100644 --- a/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs +++ b/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs @@ -4,7 +4,11 @@ namespace Lib9c.Tests.Action.Guild using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; using Nekoyume.Action.Guild; + using Nekoyume.Model.State; + using Nekoyume.Module; using Nekoyume.Module.Guild; using Xunit; @@ -30,7 +34,11 @@ public void Execute() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - IWorld world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .MakeGuild(guildAddress, guildMasterAddress) .ApplyGuild(appliedMemberAddress, guildAddress); diff --git a/.Lib9c.Tests/Action/Guild/ApplyGuildTest.cs b/.Lib9c.Tests/Action/Guild/ApplyGuildTest.cs index e978cf4a68..654ec9c448 100644 --- a/.Lib9c.Tests/Action/Guild/ApplyGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/ApplyGuildTest.cs @@ -4,8 +4,12 @@ namespace Lib9c.Tests.Action.Guild using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; using Nekoyume.Action.Guild; using Nekoyume.Action.Loader; + using Nekoyume.Model.State; + using Nekoyume.Module; using Nekoyume.Module.Guild; using Xunit; @@ -31,7 +35,11 @@ public void Execute() var guildAddress = AddressUtil.CreateGuildAddress(); var action = new ApplyGuild(guildAddress); - IWorld world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .MakeGuild(guildAddress, guildMasterAddress) .JoinGuild(guildAddress, guildMasterAddress); IWorld bannedWorld = world.Ban(guildAddress, guildMasterAddress, agentAddress); diff --git a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs index 114d7ff350..3188be40ca 100644 --- a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs +++ b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs @@ -4,7 +4,11 @@ namespace Lib9c.Tests.Action.Guild using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; using Nekoyume.Action.Guild; + using Nekoyume.Model.State; + using Nekoyume.Module; using Nekoyume.Module.Guild; using Xunit; @@ -33,7 +37,11 @@ public void Ban_By_GuildMaster() var guildAddress = AddressUtil.CreateGuildAddress(); var otherGuildAddress = AddressUtil.CreateGuildAddress(); - IWorld world = new World(MockWorldState.CreateModern()); + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); world = world.MakeGuild(guildAddress, guildMasterAddress) .JoinGuild(guildAddress, guildMemberAddress); world = world.MakeGuild(otherGuildAddress, otherGuildMasterAddress) @@ -124,8 +132,12 @@ public void Ban_By_GuildMember() var action = new BanGuildMember(targetGuildMemberAddress); - IWorld world = new World(MockWorldState.CreateModern()); - world = world.MakeGuild(guildAddress, guildMasterAddress) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) + .MakeGuild(guildAddress, guildMasterAddress) .JoinGuild(guildAddress, guildMemberAddress) .JoinGuild(guildAddress, targetGuildMemberAddress); @@ -163,8 +175,12 @@ public void Ban_By_Other() var targetGuildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - IWorld world = new World(MockWorldState.CreateModern()); - world = world.MakeGuild(guildAddress, guildMasterAddress) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) + .MakeGuild(guildAddress, guildMasterAddress) .JoinGuild(guildAddress, targetGuildMemberAddress); // Other tries to ban GuildMember. @@ -191,7 +207,11 @@ public void RejectGuildApplication() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var agentAddress = AddressUtil.CreateAgentAddress(); - IWorld world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .MakeGuild(guildAddress, guildMasterAddress) .ApplyGuild(agentAddress, guildAddress); Assert.True(world.TryGetGuildApplication(agentAddress, out _)); diff --git a/.Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs b/.Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs index d6ea8ee7b5..81e205a330 100644 --- a/.Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs +++ b/.Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs @@ -5,7 +5,11 @@ namespace Lib9c.Tests.Action.Guild using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; using Nekoyume.Action.Guild; + using Nekoyume.Model.State; + using Nekoyume.Module; using Nekoyume.Module.Guild; using Nekoyume.TypedAddress; using Xunit; @@ -31,7 +35,11 @@ public void Execute() var guildAddress = AddressUtil.CreateGuildAddress(); var action = new CancelGuildApplication(); - IWorld world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .MakeGuild(guildAddress, guildMasterAddress); Assert.Throws( () => action.Execute(new ActionContext diff --git a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs index 78c089f567..4098a43ed2 100644 --- a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs @@ -5,8 +5,11 @@ namespace Lib9c.Tests.Action.Guild using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Mocks; + using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action.Guild; + using Nekoyume.Model.State; + using Nekoyume.Module; using Nekoyume.Module.Guild; using Nekoyume.TypedAddress; using Xunit; @@ -44,6 +47,10 @@ public void Execute(AgentAddress guildMasterAddress, bool fail) { var action = new MakeGuild(); IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); if (fail) { diff --git a/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs b/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs index 4de4975872..bcc67adfda 100644 --- a/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs @@ -4,7 +4,11 @@ namespace Lib9c.Tests.Action.Guild using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; using Nekoyume.Action.Guild; + using Nekoyume.Model.State; + using Nekoyume.Module; using Nekoyume.Module.Guild; using Xunit; @@ -28,7 +32,11 @@ public void Execute() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var action = new QuitGuild(); - IWorld world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .MakeGuild(guildAddress, guildMasterAddress); // This case should fail because guild master cannot quit the guild. diff --git a/.Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs b/.Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs index 217096d9ed..c95b052dd1 100644 --- a/.Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs +++ b/.Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs @@ -4,8 +4,11 @@ namespace Lib9c.Tests.Action.Guild using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; using Nekoyume.Action.Guild; - using Nekoyume.Action.Loader; + using Nekoyume.Model.State; + using Nekoyume.Module; using Nekoyume.Module.Guild; using Xunit; @@ -31,7 +34,11 @@ public void Execute() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - IWorld world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .MakeGuild(guildAddress, guildMasterAddress) .ApplyGuild(appliedMemberAddress, guildAddress); diff --git a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs index 766094d4c3..fc6ca02420 100644 --- a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs @@ -4,7 +4,11 @@ namespace Lib9c.Tests.Action.Guild using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; using Nekoyume.Action.Guild; + using Nekoyume.Model.State; + using Nekoyume.Module; using Nekoyume.Module.Guild; using Xunit; @@ -29,8 +33,12 @@ public void Execute_By_GuildMember() var guildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - IWorld world = new World(MockWorldState.CreateModern()); - world = world.MakeGuild(guildAddress, guildMasterAddress); + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) + .MakeGuild(guildAddress, guildMasterAddress); Assert.Throws(() => action.Execute(new ActionContext { @@ -47,8 +55,12 @@ public void Execute_By_GuildMaster() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - IWorld world = new World(MockWorldState.CreateModern()); - world = world.MakeGuild(guildAddress, guildMasterAddress); + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) + .MakeGuild(guildAddress, guildMasterAddress); var changedWorld = action.Execute(new ActionContext { @@ -69,8 +81,12 @@ public void Execute_By_Other() var otherAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - IWorld world = new World(MockWorldState.CreateModern()); - world = world.MakeGuild(guildAddress, guildMasterAddress); + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) + .MakeGuild(guildAddress, guildMasterAddress); Assert.Throws(() => action.Execute(new ActionContext { @@ -88,8 +104,12 @@ public void ResetBannedAddresses() var guildAddress = AddressUtil.CreateGuildAddress(); var bannedAddress = AddressUtil.CreateAgentAddress(); - IWorld world = new World(MockWorldState.CreateModern()); - world = world.MakeGuild(guildAddress, guildMasterAddress) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) + .MakeGuild(guildAddress, guildMasterAddress) .Ban(guildAddress, guildMasterAddress, bannedAddress); Assert.True(world.IsBanned(guildAddress, bannedAddress)); diff --git a/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs index 9aedb745e7..87a6facdc6 100644 --- a/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs +++ b/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs @@ -5,7 +5,11 @@ namespace Lib9c.Tests.Action.Guild using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; using Nekoyume.Action.Guild; + using Nekoyume.Model.State; + using Nekoyume.Module; using Nekoyume.Module.Guild; using Xunit; @@ -33,8 +37,11 @@ public void Unban_By_GuildMember() var action = new UnbanGuildMember(targetGuildMemberAddress); - IWorld world = new World(MockWorldState.CreateModern()); - world = world.MakeGuild(guildAddress, guildMasterAddress) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .JoinGuild(guildAddress, guildMemberAddress) .JoinGuild(guildAddress, targetGuildMemberAddress); @@ -62,8 +69,12 @@ public void Unban_By_GuildMaster() var action = new UnbanGuildMember(targetGuildMemberAddress); - IWorld world = new World(MockWorldState.CreateModern()); - world = world.MakeGuild(guildAddress, guildMasterAddress) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) + .MakeGuild(guildAddress, guildMasterAddress) .Ban(guildAddress, guildMasterAddress, targetGuildMemberAddress); Assert.True(world.IsBanned(guildAddress, targetGuildMemberAddress)); diff --git a/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs b/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs index d88fca8fde..7d9df89dc1 100644 --- a/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs +++ b/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.PolicyAction.Tx.Begin using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Mocks; + using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; using Nekoyume.Action.Guild; @@ -36,7 +37,11 @@ public void Execute_When_WithPledgeContract() var guildAddress = AddressUtil.CreateGuildAddress(); var agentAddress = AddressUtil.CreateAgentAddress(); var pledgeAddress = agentAddress.GetPledgeAddress(); - var world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .MakeGuild(guildAddress, guildMasterAddress) .JoinGuild(guildAddress, guildMasterAddress) .SetLegacyState(pledgeAddress, new List( @@ -64,7 +69,11 @@ public void Execute_When_WithoutPledgeContract() var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); var agentAddress = AddressUtil.CreateAgentAddress(); - var world = new World(MockUtil.MockModernWorldState) + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .MakeGuild(guildAddress, guildMasterAddress) .JoinGuild(guildAddress, guildMasterAddress); From de79d379371755ea2a204bdfa77cffaf32a2ca77 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 22 Aug 2024 20:36:03 +0900 Subject: [PATCH 026/206] feat: Add delegator, delegatee for validator --- .Lib9c.Tests/Delegation/DelegateeTest.cs | 20 +-- .Lib9c.Tests/Delegation/DelegatorTest.cs | 164 +++++++++--------- .Lib9c.Tests/Delegation/DummyDelegatee.cs | 2 +- .Lib9c.Tests/Delegation/TestDelegatee.cs | 2 +- Lib9c/Addresses.cs | 22 +++ Lib9c/Delegation/Delegatee.cs | 12 +- Lib9c/Delegation/IDelegatee.cs | 2 +- Lib9c/Delegation/UnbondingSet.cs | 4 +- Lib9c/Model/Guild/Guild.cs | 4 +- Lib9c/Module/Guild/GuildParticipantModule.cs | 2 +- .../Validator/ValidatorDelegateeModule.cs | 102 +++++++++++ .../Validator/ValidatorDelegatorModule.cs | 152 ++++++++++++++++ .../ValidatorDelegation/ValidatorDelegatee.cs | 102 +++++++++++ .../ValidatorDelegation/ValidatorDelegator.cs | 25 +++ Lib9c/ValidatorDelegation/ValidatorList.cs | 98 +++++++++++ .../ValidatorRepository.cs | 35 ++++ 16 files changed, 643 insertions(+), 105 deletions(-) create mode 100644 Lib9c/Module/Validator/ValidatorDelegateeModule.cs create mode 100644 Lib9c/Module/Validator/ValidatorDelegatorModule.cs create mode 100644 Lib9c/ValidatorDelegation/ValidatorDelegatee.cs create mode 100644 Lib9c/ValidatorDelegation/ValidatorDelegator.cs create mode 100644 Lib9c/ValidatorDelegation/ValidatorList.cs create mode 100644 Lib9c/ValidatorDelegation/ValidatorRepository.cs diff --git a/.Lib9c.Tests/Delegation/DelegateeTest.cs b/.Lib9c.Tests/Delegation/DelegateeTest.cs index abf4739499..54b21fec93 100644 --- a/.Lib9c.Tests/Delegation/DelegateeTest.cs +++ b/.Lib9c.Tests/Delegation/DelegateeTest.cs @@ -22,7 +22,7 @@ public void Ctor() var address = new Address("0xe8327129891e1A0B2E3F0bfa295777912295942a"); var delegatee = new TestDelegatee(address, _fixture.Repository); Assert.Equal(address, delegatee.Address); - Assert.Equal(DelegationFixture.TestCurrency, delegatee.Currency); + Assert.Equal(DelegationFixture.TestCurrency, delegatee.DelegationCurrency); Assert.Equal(3, delegatee.UnbondingPeriod); Assert.Equal(new byte[] { 0x01 }, delegatee.DelegateeId); } @@ -33,7 +33,7 @@ public void CtorWithBencoded() var address = new Address("0xe8327129891e1A0B2E3F0bfa295777912295942a"); var delegatee = _fixture.TestDelegatee1; var delegator = _fixture.TestDelegator1; - delegatee.Bond(delegator, delegatee.Currency * 10, 10L); + delegatee.Bond(delegator, delegatee.DelegationCurrency * 10, 10L); var delegateeRecon = new TestDelegatee(address, delegatee.Bencoded, delegatee.Repository); Assert.Equal(address, delegateeRecon.Address); @@ -59,9 +59,9 @@ public void Bond() var share1 = BigInteger.Zero; var share2 = BigInteger.Zero; var totalShare = BigInteger.Zero; - var totalBonding = testDelegatee.Currency * 0; + var totalBonding = testDelegatee.DelegationCurrency * 0; - var bonding = testDelegatee.Currency * 10; + var bonding = testDelegatee.DelegationCurrency * 10; var share = testDelegatee.ShareToBond(bonding); share1 += share; totalShare += share; @@ -75,7 +75,7 @@ public void Bond() Assert.Equal(totalShare, testDelegatee.TotalShares); Assert.Equal(totalBonding, testDelegatee.TotalDelegated); - bonding = testDelegatee.Currency * 20; + bonding = testDelegatee.DelegationCurrency * 20; share = testDelegatee.ShareToBond(bonding); share1 += share; totalShare += share; @@ -88,7 +88,7 @@ public void Bond() Assert.Equal(totalShare, testDelegatee.TotalShares); Assert.Equal(totalBonding, testDelegatee.TotalDelegated); - bonding = testDelegatee.Currency * 30; + bonding = testDelegatee.DelegationCurrency * 30; share = testDelegatee.ShareToBond(bonding); share2 += share; totalShare += share; @@ -113,7 +113,7 @@ public void CannotBondInvalidDelegator() Assert.Throws( () => testDelegatee.Bond( - dummyDelegator, testDelegatee.Currency * 10, 10L)); + dummyDelegator, testDelegatee.DelegationCurrency * 10, 10L)); } [Fact] @@ -139,16 +139,16 @@ public void Unbond() var share1 = BigInteger.Zero; var share2 = BigInteger.Zero; var totalShares = BigInteger.Zero; - var totalDelegated = testDelegatee.Currency * 0; + var totalDelegated = testDelegatee.DelegationCurrency * 0; - var bonding = testDelegatee.Currency * 100; + var bonding = testDelegatee.DelegationCurrency * 100; var share = testDelegatee.ShareToBond(bonding); share1 += share; totalShares += share; totalDelegated += bonding; testDelegatee.Bond(testDelegator1, bonding, 1L); - bonding = testDelegatee.Currency * 50; + bonding = testDelegatee.DelegationCurrency * 50; share = testDelegatee.ShareToBond(bonding); share2 += share; totalShares += share; diff --git a/.Lib9c.Tests/Delegation/DelegatorTest.cs b/.Lib9c.Tests/Delegation/DelegatorTest.cs index 01c37f40b2..ee1b0837f2 100644 --- a/.Lib9c.Tests/Delegation/DelegatorTest.cs +++ b/.Lib9c.Tests/Delegation/DelegatorTest.cs @@ -29,8 +29,8 @@ public void CtorWithBencoded() var repo = _fixture.Repository; var delegator = _fixture.TestDelegator1; var delegatee = _fixture.TestDelegatee1; - repo.MintAsset(delegator.Address, delegatee.Currency * 100); - delegator.Delegate(delegatee, delegatee.Currency * 10, 10L); + repo.MintAsset(delegator.Address, delegatee.DelegationCurrency * 100); + delegator.Delegate(delegatee, delegatee.DelegationCurrency * 10, 10L); var delegatorRecon = new TestDelegator(delegator.Address, delegator.Bencoded, delegator.Repository); Assert.Equal(delegator.Address, delegatorRecon.Address); @@ -44,14 +44,14 @@ public void Delegate() var delegator = _fixture.TestDelegator1; var delegatee1 = _fixture.TestDelegatee1; var delegatee2 = _fixture.TestDelegatee2; - var delegatorInitialBalance = delegatee1.Currency * 100; + var delegatorInitialBalance = delegatee1.DelegationCurrency * 100; repo.MintAsset(delegator.Address, delegatorInitialBalance); - var delegateFAV = delegatee1.Currency * 10; + var delegateFAV = delegatee1.DelegationCurrency * 10; var delegateShare = delegatee1.ShareToBond(delegateFAV); delegator.Delegate(delegatee1, delegateFAV, 1L); - var delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.Currency); - var delegateeBalance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.Currency); + var delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.DelegationCurrency); + var delegateeBalance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.DelegationCurrency); var share = repo.GetBond(delegatee1, delegator.Address).Share; Assert.Equal(delegatorInitialBalance - delegateFAV, delegatorBalance); Assert.Equal(delegateFAV, delegateeBalance); @@ -61,11 +61,11 @@ public void Delegate() Assert.Equal(delegator.Address, Assert.Single(delegatee1.Delegators)); Assert.Equal(delegatee1.Address, Assert.Single(delegator.Delegatees)); - var delegateFAV2 = delegatee1.Currency * 20; + var delegateFAV2 = delegatee1.DelegationCurrency * 20; var delegateShare2 = delegatee1.ShareToBond(delegateFAV2); delegator.Delegate(delegatee1, delegateFAV2, 2L); - delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.Currency); - delegateeBalance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.Currency); + delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.DelegationCurrency); + delegateeBalance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.DelegationCurrency); share = repo.GetBond(delegatee1, delegator.Address).Share; Assert.Equal(delegatorInitialBalance - delegateFAV - delegateFAV2, delegatorBalance); Assert.Equal(delegateFAV + delegateFAV2, delegateeBalance); @@ -76,8 +76,8 @@ public void Delegate() Assert.Equal(delegatee1.Address, Assert.Single(delegator.Delegatees)); delegator.Delegate(delegatee2, delegateFAV, 3L); - delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee2.Currency); - delegateeBalance = repo.World.GetBalance(delegatee2.DelegationPoolAddress, delegatee2.Currency); + delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee2.DelegationCurrency); + delegateeBalance = repo.World.GetBalance(delegatee2.DelegationPoolAddress, delegatee2.DelegationCurrency); share = repo.GetBond(delegatee2, delegator.Address).Share; Assert.Equal(delegatorInitialBalance - delegateFAV * 2 - delegateFAV2, delegatorBalance); Assert.Equal(delegateFAV, delegateeBalance); @@ -95,16 +95,16 @@ public void Undelegate() var repo = _fixture.Repository; var delegator = _fixture.TestDelegator1; var delegatee = _fixture.TestDelegatee1; - var delegatorInitialBalance = delegatee.Currency * 100; + var delegatorInitialBalance = delegatee.DelegationCurrency * 100; repo.MintAsset(delegator.Address, delegatorInitialBalance); - var delegatingFAV = delegatee.Currency * 10; + var delegatingFAV = delegatee.DelegationCurrency * 10; delegator.Delegate(delegatee, delegatingFAV, 10L); var initialShare = repo.GetBond(delegatee, delegator.Address).Share; var undelegatingShare = initialShare / 3; var undelegatingFAV = delegatee.FAVToUnbond(undelegatingShare); delegator.Undelegate(delegatee, undelegatingShare, 10L); - var delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.Currency); - var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.DelegationCurrency); + var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); var share1 = repo.GetBond(delegatee, delegator.Address).Share; var unbondLockIn = repo.GetUnbondLockIn(delegatee, delegator.Address); var unbondingSet = repo.GetUnbondingSet(); @@ -127,15 +127,15 @@ public void Undelegate() undelegatingShare = repo.GetBond(delegatee, delegator.Address).Share; var undelegatingFAV2 = delegatee.FAVToUnbond(undelegatingShare); delegator.Undelegate(delegatee, undelegatingShare, 12L); - delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.Currency); - delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.DelegationCurrency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); var share2 = repo.GetBond(delegatee, delegator.Address).Share; unbondLockIn = repo.GetUnbondLockIn(delegatee, delegator.Address); unbondingSet = repo.GetUnbondingSet(); Assert.Equal(delegatorInitialBalance - delegatingFAV, delegatorBalance); Assert.Equal(delegatingFAV, delegateeBalance); Assert.Equal(share1 - undelegatingShare, share2); - Assert.Equal(delegatee.Currency * 0, delegatee.TotalDelegated); + Assert.Equal(delegatee.DelegationCurrency * 0, delegatee.TotalDelegated); Assert.Equal(System.Numerics.BigInteger.Zero, delegatee.TotalShares); Assert.Empty(delegator.Delegatees); Assert.Empty(delegatee.Delegators); @@ -143,8 +143,8 @@ public void Undelegate() Assert.Equal(2, unbondLockIn.Entries.Count); unbondLockIn = unbondLockIn.Release(10L + delegatee.UnbondingPeriod - 1); - delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.Currency); - delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.DelegationCurrency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); Assert.Equal(2, unbondLockIn.Entries.Count); entriesByExpireHeight = unbondLockIn.Entries.ElementAt(0); Assert.Equal(10L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); @@ -164,8 +164,8 @@ public void Undelegate() Assert.Equal(delegatingFAV, delegateeBalance); unbondLockIn = unbondLockIn.Release(10L + delegatee.UnbondingPeriod); - delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.Currency); - delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.DelegationCurrency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); entriesByExpireHeight = Assert.Single(unbondLockIn.Entries); Assert.Equal(12L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); entry = Assert.Single(entriesByExpireHeight.Value); @@ -177,11 +177,11 @@ public void Undelegate() Assert.Equal(delegatingFAV - undelegatingFAV, delegateeBalance); unbondLockIn = unbondLockIn.Release(12L + delegatee.UnbondingPeriod); - delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.Currency); - delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.DelegationCurrency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); Assert.Empty(unbondLockIn.Entries); Assert.Equal(delegatorInitialBalance, delegatorBalance); - Assert.Equal(delegatee.Currency * 0, delegateeBalance); + Assert.Equal(delegatee.DelegationCurrency * 0, delegateeBalance); } [Fact] @@ -191,9 +191,9 @@ public void Redelegate() var delegator = _fixture.TestDelegator1; var delegatee1 = _fixture.TestDelegatee1; var delegatee2 = _fixture.TestDelegatee2; - var delegatorInitialBalance = delegatee1.Currency * 100; + var delegatorInitialBalance = delegatee1.DelegationCurrency * 100; repo.MintAsset(delegator.Address, delegatorInitialBalance); - var delegatingFAV = delegatee1.Currency * 10; + var delegatingFAV = delegatee1.DelegationCurrency * 10; delegator.Delegate(delegatee1, delegatingFAV, 1L); Assert.Equal(delegatee1.Address, Assert.Single(delegator.Delegatees)); var initialShare = repo.GetBond(delegatee1, delegator.Address).Share; @@ -201,9 +201,9 @@ public void Redelegate() var redelegatingFAV = delegatee1.FAVToUnbond(redelegatingShare); var redelegatedDstShare = delegatee2.ShareToBond(redelegatingFAV); delegator.Redelegate(delegatee1, delegatee2, redelegatingShare, 10L); - var delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.Currency); - var delegatee1Balance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.Currency); - var delegatee2Balance = repo.World.GetBalance(delegatee2.DelegationPoolAddress, delegatee2.Currency); + var delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.DelegationCurrency); + var delegatee1Balance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.DelegationCurrency); + var delegatee2Balance = repo.World.GetBalance(delegatee2.DelegationPoolAddress, delegatee2.DelegationCurrency); var share1 = repo.GetBond(delegatee1, delegator.Address).Share; var share2 = repo.GetBond(delegatee2, delegator.Address).Share; var rebondGrace = repo.GetRebondGrace(delegatee1, delegator.Address); @@ -233,9 +233,9 @@ public void Redelegate() var redelegatingFAV2 = delegatee1.FAVToUnbond(redelegatingShare2); var redelegatedDstShare2 = delegatee2.ShareToBond(redelegatingFAV2); delegator.Redelegate(delegatee1, delegatee2, redelegatingShare2, 12L); - delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.Currency); - delegatee1Balance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.Currency); - delegatee2Balance = repo.World.GetBalance(delegatee2.DelegationPoolAddress, delegatee2.Currency); + delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.DelegationCurrency); + delegatee1Balance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.DelegationCurrency); + delegatee2Balance = repo.World.GetBalance(delegatee2.DelegationPoolAddress, delegatee2.DelegationCurrency); share1 = repo.GetBond(delegatee1, delegator.Address).Share; share2 = repo.GetBond(delegatee2, delegator.Address).Share; rebondGrace = repo.GetRebondGrace(delegatee1, delegator.Address); @@ -294,26 +294,26 @@ public void RewardOnDelegate() var delegator1 = _fixture.TestDelegator1; var delegator2 = _fixture.TestDelegator2; var delegatee = _fixture.TestDelegatee1; - var delegatorInitialBalance = delegatee.Currency * 100; + var delegatorInitialBalance = delegatee.DelegationCurrency * 100; repo.MintAsset(delegator1.Address, delegatorInitialBalance); repo.MintAsset(delegator2.Address, delegatorInitialBalance); - var reward = delegatee.Currency * 100; + var reward = delegatee.DelegationCurrency * 100; repo.MintAsset(delegatee.RewardDistributorAddress, reward); // EndBlock after delegatee's reward repo.AddLumpSumRewards(delegatee, 10L, reward); - var delegatingFAV1 = delegatee.Currency * 10; + var delegatingFAV1 = delegatee.DelegationCurrency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 10L); - var delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); - var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); + var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); var share1 = repo.GetBond(delegatee, delegator1.Address).Share; Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); - var delegatingFAV2 = delegatee.Currency * 20; + var delegatingFAV2 = delegatee.DelegationCurrency * 20; delegator2.Delegate(delegatee, delegatingFAV2, 10L); - var delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); - delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); var share2 = repo.GetBond(delegatee, delegator2.Address).Share; Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); @@ -323,10 +323,10 @@ public void RewardOnDelegate() // EndBlock after delegatee's reward repo.AddLumpSumRewards(delegatee, 10L, reward); - delegatingFAV1 = delegatee.Currency * 10; + delegatingFAV1 = delegatee.DelegationCurrency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 11L); - delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); - var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); + delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); + var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); var reward1 = (reward * share1).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 * 2 + reward1, delegator1Balance); @@ -334,8 +334,8 @@ public void RewardOnDelegate() Assert.Equal(reward * 2 - reward1, rewardPoolBalance); delegator2.Delegate(delegatee, delegatingFAV2, 11L); - delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); - rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); + delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); + rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); var reward2 = (reward * share2).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 * 2 + reward1, delegator1Balance); @@ -350,26 +350,26 @@ public void RewardOnUndelegate() var delegator1 = _fixture.TestDelegator1; var delegator2 = _fixture.TestDelegator2; var delegatee = _fixture.TestDelegatee1; - var delegatorInitialBalance = delegatee.Currency * 100; + var delegatorInitialBalance = delegatee.DelegationCurrency * 100; repo.MintAsset(delegator1.Address, delegatorInitialBalance); repo.MintAsset(delegator2.Address, delegatorInitialBalance); - var reward = delegatee.Currency * 100; + var reward = delegatee.DelegationCurrency * 100; repo.MintAsset(delegatee.RewardDistributorAddress, reward); // EndBlock after delegatee's reward repo.AddLumpSumRewards(delegatee, 10L, reward); - var delegatingFAV1 = delegatee.Currency * 10; + var delegatingFAV1 = delegatee.DelegationCurrency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 10L); - var delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); - var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); + var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); var share1 = repo.GetBond(delegatee, delegator1.Address).Share; Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); - var delegatingFAV2 = delegatee.Currency * 20; + var delegatingFAV2 = delegatee.DelegationCurrency * 20; delegator2.Delegate(delegatee, delegatingFAV2, 10L); - var delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); - delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); var share2 = repo.GetBond(delegatee, delegator2.Address).Share; Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); @@ -381,8 +381,8 @@ public void RewardOnUndelegate() var shareToUndelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; delegator1.Undelegate(delegatee, shareToUndelegate, 11L); - delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); - var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); + delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); + var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); var reward1 = (reward * share1).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); @@ -391,8 +391,8 @@ public void RewardOnUndelegate() shareToUndelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.Undelegate(delegatee, shareToUndelegate, 11L); - delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); - rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); + delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); + rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); var reward2 = (reward * share2).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); @@ -408,26 +408,26 @@ public void RewardOnRedelegate() var delegator2 = _fixture.TestDelegator2; var delegatee = _fixture.TestDelegatee1; var dstDelegatee = _fixture.TestDelegatee2; - var delegatorInitialBalance = delegatee.Currency * 100; + var delegatorInitialBalance = delegatee.DelegationCurrency * 100; repo.MintAsset(delegator1.Address, delegatorInitialBalance); repo.MintAsset(delegator2.Address, delegatorInitialBalance); - var reward = delegatee.Currency * 100; + var reward = delegatee.DelegationCurrency * 100; repo.MintAsset(delegatee.RewardDistributorAddress, reward); // EndBlock after delegatee's reward repo.AddLumpSumRewards(delegatee, 10L, reward); - var delegatingFAV1 = delegatee.Currency * 10; + var delegatingFAV1 = delegatee.DelegationCurrency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 10L); - var delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); - var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); + var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); var share1 = repo.GetBond(delegatee, delegator1.Address).Share; Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); - var delegatingFAV2 = delegatee.Currency * 20; + var delegatingFAV2 = delegatee.DelegationCurrency * 20; delegator2.Delegate(delegatee, delegatingFAV2, 10L); - var delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); - delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); var share2 = repo.GetBond(delegatee, delegator2.Address).Share; Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); @@ -439,8 +439,8 @@ public void RewardOnRedelegate() var shareToRedelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; delegator1.Redelegate(delegatee, dstDelegatee, shareToRedelegate, 11L); - delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); - var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); + delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); + var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); var reward1 = (reward * share1).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); @@ -449,8 +449,8 @@ public void RewardOnRedelegate() shareToRedelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.Redelegate(delegatee, dstDelegatee, shareToRedelegate, 11L); - delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); - rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); + delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); + rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); var reward2 = (reward * share2).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); @@ -466,26 +466,26 @@ public void RewardOnClaim() var delegator2 = _fixture.TestDelegator2; var delegatee = _fixture.TestDelegatee1; var dstDelegatee = _fixture.TestDelegatee2; - var delegatorInitialBalance = delegatee.Currency * 100; + var delegatorInitialBalance = delegatee.DelegationCurrency * 100; repo.MintAsset(delegator1.Address, delegatorInitialBalance); repo.MintAsset(delegator2.Address, delegatorInitialBalance); - var reward = delegatee.Currency * 100; + var reward = delegatee.DelegationCurrency * 100; repo.MintAsset(delegatee.RewardDistributorAddress, reward); // EndBlock after delegatee's reward repo.AddLumpSumRewards(delegatee, 10L, reward); - var delegatingFAV1 = delegatee.Currency * 10; + var delegatingFAV1 = delegatee.DelegationCurrency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 10L); - var delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); - var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); + var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); var share1 = repo.GetBond(delegatee, delegator1.Address).Share; Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); - var delegatingFAV2 = delegatee.Currency * 20; + var delegatingFAV2 = delegatee.DelegationCurrency * 20; delegator2.Delegate(delegatee, delegatingFAV2, 10L); - var delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); - delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.Currency); + var delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); + delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); var share2 = repo.GetBond(delegatee, delegator2.Address).Share; Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); @@ -497,8 +497,8 @@ public void RewardOnClaim() var shareToRedelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; delegator1.ClaimReward(delegatee, 11L); - delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.Currency); - var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); + delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); + var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); var reward1 = (reward * share1).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); @@ -507,8 +507,8 @@ public void RewardOnClaim() shareToRedelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.ClaimReward(delegatee, 11L); - delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.Currency); - rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.Currency); + delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); + rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); var reward2 = (reward * share2).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); diff --git a/.Lib9c.Tests/Delegation/DummyDelegatee.cs b/.Lib9c.Tests/Delegation/DummyDelegatee.cs index 26551a34d0..1febdc41e6 100644 --- a/.Lib9c.Tests/Delegation/DummyDelegatee.cs +++ b/.Lib9c.Tests/Delegation/DummyDelegatee.cs @@ -10,7 +10,7 @@ public DummyDelegatee(Address address, IDelegationRepository repository) { } - public override Currency Currency => DelegationFixture.TestCurrency; + public override Currency DelegationCurrency => DelegationFixture.TestCurrency; public override Currency RewardCurrency => DelegationFixture.TestCurrency; diff --git a/.Lib9c.Tests/Delegation/TestDelegatee.cs b/.Lib9c.Tests/Delegation/TestDelegatee.cs index d4c3594fe3..7a46d1f8fb 100644 --- a/.Lib9c.Tests/Delegation/TestDelegatee.cs +++ b/.Lib9c.Tests/Delegation/TestDelegatee.cs @@ -17,7 +17,7 @@ public TestDelegatee(Address address, IValue bencoded, IDelegationRepository rep { } - public override Currency Currency => DelegationFixture.TestCurrency; + public override Currency DelegationCurrency => DelegationFixture.TestCurrency; public override Currency RewardCurrency => DelegationFixture.TestCurrency; diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index 2a1adaee1d..49909eef72 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -130,6 +130,28 @@ public static readonly Address LumpSumRewardsRecord #endregion + #region Validator + + /// + /// An address of an account having . + /// + public static readonly Address ValidatorList + = new Address("0000000000000000000000000000000000000400"); + + /// + /// An address of an account having . + /// + public static readonly Address ValidatorDelegatee + = new Address("0000000000000000000000000000000000000401"); + + /// + /// An address of an account having . + /// + public static readonly Address ValidatorDelegator + = new Address("0000000000000000000000000000000000000402"); + + #endregion + public static Address GetSheetAddress() where T : ISheet => GetSheetAddress(typeof(T).Name); public static Address GetSheetAddress(string sheetName) => TableSheet.Derive(sheetName); diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 49339ef8ba..3649c5829c 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -30,7 +30,7 @@ public Delegatee(Address address, IDelegationRepository? repository = null) { Address = address; Delegators = ImmutableSortedSet
.Empty; - TotalDelegated = Currency * 0; + TotalDelegated = DelegationCurrency * 0; TotalShares = BigInteger.Zero; _repository = repository; } @@ -57,7 +57,7 @@ private Delegatee( BigInteger totalShares, IDelegationRepository? repository) { - if (!totalDelegated.Currency.Equals(Currency)) + if (!totalDelegated.Currency.Equals(DelegationCurrency)) { throw new InvalidOperationException("Invalid currency."); } @@ -87,7 +87,7 @@ private Delegatee( public Address Address { get; } - public abstract Currency Currency { get; } + public abstract Currency DelegationCurrency { get; } public abstract Currency RewardCurrency { get; } @@ -146,7 +146,7 @@ public BigInteger Bond(T delegator, FungibleAssetValue fav, long height) CannotMutateRelationsWithoutRepository(delegator); DistributeReward(delegator, height); - if (!fav.Currency.Equals(Currency)) + if (!fav.Currency.Equals(DelegationCurrency)) { throw new InvalidOperationException( "Cannot bond with invalid currency."); @@ -237,9 +237,11 @@ public bool Equals(IDelegatee? other) || (other is Delegatee delegatee && (GetType() != delegatee.GetType()) && Address.Equals(delegatee.Address) - && Currency.Equals(delegatee.Currency) + && DelegationCurrency.Equals(delegatee.DelegationCurrency) + && RewardCurrency.Equals(delegatee.RewardCurrency) && DelegationPoolAddress.Equals(delegatee.DelegationPoolAddress) && UnbondingPeriod == delegatee.UnbondingPeriod + && RewardCollectorAddress.Equals(delegatee.RewardCollectorAddress) && RewardDistributorAddress.Equals(delegatee.RewardDistributorAddress) && Delegators.SequenceEqual(delegatee.Delegators) && TotalDelegated.Equals(delegatee.TotalDelegated) diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index 6312c22fc1..3a504215b7 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -12,7 +12,7 @@ public interface IDelegatee : IBencodable, IEquatable { Address Address { get; } - Currency Currency { get; } + Currency DelegationCurrency { get; } Currency RewardCurrency { get; } diff --git a/Lib9c/Delegation/UnbondingSet.cs b/Lib9c/Delegation/UnbondingSet.cs index 8823081b6e..301d264668 100644 --- a/Lib9c/Delegation/UnbondingSet.cs +++ b/Lib9c/Delegation/UnbondingSet.cs @@ -67,8 +67,8 @@ private UnbondingSet( public static Address Address => new Address( ImmutableArray.Create( - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55)); public ImmutableSortedDictionary> Unbondings { get; } diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index a174086537..4c4e09706f 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -53,7 +53,7 @@ public Guild(List list, Currency rewardCurrency, IDelegationRepository repositor } } - public override Currency Currency => Currencies.GuildGold; + public override Currency DelegationCurrency => Currencies.GuildGold; public override Currency RewardCurrency { get; } @@ -61,7 +61,7 @@ public Guild(List list, Currency rewardCurrency, IDelegationRepository repositor public override long UnbondingPeriod => 75600L; - public override byte[] DelegateeId => new byte[] { 0x047 }; // `G` + public override byte[] DelegateeId => new byte[] { 0x47 }; // `G` public override int MaxUnbondLockInEntries => 10; diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index e8244ac30e..768808447f 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -235,7 +235,7 @@ public static IWorld Redelegate( var srcGuild = world.TryGetGuild(srcGuildAddress, repo, out var s) ? s : throw new InvalidOperationException("The guild does not exist."); - var dstGuild = world.TryGetGuild(srcGuildAddress, repo, out var d) + var dstGuild = world.TryGetGuild(dstGuildAddress, repo, out var d) ? d : throw new InvalidOperationException("The guild does not exist."); guildParticipant.Redelegate(srcGuild, dstGuild, share, context.BlockIndex); diff --git a/Lib9c/Module/Validator/ValidatorDelegateeModule.cs b/Lib9c/Module/Validator/ValidatorDelegateeModule.cs new file mode 100644 index 0000000000..1e86512c94 --- /dev/null +++ b/Lib9c/Module/Validator/ValidatorDelegateeModule.cs @@ -0,0 +1,102 @@ +#nullable enable +using System; +using System.Diagnostics.CodeAnalysis; +using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Action; +using Nekoyume.Extensions; +using Nekoyume.ValidatorDelegation; + +namespace Nekoyume.Module.Validator +{ + public static class ValidatorDelegateeModule + { + public static ValidatorDelegatee GetValidatorDelegatee(this IWorldState worldState, Address address) + => worldState.TryGetValidatorDelegatee(address, out var validatorDelegatee) + ? validatorDelegatee + : throw new FailedLoadStateException("There is no such validator delegatee"); + + public static ValidatorDelegatee GetValidatorDelegatee( + this IWorldState worldState, Address address, ValidatorRepository repository) + => worldState.TryGetValidatorDelegatee(address, repository, out var validatorDelegatee) + ? validatorDelegatee + : throw new FailedLoadStateException("There is no such validator delegatee"); + + public static bool TryGetValidatorDelegatee( + this IWorldState worldState, + Address address, + [NotNullWhen(true)] out ValidatorDelegatee? validatorDelegatee) + { + try + { + var value = worldState.GetAccountState(Addresses.ValidatorDelegatee).GetState(address); + if (!(value is List list)) + { + validatorDelegatee = null; + return false; + } + + validatorDelegatee = new ValidatorDelegatee(address, list, worldState.GetGoldCurrency()); + return true; + } + catch + { + validatorDelegatee = null; + return false; + } + } + + public static bool TryGetValidatorDelegatee( + this IWorldState worldState, + Address address, + ValidatorRepository repository, + [NotNullWhen(true)] out ValidatorDelegatee? validatorDelegatee) + { + try + { + var value = worldState.GetAccountState(Addresses.ValidatorDelegatee).GetState(address); + if (!(value is List list)) + { + validatorDelegatee = null; + return false; + } + + validatorDelegatee = new ValidatorDelegatee(address, list, worldState.GetGoldCurrency(), repository); + return true; + } + catch + { + validatorDelegatee = null; + return false; + } + } + + public static IWorld SetValidatorDelegatee(this IWorld world, ValidatorDelegatee validatorDelegatee) + => world.MutateAccount( + Addresses.ValidatorDelegatee, + account => account.SetState(validatorDelegatee.Address, validatorDelegatee.Bencoded)); + + public static IWorld CreateValidatorDelegatee(this IWorld world, IActionContext context, PublicKey publicKey) + { + var signer = context.Signer; + + if (!publicKey.Address.Equals(signer)) + { + throw new ArgumentException("The public key does not match the signer."); + } + + if (world.TryGetValidatorDelegatee(context.Signer, out _)) + { + throw new InvalidOperationException("The signer already has a validator delegatee."); + } + + return world.MutateAccount( + Addresses.ValidatorDelegatee, + account => account.SetState( + signer, + new ValidatorDelegatee(signer, publicKey, world.GetGoldCurrency()).Bencoded)); + } + } +} diff --git a/Lib9c/Module/Validator/ValidatorDelegatorModule.cs b/Lib9c/Module/Validator/ValidatorDelegatorModule.cs new file mode 100644 index 0000000000..f2bc5042b8 --- /dev/null +++ b/Lib9c/Module/Validator/ValidatorDelegatorModule.cs @@ -0,0 +1,152 @@ +#nullable enable +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Types.Assets; +using Nekoyume.Extensions; +using Nekoyume.ValidatorDelegation; + +namespace Nekoyume.Module.Validator +{ + public static class ValidatorDelegatorModule + { + public static IWorld Delegate( + this IWorld world, + IActionContext context, + Address address, + FungibleAssetValue fav) + { + var repo = new ValidatorRepository(world, context); + + var validatorDelegator = world.GetValidatorDelegator(context.Signer, repo); + var validatorDelegatee = world.GetValidatorDelegatee(address, repo); + validatorDelegator.Delegate(validatorDelegatee, fav, context.BlockIndex); + + return repo.World + .SetValidatorDelegatee(validatorDelegatee) + .SetValidatorDelegator(validatorDelegator); + } + + public static IWorld Undelegate( + this IWorld world, + IActionContext context, + Address address, + BigInteger share) + { + var repo = new ValidatorRepository(world, context); + + var validatorDelegator = world.GetValidatorDelegator(context.Signer, repo); + var validatorDelegatee = world.GetValidatorDelegatee(address, repo); + validatorDelegator.Undelegate(validatorDelegatee, share, context.BlockIndex); + + return repo.World + .SetValidatorDelegatee(validatorDelegatee) + .SetValidatorDelegator(validatorDelegator); + } + + public static IWorld Redelegate( + this IWorld world, + IActionContext context, + Address srcAddress, + Address dstAddress, + BigInteger share) + { + var repo = new ValidatorRepository(world, context); + + var validatorDelegator = world.GetValidatorDelegator(context.Signer, repo); + var srcValidatorDelegatee = world.GetValidatorDelegatee(srcAddress, repo); + var dstValidatorDelegatee = world.GetValidatorDelegatee(dstAddress, repo); + validatorDelegator.Redelegate(srcValidatorDelegatee, dstValidatorDelegatee, share, context.BlockIndex); + + return repo.World + .SetValidatorDelegatee(srcValidatorDelegatee) + .SetValidatorDelegatee(dstValidatorDelegatee) + .SetValidatorDelegator(validatorDelegator); + } + + public static IWorld ClaimReward( + this IWorld world, + IActionContext context, + Address address) + { + var repo = new ValidatorRepository(world, context); + + var validatorDelegator = world.GetValidatorDelegator(context.Signer, repo); + var validatorDelegatee = world.GetValidatorDelegatee(address, repo); + + validatorDelegator.ClaimReward(validatorDelegatee, context.BlockIndex); + + return repo.World + .SetValidatorDelegatee(validatorDelegatee) + .SetValidatorDelegator(validatorDelegator); + } + + public static ValidatorDelegator GetValidatorDelegator(this IWorldState worldState, Address address) + => worldState.TryGetValidatorDelegator(address, out var validatorDelegator) + ? validatorDelegator + : new ValidatorDelegator(address); + + public static ValidatorDelegator GetValidatorDelegator( + this IWorldState worldState, Address address, ValidatorRepository repository) + => worldState.TryGetValidatorDelegator(address, repository, out var validatorDelegator) + ? validatorDelegator + : new ValidatorDelegator(address, repository); + + public static bool TryGetValidatorDelegator( + this IWorldState worldState, + Address address, + [NotNullWhen(true)] out ValidatorDelegator? validatorDelegator) + { + try + { + var value = worldState.GetAccountState(Addresses.ValidatorDelegator).GetState(address); + if (!(value is List list)) + { + validatorDelegator = null; + return false; + } + + validatorDelegator = new ValidatorDelegator(address, list); + return true; + } + catch + { + validatorDelegator = null; + return false; + } + } + + public static bool TryGetValidatorDelegator( + this IWorldState worldState, + Address address, + ValidatorRepository repository, + [NotNullWhen(true)] out ValidatorDelegator? validatorDelegator) + { + try + { + var value = worldState.GetAccountState(Addresses.ValidatorDelegator).GetState(address); + if (!(value is List list)) + { + validatorDelegator = null; + return false; + } + + validatorDelegator = new ValidatorDelegator(address, list, repository); + return true; + } + catch + { + validatorDelegator = null; + return false; + } + } + + private static IWorld SetValidatorDelegator(this IWorld world, ValidatorDelegator validatorDelegator) + => world.MutateAccount( + Addresses.ValidatorDelegator, + account => account.SetState(validatorDelegator.Address, validatorDelegator.Bencoded)); + } +} diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs new file mode 100644 index 0000000000..c08f666480 --- /dev/null +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -0,0 +1,102 @@ +#nullable enable +using System; +using System.Collections.Immutable; +using System.Numerics; +using Bencodex; +using Bencodex.Types; +using Lib9c; +using Libplanet.Crypto; +using Libplanet.Types.Assets; +using Libplanet.Types.Consensus; +using Nekoyume.Delegation; + +namespace Nekoyume.ValidatorDelegation +{ + public class ValidatorDelegatee : Delegatee, IEquatable, IBencodable + { + public ValidatorDelegatee(Address address, PublicKey publicKey, Currency rewardCurrency, ValidatorRepository? repository = null) + : base(address, repository) + { + if (!address.Equals(publicKey.Address)) + { + throw new ArgumentException("The address and the public key do not match."); + } + + Publickey = publicKey; + IsBonded = false; + RewardCurrency = rewardCurrency; + } + + public ValidatorDelegatee(Address address, IValue bencoded, Currency rewardCurrency, ValidatorRepository? repository = null) + : this(address, (List)bencoded, rewardCurrency, repository) + { + } + + public ValidatorDelegatee(Address address, List bencoded, Currency rewardCurrency, ValidatorRepository? repository = null) + : base(address, bencoded[0], repository) + { + Publickey = new PublicKey(((Binary)bencoded[1]).ByteArray); + IsBonded = (Bencodex.Types.Boolean)bencoded[2]; + RewardCurrency = rewardCurrency; + } + + public override byte[] DelegateeId => new byte[] { 0x56 }; // `V` + + public override Address DelegationPoolAddress => IsBonded + ? BondedPoolAddress + : UnbondedPoolAddress; + + public override Currency DelegationCurrency => Currencies.GuildGold; + + public override Currency RewardCurrency { get; } + + public override long UnbondingPeriod => 0L; + + public override int MaxUnbondLockInEntries => 10; + + public override int MaxRebondGraceEntries => 10; + + public new List Bencoded => base.Bencoded + .Add(Publickey.Format(true)) + .Add(IsBonded); + + IValue IBencodable.Bencoded => Bencoded; + + public PublicKey Publickey { get; } + + public bool IsBonded { get; private set; } + + public BigInteger Power => TotalDelegated.RawValue; + + public Validator Validator => new(Publickey, Power); + + public new BigInteger Bond(ValidatorDelegator delegator, FungibleAssetValue fav, long height) + { + BigInteger share = base.Bond(delegator, fav, height); + ((ValidatorRepository)Repository!).GetValidatorList().SetValidator(Validator); + return share; + } + + public new FungibleAssetValue Unbond(ValidatorDelegator delegator, BigInteger amount, long height) + { + FungibleAssetValue fav = base.Unbond(delegator, amount, height); + ((ValidatorRepository)Repository!).GetValidatorList().SetValidator(Validator); + return fav; + } + + public bool Equals(ValidatorDelegatee? other) + => base.Equals(other) + && Publickey.Equals(other.Publickey) + && IsBonded == other.IsBonded; + + public static Address BondedPoolAddress => new Address( + ImmutableArray.Create( + 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42)); + + public static Address UnbondedPoolAddress => new Address( + ImmutableArray.Create( + 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55)); + } +} diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegator.cs b/Lib9c/ValidatorDelegation/ValidatorDelegator.cs new file mode 100644 index 0000000000..9e9f6ec0d5 --- /dev/null +++ b/Lib9c/ValidatorDelegation/ValidatorDelegator.cs @@ -0,0 +1,25 @@ +#nullable enable +using Bencodex.Types; +using Libplanet.Crypto; +using Nekoyume.Delegation; + +namespace Nekoyume.ValidatorDelegation +{ + public class ValidatorDelegator : Delegator + { + public ValidatorDelegator(Address address, ValidatorRepository? repository = null) + : base(address, repository) + { + } + + public ValidatorDelegator(Address address, IValue bencoded, ValidatorRepository? repository = null) + : base(address, bencoded, repository) + { + } + + public ValidatorDelegator(Address address, List bencoded, ValidatorRepository? repository = null) + : base(address, bencoded, repository) + { + } + } +} diff --git a/Lib9c/ValidatorDelegation/ValidatorList.cs b/Lib9c/ValidatorDelegation/ValidatorList.cs new file mode 100644 index 0000000000..9b35adebb3 --- /dev/null +++ b/Lib9c/ValidatorDelegation/ValidatorList.cs @@ -0,0 +1,98 @@ +#nullable enable +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Bencodex; +using Bencodex.Types; +using Libplanet.Crypto; +using Libplanet.Types.Consensus; + +namespace Nekoyume.ValidatorDelegation +{ + public class ValidatorList : IBencodable + { + private static readonly IComparer _reversedComparer + = Comparer.Create((y, x) => new ValidatorComparer().Compare(x, y)); + + public ValidatorList() + : this(ImmutableList.Empty) + { + } + + public ValidatorList(IValue bencoded) + : this((List)bencoded) + { + } + + public ValidatorList(List bencoded) + : this(bencoded.Select(v => new Validator(v)).ToImmutableList()) + { + } + + + private ValidatorList(ImmutableList validators) + { + Validators = validators; + } + + public static Address Address => new Address( + ImmutableArray.Create( + 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56)); + + public ImmutableList Validators { get; } + + public static int MaxBondedSetSize => 100; + + public List Bencoded => new List(Validators.Select(v => v.Bencoded)); + + IValue IBencodable.Bencoded => Bencoded; + + public List GetBonded() => Validators.Take(MaxBondedSetSize).ToList(); + + public List GetUnbonded() => Validators.Skip(MaxBondedSetSize).ToList(); + + public ValidatorList SetValidator(Validator validator) + => RemoveValidator(validator.PublicKey).AddValidator(validator); + + private ValidatorList AddValidator(Validator validator) + { + int index = Validators.BinarySearch(validator, _reversedComparer); + return UpdateValidators(Validators.Insert(index < 0 ? ~index : index, validator)); + } + private ValidatorList RemoveValidator(PublicKey publicKey) + => UpdateValidators(Validators.RemoveAll(v => v.PublicKey.Equals(publicKey))); + + private ValidatorList UpdateValidators(ImmutableList validators) + => new ValidatorList(validators); + } + + public class ValidatorComparer : IComparer + { + public int Compare(Validator? x, Validator? y) + { + if (ReferenceEquals(x, y)) + { + return 0; + } + + if (x is null) + { + return -1; + } + + if (y is null) + { + return 1; + } + + int comparison = x.Power.CompareTo(y.Power); + if (comparison != 0) + { + return comparison; + } + + return x.OperatorAddress.CompareTo(y.OperatorAddress); + } + } +} diff --git a/Lib9c/ValidatorDelegation/ValidatorRepository.cs b/Lib9c/ValidatorDelegation/ValidatorRepository.cs new file mode 100644 index 0000000000..2dfdf62b85 --- /dev/null +++ b/Lib9c/ValidatorDelegation/ValidatorRepository.cs @@ -0,0 +1,35 @@ +#nullable enable +using Libplanet.Action.State; +using Libplanet.Action; +using Nekoyume.Delegation; +using Bencodex.Types; +using Libplanet.Crypto; + +namespace Nekoyume.ValidatorDelegation +{ + public class ValidatorRepository : DelegationRepository + { + private readonly Address validatorListAddress = Addresses.ValidatorList; + + private IAccount _validatorList; + + public ValidatorRepository(IWorld world, IActionContext context) + : base(world, context) + { + _validatorList = world.GetAccount(validatorListAddress); + } + + public ValidatorList GetValidatorList() + { + IValue? value = _validatorList.GetState(ValidatorList.Address); + return value is IValue bencoded + ? new ValidatorList(bencoded) + : new ValidatorList(); + } + + public void SetValidatorList(ValidatorList validatorList) + { + _validatorList = _validatorList.SetState(ValidatorList.Address, validatorList.Bencoded); + } + } +} From 95f7ea65e339c4f8572b236f0832103d94928ea5 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 22 Aug 2024 23:04:54 +0900 Subject: [PATCH 027/206] feat: Add actions for delegator --- .../Action/Validator/ClaimRewardValidator.cs | 52 +++++++++++++++ Lib9c/Action/Validator/DelegateValidator.cs | 58 +++++++++++++++++ Lib9c/Action/Validator/RedelegateValidator.cs | 64 +++++++++++++++++++ Lib9c/Action/Validator/UndelegateValidator.cs | 58 +++++++++++++++++ .../Validator/ValidatorDelegatorModule.cs | 8 +-- 5 files changed, 236 insertions(+), 4 deletions(-) create mode 100644 Lib9c/Action/Validator/ClaimRewardValidator.cs create mode 100644 Lib9c/Action/Validator/DelegateValidator.cs create mode 100644 Lib9c/Action/Validator/RedelegateValidator.cs create mode 100644 Lib9c/Action/Validator/UndelegateValidator.cs diff --git a/Lib9c/Action/Validator/ClaimRewardValidator.cs b/Lib9c/Action/Validator/ClaimRewardValidator.cs new file mode 100644 index 0000000000..167d1edfa6 --- /dev/null +++ b/Lib9c/Action/Validator/ClaimRewardValidator.cs @@ -0,0 +1,52 @@ +using System; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Crypto; +using Nekoyume.Module.Validator; + +namespace Nekoyume.Action.Validator +{ + public class ClaimRewardValidator : ActionBase + { + public const string TypeIdentifier = "claim_reward_validator"; + + private const string TargetKey = "t"; + + 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)); + + 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(); + } + + ValidatorDelegatee = new Address(values[0]); + } + + public override IWorld Execute(IActionContext context) + { + context.UseGas(1); + + var world = context.PreviousState; + + return world.ClaimRewardValidator(context, ValidatorDelegatee); + } + } +} diff --git a/Lib9c/Action/Validator/DelegateValidator.cs b/Lib9c/Action/Validator/DelegateValidator.cs new file mode 100644 index 0000000000..9bc6056270 --- /dev/null +++ b/Lib9c/Action/Validator/DelegateValidator.cs @@ -0,0 +1,58 @@ +using System; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Crypto; +using Libplanet.Types.Assets; +using Nekoyume.Module.Validator; + +namespace Nekoyume.Action.Validator +{ + public class DelegateValidator : ActionBase + { + public const string TypeIdentifier = "delegate_validator"; + + private const string TargetKey = "t"; + + public DelegateValidator() { } + + public DelegateValidator(Address validatorDelegatee, FungibleAssetValue fav) + { + ValidatorDelegatee = validatorDelegatee; + FAV = fav; + } + + public Address ValidatorDelegatee { get; private set; } + + public FungibleAssetValue FAV { get; private set; } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", List.Empty + .Add(ValidatorDelegatee.Bencoded) + .Add(FAV.Serialize())); + + 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(); + } + + ValidatorDelegatee = new Address(values[0]); + FAV = new FungibleAssetValue(values[1]); + } + + public override IWorld Execute(IActionContext context) + { + context.UseGas(1); + + var world = context.PreviousState; + + return world.DelegateValidator(context, ValidatorDelegatee, FAV); + } + } +} diff --git a/Lib9c/Action/Validator/RedelegateValidator.cs b/Lib9c/Action/Validator/RedelegateValidator.cs new file mode 100644 index 0000000000..5fd7bb1f93 --- /dev/null +++ b/Lib9c/Action/Validator/RedelegateValidator.cs @@ -0,0 +1,64 @@ +using System; +using System.Numerics; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Crypto; +using Nekoyume.Module.Validator; + +namespace Nekoyume.Action.Validator +{ + public class RedelegateValidator : ActionBase + { + public const string TypeIdentifier = "redelegate_validator"; + + private const string TargetKey = "t"; + + public RedelegateValidator() { } + + public RedelegateValidator( + Address srcValidatorDelegatee, Address dstValidatorDelegatee, BigInteger share) + { + SrcValidatorDelegatee = srcValidatorDelegatee; + DstValidatorDelegatee = dstValidatorDelegatee; + Share = share; + } + + public Address SrcValidatorDelegatee { get; private set; } + + public Address DstValidatorDelegatee { get; private set; } + + public BigInteger Share { get; private set; } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", List.Empty + .Add(SrcValidatorDelegatee.Bencoded) + .Add(DstValidatorDelegatee.Bencoded) + .Add(Share)); + + 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(); + } + + SrcValidatorDelegatee = new Address(values[0]); + DstValidatorDelegatee = new Address(values[1]); + Share = (Integer)values[2]; + } + + public override IWorld Execute(IActionContext context) + { + context.UseGas(1); + + var world = context.PreviousState; + + return world.RedelegateValidator(context, SrcValidatorDelegatee, DstValidatorDelegatee, Share); + } + } +} diff --git a/Lib9c/Action/Validator/UndelegateValidator.cs b/Lib9c/Action/Validator/UndelegateValidator.cs new file mode 100644 index 0000000000..96e9f5e3a5 --- /dev/null +++ b/Lib9c/Action/Validator/UndelegateValidator.cs @@ -0,0 +1,58 @@ +using System; +using System.Numerics; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Crypto; +using Nekoyume.Module.Validator; + +namespace Nekoyume.Action.Validator +{ + public class UndelegateValidator : ActionBase + { + public const string TypeIdentifier = "undelegate_validator"; + + private const string TargetKey = "t"; + + public UndelegateValidator() { } + + public UndelegateValidator(Address validatorDelegatee, BigInteger share) + { + ValidatorDelegatee = validatorDelegatee; + Share = share; + } + + public Address ValidatorDelegatee { get; private set; } + + public BigInteger Share { get; private set; } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", List.Empty + .Add(ValidatorDelegatee.Bencoded) + .Add(Share)); + + 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(); + } + + ValidatorDelegatee = new Address(values[0]); + Share = (Integer)values[1]; + } + + public override IWorld Execute(IActionContext context) + { + context.UseGas(1); + + var world = context.PreviousState; + + return world.UndelegateValidator(context, ValidatorDelegatee, Share); + } + } +} diff --git a/Lib9c/Module/Validator/ValidatorDelegatorModule.cs b/Lib9c/Module/Validator/ValidatorDelegatorModule.cs index f2bc5042b8..de29d38cd4 100644 --- a/Lib9c/Module/Validator/ValidatorDelegatorModule.cs +++ b/Lib9c/Module/Validator/ValidatorDelegatorModule.cs @@ -13,7 +13,7 @@ namespace Nekoyume.Module.Validator { public static class ValidatorDelegatorModule { - public static IWorld Delegate( + public static IWorld DelegateValidator( this IWorld world, IActionContext context, Address address, @@ -30,7 +30,7 @@ public static IWorld Delegate( .SetValidatorDelegator(validatorDelegator); } - public static IWorld Undelegate( + public static IWorld UndelegateValidator( this IWorld world, IActionContext context, Address address, @@ -47,7 +47,7 @@ public static IWorld Undelegate( .SetValidatorDelegator(validatorDelegator); } - public static IWorld Redelegate( + public static IWorld RedelegateValidator( this IWorld world, IActionContext context, Address srcAddress, @@ -67,7 +67,7 @@ public static IWorld Redelegate( .SetValidatorDelegator(validatorDelegator); } - public static IWorld ClaimReward( + public static IWorld ClaimRewardValidator( this IWorld world, IActionContext context, Address address) From 3d8fcec3c73e93091d40767df98257956818a5cb Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sun, 25 Aug 2024 10:12:32 +0900 Subject: [PATCH 028/206] feat: Update gas tracer --- .../CustomActionsDeserializableValidatorTest.cs | 2 +- .Lib9c.Tests/Action/ActionContext.cs | 16 +++++----------- .Lib9c.Tests/Action/Scenario/MeadScenarioTest.cs | 2 +- .../Action/Craft/UnlockCraftAction.cs | 2 +- Lib9c.DevExtensions/Action/Craft/UnlockRecipe.cs | 2 +- Lib9c.DevExtensions/Action/CreateArenaDummy.cs | 2 +- .../Action/CreateOrReplaceAvatar.cs | 2 +- Lib9c.DevExtensions/Action/CreateTestbed.cs | 2 +- Lib9c.DevExtensions/Action/FaucetCurrency.cs | 2 +- Lib9c.DevExtensions/Action/FaucetRune.cs | 2 +- Lib9c.DevExtensions/Action/ManipulateState.cs | 2 +- Lib9c.DevExtensions/Action/Stage/ClearStage.cs | 2 +- Lib9c/Action/ActivateCollection.cs | 2 +- Lib9c/Action/AddRedeemCode.cs | 2 +- .../AdventureBoss/ClaimAdventureBossReward.cs | 2 +- .../Action/AdventureBoss/ExploreAdventureBoss.cs | 2 +- Lib9c/Action/AdventureBoss/SweepAdventureBoss.cs | 2 +- Lib9c/Action/AdventureBoss/UnlockFloor.cs | 2 +- Lib9c/Action/AdventureBoss/Wanted.cs | 2 +- Lib9c/Action/ApprovePledge.cs | 2 +- Lib9c/Action/AuraSummon.cs | 2 +- Lib9c/Action/BattleArena.cs | 2 +- Lib9c/Action/BurnAsset.cs | 2 +- Lib9c/Action/Buy.cs | 2 +- Lib9c/Action/Buy7.cs | 2 +- Lib9c/Action/BuyMultiple.cs | 2 +- Lib9c/Action/BuyProduct.cs | 2 +- Lib9c/Action/CancelProductRegistration.cs | 2 +- Lib9c/Action/ChargeActionPoint.cs | 2 +- Lib9c/Action/ClaimItems.cs | 2 +- Lib9c/Action/ClaimRaidReward.cs | 2 +- Lib9c/Action/ClaimStakeReward.cs | 2 +- Lib9c/Action/ClaimWordBossKillReward.cs | 2 +- Lib9c/Action/CombinationConsumable.cs | 2 +- Lib9c/Action/CombinationConsumable5.cs | 2 +- Lib9c/Action/CombinationEquipment.cs | 2 +- Lib9c/Action/Coupons/IssueCoupons.cs | 2 +- Lib9c/Action/Coupons/RedeemCoupon.cs | 2 +- Lib9c/Action/Coupons/TransferCoupons.cs | 2 +- Lib9c/Action/CreateAvatar.cs | 2 +- Lib9c/Action/CreatePendingActivation.cs | 2 +- Lib9c/Action/CreatePendingActivations.cs | 2 +- Lib9c/Action/CreatePledge.cs | 2 +- Lib9c/Action/DailyReward.cs | 2 +- Lib9c/Action/DailyReward2.cs | 2 +- Lib9c/Action/EndPledge.cs | 2 +- Lib9c/Action/EventConsumableItemCrafts.cs | 2 +- Lib9c/Action/EventDungeonBattle.cs | 2 +- Lib9c/Action/EventMaterialItemCrafts.cs | 2 +- Lib9c/Action/Garages/BulkUnloadFromGarages.cs | 2 +- Lib9c/Action/Garages/DeliverToOthersGarages.cs | 2 +- Lib9c/Action/Garages/LoadIntoMyGarages.cs | 2 +- Lib9c/Action/Garages/UnloadFromMyGarages.cs | 2 +- Lib9c/Action/Grinding.cs | 2 +- Lib9c/Action/Guild/AcceptGuildApplication.cs | 2 +- Lib9c/Action/Guild/ApplyGuild.cs | 2 +- Lib9c/Action/Guild/BanGuildMember.cs | 2 +- Lib9c/Action/Guild/CancelGuildApplication.cs | 2 +- Lib9c/Action/Guild/MakeGuild.cs | 2 +- .../Guild/Migration/MigratePledgeToGuild.cs | 2 +- Lib9c/Action/Guild/QuitGuild.cs | 2 +- Lib9c/Action/Guild/RejectGuildApplication.cs | 2 +- Lib9c/Action/Guild/RemoveGuild.cs | 2 +- Lib9c/Action/Guild/UnbanGuildMember.cs | 2 +- Lib9c/Action/HackAndSlash.cs | 2 +- Lib9c/Action/HackAndSlashRandomBuff.cs | 2 +- Lib9c/Action/HackAndSlashSweep.cs | 2 +- Lib9c/Action/InitializeStates.cs | 2 +- Lib9c/Action/IssueToken.cs | 2 +- Lib9c/Action/IssueTokensFromGarage.cs | 2 +- Lib9c/Action/ItemEnhancement.cs | 2 +- Lib9c/Action/ItemEnhancement10.cs | 2 +- Lib9c/Action/ItemEnhancement11.cs | 2 +- Lib9c/Action/ItemEnhancement12.cs | 2 +- Lib9c/Action/ItemEnhancement13.cs | 2 +- Lib9c/Action/ItemEnhancement7.cs | 2 +- Lib9c/Action/ItemEnhancement9.cs | 2 +- Lib9c/Action/JoinArena.cs | 2 +- Lib9c/Action/JoinArena1.cs | 2 +- Lib9c/Action/JoinArena3.cs | 2 +- Lib9c/Action/MigrateAgentAvatar.cs | 2 +- Lib9c/Action/MigrateFee.cs | 2 +- Lib9c/Action/MigrateMonsterCollection.cs | 2 +- Lib9c/Action/MigrationActivatedAccountsState.cs | 2 +- Lib9c/Action/MintAssets.cs | 2 +- Lib9c/Action/PatchTableSheet.cs | 2 +- Lib9c/Action/PetEnhancement.cs | 2 +- Lib9c/Action/PrepareRewardAssets.cs | 2 +- Lib9c/Action/Raid.cs | 2 +- Lib9c/Action/RankingBattle.cs | 2 +- Lib9c/Action/RankingBattle11.cs | 2 +- Lib9c/Action/RapidCombination.cs | 2 +- Lib9c/Action/RapidCombination0.cs | 2 +- Lib9c/Action/RapidCombination5.cs | 2 +- Lib9c/Action/ReRegisterProduct.cs | 2 +- Lib9c/Action/RedeemCode.cs | 2 +- Lib9c/Action/RedeemCode2.cs | 2 +- Lib9c/Action/RegisterProduct.cs | 2 +- Lib9c/Action/RegisterProduct0.cs | 2 +- Lib9c/Action/RenewAdminState.cs | 2 +- Lib9c/Action/RequestPledge.cs | 2 +- Lib9c/Action/RetrieveAvatarAssets.cs | 2 +- Lib9c/Action/RewardGold.cs | 2 +- Lib9c/Action/RuneEnhancement.cs | 2 +- Lib9c/Action/RuneSummon.cs | 2 +- Lib9c/Action/SecureMiningReward.cs | 2 +- Lib9c/Action/Sell.cs | 2 +- Lib9c/Action/Sell6.cs | 2 +- Lib9c/Action/SellCancellation.cs | 2 +- Lib9c/Action/Stake.cs | 2 +- Lib9c/Action/Stake2.cs | 2 +- Lib9c/Action/TransferAsset.cs | 2 +- Lib9c/Action/TransferAssets.cs | 2 +- Lib9c/Action/UnlockEquipmentRecipe.cs | 2 +- Lib9c/Action/UnlockRuneSlot.cs | 2 +- Lib9c/Action/UnlockWorld.cs | 2 +- Lib9c/Action/UpdateSell.cs | 2 +- Lib9c/Action/ValidatorSetOperate.cs | 2 +- 118 files changed, 122 insertions(+), 128 deletions(-) diff --git a/.Lib9c.Miner.Tests/CustomActionsDeserializableValidatorTest.cs b/.Lib9c.Miner.Tests/CustomActionsDeserializableValidatorTest.cs index 104a8c1f5d..cf58ed01d1 100644 --- a/.Lib9c.Miner.Tests/CustomActionsDeserializableValidatorTest.cs +++ b/.Lib9c.Miner.Tests/CustomActionsDeserializableValidatorTest.cs @@ -49,7 +49,7 @@ public void LoadPlainValue(IValue plainValue) public IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); return context.PreviousState; } } diff --git a/.Lib9c.Tests/Action/ActionContext.cs b/.Lib9c.Tests/Action/ActionContext.cs index c366d29099..0323a0c918 100644 --- a/.Lib9c.Tests/Action/ActionContext.cs +++ b/.Lib9c.Tests/Action/ActionContext.cs @@ -7,14 +7,13 @@ namespace Lib9c.Tests.Action using Libplanet.Action.State; using Libplanet.Common; using Libplanet.Crypto; + using Libplanet.Types.Assets; using Libplanet.Types.Blocks; using Libplanet.Types.Evidence; using Libplanet.Types.Tx; public class ActionContext : IActionContext { - private long _gasUsed; - private IRandom _random = null; private IReadOnlyList _txs = null; @@ -35,6 +34,8 @@ public class ActionContext : IActionContext public int BlockProtocolVersion { get; set; } = BlockMetadata.CurrentProtocolVersion; + public BlockCommit LastCommit { get; set; } + public IWorld PreviousState { get; set; } public int RandomSeed { get; set; } @@ -43,6 +44,8 @@ public class ActionContext : IActionContext public bool IsPolicyAction { get; set; } + public FungibleAssetValue? MaxGasPrice { get; set; } + public IReadOnlyList Txs { get => _txs ?? ImmutableList.Empty; @@ -55,17 +58,8 @@ public IReadOnlyList Evidence set => _evs = value; } - public void UseGas(long gas) - { - _gasUsed += gas; - } - public IRandom GetRandom() => _random ?? new TestRandom(RandomSeed); - public long GasUsed() => _gasUsed; - - public long GasLimit() => 0; - // FIXME: Temporary measure to allow inheriting already mutated IRandom. public void SetRandom(IRandom random) { diff --git a/.Lib9c.Tests/Action/Scenario/MeadScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/MeadScenarioTest.cs index 0bf467e478..bb9ad8b992 100644 --- a/.Lib9c.Tests/Action/Scenario/MeadScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/MeadScenarioTest.cs @@ -119,7 +119,7 @@ bool IsTarget(Type type) long expectedGasLimit = action is ITransferAsset || action is ITransferAssets ? expectedTransferActionGasLimit : expectedActionGasLimit; - long gasUsed = actionContext.GasUsed(); + long gasUsed = GasTracer.GasUsed; Assert.True(expectedGasLimit == gasUsed, $"{action} invalid used gas. {gasUsed}"); } } diff --git a/Lib9c.DevExtensions/Action/Craft/UnlockCraftAction.cs b/Lib9c.DevExtensions/Action/Craft/UnlockCraftAction.cs index cdb8abace7..eb4632913e 100644 --- a/Lib9c.DevExtensions/Action/Craft/UnlockCraftAction.cs +++ b/Lib9c.DevExtensions/Action/Craft/UnlockCraftAction.cs @@ -24,7 +24,7 @@ public class UnlockCraftAction : GameAction public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; int targetStage; diff --git a/Lib9c.DevExtensions/Action/Craft/UnlockRecipe.cs b/Lib9c.DevExtensions/Action/Craft/UnlockRecipe.cs index 4f0d118a70..c6e6136917 100644 --- a/Lib9c.DevExtensions/Action/Craft/UnlockRecipe.cs +++ b/Lib9c.DevExtensions/Action/Craft/UnlockRecipe.cs @@ -20,7 +20,7 @@ public class UnlockRecipe : GameAction public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var recipeIdList = List.Empty; for (var i = 1; i <= TargetStage; i++) diff --git a/Lib9c.DevExtensions/Action/CreateArenaDummy.cs b/Lib9c.DevExtensions/Action/CreateArenaDummy.cs index 8e85d7892f..18b24d412b 100644 --- a/Lib9c.DevExtensions/Action/CreateArenaDummy.cs +++ b/Lib9c.DevExtensions/Action/CreateArenaDummy.cs @@ -53,7 +53,7 @@ protected override void LoadPlainValueInternal( public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; for (var i = 0; i < accountCount; i++) diff --git a/Lib9c.DevExtensions/Action/CreateOrReplaceAvatar.cs b/Lib9c.DevExtensions/Action/CreateOrReplaceAvatar.cs index 8f4b5bf291..f911ca519e 100644 --- a/Lib9c.DevExtensions/Action/CreateOrReplaceAvatar.cs +++ b/Lib9c.DevExtensions/Action/CreateOrReplaceAvatar.cs @@ -365,7 +365,7 @@ public CreateOrReplaceAvatar( public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var random = context.GetRandom(); return Execute( context.PreviousState, diff --git a/Lib9c.DevExtensions/Action/CreateTestbed.cs b/Lib9c.DevExtensions/Action/CreateTestbed.cs index cfd770563e..0c191cb1e6 100644 --- a/Lib9c.DevExtensions/Action/CreateTestbed.cs +++ b/Lib9c.DevExtensions/Action/CreateTestbed.cs @@ -70,7 +70,7 @@ protected override void LoadPlainValueInternal( public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var sellData = TestbedHelper.LoadData("TestbedSell"); var random = context.GetRandom(); var addedItemInfos = sellData.Items diff --git a/Lib9c.DevExtensions/Action/FaucetCurrency.cs b/Lib9c.DevExtensions/Action/FaucetCurrency.cs index b49ac362a6..dc11b7051f 100644 --- a/Lib9c.DevExtensions/Action/FaucetCurrency.cs +++ b/Lib9c.DevExtensions/Action/FaucetCurrency.cs @@ -23,7 +23,7 @@ public class FaucetCurrency : GameAction, IFaucetCurrency public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; if (FaucetNcg > 0) { diff --git a/Lib9c.DevExtensions/Action/FaucetRune.cs b/Lib9c.DevExtensions/Action/FaucetRune.cs index 8f936ff013..7532495963 100644 --- a/Lib9c.DevExtensions/Action/FaucetRune.cs +++ b/Lib9c.DevExtensions/Action/FaucetRune.cs @@ -25,7 +25,7 @@ public class FaucetRune : GameAction, IFaucetRune public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; if (!(FaucetRuneInfos is null)) { diff --git a/Lib9c.DevExtensions/Action/ManipulateState.cs b/Lib9c.DevExtensions/Action/ManipulateState.cs index 8da52b03f7..8fe0d41391 100644 --- a/Lib9c.DevExtensions/Action/ManipulateState.cs +++ b/Lib9c.DevExtensions/Action/ManipulateState.cs @@ -47,7 +47,7 @@ protected override void LoadPlainValueInternal( public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); return Execute(context, context.PreviousState, StateList, BalanceList); } diff --git a/Lib9c.DevExtensions/Action/Stage/ClearStage.cs b/Lib9c.DevExtensions/Action/Stage/ClearStage.cs index 68e2bbdc64..2077686d6c 100644 --- a/Lib9c.DevExtensions/Action/Stage/ClearStage.cs +++ b/Lib9c.DevExtensions/Action/Stage/ClearStage.cs @@ -22,7 +22,7 @@ public class ClearStage : GameAction public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var avatarState = states.GetAvatarState(AvatarAddress); if (avatarState is null || !avatarState.agentAddress.Equals(context.Signer)) diff --git a/Lib9c/Action/ActivateCollection.cs b/Lib9c/Action/ActivateCollection.cs index a1fc454ff9..b77f6fb992 100644 --- a/Lib9c/Action/ActivateCollection.cs +++ b/Lib9c/Action/ActivateCollection.cs @@ -25,7 +25,7 @@ public class ActivateCollection : GameAction public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); if (CollectionData.Count > MaxCollectionDataCount) { throw new ArgumentOutOfRangeException( diff --git a/Lib9c/Action/AddRedeemCode.cs b/Lib9c/Action/AddRedeemCode.cs index cd2b5411f5..7df674eac4 100644 --- a/Lib9c/Action/AddRedeemCode.cs +++ b/Lib9c/Action/AddRedeemCode.cs @@ -20,7 +20,7 @@ public class AddRedeemCode : GameAction, IAddRedeemCodeV1 public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; CheckPermission(context); diff --git a/Lib9c/Action/AdventureBoss/ClaimAdventureBossReward.cs b/Lib9c/Action/AdventureBoss/ClaimAdventureBossReward.cs index e4515e23a0..cfe7ea39ca 100644 --- a/Lib9c/Action/AdventureBoss/ClaimAdventureBossReward.cs +++ b/Lib9c/Action/AdventureBoss/ClaimAdventureBossReward.cs @@ -44,7 +44,7 @@ IImmutableDictionary plainValue public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var ncg = states.GetGoldCurrency(); diff --git a/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs b/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs index 8d3f455a35..de52497144 100644 --- a/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs +++ b/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs @@ -82,7 +82,7 @@ protected override void LoadPlainValueInternal( public override IWorld Execute(IActionContext context) { var addressesHex = $"[{context.Signer.ToHex()}, {AvatarAddress.ToHex()}]"; - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; // Validation diff --git a/Lib9c/Action/AdventureBoss/SweepAdventureBoss.cs b/Lib9c/Action/AdventureBoss/SweepAdventureBoss.cs index d3e41ff041..987190d911 100644 --- a/Lib9c/Action/AdventureBoss/SweepAdventureBoss.cs +++ b/Lib9c/Action/AdventureBoss/SweepAdventureBoss.cs @@ -60,7 +60,7 @@ protected override void LoadPlainValueInternal( public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); var states = context.PreviousState; diff --git a/Lib9c/Action/AdventureBoss/UnlockFloor.cs b/Lib9c/Action/AdventureBoss/UnlockFloor.cs index e6562ecd9c..85bc7258c7 100644 --- a/Lib9c/Action/AdventureBoss/UnlockFloor.cs +++ b/Lib9c/Action/AdventureBoss/UnlockFloor.cs @@ -48,7 +48,7 @@ protected override void LoadPlainValueInternal( public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var currency = states.GetGoldCurrency(); var latestSeason = states.GetLatestAdventureBossSeason(); diff --git a/Lib9c/Action/AdventureBoss/Wanted.cs b/Lib9c/Action/AdventureBoss/Wanted.cs index d0158f29f6..3903375b1e 100644 --- a/Lib9c/Action/AdventureBoss/Wanted.cs +++ b/Lib9c/Action/AdventureBoss/Wanted.cs @@ -48,7 +48,7 @@ IImmutableDictionary plainValue public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var currency = states.GetGoldCurrency(); var gameConfig = states.GetGameConfigState(); diff --git a/Lib9c/Action/ApprovePledge.cs b/Lib9c/Action/ApprovePledge.cs index f5bad206c9..7d69303d97 100644 --- a/Lib9c/Action/ApprovePledge.cs +++ b/Lib9c/Action/ApprovePledge.cs @@ -30,7 +30,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); Address signer = context.Signer; var states = context.PreviousState; var contractAddress = signer.GetPledgeAddress(); diff --git a/Lib9c/Action/AuraSummon.cs b/Lib9c/Action/AuraSummon.cs index f8830b558f..d6157ecf68 100644 --- a/Lib9c/Action/AuraSummon.cs +++ b/Lib9c/Action/AuraSummon.cs @@ -162,7 +162,7 @@ long blockIndex public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); diff --git a/Lib9c/Action/BattleArena.cs b/Lib9c/Action/BattleArena.cs index a802db8b61..8a63536b0d 100644 --- a/Lib9c/Action/BattleArena.cs +++ b/Lib9c/Action/BattleArena.cs @@ -95,7 +95,7 @@ protected override void LoadPlainValueInternal( public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); ValidateTicket(); var states = context.PreviousState; var addressesHex = GetSignerAndOtherAddressesHex( diff --git a/Lib9c/Action/BurnAsset.cs b/Lib9c/Action/BurnAsset.cs index be4cd4b858..9219ac91c4 100644 --- a/Lib9c/Action/BurnAsset.cs +++ b/Lib9c/Action/BurnAsset.cs @@ -31,7 +31,7 @@ public BurnAsset(Address owner, FungibleAssetValue amount, string memo) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); IWorld state = context.PreviousState; diff --git a/Lib9c/Action/Buy.cs b/Lib9c/Action/Buy.cs index 9787daf3af..b866ebf8f9 100644 --- a/Lib9c/Action/Buy.cs +++ b/Lib9c/Action/Buy.cs @@ -68,7 +68,7 @@ protected override void LoadPlainValueInternal(IImmutableDictionary MaxClaimDataCount) { diff --git a/Lib9c/Action/ClaimRaidReward.cs b/Lib9c/Action/ClaimRaidReward.cs index 9782ee95e6..a8df905997 100644 --- a/Lib9c/Action/ClaimRaidReward.cs +++ b/Lib9c/Action/ClaimRaidReward.cs @@ -35,7 +35,7 @@ public ClaimRaidReward(Address avatarAddress) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); IWorld states = context.PreviousState; var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); diff --git a/Lib9c/Action/ClaimStakeReward.cs b/Lib9c/Action/ClaimStakeReward.cs index 909e1ac12a..e5e1079aaa 100644 --- a/Lib9c/Action/ClaimStakeReward.cs +++ b/Lib9c/Action/ClaimStakeReward.cs @@ -54,7 +54,7 @@ protected override void LoadPlainValueInternal( public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); var stakeStateAddr = StakeState.DeriveAddress(context.Signer); diff --git a/Lib9c/Action/ClaimWordBossKillReward.cs b/Lib9c/Action/ClaimWordBossKillReward.cs index 027c78ae39..133cc1c25d 100644 --- a/Lib9c/Action/ClaimWordBossKillReward.cs +++ b/Lib9c/Action/ClaimWordBossKillReward.cs @@ -25,7 +25,7 @@ public class ClaimWordBossKillReward : GameAction, IClaimWordBossKillRewardV1 public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); IWorld states = context.PreviousState; Dictionary sheets = states.GetSheets(sheetTypes: new [] { diff --git a/Lib9c/Action/CombinationConsumable.cs b/Lib9c/Action/CombinationConsumable.cs index 272ef752f9..3f0cfed609 100644 --- a/Lib9c/Action/CombinationConsumable.cs +++ b/Lib9c/Action/CombinationConsumable.cs @@ -55,7 +55,7 @@ protected override void LoadPlainValueInternal(IImmutableDictionary rewards, Address recip public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; CheckPermission(context); diff --git a/Lib9c/Action/Coupons/RedeemCoupon.cs b/Lib9c/Action/Coupons/RedeemCoupon.cs index 53d5177663..86875022e7 100644 --- a/Lib9c/Action/Coupons/RedeemCoupon.cs +++ b/Lib9c/Action/Coupons/RedeemCoupon.cs @@ -31,7 +31,7 @@ public RedeemCoupon(Guid couponId, Address avatarAddress) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; if (!states.TryGetAvatarState( diff --git a/Lib9c/Action/Coupons/TransferCoupons.cs b/Lib9c/Action/Coupons/TransferCoupons.cs index f8a57fb872..5623c1f134 100644 --- a/Lib9c/Action/Coupons/TransferCoupons.cs +++ b/Lib9c/Action/Coupons/TransferCoupons.cs @@ -31,7 +31,7 @@ public IImmutableDictionary> CouponsPerRecipient public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var signerWallet = states.GetCouponWallet(context.Signer); var orderedRecipients = CouponsPerRecipient.OrderBy(pair => pair.Key); diff --git a/Lib9c/Action/CreateAvatar.cs b/Lib9c/Action/CreateAvatar.cs index c2a8e7339d..9a4a0fbcf0 100644 --- a/Lib9c/Action/CreateAvatar.cs +++ b/Lib9c/Action/CreateAvatar.cs @@ -76,7 +76,7 @@ protected override void LoadPlainValueInternal(IImmutableDictionary states) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); CheckPermission(context); var state = context.PreviousState; diff --git a/Lib9c/Action/CreatePledge.cs b/Lib9c/Action/CreatePledge.cs index f4d9a0f80f..cbaa8bc089 100644 --- a/Lib9c/Action/CreatePledge.cs +++ b/Lib9c/Action/CreatePledge.cs @@ -48,7 +48,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); CheckPermission(context); var states = context.PreviousState; var mead = Mead * Currencies.Mead; diff --git a/Lib9c/Action/DailyReward.cs b/Lib9c/Action/DailyReward.cs index 6fb6ff8b43..46c8d41056 100644 --- a/Lib9c/Action/DailyReward.cs +++ b/Lib9c/Action/DailyReward.cs @@ -32,7 +32,7 @@ public class DailyReward : GameAction, IDailyRewardV1 public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); var started = DateTimeOffset.UtcNow; diff --git a/Lib9c/Action/DailyReward2.cs b/Lib9c/Action/DailyReward2.cs index cc95089a30..ff3b509a36 100644 --- a/Lib9c/Action/DailyReward2.cs +++ b/Lib9c/Action/DailyReward2.cs @@ -29,7 +29,7 @@ public class DailyReward2 : GameAction, IDailyRewardV1 public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); IActionContext ctx = context; var states = ctx.PreviousState; diff --git a/Lib9c/Action/EndPledge.cs b/Lib9c/Action/EndPledge.cs index c0197214e9..980a1597d8 100644 --- a/Lib9c/Action/EndPledge.cs +++ b/Lib9c/Action/EndPledge.cs @@ -28,7 +28,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); Address signer = context.Signer; var states = context.PreviousState; var contractAddress = AgentAddress.GetPledgeAddress(); diff --git a/Lib9c/Action/EventConsumableItemCrafts.cs b/Lib9c/Action/EventConsumableItemCrafts.cs index 675cd168e7..e0961ac1ec 100644 --- a/Lib9c/Action/EventConsumableItemCrafts.cs +++ b/Lib9c/Action/EventConsumableItemCrafts.cs @@ -81,7 +81,7 @@ protected override void LoadPlainValueInternal(IImmutableDictionary public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); IActionContext ctx = context; var states = ctx.PreviousState; var slotAddress = avatarAddress.Derive( diff --git a/Lib9c/Action/ItemEnhancement9.cs b/Lib9c/Action/ItemEnhancement9.cs index e3a6c29d91..2686b7020b 100644 --- a/Lib9c/Action/ItemEnhancement9.cs +++ b/Lib9c/Action/ItemEnhancement9.cs @@ -117,7 +117,7 @@ protected override void LoadPlainValueInternal(IImmutableDictionary 2) diff --git a/Lib9c/Action/JoinArena3.cs b/Lib9c/Action/JoinArena3.cs index c6641ca923..6d0afa20da 100644 --- a/Lib9c/Action/JoinArena3.cs +++ b/Lib9c/Action/JoinArena3.cs @@ -69,7 +69,7 @@ protected override void LoadPlainValueInternal( public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); var started = DateTimeOffset.UtcNow; diff --git a/Lib9c/Action/MigrateAgentAvatar.cs b/Lib9c/Action/MigrateAgentAvatar.cs index e4dff011c9..028ab162a1 100644 --- a/Lib9c/Action/MigrateAgentAvatar.cs +++ b/Lib9c/Action/MigrateAgentAvatar.cs @@ -42,7 +42,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); #if !LIB9C_DEV_EXTENSIONS && !UNITY_EDITOR if (context.Signer != Operator) diff --git a/Lib9c/Action/MigrateFee.cs b/Lib9c/Action/MigrateFee.cs index ac4bbb666b..5edc8f9456 100644 --- a/Lib9c/Action/MigrateFee.cs +++ b/Lib9c/Action/MigrateFee.cs @@ -67,7 +67,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); CheckPermission(context); var states = context.PreviousState; diff --git a/Lib9c/Action/MigrateMonsterCollection.cs b/Lib9c/Action/MigrateMonsterCollection.cs index a79c0906b7..82c1b22d6b 100644 --- a/Lib9c/Action/MigrateMonsterCollection.cs +++ b/Lib9c/Action/MigrateMonsterCollection.cs @@ -48,7 +48,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); var started = DateTimeOffset.UtcNow; diff --git a/Lib9c/Action/MigrationActivatedAccountsState.cs b/Lib9c/Action/MigrationActivatedAccountsState.cs index 4c5842daa0..169814aaa1 100644 --- a/Lib9c/Action/MigrationActivatedAccountsState.cs +++ b/Lib9c/Action/MigrationActivatedAccountsState.cs @@ -18,7 +18,7 @@ public class MigrationActivatedAccountsState : GameAction, IMigrationActivatedAc { public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; CheckPermission(context); diff --git a/Lib9c/Action/MintAssets.cs b/Lib9c/Action/MintAssets.cs index 2e1d30f863..f0c097c0b1 100644 --- a/Lib9c/Action/MintAssets.cs +++ b/Lib9c/Action/MintAssets.cs @@ -36,7 +36,7 @@ public MintAssets(IEnumerable specs, string? memo) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); if (MintSpecs is null) { diff --git a/Lib9c/Action/PatchTableSheet.cs b/Lib9c/Action/PatchTableSheet.cs index fd70e54b74..fef0c3f233 100644 --- a/Lib9c/Action/PatchTableSheet.cs +++ b/Lib9c/Action/PatchTableSheet.cs @@ -38,7 +38,7 @@ public class PatchTableSheet : GameAction, IPatchTableSheetV1 public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); IActionContext ctx = context; var states = ctx.PreviousState; var sheetAddress = Addresses.TableSheet.Derive(TableName); diff --git a/Lib9c/Action/PetEnhancement.cs b/Lib9c/Action/PetEnhancement.cs index 16a2e7095a..dcf1739972 100644 --- a/Lib9c/Action/PetEnhancement.cs +++ b/Lib9c/Action/PetEnhancement.cs @@ -43,7 +43,7 @@ protected override void LoadPlainValueInternal( public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var addresses = GetSignerAndOtherAddressesHex(context, AvatarAddress); // NOTE: The `AvatarAddress` must contained in `Signer`'s `AgentState.avatarAddresses`. diff --git a/Lib9c/Action/PrepareRewardAssets.cs b/Lib9c/Action/PrepareRewardAssets.cs index e5e1441b3d..12a53743f1 100644 --- a/Lib9c/Action/PrepareRewardAssets.cs +++ b/Lib9c/Action/PrepareRewardAssets.cs @@ -45,7 +45,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); IWorld states = context.PreviousState; CheckPermission(context); diff --git a/Lib9c/Action/Raid.cs b/Lib9c/Action/Raid.cs index 4775f90581..ab7a737749 100644 --- a/Lib9c/Action/Raid.cs +++ b/Lib9c/Action/Raid.cs @@ -48,7 +48,7 @@ public class Raid : GameAction, IRaidV2 public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); IWorld states = context.PreviousState; var addressHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); var started = DateTimeOffset.UtcNow; diff --git a/Lib9c/Action/RankingBattle.cs b/Lib9c/Action/RankingBattle.cs index 56272890ba..706d577607 100644 --- a/Lib9c/Action/RankingBattle.cs +++ b/Lib9c/Action/RankingBattle.cs @@ -47,7 +47,7 @@ public class RankingBattle : GameAction, IRankingBattleV2 public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var ctx = context; var states = ctx.PreviousState; var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress, enemyAddress); diff --git a/Lib9c/Action/RankingBattle11.cs b/Lib9c/Action/RankingBattle11.cs index 3fb8a58151..e2c2716906 100644 --- a/Lib9c/Action/RankingBattle11.cs +++ b/Lib9c/Action/RankingBattle11.cs @@ -53,7 +53,7 @@ public class RankingBattle11 : GameAction, IRankingBattleV2 public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); IActionContext ctx = context; var states = ctx.PreviousState; diff --git a/Lib9c/Action/RapidCombination.cs b/Lib9c/Action/RapidCombination.cs index b8c001e817..877efd843f 100644 --- a/Lib9c/Action/RapidCombination.cs +++ b/Lib9c/Action/RapidCombination.cs @@ -33,7 +33,7 @@ public class RapidCombination : GameAction, IRapidCombinationV2 public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); var started = DateTimeOffset.UtcNow; diff --git a/Lib9c/Action/RapidCombination0.cs b/Lib9c/Action/RapidCombination0.cs index 6cba135383..cd439a5bda 100644 --- a/Lib9c/Action/RapidCombination0.cs +++ b/Lib9c/Action/RapidCombination0.cs @@ -53,7 +53,7 @@ public override IValue Serialize() => public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var slotAddress = avatarAddress.Derive( string.Format( diff --git a/Lib9c/Action/RapidCombination5.cs b/Lib9c/Action/RapidCombination5.cs index c0b9f1c231..b7bbdce416 100644 --- a/Lib9c/Action/RapidCombination5.cs +++ b/Lib9c/Action/RapidCombination5.cs @@ -56,7 +56,7 @@ public override IValue Serialize() => public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var slotAddress = avatarAddress.Derive( string.Format( diff --git a/Lib9c/Action/ReRegisterProduct.cs b/Lib9c/Action/ReRegisterProduct.cs index a6dad6fb4e..592e57cec3 100644 --- a/Lib9c/Action/ReRegisterProduct.cs +++ b/Lib9c/Action/ReRegisterProduct.cs @@ -28,7 +28,7 @@ public class ReRegisterProduct : GameAction public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); IWorld states = context.PreviousState; if (!ReRegisterInfos.Any()) diff --git a/Lib9c/Action/RedeemCode.cs b/Lib9c/Action/RedeemCode.cs index 1e89ca519e..31fb1e20ef 100644 --- a/Lib9c/Action/RedeemCode.cs +++ b/Lib9c/Action/RedeemCode.cs @@ -44,7 +44,7 @@ public RedeemCode(string code, Address avatarAddress) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); var started = DateTimeOffset.UtcNow; diff --git a/Lib9c/Action/RedeemCode2.cs b/Lib9c/Action/RedeemCode2.cs index 2ffe4bb36a..c0c93c3c71 100644 --- a/Lib9c/Action/RedeemCode2.cs +++ b/Lib9c/Action/RedeemCode2.cs @@ -40,7 +40,7 @@ public RedeemCode2(string code, Address avatarAddress) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); diff --git a/Lib9c/Action/RegisterProduct.cs b/Lib9c/Action/RegisterProduct.cs index 2b0578aa41..dfc1b463fa 100644 --- a/Lib9c/Action/RegisterProduct.cs +++ b/Lib9c/Action/RegisterProduct.cs @@ -43,7 +43,7 @@ public override IWorld Execute(IActionContext context) { var sw = new Stopwatch(); - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; sw.Start(); diff --git a/Lib9c/Action/RegisterProduct0.cs b/Lib9c/Action/RegisterProduct0.cs index 3d0fdb30b6..02c6221494 100644 --- a/Lib9c/Action/RegisterProduct0.cs +++ b/Lib9c/Action/RegisterProduct0.cs @@ -30,7 +30,7 @@ public class RegisterProduct0 : GameAction public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; if (!RegisterInfos.Any()) diff --git a/Lib9c/Action/RenewAdminState.cs b/Lib9c/Action/RenewAdminState.cs index d1f19dc15c..05766f5622 100644 --- a/Lib9c/Action/RenewAdminState.cs +++ b/Lib9c/Action/RenewAdminState.cs @@ -35,7 +35,7 @@ public RenewAdminState(long newValidUntil) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; if (TryGetAdminState(context, out AdminState adminState)) diff --git a/Lib9c/Action/RequestPledge.cs b/Lib9c/Action/RequestPledge.cs index 5e74498027..de18748018 100644 --- a/Lib9c/Action/RequestPledge.cs +++ b/Lib9c/Action/RequestPledge.cs @@ -37,7 +37,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var contractAddress = AgentAddress.GetPledgeAddress(); if (states.TryGetLegacyState(contractAddress, out List _)) diff --git a/Lib9c/Action/RetrieveAvatarAssets.cs b/Lib9c/Action/RetrieveAvatarAssets.cs index 719a3068da..721c832418 100644 --- a/Lib9c/Action/RetrieveAvatarAssets.cs +++ b/Lib9c/Action/RetrieveAvatarAssets.cs @@ -36,7 +36,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); Address signer = context.Signer; var state = context.PreviousState; var agentState = state.GetAgentState(signer); diff --git a/Lib9c/Action/RewardGold.cs b/Lib9c/Action/RewardGold.cs index a4a60fcafb..5e9baa39d8 100644 --- a/Lib9c/Action/RewardGold.cs +++ b/Lib9c/Action/RewardGold.cs @@ -37,7 +37,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; states = TransferMead(context, states); states = GenesisGoldDistribution(context, states); diff --git a/Lib9c/Action/RuneEnhancement.cs b/Lib9c/Action/RuneEnhancement.cs index af6bf6718f..6d10145a19 100644 --- a/Lib9c/Action/RuneEnhancement.cs +++ b/Lib9c/Action/RuneEnhancement.cs @@ -59,7 +59,7 @@ protected override void LoadPlainValueInternal( public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; if (!states.TryGetAvatarState(context.Signer, AvatarAddress, out _)) diff --git a/Lib9c/Action/RuneSummon.cs b/Lib9c/Action/RuneSummon.cs index bc903b826b..e459e88884 100644 --- a/Lib9c/Action/RuneSummon.cs +++ b/Lib9c/Action/RuneSummon.cs @@ -38,7 +38,7 @@ public class RuneSummon : GameAction, IRuneSummonV1 public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); diff --git a/Lib9c/Action/SecureMiningReward.cs b/Lib9c/Action/SecureMiningReward.cs index 8e9593d76f..672cc02f40 100644 --- a/Lib9c/Action/SecureMiningReward.cs +++ b/Lib9c/Action/SecureMiningReward.cs @@ -57,7 +57,7 @@ public SecureMiningReward(Address recipient) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); IWorld state = context.PreviousState; CheckPermission(context); diff --git a/Lib9c/Action/Sell.cs b/Lib9c/Action/Sell.cs index 8f885e2770..f45dc78f20 100644 --- a/Lib9c/Action/Sell.cs +++ b/Lib9c/Action/Sell.cs @@ -64,7 +64,7 @@ protected override void LoadPlainValueInternal( public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; Address shopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); Address itemAddress = Addresses.GetItemAddress(tradableId); diff --git a/Lib9c/Action/Sell6.cs b/Lib9c/Action/Sell6.cs index e07fde2116..08b82743b6 100644 --- a/Lib9c/Action/Sell6.cs +++ b/Lib9c/Action/Sell6.cs @@ -61,7 +61,7 @@ protected override void LoadPlainValueInternal( public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); diff --git a/Lib9c/Action/SellCancellation.cs b/Lib9c/Action/SellCancellation.cs index 350f10269e..f0923a0a23 100644 --- a/Lib9c/Action/SellCancellation.cs +++ b/Lib9c/Action/SellCancellation.cs @@ -90,7 +90,7 @@ protected override void LoadPlainValueInternal(IImmutableDictionary RecipientsCapacity) diff --git a/Lib9c/Action/UnlockEquipmentRecipe.cs b/Lib9c/Action/UnlockEquipmentRecipe.cs index 1993528aae..4cc13051f2 100644 --- a/Lib9c/Action/UnlockEquipmentRecipe.cs +++ b/Lib9c/Action/UnlockEquipmentRecipe.cs @@ -29,7 +29,7 @@ public class UnlockEquipmentRecipe : GameAction, IUnlockEquipmentRecipeV1 public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; var unlockedRecipeIdsAddress = AvatarAddress.Derive("recipe_ids"); var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); diff --git a/Lib9c/Action/UnlockRuneSlot.cs b/Lib9c/Action/UnlockRuneSlot.cs index 11ce62bc9d..dbb8b58c2f 100644 --- a/Lib9c/Action/UnlockRuneSlot.cs +++ b/Lib9c/Action/UnlockRuneSlot.cs @@ -45,7 +45,7 @@ protected override void LoadPlainValueInternal(IImmutableDictionary public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); if (Error != null) { throw new InvalidOperationException(Error); From 51bd924c78caf55d130a59d0574bd11b9c79e605 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sun, 25 Aug 2024 10:14:05 +0900 Subject: [PATCH 029/206] feat: Update for validator reward --- .../ValidatorDelegation/AllocateReward.cs | 182 ++++++++++++++++++ .../ClaimRewardValidator.cs | 6 +- .../DelegateValidator.cs | 6 +- .../ValidatorDelegation/PromoteValidator.cs | 60 ++++++ .../ValidatorDelegation/RecordProposer.cs | 40 ++++ .../RedelegateValidator.cs | 6 +- .../UndelegateValidator.cs | 6 +- Lib9c/Addresses.cs | 18 +- Lib9c/Delegation/Delegatee.cs | 22 +-- Lib9c/Delegation/DelegationRepository.cs | 32 +-- Lib9c/Delegation/Delegator.cs | 14 +- Lib9c/Model/Guild/Guild.cs | 2 +- Lib9c/Model/Guild/GuildParticipant.cs | 2 +- .../ValidatorDelegateeModule.cs | 2 +- .../ValidatorDelegatorModule.cs | 2 +- .../ValidatorListModule.cs | 79 ++++++++ Lib9c/ValidatorDelegation/ProposerInfo.cs | 40 ++++ .../ValidatorDelegation/ValidatorDelegatee.cs | 60 +++++- .../ValidatorRepository.cs | 4 + 19 files changed, 524 insertions(+), 59 deletions(-) create mode 100644 Lib9c/Action/ValidatorDelegation/AllocateReward.cs rename Lib9c/Action/{Validator => ValidatorDelegation}/ClaimRewardValidator.cs (91%) rename Lib9c/Action/{Validator => ValidatorDelegation}/DelegateValidator.cs (92%) create mode 100644 Lib9c/Action/ValidatorDelegation/PromoteValidator.cs create mode 100644 Lib9c/Action/ValidatorDelegation/RecordProposer.cs rename Lib9c/Action/{Validator => ValidatorDelegation}/RedelegateValidator.cs (93%) rename Lib9c/Action/{Validator => ValidatorDelegation}/UndelegateValidator.cs (92%) rename Lib9c/Module/{Validator => ValidatorDelegation}/ValidatorDelegateeModule.cs (98%) rename Lib9c/Module/{Validator => ValidatorDelegation}/ValidatorDelegatorModule.cs (99%) create mode 100644 Lib9c/Module/ValidatorDelegation/ValidatorListModule.cs create mode 100644 Lib9c/ValidatorDelegation/ProposerInfo.cs diff --git a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs new file mode 100644 index 0000000000..dc80666494 --- /dev/null +++ b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs @@ -0,0 +1,182 @@ +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Types.Assets; +using Libplanet.Types.Consensus; +using Nekoyume.ValidatorDelegation; +using Nekoyume.Module.ValidatorDelegation; +using Nekoyume.Module; + +namespace Nekoyume.Action.ValidatorDelegation +{ + public class AllocateReward : ActionBase + { + public const string TypeIdentifier = "distribute_validators"; + + private const string TargetKey = "t"; + + public AllocateReward() + { + } + + public override IValue PlainValue => Dictionary.Empty; + + public override void LoadPlainValue(IValue plainValue) + { + } + + public override IWorld Execute(IActionContext context) + { + var world = context.PreviousState; + var rewardCurrency = world.GetGoldCurrency(); + + var proposerInfo = world.GetProposerInfo(); + + world = DistributeProposerReward( + world, + context, + rewardCurrency, + proposerInfo, + context.LastCommit.Votes); + + world = DistributeValidatorReward( + world, + context, + rewardCurrency, + context.LastCommit.Votes); + + var communityFund = world.GetBalance(Addresses.RewardPool, rewardCurrency); + + if (communityFund.Sign > 0) + { + world = world.TransferAsset( + context, + Addresses.RewardPool, + Addresses.CommunityPool, + communityFund); + } + + return world; + } + + internal static IWorld DistributeProposerReward( + IWorld states, + IActionContext ctx, + Currency rewardCurrency, + ProposerInfo proposerInfo, + IEnumerable lastVotes) + { + FungibleAssetValue blockReward = states.GetBalance( + Addresses.RewardPool, rewardCurrency); + + if (proposerInfo.BlockIndex != ctx.BlockIndex - 1) + { + return states; + } + + if (blockReward.Sign <= 0) + { + return states; + } + + BigInteger votePowerNumerator + = lastVotes.Aggregate( + BigInteger.Zero, + (total, next) + => total + + (next.Flag == VoteFlag.PreCommit + ? next.ValidatorPower ?? BigInteger.Zero + : BigInteger.Zero)); + + BigInteger votePowerDenominator + = lastVotes.Aggregate( + BigInteger.Zero, + (total, next) + => total + (next.ValidatorPower ?? BigInteger.Zero)); + + if (votePowerDenominator == BigInteger.Zero) + { + return states; + } + + var baseProposerReward + = (blockReward * ValidatorDelegatee.BaseProposerRewardNumerator) + .DivRem(ValidatorDelegatee.BaseProposerRewardDenominator).Quotient; + var bonusProposerReward + = (blockReward * votePowerNumerator * ValidatorDelegatee.BonusProposerRewardNumerator) + .DivRem(votePowerDenominator * ValidatorDelegatee.BonusProposerRewardDenominator).Quotient; + FungibleAssetValue proposerReward = baseProposerReward + bonusProposerReward; + + states = states.TransferAsset( + ctx, + Addresses.RewardPool, + proposerInfo.Proposer, + proposerReward); + + return states; + } + + internal static IWorld DistributeValidatorReward( + IWorld states, + IActionContext ctx, + Currency rewardCurrency, + IEnumerable lastVotes) + { + long blockHeight = ctx.BlockIndex; + var repo = new ValidatorRepository(states, ctx); + + FungibleAssetValue rewardToAllocate + = states.GetBalance(Addresses.RewardPool, rewardCurrency); + + if (rewardToAllocate.Sign <= 0) + { + return states; + } + + BigInteger validatorSetPower + = lastVotes.Aggregate( + BigInteger.Zero, + (total, next) => total + (next.ValidatorPower ?? BigInteger.Zero)); + + if (validatorSetPower == BigInteger.Zero) + { + return states; + } + + foreach (Vote vote in lastVotes) + { + if (vote.Flag == VoteFlag.Null || vote.Flag == VoteFlag.Unknown) + { + continue; + } + + if (states.TryGetValidatorDelegatee( + vote.ValidatorPublicKey.Address, repo, out var validatorDelegatee)) + { + continue; + } + + BigInteger validatorPower = vote.ValidatorPower ?? BigInteger.Zero; + + if (validatorPower == BigInteger.Zero) + { + continue; + } + + validatorDelegatee.AllocateReward( + rewardToAllocate, + validatorPower, + validatorSetPower, + Addresses.RewardPool, + blockHeight); + + states = validatorDelegatee.Repository.World; + } + + return states; + } + } +} diff --git a/Lib9c/Action/Validator/ClaimRewardValidator.cs b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs similarity index 91% rename from Lib9c/Action/Validator/ClaimRewardValidator.cs rename to Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs index 167d1edfa6..2a83384be3 100644 --- a/Lib9c/Action/Validator/ClaimRewardValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs @@ -3,9 +3,9 @@ using Libplanet.Action.State; using Libplanet.Action; using Libplanet.Crypto; -using Nekoyume.Module.Validator; +using Nekoyume.Module.ValidatorDelegation; -namespace Nekoyume.Action.Validator +namespace Nekoyume.Action.ValidatorDelegation { public class ClaimRewardValidator : ActionBase { @@ -42,7 +42,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var world = context.PreviousState; diff --git a/Lib9c/Action/Validator/DelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs similarity index 92% rename from Lib9c/Action/Validator/DelegateValidator.cs rename to Lib9c/Action/ValidatorDelegation/DelegateValidator.cs index 9bc6056270..c42dea8efd 100644 --- a/Lib9c/Action/Validator/DelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs @@ -4,9 +4,9 @@ using Libplanet.Action; using Libplanet.Crypto; using Libplanet.Types.Assets; -using Nekoyume.Module.Validator; +using Nekoyume.Module.ValidatorDelegation; -namespace Nekoyume.Action.Validator +namespace Nekoyume.Action.ValidatorDelegation { public class DelegateValidator : ActionBase { @@ -48,7 +48,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var world = context.PreviousState; diff --git a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs new file mode 100644 index 0000000000..ba6593c347 --- /dev/null +++ b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs @@ -0,0 +1,60 @@ +using System; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Crypto; +using Libplanet.Types.Assets; +using Nekoyume.Module.ValidatorDelegation; + +namespace Nekoyume.Action.ValidatorDelegation +{ + public class PromoteValidator : ActionBase + { + public const string TypeIdentifier = "promote_validator"; + + private const string TargetKey = "t"; + + public PromoteValidator() { } + + public PromoteValidator(PublicKey publicKey, FungibleAssetValue fav) + { + PublicKey = publicKey; + FAV = fav; + } + + public PublicKey PublicKey { get; private set; } + + public FungibleAssetValue FAV { get; private set; } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", List.Empty + .Add(PublicKey.Format(true)) + .Add(FAV.Serialize())); + + 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(); + } + + PublicKey = new PublicKey(((Binary)values[0]).ByteArray); + FAV = new FungibleAssetValue(values[1]); + } + + public override IWorld Execute(IActionContext context) + { + GasTracer.UseGas(1); + + var world = context.PreviousState; + + return world + .CreateValidatorDelegatee(context, PublicKey) + .DelegateValidator(context, context.Signer, FAV); + } + } +} diff --git a/Lib9c/Action/ValidatorDelegation/RecordProposer.cs b/Lib9c/Action/ValidatorDelegation/RecordProposer.cs new file mode 100644 index 0000000000..38eedcd71e --- /dev/null +++ b/Lib9c/Action/ValidatorDelegation/RecordProposer.cs @@ -0,0 +1,40 @@ +using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.State; +using Nekoyume.Extensions; +using Nekoyume.ValidatorDelegation; + +namespace Nekoyume.Action.ValidatorDelegation +{ + /// + /// An action for recording proposer of the block to use in next block's reward distribution. + /// + public sealed class RecordProposer : ActionBase + { + /// + /// Creates a new instance of . + /// + public RecordProposer() + { + } + + /// + public override IValue PlainValue => new Bencodex.Types.Boolean(true); + + /// + public override void LoadPlainValue(IValue plainValue) + { + // Method intentionally left empty. + } + + /// + public override IWorld Execute(IActionContext context) + { + return context.PreviousState.MutateAccount( + Addresses.ValidatorList, + account => account.SetState( + ProposerInfo.Address, + new ProposerInfo(context.BlockIndex, context.Miner).Bencoded)); + } + } +} diff --git a/Lib9c/Action/Validator/RedelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs similarity index 93% rename from Lib9c/Action/Validator/RedelegateValidator.cs rename to Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs index 5fd7bb1f93..d1f354f63e 100644 --- a/Lib9c/Action/Validator/RedelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs @@ -4,9 +4,9 @@ using Libplanet.Action.State; using Libplanet.Action; using Libplanet.Crypto; -using Nekoyume.Module.Validator; +using Nekoyume.Module.ValidatorDelegation; -namespace Nekoyume.Action.Validator +namespace Nekoyume.Action.ValidatorDelegation { public class RedelegateValidator : ActionBase { @@ -54,7 +54,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var world = context.PreviousState; diff --git a/Lib9c/Action/Validator/UndelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs similarity index 92% rename from Lib9c/Action/Validator/UndelegateValidator.cs rename to Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs index 96e9f5e3a5..805a3e4ab6 100644 --- a/Lib9c/Action/Validator/UndelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs @@ -4,9 +4,9 @@ using Libplanet.Action.State; using Libplanet.Action; using Libplanet.Crypto; -using Nekoyume.Module.Validator; +using Nekoyume.Module.ValidatorDelegation; -namespace Nekoyume.Action.Validator +namespace Nekoyume.Action.ValidatorDelegation { public class UndelegateValidator : ActionBase { @@ -48,7 +48,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var world = context.PreviousState; diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index 49909eef72..2394c074f2 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -132,23 +132,35 @@ public static readonly Address LumpSumRewardsRecord #region Validator + /// + /// An address for reward pool of validators. + /// + public static readonly Address RewardPool + = new Address("0000000000000000000000000000000000000400"); + /// /// An address of an account having . /// public static readonly Address ValidatorList - = new Address("0000000000000000000000000000000000000400"); + = new Address("0000000000000000000000000000000000000401"); /// /// An address of an account having . /// public static readonly Address ValidatorDelegatee - = new Address("0000000000000000000000000000000000000401"); + = new Address("0000000000000000000000000000000000000402"); /// /// An address of an account having . /// public static readonly Address ValidatorDelegator - = new Address("0000000000000000000000000000000000000402"); + = new Address("0000000000000000000000000000000000000403"); + + /// + /// An address for community fund. + /// + public static readonly Address CommunityPool + = new Address("0000000000000000000000000000000000000499"); #endregion diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 3649c5829c..58696a396b 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -113,7 +113,7 @@ private Delegatee( public IDelegationRepository? Repository => _repository; - public List Bencoded => List.Empty + public virtual List Bencoded => List.Empty .Add(new List(Delegators.Select(delegator => delegator.Bencoded))) .Add(TotalDelegated.Serialize()) .Add(TotalShares); @@ -141,7 +141,7 @@ FungibleAssetValue IDelegatee.Unbond( void IDelegatee.DistributeReward(IDelegator delegator, long height) => DistributeReward((T)delegator, height); - public BigInteger Bond(T delegator, FungibleAssetValue fav, long height) + public virtual BigInteger Bond(T delegator, FungibleAssetValue fav, long height) { CannotMutateRelationsWithoutRepository(delegator); DistributeReward(delegator, height); @@ -164,7 +164,7 @@ public BigInteger Bond(T delegator, FungibleAssetValue fav, long height) return share; } - public FungibleAssetValue Unbond(T delegator, BigInteger share, long height) + public virtual FungibleAssetValue Unbond(T delegator, BigInteger share, long height) { CannotMutateRelationsWithoutRepository(delegator); DistributeReward(delegator, height); @@ -190,7 +190,7 @@ public FungibleAssetValue Unbond(T delegator, BigInteger share, long height) return fav; } - public void DistributeReward(T delegator, long height) + public virtual void DistributeReward(T delegator, long height) { CannotMutateRelationsWithoutRepository(delegator); BigInteger share = _repository!.GetBond(this, delegator.Address).Share; @@ -205,7 +205,7 @@ public void DistributeReward(T delegator, long height) delegator.UpdateLastRewardHeight(height); } - public void CollectRewards(long height) + public virtual void CollectRewards(long height) { CannotMutateRelationsWithoutRepository(); FungibleAssetValue rewards = _repository!.GetBalance(RewardCollectorAddress, RewardCurrency); @@ -213,26 +213,26 @@ public void CollectRewards(long height) _repository!.TransferAsset(RewardCollectorAddress, RewardDistributorAddress, rewards); } - public Address BondAddress(Address delegatorAddress) + public virtual Address BondAddress(Address delegatorAddress) => DeriveAddress(BondId, delegatorAddress); - public Address UnbondLockInAddress(Address delegatorAddress) + public virtual Address UnbondLockInAddress(Address delegatorAddress) => DeriveAddress(UnbondLockInId, delegatorAddress); - public Address RebondGraceAddress(Address delegatorAddress) + public virtual Address RebondGraceAddress(Address delegatorAddress) => DeriveAddress(RebondGraceId, delegatorAddress); - public Address CurrentLumpSumRewardsRecordAddress() + public virtual Address CurrentLumpSumRewardsRecordAddress() => DeriveAddress(LumpSumRewardsRecordId); - public Address LumpSumRewardsRecordAddress(long height) + public virtual Address LumpSumRewardsRecordAddress(long height) => DeriveAddress(LumpSumRewardsRecordId, BitConverter.GetBytes(height)); public override bool Equals(object? obj) => obj is IDelegatee other && Equals(other); - public bool Equals(IDelegatee? other) + public virtual bool Equals(IDelegatee? other) => ReferenceEquals(this, other) || (other is Delegatee delegatee && (GetType() != delegatee.GetType()) diff --git a/Lib9c/Delegation/DelegationRepository.cs b/Lib9c/Delegation/DelegationRepository.cs index 85ed1168a1..ba37e801c3 100644 --- a/Lib9c/Delegation/DelegationRepository.cs +++ b/Lib9c/Delegation/DelegationRepository.cs @@ -35,14 +35,14 @@ public DelegationRepository(IWorld world, IActionContext context) _lumpSumRewardsRecord = world.GetAccount(lumpSumRewardsRecordAddress); } - public IWorld World => _world + public virtual IWorld World => _world .SetAccount(bondAddress, _bond) .SetAccount(unbondLockInAddress, _unbondLockIn) .SetAccount(rebondGraceAddress, _rebondGrace) .SetAccount(unbondingSetAddress, _unbondingSet) .SetAccount(lumpSumRewardsRecordAddress, _lumpSumRewardsRecord); - public Bond GetBond(IDelegatee delegatee, Address delegatorAddress) + public virtual Bond GetBond(IDelegatee delegatee, Address delegatorAddress) { Address address = delegatee.BondAddress(delegatorAddress); IValue? value = _bond.GetState(address); @@ -51,7 +51,7 @@ public Bond GetBond(IDelegatee delegatee, Address delegatorAddress) : new Bond(address); } - public UnbondLockIn GetUnbondLockIn(IDelegatee delegatee, Address delegatorAddress) + public virtual UnbondLockIn GetUnbondLockIn(IDelegatee delegatee, Address delegatorAddress) { Address address = delegatee.UnbondLockInAddress(delegatorAddress); IValue? value = _unbondLockIn.GetState(address); @@ -60,7 +60,7 @@ public UnbondLockIn GetUnbondLockIn(IDelegatee delegatee, Address delegatorAddre : new UnbondLockIn(address, delegatee.MaxUnbondLockInEntries, delegatee.DelegationPoolAddress, delegatorAddress, this); } - public UnbondLockIn GetUnlimitedUnbondLockIn(Address address) + public virtual UnbondLockIn GetUnlimitedUnbondLockIn(Address address) { IValue? value = _unbondLockIn.GetState(address); return value is IValue bencoded @@ -68,7 +68,7 @@ public UnbondLockIn GetUnlimitedUnbondLockIn(Address address) : throw new InvalidOperationException("UnbondLockIn not found."); } - public RebondGrace GetRebondGrace(IDelegatee delegatee, Address delegatorAddress) + public virtual RebondGrace GetRebondGrace(IDelegatee delegatee, Address delegatorAddress) { Address address = delegatee.RebondGraceAddress(delegatorAddress); IValue? value = _rebondGrace.GetState(address); @@ -77,7 +77,7 @@ public RebondGrace GetRebondGrace(IDelegatee delegatee, Address delegatorAddress : new RebondGrace(address, delegatee.MaxRebondGraceEntries, this); } - public RebondGrace GetUnlimitedRebondGrace(Address address) + public virtual RebondGrace GetUnlimitedRebondGrace(Address address) { IValue? value = _rebondGrace.GetState(address); return value is IValue bencoded @@ -85,12 +85,12 @@ public RebondGrace GetUnlimitedRebondGrace(Address address) : throw new InvalidOperationException("RebondGrace not found."); } - public UnbondingSet GetUnbondingSet() + public virtual UnbondingSet GetUnbondingSet() => _unbondingSet.GetState(UnbondingSet.Address) is IValue bencoded ? new UnbondingSet(bencoded, this) : new UnbondingSet(this); - public LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height) + public virtual LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height) { Address address = delegatee.LumpSumRewardsRecordAddress(height); IValue? value = _lumpSumRewardsRecord.GetState(address); @@ -99,7 +99,7 @@ public UnbondingSet GetUnbondingSet() : null; } - public LumpSumRewardsRecord? GetCurrentLumpSumRewardsRecord(IDelegatee delegatee) + public virtual LumpSumRewardsRecord? GetCurrentLumpSumRewardsRecord(IDelegatee delegatee) { Address address = delegatee.CurrentLumpSumRewardsRecordAddress(); IValue? value = _lumpSumRewardsRecord.GetState(address); @@ -108,44 +108,44 @@ public UnbondingSet GetUnbondingSet() : null; } - public FungibleAssetValue GetBalance(Address address, Currency currency) + public virtual FungibleAssetValue GetBalance(Address address, Currency currency) => _world.GetBalance(address, currency); - public void SetBond(Bond bond) + public virtual void SetBond(Bond bond) { _bond = bond.IsEmpty ? _bond.RemoveState(bond.Address) : _bond.SetState(bond.Address, bond.Bencoded); } - public void SetUnbondLockIn(UnbondLockIn unbondLockIn) + public virtual void SetUnbondLockIn(UnbondLockIn unbondLockIn) { _unbondLockIn = unbondLockIn.IsEmpty ? _unbondLockIn.RemoveState(unbondLockIn.Address) : _unbondLockIn.SetState(unbondLockIn.Address, unbondLockIn.Bencoded); } - public void SetRebondGrace(RebondGrace rebondGrace) + public virtual void SetRebondGrace(RebondGrace rebondGrace) { _rebondGrace = rebondGrace.IsEmpty ? _rebondGrace.RemoveState(rebondGrace.Address) : _rebondGrace.SetState(rebondGrace.Address, rebondGrace.Bencoded); } - public void SetUnbondingSet(UnbondingSet unbondingSet) + public virtual void SetUnbondingSet(UnbondingSet unbondingSet) { _unbondingSet = unbondingSet.IsEmpty ? _unbondingSet.RemoveState(UnbondingSet.Address) : _unbondingSet.SetState(UnbondingSet.Address, unbondingSet.Bencoded); } - public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) + public virtual void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) { _lumpSumRewardsRecord = _lumpSumRewardsRecord.SetState( lumpSumRewardsRecord.Address, lumpSumRewardsRecord.Bencoded); } - public void AddLumpSumRewards(IDelegatee delegatee, long height, FungibleAssetValue rewards) + public virtual void AddLumpSumRewards(IDelegatee delegatee, long height, FungibleAssetValue rewards) { LumpSumRewardsRecord record = GetCurrentLumpSumRewardsRecord(delegatee) ?? new LumpSumRewardsRecord( diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index ccabfdc0d3..c5cf491642 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -55,7 +55,7 @@ private Delegator( public IDelegationRepository? Repository => _repository; - public List Bencoded + public virtual List Bencoded => List.Empty .Add(new List(Delegatees.Select(a => a.Bencoded))) .Add(Null.Value); @@ -78,7 +78,7 @@ void IDelegator.ClaimReward( IDelegatee delegatee, long height) => ClaimReward((T)delegatee, height); - public void Delegate( + public virtual void Delegate( T delegatee, FungibleAssetValue fav, long height) { CannotMutateRelationsWithoutRepository(delegatee); @@ -93,7 +93,7 @@ public void Delegate( _repository!.TransferAsset(Address, delegatee.DelegationPoolAddress, fav); } - public void Undelegate( + public virtual void Undelegate( T delegatee, BigInteger share, long height) { CannotMutateRelationsWithoutRepository(delegatee); @@ -130,7 +130,7 @@ public void Undelegate( _repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); } - public void Redelegate( + public virtual void Redelegate( T srcDelegatee, T dstDelegatee, BigInteger share, long height) { CannotMutateRelationsWithoutRepository(srcDelegatee); @@ -169,7 +169,7 @@ public void Redelegate( _repository.GetUnbondingSet().SetUnbonding(srcRebondGrace)); } - public void CancelUndelegate( + public virtual void CancelUndelegate( T delegatee, FungibleAssetValue fav, long height) { CannotMutateRelationsWithoutRepository(delegatee); @@ -201,7 +201,7 @@ public void CancelUndelegate( _repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); } - public void ClaimReward( + public virtual void ClaimReward( T delegatee, long height) { CannotMutateRelationsWithoutRepository(delegatee); @@ -216,7 +216,7 @@ public void UpdateLastRewardHeight(long height) public override bool Equals(object? obj) => obj is IDelegator other && Equals(other); - public bool Equals(IDelegator? other) + public virtual bool Equals(IDelegator? other) => ReferenceEquals(this, other) || (other is Delegator delegator && GetType() != delegator.GetType() diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index 4c4e09706f..054651997f 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -67,7 +67,7 @@ public Guild(List list, Currency rewardCurrency, IDelegationRepository repositor public override int MaxRebondGraceEntries => 10; - public new List Bencoded => List.Empty + public override List Bencoded => List.Empty .Add(StateTypeName) .Add(StateVersion) .Add(GuildMasterAddress.Bencoded) diff --git a/Lib9c/Model/Guild/GuildParticipant.cs b/Lib9c/Model/Guild/GuildParticipant.cs index dc97f49411..5a440908ae 100644 --- a/Lib9c/Model/Guild/GuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildParticipant.cs @@ -51,7 +51,7 @@ public GuildParticipant(AgentAddress agentAddress, List list, IDelegationReposit public AgentAddress AgentAddress => new AgentAddress(Address); - public new List Bencoded => List.Empty + public override List Bencoded => List.Empty .Add(StateTypeName) .Add(StateVersion) .Add(GuildAddress.Bencoded) diff --git a/Lib9c/Module/Validator/ValidatorDelegateeModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs similarity index 98% rename from Lib9c/Module/Validator/ValidatorDelegateeModule.cs rename to Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs index 1e86512c94..a7b7dae168 100644 --- a/Lib9c/Module/Validator/ValidatorDelegateeModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs @@ -9,7 +9,7 @@ using Nekoyume.Extensions; using Nekoyume.ValidatorDelegation; -namespace Nekoyume.Module.Validator +namespace Nekoyume.Module.ValidatorDelegation { public static class ValidatorDelegateeModule { diff --git a/Lib9c/Module/Validator/ValidatorDelegatorModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs similarity index 99% rename from Lib9c/Module/Validator/ValidatorDelegatorModule.cs rename to Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs index de29d38cd4..c456401780 100644 --- a/Lib9c/Module/Validator/ValidatorDelegatorModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs @@ -9,7 +9,7 @@ using Nekoyume.Extensions; using Nekoyume.ValidatorDelegation; -namespace Nekoyume.Module.Validator +namespace Nekoyume.Module.ValidatorDelegation { public static class ValidatorDelegatorModule { diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorListModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorListModule.cs new file mode 100644 index 0000000000..01f315e54d --- /dev/null +++ b/Lib9c/Module/ValidatorDelegation/ValidatorListModule.cs @@ -0,0 +1,79 @@ +#nullable enable +using System.Diagnostics.CodeAnalysis; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Types.Consensus; +using Nekoyume.Action; +using Nekoyume.Extensions; +using Nekoyume.ValidatorDelegation; + +namespace Nekoyume.Module.ValidatorDelegation +{ + public static class ValidatorListModule + { + public static ProposerInfo GetProposerInfo(this IWorldState worldState) + => worldState.TryGetProposerInfo(ProposerInfo.Address, out var proposerInfo) + ? proposerInfo + : throw new FailedLoadStateException("There is no such proposer info"); + + public static bool TryGetProposerInfo( + this IWorldState worldState, + Address address, + [NotNullWhen(true)] out ProposerInfo? proposerInfo) + { + try + { + var value = worldState.GetAccountState(Addresses.ValidatorList).GetState(address); + if (!(value is List list)) + { + proposerInfo = null; + return false; + } + + proposerInfo = new ProposerInfo(list); + return true; + } + catch + { + proposerInfo = null; + return false; + } + } + + public static IWorld SetProposerInfo( + this IWorld world, + ProposerInfo proposerInfo) + => world.MutateAccount( + Addresses.ValidatorList, + state => state.SetState(ProposerInfo.Address, proposerInfo.Bencoded)); + + public static ValidatorList GetValidatorList(this IWorldState worldState) + => worldState.TryGetValidatorList(out var validatorList) + ? validatorList + : throw new FailedLoadStateException("There is no such validator list"); + + public static bool TryGetValidatorList( + this IWorldState worldState, + [NotNullWhen(true)] out ValidatorList? validatorList) + { + try + { + var value = worldState.GetAccountState(Addresses.ValidatorList).GetState(ValidatorList.Address); + if (!(value is List list)) + { + validatorList = null; + return false; + } + + validatorList = new ValidatorList(list); + return true; + } + catch + { + validatorList = null; + return false; + } + } + } +} diff --git a/Lib9c/ValidatorDelegation/ProposerInfo.cs b/Lib9c/ValidatorDelegation/ProposerInfo.cs new file mode 100644 index 0000000000..6139730c4a --- /dev/null +++ b/Lib9c/ValidatorDelegation/ProposerInfo.cs @@ -0,0 +1,40 @@ +using Bencodex.Types; +using Libplanet.Crypto; +using Nekoyume.Model.State; +using System.Collections.Immutable; + +namespace Nekoyume.ValidatorDelegation +{ + public class ProposerInfo + { + public ProposerInfo(long blockIndex, Address proposer) + { + BlockIndex = blockIndex; + Proposer = proposer; + } + + public ProposerInfo(IValue bencoded) + : this((List)bencoded) + { + } + + public ProposerInfo(List bencoded) + : this((Integer)bencoded[0], new Address(bencoded[1])) + { + } + + public static Address Address => new Address( + ImmutableArray.Create( + 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50)); + + public long BlockIndex { get; } + + public Address Proposer { get; } + + public IValue Bencoded + => List.Empty + .Add(BlockIndex) + .Add(Proposer.Serialize()); + } +} diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index c08f666480..46175b0bcd 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -56,7 +56,8 @@ public ValidatorDelegatee(Address address, List bencoded, Currency rewardCurrenc public override int MaxRebondGraceEntries => 10; - public new List Bencoded => base.Bencoded + public override List Bencoded => List.Empty + .Add(base.Bencoded) .Add(Publickey.Format(true)) .Add(IsBonded); @@ -70,25 +71,72 @@ public ValidatorDelegatee(Address address, List bencoded, Currency rewardCurrenc public Validator Validator => new(Publickey, Power); - public new BigInteger Bond(ValidatorDelegator delegator, FungibleAssetValue fav, long height) + public static BigInteger BaseProposerRewardNumerator => 1; + + public static BigInteger BaseProposerRewardDenominator => 100; + + public static BigInteger BonusProposerRewardNumerator => 4; + + public static BigInteger BonusProposerRewardDenominator => 100; + + public static BigInteger CommissionNumerator => 1; + + public static BigInteger CommissionDenominator => 10; + + public static double CommissionMaxRate => 0.2; + + public static double CommissionMaxChangeRate => 0.01; + + public override BigInteger Bond(ValidatorDelegator delegator, FungibleAssetValue fav, long height) { BigInteger share = base.Bond(delegator, fav, height); - ((ValidatorRepository)Repository!).GetValidatorList().SetValidator(Validator); + var repo = ((ValidatorRepository)Repository!); + repo.SetValidatorList(repo.GetValidatorList().SetValidator(Validator)); return share; } - public new FungibleAssetValue Unbond(ValidatorDelegator delegator, BigInteger amount, long height) + public override FungibleAssetValue Unbond(ValidatorDelegator delegator, BigInteger share, long height) { - FungibleAssetValue fav = base.Unbond(delegator, amount, height); - ((ValidatorRepository)Repository!).GetValidatorList().SetValidator(Validator); + FungibleAssetValue fav = base.Unbond(delegator, share, height); + var repo = ((ValidatorRepository)Repository!); + repo.SetValidatorList(repo.GetValidatorList().SetValidator(Validator)); return fav; } + public void AllocateReward( + FungibleAssetValue rewardToAllocate, + BigInteger validatorPower, + BigInteger validatorSetPower, + Address RewardSource, + long height) + { + ValidatorRepository repo = (ValidatorRepository)Repository!; + + FungibleAssetValue rewardAllocated + = (rewardToAllocate * validatorPower).DivRem(validatorSetPower).Quotient; + FungibleAssetValue commission + = (rewardAllocated * CommissionNumerator).DivRem(CommissionDenominator).Quotient; + FungibleAssetValue delegationRewards = rewardAllocated - commission; + + repo.TransferAsset(RewardSource, Address, commission); + repo.TransferAsset(RewardSource, RewardCollectorAddress, delegationRewards); + CollectRewards(height); + } + public bool Equals(ValidatorDelegatee? other) => base.Equals(other) && Publickey.Equals(other.Publickey) && IsBonded == other.IsBonded; + public override bool Equals(IDelegatee? other) + => Equals(other as ValidatorDelegatee); + + public override bool Equals(object? obj) + => Equals(obj as ValidatorDelegatee); + + public override int GetHashCode() + => base.GetHashCode(); + public static Address BondedPoolAddress => new Address( ImmutableArray.Create( 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/Lib9c/ValidatorDelegation/ValidatorRepository.cs b/Lib9c/ValidatorDelegation/ValidatorRepository.cs index 2dfdf62b85..35cc3949e5 100644 --- a/Lib9c/ValidatorDelegation/ValidatorRepository.cs +++ b/Lib9c/ValidatorDelegation/ValidatorRepository.cs @@ -4,6 +4,7 @@ using Nekoyume.Delegation; using Bencodex.Types; using Libplanet.Crypto; +using static Nekoyume.Model.WorldInformation; namespace Nekoyume.ValidatorDelegation { @@ -19,6 +20,9 @@ public ValidatorRepository(IWorld world, IActionContext context) _validatorList = world.GetAccount(validatorListAddress); } + public override IWorld World => base.World + .SetAccount(validatorListAddress, _validatorList); + public ValidatorList GetValidatorList() { IValue? value = _validatorList.GetState(ValidatorList.Address); From 728ac60ff8c24bef52c00ac157995146ee957b1e Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 26 Aug 2024 09:10:32 +0900 Subject: [PATCH 030/206] fix: Fix AllocateReward with unit test --- .../ValidatorDelegation/AllocateRewardTest.cs | 135 ++++++++++++++++++ .../PromoteValidatorTest.cs | 135 ++++++++++++++++++ .../ValidatorDelegation/AllocateReward.cs | 2 +- 3 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs new file mode 100644 index 0000000000..d4b708e02a --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -0,0 +1,135 @@ +#nullable enable +namespace Lib9c.Tests.Action.ValidatorDelegation +{ + using System; + using System.Collections.Immutable; + using System.Linq; + using System.Numerics; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Libplanet.Types.Blocks; + using Libplanet.Types.Consensus; + using Nekoyume; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.Model.State; + using Nekoyume.Module; + using Nekoyume.Module.ValidatorDelegation; + using Nekoyume.ValidatorDelegation; + using Xunit; + + public class AllocateRewardTest + { + [Fact] + public void Serialization() + { + var action = new AllocateReward(); + var plainValue = action.PlainValue; + + var deserialized = new AllocateReward(); + deserialized.LoadPlainValue(plainValue); + } + + [Fact] + public void Execute() + { + IWorld world = new World(MockUtil.MockModernWorldState); + var context = new ActionContext { }; + var ncg = Currency.Uncapped("NCG", 2, null); + var gg = Currencies.GuildGold; + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var privateKeys = Enumerable.Range(0, 200).Select(_ => new PrivateKey()).ToArray(); + var favs = Enumerable.Range(0, 200).Select(i => gg * (i + 1)).ToArray(); + + for (int i = 0; i < 200; i++) + { + var signer = privateKeys[i]; + world = world.MintAsset(context, signer.Address, gg * 1000); + world = new PromoteValidator(signer.PublicKey, favs[i]).Execute(new ActionContext + { + PreviousState = world, + Signer = signer.Address, + BlockIndex = 10L, + }); + } + + var blockHash = new BlockHash(Enumerable.Repeat((byte)0x01, BlockHash.Size).ToArray()); + var timestamp = DateTimeOffset.UtcNow; + var voteFlags = Enumerable.Range(0, 100).Select(i => i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null).ToArray(); + var bondedSet = world.GetValidatorList().GetBonded(); + + var proposer = bondedSet.First(); + world = world.SetProposerInfo(new ProposerInfo(9L, proposer.OperatorAddress)); + var votes = bondedSet.Select( + (v, i) => new VoteMetadata( + 9L, 0, blockHash, timestamp, v.PublicKey, v.Power, i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null) + .Sign(i % 2 == 0 ? privateKeys.First(k => k.PublicKey.Equals(v.PublicKey)) : null)).ToImmutableArray(); + + var totalReward = ncg * 1000; + world = world.MintAsset(context, Addresses.RewardPool, totalReward); + + context = new ActionContext + { + BlockIndex = 10L, + PreviousState = world, + Signer = privateKeys[199].Address, + LastCommit = new BlockCommit(9L, 0, blockHash, votes), + }; + + var action = new AllocateReward(); + world = action.Execute(context); + + BigInteger totalPower = votes.Aggregate( + BigInteger.Zero, + (accum, next) => accum + (BigInteger)next.ValidatorPower!); + + BigInteger preCommitPower = votes.Aggregate( + BigInteger.Zero, + (accum, next) => accum + (BigInteger)next.ValidatorPower! * (next.Flag == VoteFlag.PreCommit ? 1 : 0)); + + var baseProposerReward + = (totalReward * ValidatorDelegatee.BaseProposerRewardNumerator) + .DivRem(ValidatorDelegatee.BaseProposerRewardDenominator).Quotient; + var bonusProposerReward + = (totalReward * preCommitPower * ValidatorDelegatee.BonusProposerRewardNumerator) + .DivRem(totalPower * ValidatorDelegatee.BonusProposerRewardDenominator).Quotient; + + var proposerReward = baseProposerReward + bonusProposerReward; + var remains = totalReward - proposerReward; + + foreach (var vote in votes) + { + var validator = world.GetValidatorDelegatee(vote.ValidatorPublicKey.Address); + + FungibleAssetValue rewardAllocated + = (remains * vote.ValidatorPower!.Value).DivRem(totalPower).Quotient; + FungibleAssetValue commission + = (rewardAllocated * ValidatorDelegatee.CommissionNumerator) + .DivRem(ValidatorDelegatee.CommissionDenominator).Quotient; + + if (vote.Flag == VoteFlag.Null) + { + Assert.Equal(ncg * 0, world.GetBalance(vote.ValidatorPublicKey.Address, ncg)); + Assert.Equal(ncg * 0, world.GetBalance(validator.RewardDistributorAddress, ncg)); + continue; + } + + if (vote.ValidatorPublicKey.Equals(proposer.PublicKey)) + { + Assert.Equal( + proposerReward + commission, + world.GetBalance(vote.ValidatorPublicKey.Address, ncg)); + } + else + { + Assert.Equal(commission, world.GetBalance(vote.ValidatorPublicKey.Address, ncg)); + } + + Assert.Equal(rewardAllocated - commission, world.GetBalance(validator.RewardDistributorAddress, ncg)); + } + } + } +} diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs new file mode 100644 index 0000000000..aa52479e47 --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs @@ -0,0 +1,135 @@ +namespace Lib9c.Tests.Action.ValidatorDelegation +{ + using System; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.Model.State; + using Nekoyume.Module; + using Nekoyume.Module.Delegation; + using Nekoyume.Module.ValidatorDelegation; + using Xunit; + + public class PromoteValidatorTest + { + [Fact] + public void Serialization() + { + var publicKey = new PrivateKey().PublicKey; + var gg = Currencies.GuildGold; + var fav = gg * 10; + var action = new PromoteValidator(publicKey, fav); + var plainValue = action.PlainValue; + + var deserialized = new PromoteValidator(); + deserialized.LoadPlainValue(plainValue); + Assert.Equal(publicKey, deserialized.PublicKey); + Assert.Equal(fav, deserialized.FAV); + } + + [Fact] + public void Execute() + { + IWorld world = new World(MockUtil.MockModernWorldState); + var context = new ActionContext { }; + var ncg = Currency.Uncapped("NCG", 2, null); + var gg = Currencies.GuildGold; + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + var publicKey = new PrivateKey().PublicKey; + world = world.MintAsset(context, publicKey.Address, gg * 100); + var fav = gg * 10; + var action = new PromoteValidator(publicKey, fav); + + world = action.Execute(new ActionContext + { + PreviousState = world, + Signer = publicKey.Address, + }); + + var validator = world.GetValidatorDelegatee(publicKey.Address); + var bond = world.GetBond(validator, publicKey.Address); + var validatorList = world.GetValidatorList(); + + Assert.Equal(publicKey.Address, Assert.Single(validator.Delegators)); + Assert.Equal(fav.RawValue, bond.Share); + Assert.Equal(validator.Validator, Assert.Single(validatorList.Validators)); + Assert.Equal(validator.Validator, Assert.Single(validatorList.GetBonded())); + Assert.Equal(gg * 90, world.GetBalance(publicKey.Address, gg)); + Assert.Empty(validatorList.GetUnbonded()); + } + + [Fact] + public void CannotPromoteWithInvalidPublicKey() + { + IWorld world = new World(MockUtil.MockModernWorldState); + var context = new ActionContext { }; + var ncg = Currency.Uncapped("NCG", 2, null); + var gg = Currencies.GuildGold; + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + var publicKey = new PrivateKey().PublicKey; + world = world.MintAsset(context, publicKey.Address, gg * 100); + var fav = gg * 10; + var action = new PromoteValidator(new PrivateKey().PublicKey, fav); + + Assert.Throws(() => action.Execute(new ActionContext + { + PreviousState = world, + Signer = publicKey.Address, + })); + } + + [Fact] + public void CannotPromoteWithInvalidCurrency() + { + IWorld world = new World(MockUtil.MockModernWorldState); + var context = new ActionContext { }; + var ncg = Currency.Uncapped("NCG", 2, null); + var gg = Currency.Uncapped("invalid", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + var publicKey = new PrivateKey().PublicKey; + world = world.MintAsset(context, publicKey.Address, gg * 100); + var fav = gg * 10; + var action = new PromoteValidator(publicKey, fav); + + Assert.Throws(() => action.Execute(new ActionContext + { + PreviousState = world, + Signer = publicKey.Address, + })); + } + + [Fact] + public void CannotPromoteWithInsufficientBalance() + { + IWorld world = new World(MockUtil.MockModernWorldState); + var context = new ActionContext { }; + var ncg = Currency.Uncapped("NCG", 2, null); + var gg = Currencies.GuildGold; + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + var publicKey = new PrivateKey().PublicKey; + var fav = gg * 10; + var action = new PromoteValidator(publicKey, fav); + + Assert.Throws(() => action.Execute(new ActionContext + { + PreviousState = world, + Signer = publicKey.Address, + })); + } + } +} diff --git a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs index dc80666494..99cba0db77 100644 --- a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs +++ b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs @@ -153,7 +153,7 @@ BigInteger validatorSetPower continue; } - if (states.TryGetValidatorDelegatee( + if (!states.TryGetValidatorDelegatee( vote.ValidatorPublicKey.Address, repo, out var validatorDelegatee)) { continue; From b908ef1d22c9dec2c7a5d6dc4d0469d026ba3247 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 28 Aug 2024 01:19:23 +0900 Subject: [PATCH 031/206] feat: Update to handle zero unbonding period --- Lib9c/Delegation/RebondGrace.cs | 7 ++++--- Lib9c/Delegation/UnbondLockIn.cs | 6 +++--- Lib9c/Delegation/UnbondingSet.cs | 9 ++++++++- Lib9c/ValidatorDelegation/ValidatorDelegatee.cs | 11 +++++++++-- Lib9c/ValidatorDelegation/ValidatorList.cs | 5 +++-- 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/Lib9c/Delegation/RebondGrace.cs b/Lib9c/Delegation/RebondGrace.cs index 47084b041d..6910a152a1 100644 --- a/Lib9c/Delegation/RebondGrace.cs +++ b/Lib9c/Delegation/RebondGrace.cs @@ -159,9 +159,10 @@ internal RebondGrace Grace( long creationHeight, long expireHeight) { - if (expireHeight == creationHeight) + if (expireHeight < creationHeight) { - return this; + throw new ArgumentException( + "The expire height must be greater than the creation height."); } return AddEntry( @@ -270,7 +271,7 @@ private RebondGraceEntry( "The creation height must be greater than or equal to zero."); } - if (expireHeight <= creationHeight) + if (expireHeight < creationHeight) { throw new ArgumentOutOfRangeException( nameof(expireHeight), diff --git a/Lib9c/Delegation/UnbondLockIn.cs b/Lib9c/Delegation/UnbondLockIn.cs index 4eee12f8da..11e85768d6 100644 --- a/Lib9c/Delegation/UnbondLockIn.cs +++ b/Lib9c/Delegation/UnbondLockIn.cs @@ -197,9 +197,9 @@ public override int GetHashCode() internal UnbondLockIn LockIn( FungibleAssetValue lockInFAV, long creationHeight, long expireHeight) { - if (expireHeight == creationHeight) + if (expireHeight < creationHeight) { - return this; + throw new ArgumentException("The expire height must be greater than the creation height."); } return AddEntry(new UnbondLockInEntry(lockInFAV, creationHeight, expireHeight)); @@ -374,7 +374,7 @@ private UnbondLockInEntry( "The creation height must be greater than or equal to zero."); } - if (expireHeight <= creationHeight) + if (expireHeight < creationHeight) { throw new ArgumentOutOfRangeException( nameof(expireHeight), diff --git a/Lib9c/Delegation/UnbondingSet.cs b/Lib9c/Delegation/UnbondingSet.cs index 301d264668..c9be83e3df 100644 --- a/Lib9c/Delegation/UnbondingSet.cs +++ b/Lib9c/Delegation/UnbondingSet.cs @@ -126,7 +126,14 @@ public UnbondingSet SetUnbonding(IUnbonding unbonding) { if (unbonding.IsEmpty) { - return RemoveUnbonding(unbonding.Address); + try + { + return RemoveUnbonding(unbonding.Address); + } + catch (ArgumentException) + { + return this; + } } if (_lowestExpireHeights.TryGetValue(unbonding.Address, out var lowestExpireHeight)) diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index 46175b0bcd..de086c17cd 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -90,7 +90,7 @@ public ValidatorDelegatee(Address address, List bencoded, Currency rewardCurrenc public override BigInteger Bond(ValidatorDelegator delegator, FungibleAssetValue fav, long height) { BigInteger share = base.Bond(delegator, fav, height); - var repo = ((ValidatorRepository)Repository!); + ValidatorRepository repo = (ValidatorRepository)Repository!; repo.SetValidatorList(repo.GetValidatorList().SetValidator(Validator)); return share; } @@ -98,7 +98,14 @@ public override BigInteger Bond(ValidatorDelegator delegator, FungibleAssetValue public override FungibleAssetValue Unbond(ValidatorDelegator delegator, BigInteger share, long height) { FungibleAssetValue fav = base.Unbond(delegator, share, height); - var repo = ((ValidatorRepository)Repository!); + ValidatorRepository repo = (ValidatorRepository)Repository!; + + if (Validator.Power.IsZero) + { + repo.SetValidatorList(repo.GetValidatorList().RemoveValidator(Validator.PublicKey)); + return fav; + } + repo.SetValidatorList(repo.GetValidatorList().SetValidator(Validator)); return fav; } diff --git a/Lib9c/ValidatorDelegation/ValidatorList.cs b/Lib9c/ValidatorDelegation/ValidatorList.cs index 9b35adebb3..a2348d4c75 100644 --- a/Lib9c/ValidatorDelegation/ValidatorList.cs +++ b/Lib9c/ValidatorDelegation/ValidatorList.cs @@ -55,13 +55,14 @@ private ValidatorList(ImmutableList validators) public ValidatorList SetValidator(Validator validator) => RemoveValidator(validator.PublicKey).AddValidator(validator); + public ValidatorList RemoveValidator(PublicKey publicKey) + => UpdateValidators(Validators.RemoveAll(v => v.PublicKey.Equals(publicKey))); + private ValidatorList AddValidator(Validator validator) { int index = Validators.BinarySearch(validator, _reversedComparer); return UpdateValidators(Validators.Insert(index < 0 ? ~index : index, validator)); } - private ValidatorList RemoveValidator(PublicKey publicKey) - => UpdateValidators(Validators.RemoveAll(v => v.PublicKey.Equals(publicKey))); private ValidatorList UpdateValidators(ImmutableList validators) => new ValidatorList(validators); From f88b836ffa8ccd9009264e7e79db6978039c899e Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 28 Aug 2024 01:19:40 +0900 Subject: [PATCH 032/206] test: Update tests --- .../DelegateValidatorTest.cs | 136 ++++++++++++++++++ .../RedelegateValidatorTest.cs | 87 +++++++++++ .../UndelegateValidatorTest.cs | 82 +++++++++++ 3 files changed, 305 insertions(+) create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs new file mode 100644 index 0000000000..f2943964c1 --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -0,0 +1,136 @@ +namespace Lib9c.Tests.Action.ValidatorDelegation +{ + using System; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.Model.State; + using Nekoyume.Module; + using Nekoyume.Module.Delegation; + using Nekoyume.Module.ValidatorDelegation; + using Xunit; + + public class DelegateValidatorTest + { + [Fact] + public void Serialization() + { + var address = new PrivateKey().Address; + var gg = Currencies.GuildGold; + var fav = gg * 10; + var action = new DelegateValidator(address, fav); + var plainValue = action.PlainValue; + + var deserialized = new DelegateValidator(); + deserialized.LoadPlainValue(plainValue); + Assert.Equal(address, deserialized.ValidatorDelegatee); + Assert.Equal(fav, deserialized.FAV); + } + + [Fact] + public void Execute() + { + IWorld world = new World(MockUtil.MockModernWorldState); + var context = new ActionContext { }; + var ncg = Currency.Uncapped("NCG", 2, null); + var gg = Currencies.GuildGold; + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + var validatorPublicKey = new PrivateKey().PublicKey; + world = world.MintAsset(context, validatorPublicKey.Address, gg * 100); + var promoteFAV = gg * 10; + world = new PromoteValidator(validatorPublicKey, promoteFAV).Execute(new ActionContext + { + PreviousState = world, + Signer = validatorPublicKey.Address, + }); + + var publicKey = new PrivateKey().PublicKey; + world = world.MintAsset(context, publicKey.Address, gg * 100); + var delegateFAV = gg * 20; + var action = new DelegateValidator(validatorPublicKey.Address, delegateFAV); + world = action.Execute(new ActionContext + { + PreviousState = world, + Signer = publicKey.Address, + }); + + var validator = world.GetValidatorDelegatee(validatorPublicKey.Address); + var bond = world.GetBond(validator, publicKey.Address); + var validatorList = world.GetValidatorList(); + + Assert.Contains(publicKey.Address, validator.Delegators); + Assert.Equal(delegateFAV.RawValue, bond.Share); + Assert.Equal(promoteFAV.RawValue + delegateFAV.RawValue, validator.Validator.Power); + Assert.Equal(validator.Validator, Assert.Single(validatorList.Validators)); + Assert.Equal(gg * 80, world.GetBalance(publicKey.Address, gg)); + } + + [Fact] + public void CannotDelegateWithInvalidCurrency() + { + IWorld world = new World(MockUtil.MockModernWorldState); + var context = new ActionContext { }; + var ncg = Currency.Uncapped("NCG", 2, null); + var gg = Currencies.GuildGold; + var invalid = Currency.Uncapped("invalid", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + var validatorPublicKey = new PrivateKey().PublicKey; + world = world.MintAsset(context, validatorPublicKey.Address, gg * 100); + var promoteFAV = gg * 10; + world = new PromoteValidator(validatorPublicKey, promoteFAV).Execute(new ActionContext + { + PreviousState = world, + Signer = validatorPublicKey.Address, + }); + + var publicKey = new PrivateKey().PublicKey; + world = world.MintAsset(context, publicKey.Address, invalid * 100); + var delegateFAV = invalid * 20; + var action = new DelegateValidator(validatorPublicKey.Address, delegateFAV); + Assert.Throws(() => action.Execute(new ActionContext + { + PreviousState = world, + Signer = publicKey.Address, + })); + } + + [Fact] + public void CannotDelegateWithInsufficientBalance() + { + IWorld world = new World(MockUtil.MockModernWorldState); + var context = new ActionContext { }; + var ncg = Currency.Uncapped("NCG", 2, null); + var gg = Currencies.GuildGold; + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + var validatorPublicKey = new PrivateKey().PublicKey; + world = world.MintAsset(context, validatorPublicKey.Address, gg * 100); + var promoteFAV = gg * 10; + world = new PromoteValidator(validatorPublicKey, promoteFAV).Execute(new ActionContext + { + PreviousState = world, + Signer = validatorPublicKey.Address, + }); + + var publicKey = new PrivateKey().PublicKey; + var delegateFAV = gg * 20; + var action = new DelegateValidator(validatorPublicKey.Address, delegateFAV); + Assert.Throws(() => action.Execute(new ActionContext + { + PreviousState = world, + Signer = publicKey.Address, + })); + } + } +} diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs new file mode 100644 index 0000000000..b4ba976483 --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs @@ -0,0 +1,87 @@ +namespace Lib9c.Tests.Action.ValidatorDelegation +{ + using System.Numerics; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.Model.State; + using Nekoyume.Module; + using Nekoyume.Module.Delegation; + using Nekoyume.Module.ValidatorDelegation; + using Xunit; + + public class RedelegateValidatorTest + { + [Fact] + public void Serialization() + { + var srcAddress = new PrivateKey().Address; + var dstAddress = new PrivateKey().Address; + var share = BigInteger.One; + var action = new RedelegateValidator(srcAddress, dstAddress, share); + var plainValue = action.PlainValue; + + var deserialized = new RedelegateValidator(); + deserialized.LoadPlainValue(plainValue); + Assert.Equal(srcAddress, deserialized.SrcValidatorDelegatee); + Assert.Equal(dstAddress, deserialized.DstValidatorDelegatee); + Assert.Equal(share, deserialized.Share); + } + + [Fact] + public void Execute() + { + IWorld world = new World(MockUtil.MockModernWorldState); + var context = new ActionContext { }; + var ncg = Currency.Uncapped("NCG", 2, null); + var gg = Currencies.GuildGold; + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + var srcPublicKey = new PrivateKey().PublicKey; + world = world.MintAsset(context, srcPublicKey.Address, gg * 100); + var promoteFAV = gg * 10; + world = new PromoteValidator(srcPublicKey, promoteFAV).Execute(new ActionContext + { + PreviousState = world, + Signer = srcPublicKey.Address, + }); + + var dstPublicKey = new PrivateKey().PublicKey; + world = world.MintAsset(context, dstPublicKey.Address, gg * 100); + world = new PromoteValidator(dstPublicKey, promoteFAV).Execute(new ActionContext + { + PreviousState = world, + Signer = dstPublicKey.Address, + }); + + var srcValidator = world.GetValidatorDelegatee(srcPublicKey.Address); + var bond = world.GetBond(srcValidator, srcPublicKey.Address); + var action = new RedelegateValidator(srcPublicKey.Address, dstPublicKey.Address, bond.Share); + context = new ActionContext + { + PreviousState = world, + Signer = srcPublicKey.Address, + BlockIndex = 1, + }; + + world = action.Execute(context); + + srcValidator = world.GetValidatorDelegatee(srcPublicKey.Address); + var dstValidator = world.GetValidatorDelegatee(dstPublicKey.Address); + var validatorList = world.GetValidatorList(); + var dstBond = world.GetBond(dstValidator, srcPublicKey.Address); + + Assert.Contains(srcPublicKey.Address, dstValidator.Delegators); + Assert.Equal(dstValidator.Validator, Assert.Single(validatorList.Validators)); + Assert.Equal((gg * 10).RawValue, dstBond.Share); + Assert.Equal(gg * 20, dstValidator.TotalDelegated); + Assert.Equal((gg * 20).RawValue, dstValidator.TotalShares); + Assert.Equal((gg * 20).RawValue, dstValidator.Power); + } + } +} diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs new file mode 100644 index 0000000000..58641490d1 --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -0,0 +1,82 @@ +namespace Lib9c.Tests.Action.ValidatorDelegation +{ + using System.Numerics; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.Model.State; + using Nekoyume.Module; + using Nekoyume.Module.Delegation; + using Nekoyume.Module.ValidatorDelegation; + using Xunit; + + public class UndelegateValidatorTest + { + [Fact] + public void Serialization() + { + var address = new PrivateKey().Address; + var share = BigInteger.One; + var action = new UndelegateValidator(address, share); + var plainValue = action.PlainValue; + + var deserialized = new UndelegateValidator(); + deserialized.LoadPlainValue(plainValue); + Assert.Equal(address, deserialized.ValidatorDelegatee); + Assert.Equal(share, deserialized.Share); + } + + [Fact] + public void Execute() + { + IWorld world = new World(MockUtil.MockModernWorldState); + var context = new ActionContext { }; + var ncg = Currency.Uncapped("NCG", 2, null); + var gg = Currencies.GuildGold; + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + var publicKey = new PrivateKey().PublicKey; + world = world.MintAsset(context, publicKey.Address, gg * 100); + var promoteFAV = gg * 10; + world = new PromoteValidator(publicKey, promoteFAV).Execute(new ActionContext + { + PreviousState = world, + Signer = publicKey.Address, + }); + + var validator = world.GetValidatorDelegatee(publicKey.Address); + var bond = world.GetBond(validator, publicKey.Address); + var action = new UndelegateValidator(publicKey.Address, bond.Share); + context = new ActionContext + { + PreviousState = world, + Signer = publicKey.Address, + BlockIndex = 1, + }; + + world = action.Execute(context); + + validator = world.GetValidatorDelegatee(publicKey.Address); + var validatorList = world.GetValidatorList(); + + Assert.Empty(validator.Delegators); + Assert.Equal(BigInteger.Zero, validator.Validator.Power); + Assert.Empty(validatorList.Validators); + + context = new ActionContext + { + PreviousState = world, + Signer = publicKey.Address, + BlockIndex = 1, + }; + + world = world.ReleaseUnbondings(context); + Assert.Equal(gg * 100, world.GetBalance(publicKey.Address, gg)); + } + } +} From ad6371c4a53defc0b78a0b6104e3fa1ae17b497d Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 28 Aug 2024 01:20:13 +0900 Subject: [PATCH 033/206] bump: Libplanet for DPoS --- .Libplanet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.Libplanet b/.Libplanet index 46cff67d3e..c1a1c5e2e3 160000 --- a/.Libplanet +++ b/.Libplanet @@ -1 +1 @@ -Subproject commit 46cff67d3e5e0be88e23aa0924e4b5f88e20c672 +Subproject commit c1a1c5e2e377b147dc96e7dc480bca3481c41a99 From ff9dcc3f6d74d5eba3c185cba49dd51e1b414dab Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 28 Aug 2024 13:03:07 +0900 Subject: [PATCH 034/206] test: Skip test for gas tracing --- .Lib9c.Tests/Action/Scenario/MeadScenarioTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.Lib9c.Tests/Action/Scenario/MeadScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/MeadScenarioTest.cs index bb9ad8b992..c0db972084 100644 --- a/.Lib9c.Tests/Action/Scenario/MeadScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/MeadScenarioTest.cs @@ -79,7 +79,7 @@ public void Contract() Assert.Equal(4 * mead, states6.GetBalance(agentAddress, mead)); } - [Fact] + [Fact(Skip = "No way tracing gas usage outside of ActionEvaluator for now")] public void UseGas() { Type baseType = typeof(Nekoyume.Action.ActionBase); From 5eae1fde8e8edb5721e21cb8c637ea0e05bc9e12 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 30 Aug 2024 11:49:42 +0900 Subject: [PATCH 035/206] feat: Implement slash logic --- Lib9c/Delegation/Delegatee.cs | 50 +++++++- Lib9c/Delegation/Delegator.cs | 9 ++ Lib9c/Delegation/IDelegatee.cs | 4 + Lib9c/Delegation/IUnbonding.cs | 3 +- Lib9c/Delegation/RebondGrace.cs | 34 ++++- Lib9c/Delegation/UnbondLockIn.cs | 35 +++-- Lib9c/Delegation/UnbondingFactory.cs | 31 +++++ Lib9c/Delegation/UnbondingRef.cs | 95 ++++++++++++++ Lib9c/Delegation/UnbondingSet.cs | 121 ++++++------------ Lib9c/Delegation/UnbondingType.cs | 9 ++ Lib9c/Model/Guild/Guild.cs | 3 + .../ValidatorDelegation/ValidatorDelegatee.cs | 2 + 12 files changed, 293 insertions(+), 103 deletions(-) create mode 100644 Lib9c/Delegation/UnbondingFactory.cs create mode 100644 Lib9c/Delegation/UnbondingRef.cs create mode 100644 Lib9c/Delegation/UnbondingType.cs diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 58696a396b..3f8e888bd3 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -25,6 +25,7 @@ public abstract class Delegatee : IDelegatee protected readonly byte[] PoolId = new byte[] { 0x70 }; // `p` private readonly IDelegationRepository? _repository; + private ImmutableSortedSet _unbondingRefs; public Delegatee(Address address, IDelegationRepository? repository = null) { @@ -32,6 +33,7 @@ public Delegatee(Address address, IDelegationRepository? repository = null) Delegators = ImmutableSortedSet
.Empty; TotalDelegated = DelegationCurrency * 0; TotalShares = BigInteger.Zero; + _unbondingRefs = ImmutableSortedSet.Empty; _repository = repository; } @@ -46,6 +48,7 @@ public Delegatee(Address address, List bencoded, IDelegationRepository? reposito ((List)bencoded[0]).Select(item => new Address(item)), new FungibleAssetValue(bencoded[1]), (Integer)bencoded[2], + ((List)bencoded[3]).Select(item => new UnbondingRef(item)), repository) { } @@ -55,6 +58,7 @@ private Delegatee( IEnumerable
delegators, FungibleAssetValue totalDelegated, BigInteger totalShares, + IEnumerable unbondingRefs, IDelegationRepository? repository) { if (!totalDelegated.Currency.Equals(DelegationCurrency)) @@ -82,6 +86,7 @@ private Delegatee( Delegators = delegators.ToImmutableSortedSet(); TotalDelegated = totalDelegated; TotalShares = totalShares; + _unbondingRefs = unbondingRefs.ToImmutableSortedSet(); _repository = repository; } @@ -101,6 +106,8 @@ private Delegatee( public abstract int MaxRebondGraceEntries { get; } + public abstract BigInteger SlashFactor { get; } + public Address RewardCollectorAddress => DeriveAddress(RewardCollectorId); public Address RewardDistributorAddress => DeriveAddress(RewardDistributorId); @@ -116,7 +123,8 @@ private Delegatee( public virtual List Bencoded => List.Empty .Add(new List(Delegators.Select(delegator => delegator.Bencoded))) .Add(TotalDelegated.Serialize()) - .Add(TotalShares); + .Add(TotalShares) + .Add(new List(_unbondingRefs.Select(unbondingRef => unbondingRef.Bencoded))); IValue IBencodable.Bencoded => Bencoded; @@ -128,7 +136,7 @@ public BigInteger ShareToBond(FungibleAssetValue fav) public FungibleAssetValue FAVToUnbond(BigInteger share) => TotalShares == share ? TotalDelegated - : (TotalDelegated * share).DivRem(TotalShares, out _); + : (TotalDelegated * share).DivRem(TotalShares).Quotient; BigInteger IDelegatee.Bond( IDelegator delegator, FungibleAssetValue fav, long height) @@ -213,6 +221,43 @@ public virtual void CollectRewards(long height) _repository!.TransferAsset(RewardCollectorAddress, RewardDistributorAddress, rewards); } + public void Slash(long infractionHeight) + { + CannotMutateRelationsWithoutRepository(); + foreach (var item in _unbondingRefs) + { + var unbonding = UnbondingFactory.GetUnbondingFromRef(item, _repository) + .Slash(SlashFactor, infractionHeight); + + if (unbonding.IsEmpty) + { + RemoveUnbondingRef(item); + } + + switch (unbonding) + { + case UnbondLockIn unbondLockIn: + _repository!.SetUnbondLockIn(unbondLockIn); + break; + case RebondGrace rebondGrace: + _repository!.SetRebondGrace(rebondGrace); + break; + default: + throw new InvalidOperationException("Invalid unbonding type."); + } + } + } + + public void AddUnbondingRef(UnbondingRef unbondingRef) + { + _unbondingRefs = _unbondingRefs.Add(unbondingRef); + } + + public void RemoveUnbondingRef(UnbondingRef unbondingRef) + { + _unbondingRefs = _unbondingRefs.Remove(unbondingRef); + } + public virtual Address BondAddress(Address delegatorAddress) => DeriveAddress(BondId, delegatorAddress); @@ -246,6 +291,7 @@ public virtual bool Equals(IDelegatee? other) && Delegators.SequenceEqual(delegatee.Delegators) && TotalDelegated.Equals(delegatee.TotalDelegated) && TotalShares.Equals(delegatee.TotalShares) + && _unbondingRefs.SequenceEqual(delegatee._unbondingRefs) && DelegateeId.SequenceEqual(delegatee.DelegateeId)); public override int GetHashCode() diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index c5cf491642..efd0a83ea2 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -125,6 +125,8 @@ public virtual void Undelegate( Delegatees = Delegatees.Remove(delegatee.Address); } + delegatee.AddUnbondingRef(UnbondingFactory.ToReference(unbondLockIn)); + _repository.SetUnbondLockIn(unbondLockIn); _repository.SetUnbondingSet( _repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); @@ -164,6 +166,8 @@ public virtual void Redelegate( Delegatees = Delegatees.Add(dstDelegatee.Address); + srcDelegatee.AddUnbondingRef(UnbondingFactory.ToReference(srcRebondGrace)); + _repository.SetRebondGrace(srcRebondGrace); _repository.SetUnbondingSet( _repository.GetUnbondingSet().SetUnbonding(srcRebondGrace)); @@ -196,6 +200,11 @@ public virtual void CancelUndelegate( unbondLockIn = unbondLockIn.Cancel(fav, height); Delegatees = Delegatees.Add(delegatee.Address); + if (unbondLockIn.IsEmpty) + { + delegatee.RemoveUnbondingRef(UnbondingFactory.ToReference(unbondLockIn)); + } + _repository.SetUnbondLockIn(unbondLockIn); _repository.SetUnbondingSet( _repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index 3a504215b7..93e5275135 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -24,6 +24,8 @@ public interface IDelegatee : IBencodable, IEquatable int MaxRebondGraceEntries { get; } + BigInteger SlashFactor { get; } + Address RewardCollectorAddress { get; } Address RewardDistributorAddress { get; } @@ -42,6 +44,8 @@ public interface IDelegatee : IBencodable, IEquatable void CollectRewards(long height); + void Slash(long infractionHeight); + Address BondAddress(Address delegatorAddress); Address UnbondLockInAddress(Address delegatorAddress); diff --git a/Lib9c/Delegation/IUnbonding.cs b/Lib9c/Delegation/IUnbonding.cs index 5df39151ef..d858c16936 100644 --- a/Lib9c/Delegation/IUnbonding.cs +++ b/Lib9c/Delegation/IUnbonding.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Libplanet.Crypto; namespace Nekoyume.Delegation @@ -14,6 +15,6 @@ public interface IUnbonding IUnbonding Release(long height); - IUnbonding Slash(); + IUnbonding Slash(BigInteger slashFactor, long infractionHeight); } } diff --git a/Lib9c/Delegation/RebondGrace.cs b/Lib9c/Delegation/RebondGrace.cs index 6910a152a1..d1c0bc199d 100644 --- a/Lib9c/Delegation/RebondGrace.cs +++ b/Lib9c/Delegation/RebondGrace.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Numerics; using Bencodex; using Bencodex.Types; using Libplanet.Crypto; @@ -134,11 +135,16 @@ public RebondGrace Release(long height) IUnbonding IUnbonding.Release(long height) => Release(height); - [Obsolete("This method is not implemented yet.")] - public RebondGrace Slash() - => throw new NotImplementedException(); + public RebondGrace Slash(BigInteger slashFactor, long infractionHeight) + => UpdateEntries(Entries.TakeWhile(e => e.Key >= infractionHeight) + .Select(kv => KeyValuePair.Create( + kv.Key, + kv.Value.Select(v => v.Slash(slashFactor, infractionHeight)).ToImmutableList())) + .Concat(Entries.SkipWhile(e => e.Key >= infractionHeight)) + .ToImmutableSortedDictionary()); - IUnbonding IUnbonding.Slash() => Slash(); + IUnbonding IUnbonding.Slash(BigInteger slashFactor, long infractionHeight) + => Slash(slashFactor, infractionHeight); public override bool Equals(object? obj) => obj is RebondGrace other && Equals(other); @@ -335,9 +341,23 @@ public override int GetHashCode() return hash; } - [Obsolete("This method is not implemented yet.")] - public RebondGraceEntry Slash() - => throw new NotImplementedException(); + public RebondGraceEntry Slash(BigInteger slashFactor, long infractionHeight) + { + if (CreationHeight > infractionHeight || + ExpireHeight < infractionHeight) + { + throw new ArgumentOutOfRangeException( + nameof(infractionHeight), + infractionHeight, + "The infraction height must be between in creation height and expire height of entry."); + } + + return new RebondGraceEntry( + RebondeeAddress, + GraceFAV - GraceFAV.DivRem(slashFactor).Quotient, + CreationHeight, + ExpireHeight); + } } public class RebondGraceEntryComparer : IComparer diff --git a/Lib9c/Delegation/UnbondLockIn.cs b/Lib9c/Delegation/UnbondLockIn.cs index 11e85768d6..99ce47f55b 100644 --- a/Lib9c/Delegation/UnbondLockIn.cs +++ b/Lib9c/Delegation/UnbondLockIn.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Numerics; using Bencodex; using Bencodex.Types; using Libplanet.Crypto; @@ -172,12 +173,16 @@ public UnbondLockIn Release(long height) IUnbonding IUnbonding.Release(long height) => Release(height); - [Obsolete("This method is not implemented yet.")] - public UnbondLockIn Slash() - => throw new NotImplementedException(); - - IUnbonding IUnbonding.Slash() => Slash(); + public UnbondLockIn Slash(BigInteger slashFactor, long infractionHeight) + => UpdateEntries(Entries.TakeWhile(e => e.Key >= infractionHeight) + .Select(kv => KeyValuePair.Create( + kv.Key, + kv.Value.Select(v => v.Slash(slashFactor, infractionHeight)).ToImmutableList())) + .Concat(Entries.SkipWhile(e => e.Key >= infractionHeight)) + .ToImmutableSortedDictionary()); + IUnbonding IUnbonding.Slash(BigInteger slashFactor, long infractionHeight) + => Slash(slashFactor, infractionHeight); public override bool Equals(object? obj) => obj is UnbondLockIn other && Equals(other); @@ -432,9 +437,23 @@ public override int GetHashCode() return hash; } - [Obsolete("This method is not implemented yet.")] - public UnbondLockInEntry Slash() - => throw new NotImplementedException(); + public UnbondLockInEntry Slash(BigInteger slashFactor, long infractionHeight) + { + if (CreationHeight > infractionHeight || + ExpireHeight < infractionHeight) + { + throw new ArgumentOutOfRangeException( + nameof(infractionHeight), + infractionHeight, + "The infraction height must be between in creation height and expire height of entry."); + } + + return new UnbondLockInEntry( + InitialLockInFAV, + LockInFAV - LockInFAV.DivRem(slashFactor).Quotient, + CreationHeight, + ExpireHeight); + } internal UnbondLockInEntry Cancel(FungibleAssetValue cancellingFAV) { diff --git a/Lib9c/Delegation/UnbondingFactory.cs b/Lib9c/Delegation/UnbondingFactory.cs new file mode 100644 index 0000000000..67f549865b --- /dev/null +++ b/Lib9c/Delegation/UnbondingFactory.cs @@ -0,0 +1,31 @@ +using System; +using Bencodex.Types; + +namespace Nekoyume.Delegation +{ + public static class UnbondingFactory + { + public static IUnbonding GetUnbondingFromRef( + UnbondingRef reference, IDelegationRepository repository) + => reference.UnbondingType switch + { + UnbondingType.UnbondLockIn => repository.GetUnlimitedUnbondLockIn(reference.Address), + UnbondingType.RebondGrace => repository.GetUnlimitedRebondGrace(reference.Address), + _ => throw new ArgumentException("Invalid unbonding type.") + }; + + public static IUnbonding GetUnbondingFromRef( + IValue bencoded, IDelegationRepository repository) + => GetUnbondingFromRef(new UnbondingRef(bencoded), repository); + + public static UnbondingRef ToReference(IUnbonding unbonding) + => unbonding switch + { + UnbondLockIn unbondLockIn + => new UnbondingRef(unbonding.Address, UnbondingType.UnbondLockIn), + RebondGrace rebondGrace + => new UnbondingRef(unbonding.Address, UnbondingType.RebondGrace), + _ => throw new ArgumentException("Invalid unbonding type.") + }; + } +} diff --git a/Lib9c/Delegation/UnbondingRef.cs b/Lib9c/Delegation/UnbondingRef.cs new file mode 100644 index 0000000000..d14d256e4f --- /dev/null +++ b/Lib9c/Delegation/UnbondingRef.cs @@ -0,0 +1,95 @@ +#nullable enable +using Bencodex; +using Bencodex.Types; +using Libplanet.Crypto; +using System; + +namespace Nekoyume.Delegation +{ + public class UnbondingRef : IEquatable, IComparable, IComparable, IBencodable + { + public UnbondingRef(Address address, UnbondingType unbondingType) + { + Address = address; + UnbondingType = unbondingType; + } + + public UnbondingRef(IValue bencoded) + : this((List)bencoded) + { + } + + public UnbondingRef(List list) + : this(new Address((Binary)list[0]), (UnbondingType)(int)(Integer)list[1]) + { + } + + public Address Address { get; } + + public UnbondingType UnbondingType { get; } + + public List Bencoded => List.Empty + .Add(Address.Bencoded) + .Add((int)UnbondingType); + + public static bool operator ==(UnbondingRef? left, UnbondingRef? right) + => left?.Equals(right) ?? right is null; + + public static bool operator !=(UnbondingRef? left, UnbondingRef? right) + => !(left == right); + + public static bool operator <(UnbondingRef left, UnbondingRef right) + => left.CompareTo(right) < 0; + + public static bool operator <=(UnbondingRef left, UnbondingRef right) + => left.CompareTo(right) <= 0; + + public static bool operator >(UnbondingRef left, UnbondingRef right) + => left.CompareTo(right) > 0; + + public static bool operator >=(UnbondingRef left, UnbondingRef right) + => left.CompareTo(right) >= 0; + + IValue IBencodable.Bencoded => Bencoded; + + public int CompareTo(object? obj) + => obj is UnbondingRef other + ? CompareTo(other) + : throw new ArgumentException("Object is not a UnbondingRef."); + + public int CompareTo(UnbondingRef? other) + { + if (ReferenceEquals(this, other)) + { + return 0; + } + + if (other is null) + { + return 1; + } + + int addressComparison = Address.CompareTo(other.Address); + if (addressComparison != 0) + { + return addressComparison; + } + + return UnbondingType.CompareTo(other.UnbondingType); + } + + public override bool Equals(object? obj) + => obj is UnbondingRef other && Equals(other); + + public bool Equals(UnbondingRef? other) + => ReferenceEquals(this, other) + || (other is UnbondingRef unbondingRef + && Address.Equals(unbondingRef.Address) + && UnbondingType == unbondingRef.UnbondingType); + + public override int GetHashCode() + { + return HashCode.Combine(Address, UnbondingType); + } + } +} diff --git a/Lib9c/Delegation/UnbondingSet.cs b/Lib9c/Delegation/UnbondingSet.cs index c9be83e3df..b9394caa44 100644 --- a/Lib9c/Delegation/UnbondingSet.cs +++ b/Lib9c/Delegation/UnbondingSet.cs @@ -11,18 +11,13 @@ namespace Nekoyume.Delegation { public sealed class UnbondingSet : IBencodable { - private static readonly byte[] _unbondLockInTypeBytes = new byte[] { 0x75 }; // 'u' - private static readonly byte[] _rebondGraceTypeBytes = new byte[] { 0x72 }; // 'r' - private readonly IDelegationRepository _repository; - private ImmutableSortedDictionary _lowestExpireHeights; - private ImmutableSortedDictionary _typeDict; + private ImmutableSortedDictionary _lowestExpireHeights; public UnbondingSet(IDelegationRepository repository) : this( - ImmutableSortedDictionary>.Empty, - ImmutableSortedDictionary.Empty, - ImmutableSortedDictionary.Empty, + ImmutableSortedDictionary>.Empty, + ImmutableSortedDictionary.Empty, repository) { } @@ -35,33 +30,26 @@ public UnbondingSet(IValue bencoded, IDelegationRepository repository) public UnbondingSet(List bencoded, IDelegationRepository repository) : this( ((List)bencoded[0]).Select( - kv => new KeyValuePair>( + kv => new KeyValuePair>( (Integer)((List)kv)[0], - ((List)((List)kv)[1]).Select(a => new Address(a)).ToImmutableSortedSet())) + ((List)((List)kv)[1]).Select(a => new UnbondingRef(a)).ToImmutableSortedSet())) .ToImmutableSortedDictionary(), ((List)bencoded[1]).Select( - kv => new KeyValuePair( - new Address(((List)kv)[0]), + kv => new KeyValuePair( + new UnbondingRef(((List)kv)[0]), (Integer)((List)kv)[1])) .ToImmutableSortedDictionary(), - ((List)bencoded[2]).Select( - kv => new KeyValuePair( - new Address(((List)kv)[0]), - ((Binary)((List)kv)[1]).ToArray())) - .ToImmutableSortedDictionary(), repository) { } private UnbondingSet( - ImmutableSortedDictionary> unbondings, - ImmutableSortedDictionary lowestExpireHeights, - ImmutableSortedDictionary typeDict, + ImmutableSortedDictionary> unbondings, + ImmutableSortedDictionary lowestExpireHeights, IDelegationRepository repository) { - Unbondings = unbondings; + UnbondingRefs = unbondings; _lowestExpireHeights = lowestExpireHeights; - _typeDict = typeDict; _repository = repository; } @@ -70,17 +58,17 @@ private UnbondingSet( 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55)); - public ImmutableSortedDictionary> Unbondings { get; } + public ImmutableSortedDictionary> UnbondingRefs { get; } - public ImmutableArray
FlattenedUnbondings - => Unbondings.Values.SelectMany(e => e).ToImmutableArray(); + public ImmutableArray FlattenedUnbondingRefs + => UnbondingRefs.Values.SelectMany(e => e).ToImmutableArray(); public IDelegationRepository Repository => _repository; public List Bencoded => List.Empty .Add(new List( - Unbondings.Select( + UnbondingRefs.Select( sortedDict => new List( (Integer)sortedDict.Key, new List(sortedDict.Value.Select(a => a.Bencoded)))))) @@ -88,27 +76,17 @@ public List Bencoded _lowestExpireHeights.Select( sortedDict => new List( sortedDict.Key.Bencoded, - (Integer)sortedDict.Value)))) - .Add(new List( - _typeDict.Select( - sortedDict => new List( - sortedDict.Key.Bencoded, - (Binary)sortedDict.Value)))); + (Integer)sortedDict.Value)))); IValue IBencodable.Bencoded => Bencoded; - public bool IsEmpty => Unbondings.IsEmpty; + public bool IsEmpty => UnbondingRefs.IsEmpty; public ImmutableArray UnbondingsToRelease(long height) - => Unbondings + => UnbondingRefs .TakeWhile(kv => kv.Key <= height) .SelectMany(kv => kv.Value) - .Select(address => ( - Address: address, - Type: ToUnbondingType(_typeDict[address]))) - .Select(tuple => LoadUnbonding( - tuple.Address, - tuple.Type)) + .Select(unbondingRef => UnbondingFactory.GetUnbondingFromRef(unbondingRef, _repository)) .ToImmutableArray(); public UnbondingSet SetUnbondings(IEnumerable unbondings) @@ -128,7 +106,7 @@ public UnbondingSet SetUnbonding(IUnbonding unbonding) { try { - return RemoveUnbonding(unbonding.Address); + return RemoveUnbonding(unbonding); } catch (ArgumentException) { @@ -136,46 +114,44 @@ public UnbondingSet SetUnbonding(IUnbonding unbonding) } } - if (_lowestExpireHeights.TryGetValue(unbonding.Address, out var lowestExpireHeight)) + UnbondingRef unbondigRef = UnbondingFactory.ToReference(unbonding); + + if (_lowestExpireHeights.TryGetValue(unbondigRef, out var lowestExpireHeight)) { if (lowestExpireHeight == unbonding.LowestExpireHeight) { return this; } - var addresses = Unbondings[lowestExpireHeight]; + var refs = UnbondingRefs[lowestExpireHeight]; return new UnbondingSet( - Unbondings.SetItem( + UnbondingRefs.SetItem( unbonding.LowestExpireHeight, - addresses.Add(unbonding.Address)), + refs.Add(unbondigRef)), _lowestExpireHeights.SetItem( - unbonding.Address, unbonding.LowestExpireHeight), - _typeDict.SetItem( - unbonding.Address, ToTypeBytes(unbonding)), + unbondigRef, unbonding.LowestExpireHeight), _repository); } return new UnbondingSet( - Unbondings.SetItem( + UnbondingRefs.SetItem( unbonding.LowestExpireHeight, - ImmutableSortedSet
.Empty.Add(unbonding.Address)), + ImmutableSortedSet.Empty.Add(unbondigRef)), _lowestExpireHeights.SetItem( - unbonding.Address, unbonding.LowestExpireHeight), - _typeDict.SetItem( - unbonding.Address, ToTypeBytes(unbonding)), + unbondigRef, unbonding.LowestExpireHeight), _repository); } - - private UnbondingSet RemoveUnbonding(Address address) + private UnbondingSet RemoveUnbonding(IUnbonding unbonding) { - if (_lowestExpireHeights.TryGetValue(address, out var expireHeight) - && Unbondings.TryGetValue(expireHeight, out var addresses)) + UnbondingRef unbondigRef = UnbondingFactory.ToReference(unbonding); + + if (_lowestExpireHeights.TryGetValue(unbondigRef, out var expireHeight) + && UnbondingRefs.TryGetValue(expireHeight, out var refs)) { return new UnbondingSet( - Unbondings.SetItem(expireHeight, addresses.Remove(address)), - _lowestExpireHeights.Remove(address), - _typeDict.Remove(address), + UnbondingRefs.SetItem(expireHeight, refs.Remove(unbondigRef)), + _lowestExpireHeights.Remove(unbondigRef), _repository); } else @@ -183,30 +159,5 @@ private UnbondingSet RemoveUnbonding(Address address) throw new ArgumentException("The address is not in the unbonding set."); } } - - private static byte[] ToTypeBytes(IUnbonding unbonding) - => unbonding switch - { - UnbondLockIn _ => _unbondLockInTypeBytes, - RebondGrace _ => _rebondGraceTypeBytes, - _ => throw new ArgumentException("Invalid unbonding type.") - }; - - private static Type ToUnbondingType(byte[] typeBytes) => typeBytes switch - { - _ when typeBytes.SequenceEqual(_unbondLockInTypeBytes) - => typeof(UnbondLockIn), - _ when typeBytes.SequenceEqual(_rebondGraceTypeBytes) - => typeof(RebondGrace), - _ => throw new ArgumentException("Invalid type bytes.") - }; - - private IUnbonding LoadUnbonding(Address address, Type type) - => type switch - { - var t when t == typeof(UnbondLockIn) => _repository.GetUnlimitedUnbondLockIn(address), - var t when t == typeof(RebondGrace) => _repository.GetUnlimitedRebondGrace(address), - _ => throw new ArgumentException("Invalid unbonding type.") - }; } } diff --git a/Lib9c/Delegation/UnbondingType.cs b/Lib9c/Delegation/UnbondingType.cs new file mode 100644 index 0000000000..324b5f90e9 --- /dev/null +++ b/Lib9c/Delegation/UnbondingType.cs @@ -0,0 +1,9 @@ +#nullable enable +namespace Nekoyume.Delegation +{ + public enum UnbondingType + { + UnbondLockIn, + RebondGrace, + } +} diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index 054651997f..dac87095c3 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; using Bencodex; using Bencodex.Types; using Lib9c; @@ -67,6 +68,8 @@ public Guild(List list, Currency rewardCurrency, IDelegationRepository repositor public override int MaxRebondGraceEntries => 10; + public override BigInteger SlashFactor => BigInteger.One; + public override List Bencoded => List.Empty .Add(StateTypeName) .Add(StateVersion) diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index de086c17cd..2e7e6bb53d 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -56,6 +56,8 @@ public ValidatorDelegatee(Address address, List bencoded, Currency rewardCurrenc public override int MaxRebondGraceEntries => 10; + public override BigInteger SlashFactor => 10; + public override List Bencoded => List.Empty .Add(base.Bencoded) .Add(Publickey.Format(true)) From dabe99ad4e288727564f1274b1a9d5e759005507 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 30 Aug 2024 11:50:47 +0900 Subject: [PATCH 036/206] test: Test build fix from slashing --- .Lib9c.Tests/Delegation/DelegatorTest.cs | 8 ++++---- .Lib9c.Tests/Delegation/DummyDelegatee.cs | 3 +++ .Lib9c.Tests/Delegation/TestDelegatee.cs | 3 +++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.Lib9c.Tests/Delegation/DelegatorTest.cs b/.Lib9c.Tests/Delegation/DelegatorTest.cs index ee1b0837f2..f8ce28889f 100644 --- a/.Lib9c.Tests/Delegation/DelegatorTest.cs +++ b/.Lib9c.Tests/Delegation/DelegatorTest.cs @@ -115,7 +115,7 @@ public void Undelegate() Assert.Equal(initialShare - undelegatingShare, delegatee.TotalShares); Assert.Equal(delegator.Address, Assert.Single(delegatee.Delegators)); Assert.Equal(delegatee.Address, Assert.Single(delegator.Delegatees)); - Assert.Equal(unbondLockIn.Address, Assert.Single(unbondingSet.FlattenedUnbondings)); + Assert.Equal(unbondLockIn.Address, Assert.Single(unbondingSet.FlattenedUnbondingRefs).Address); var entriesByExpireHeight = Assert.Single(unbondLockIn.Entries); Assert.Equal(10L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); var entry = Assert.Single(entriesByExpireHeight.Value); @@ -139,7 +139,7 @@ public void Undelegate() Assert.Equal(System.Numerics.BigInteger.Zero, delegatee.TotalShares); Assert.Empty(delegator.Delegatees); Assert.Empty(delegatee.Delegators); - Assert.Equal(unbondLockIn.Address, Assert.Single(unbondingSet.FlattenedUnbondings)); + Assert.Equal(unbondLockIn.Address, Assert.Single(unbondingSet.FlattenedUnbondingRefs).Address); Assert.Equal(2, unbondLockIn.Entries.Count); unbondLockIn = unbondLockIn.Release(10L + delegatee.UnbondingPeriod - 1); @@ -219,7 +219,7 @@ public void Redelegate() Assert.Equal(delegator.Address, Assert.Single(delegatee1.Delegators)); Assert.Equal(delegator.Address, Assert.Single(delegatee2.Delegators)); Assert.Equal(2, delegator.Delegatees.Count); - Assert.Equal(rebondGrace.Address, Assert.Single(unbondingSet.FlattenedUnbondings)); + Assert.Equal(rebondGrace.Address, Assert.Single(unbondingSet.FlattenedUnbondingRefs).Address); var entriesByExpireHeight = Assert.Single(rebondGrace.Entries); Assert.Equal(10L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); var entry = Assert.Single(entriesByExpireHeight.Value); @@ -251,7 +251,7 @@ public void Redelegate() Assert.Empty(delegatee1.Delegators); Assert.Equal(delegator.Address, Assert.Single(delegatee2.Delegators)); Assert.Equal(delegatee2.Address, Assert.Single(delegator.Delegatees)); - Assert.Equal(rebondGrace.Address, Assert.Single(unbondingSet.FlattenedUnbondings)); + Assert.Equal(rebondGrace.Address, Assert.Single(unbondingSet.FlattenedUnbondingRefs).Address); Assert.Equal(2, rebondGrace.Entries.Count); rebondGrace = rebondGrace.Release(10L + delegatee1.UnbondingPeriod - 1); diff --git a/.Lib9c.Tests/Delegation/DummyDelegatee.cs b/.Lib9c.Tests/Delegation/DummyDelegatee.cs index 1febdc41e6..7eeea0519e 100644 --- a/.Lib9c.Tests/Delegation/DummyDelegatee.cs +++ b/.Lib9c.Tests/Delegation/DummyDelegatee.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Lib9c.Tests.Delegation; using Libplanet.Crypto; using Libplanet.Types.Assets; @@ -23,4 +24,6 @@ public DummyDelegatee(Address address, IDelegationRepository repository) public override int MaxUnbondLockInEntries => 5; public override int MaxRebondGraceEntries => 5; + + public override BigInteger SlashFactor => 1; } diff --git a/.Lib9c.Tests/Delegation/TestDelegatee.cs b/.Lib9c.Tests/Delegation/TestDelegatee.cs index 7a46d1f8fb..0492b05280 100644 --- a/.Lib9c.Tests/Delegation/TestDelegatee.cs +++ b/.Lib9c.Tests/Delegation/TestDelegatee.cs @@ -1,5 +1,6 @@ namespace Lib9c.Tests.Delegation { + using System.Numerics; using Bencodex.Types; using Libplanet.Crypto; using Libplanet.Types.Assets; @@ -30,5 +31,7 @@ public TestDelegatee(Address address, IValue bencoded, IDelegationRepository rep public override int MaxUnbondLockInEntries => 5; public override int MaxRebondGraceEntries => 5; + + public override BigInteger SlashFactor => 1; } } From 11e96eeec036e87d0d73d7823f561af1f253d5ff Mon Sep 17 00:00:00 2001 From: ilgyu Date: Tue, 3 Sep 2024 14:13:22 +0900 Subject: [PATCH 037/206] feat: Refactor and update for validator delegation --- .Lib9c.Benchmarks/Actions/AutoJoinGuild.cs | 10 +- .../Actions/MigratePledgeToGuild.cs | 10 +- .../Guild/AcceptGuildApplicationTest.cs | 15 +- .Lib9c.Tests/Action/Guild/ApplyGuildTest.cs | 16 +- .../Action/Guild/BanGuildMemberTest.cs | 114 ++--- .../Guild/CancelGuildApplicationTest.cs | 20 +- .Lib9c.Tests/Action/Guild/MakeGuildTest.cs | 6 +- .../Guild/Migration/GuildMigrationCtrlTest.cs | 60 +-- .../Migration/MigratePledgeToGuildTest.cs | 72 ++-- .Lib9c.Tests/Action/Guild/QuitGuildTest.cs | 19 +- .../Guild/RejectGuildApplicationTest.cs | 25 +- .Lib9c.Tests/Action/Guild/RemoveGuildTest.cs | 41 +- .../Action/Guild/UnbanGuildMemberTest.cs | 30 +- .../ValidatorDelegation/AllocateRewardTest.cs | 10 +- .../DelegateValidatorTest.cs | 10 +- .../PromoteValidatorTest.cs | 9 +- .../RedelegateValidatorTest.cs | 17 +- .../UndelegateValidatorTest.cs | 17 +- .Lib9c.Tests/Delegation/DelegateeTest.cs | 26 +- .Lib9c.Tests/Delegation/DelegationFixture.cs | 4 +- .Lib9c.Tests/Delegation/DelegatorTest.cs | 61 ++- .Lib9c.Tests/Delegation/DummyDelegatee.cs | 30 +- .Lib9c.Tests/Delegation/DummyDelegator.cs | 5 + .Lib9c.Tests/Delegation/DummyRepository.cs | 61 +++ .Lib9c.Tests/Delegation/TestDelegatee.cs | 34 +- .Lib9c.Tests/Delegation/TestDelegationRepo.cs | 166 -------- .Lib9c.Tests/Delegation/TestDelegator.cs | 7 +- .Lib9c.Tests/Delegation/TestRepository.cs | 62 +++ .../Model/Guild/GuildParticipantTest.cs | 15 +- .Lib9c.Tests/Model/Guild/GuildTest.cs | 21 +- .../Tx/Begin/AutoJoinGuildTest.cs | 47 ++- Lib9c.Policy/Policy/BlockPolicySource.cs | 13 +- Lib9c/Action/Delegate/ReleaseUnbondings.cs | 31 -- Lib9c/Action/Guild/AcceptGuildApplication.cs | 5 +- Lib9c/Action/Guild/ApplyGuild.cs | 5 +- Lib9c/Action/Guild/BanGuildMember.cs | 8 +- Lib9c/Action/Guild/CancelGuildApplication.cs | 5 +- Lib9c/Action/Guild/MakeGuild.cs | 5 +- .../Migration/Controls/GuildMigrationCtrl.cs | 16 +- .../Guild/Migration/MigratePledgeToGuild.cs | 5 +- Lib9c/Action/Guild/QuitGuild.cs | 5 +- Lib9c/Action/Guild/RejectGuildApplication.cs | 5 +- Lib9c/Action/Guild/RemoveGuild.cs | 5 +- Lib9c/Action/Guild/UnbanGuildMember.cs | 7 +- Lib9c/Action/RewardGold.cs | 2 +- .../ValidatorDelegation/AllocateReward.cs | 57 +-- .../ClaimRewardValidator.cs | 7 +- .../ValidatorDelegation/DelegateValidator.cs | 7 +- .../ValidatorDelegation/PromoteValidator.cs | 11 +- .../RedelegateValidator.cs | 7 +- .../ReleaseValidatorUnbondings.cs | 50 +++ .../ValidatorDelegation/SlashValidator.cs | 87 ++++ .../UndelegateValidator.cs | 7 +- .../ValidatorDelegation/UnjailValidator.cs | 41 ++ .../ValidatorDelegation/UpdateValidators.cs | 39 ++ Lib9c/Addresses.cs | 82 +++- Lib9c/Delegation/Bond.cs | 24 +- Lib9c/Delegation/Delegatee.cs | 395 ++++++++---------- Lib9c/Delegation/DelegateeMetadata.cs | 307 ++++++++++++++ Lib9c/Delegation/DelegationAddress.cs | 147 +++++++ Lib9c/Delegation/DelegationRepository.cs | 249 +++++++---- Lib9c/Delegation/Delegator.cs | 198 ++++----- Lib9c/Delegation/DelegatorMetadata.cs | 102 +++++ Lib9c/Delegation/IDelegatee.cs | 26 +- Lib9c/Delegation/IDelegateeMetadata.cs | 51 +++ Lib9c/Delegation/IDelegationRepository.cs | 20 + Lib9c/Delegation/IDelegator.cs | 12 +- Lib9c/Delegation/IDelegatorMetadata.cs | 23 + Lib9c/Delegation/IUnbonding.cs | 7 +- Lib9c/Delegation/IUnbondingEntry.cs | 7 - Lib9c/Delegation/LumpSumRewardsRecord.cs | 2 +- Lib9c/Delegation/RebondGrace.cs | 286 +++---------- Lib9c/Delegation/UnbondLockIn.cs | 330 ++++----------- Lib9c/Delegation/UnbondingEntry.cs | 238 +++++++++++ Lib9c/Model/Guild/Guild.cs | 65 ++- Lib9c/Model/Guild/GuildParticipant.cs | 48 ++- Lib9c/Model/Guild/GuildRepository.cs | 79 ++++ Lib9c/Module/Delegation/BondModule.cs | 43 -- .../Delegation/LumpSumRewardsRecordModule.cs | 48 --- Lib9c/Module/Delegation/RebondGraceModule.cs | 49 --- Lib9c/Module/Delegation/UnbondLockInModule.cs | 50 --- Lib9c/Module/Guild/GuildApplicationModule.cs | 73 ++-- Lib9c/Module/Guild/GuildBanModule.cs | 43 +- .../Module/Guild/GuildMemberCounterModule.cs | 29 +- Lib9c/Module/Guild/GuildModule.cs | 109 ++--- Lib9c/Module/Guild/GuildParticipantModule.cs | 213 +++------- .../GuildUnbondingModule.cs} | 8 +- .../AbstainHistoryModule.cs | 37 ++ .../ValidatorDelegateeModule.cs | 72 +--- .../ValidatorDelegatorModule.cs | 114 +---- .../ValidatorListModule.cs | 29 +- .../ValidatorUnbondingModule.cs | 41 ++ Lib9c/PolicyAction/Tx/Begin/AutoJoinGuild.cs | 6 +- Lib9c/ValidatorDelegation/AbstainHistory.cs | 93 +++++ .../ValidatorDelegation/ValidatorDelegatee.cs | 167 +++++--- .../ValidatorDelegation/ValidatorDelegator.cs | 29 +- Lib9c/ValidatorDelegation/ValidatorList.cs | 2 +- .../ValidatorRepository.cs | 76 +++- 98 files changed, 3180 insertions(+), 2319 deletions(-) create mode 100644 .Lib9c.Tests/Delegation/DummyRepository.cs delete mode 100644 .Lib9c.Tests/Delegation/TestDelegationRepo.cs create mode 100644 .Lib9c.Tests/Delegation/TestRepository.cs delete mode 100644 Lib9c/Action/Delegate/ReleaseUnbondings.cs create mode 100644 Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs create mode 100644 Lib9c/Action/ValidatorDelegation/SlashValidator.cs create mode 100644 Lib9c/Action/ValidatorDelegation/UnjailValidator.cs create mode 100644 Lib9c/Action/ValidatorDelegation/UpdateValidators.cs create mode 100644 Lib9c/Delegation/DelegateeMetadata.cs create mode 100644 Lib9c/Delegation/DelegationAddress.cs create mode 100644 Lib9c/Delegation/DelegatorMetadata.cs create mode 100644 Lib9c/Delegation/IDelegateeMetadata.cs create mode 100644 Lib9c/Delegation/IDelegatorMetadata.cs delete mode 100644 Lib9c/Delegation/IUnbondingEntry.cs create mode 100644 Lib9c/Delegation/UnbondingEntry.cs create mode 100644 Lib9c/Model/Guild/GuildRepository.cs delete mode 100644 Lib9c/Module/Delegation/BondModule.cs delete mode 100644 Lib9c/Module/Delegation/LumpSumRewardsRecordModule.cs delete mode 100644 Lib9c/Module/Delegation/RebondGraceModule.cs delete mode 100644 Lib9c/Module/Delegation/UnbondLockInModule.cs rename Lib9c/Module/{Delegation/UnbondingSetModule.cs => Guild/GuildUnbondingModule.cs} (87%) create mode 100644 Lib9c/Module/ValidatorDelegation/AbstainHistoryModule.cs create mode 100644 Lib9c/Module/ValidatorDelegation/ValidatorUnbondingModule.cs create mode 100644 Lib9c/ValidatorDelegation/AbstainHistory.cs diff --git a/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs b/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs index 426ceeeb89..0e693d38d6 100644 --- a/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs +++ b/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs @@ -7,6 +7,7 @@ using Nekoyume; using Nekoyume.Action.Guild; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.Module; using Nekoyume.Module.Guild; using Nekoyume.TypedAddress; @@ -32,10 +33,11 @@ public void Setup() var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); - worldWithPledgeAndGuild = worldWithPledge - .MakeGuild(guildAddress, guildMasterAddress); - worldAfterMigration = worldWithPledgeAndGuild - .JoinGuild(guildAddress, signer); + var repository = new GuildRepository(worldWithPledge, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + worldWithPledgeAndGuild = repository.World; + repository.JoinGuild(guildAddress, signer); + worldAfterMigration = repository.World; } [Benchmark] diff --git a/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs b/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs index 2edf048b8e..e01e2aa102 100644 --- a/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs +++ b/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs @@ -7,6 +7,7 @@ using Nekoyume; using Nekoyume.Action.Guild; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.Module; using Nekoyume.Module.Guild; using Nekoyume.TypedAddress; @@ -33,10 +34,11 @@ public void Setup() var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); - worldWithPledgeAndGuild = worldWithPledge - .MakeGuild(guildAddress, guildMasterAddress); - worldAfterMigration = worldWithPledgeAndGuild - .JoinGuild(guildAddress, signer); + var repository = new GuildRepository(worldWithPledge, new ActionContext()); + worldWithPledgeAndGuild = repository + .MakeGuild(guildAddress, guildMasterAddress).World; + worldAfterMigration = repository + .JoinGuild(guildAddress, signer).World; } [Benchmark] diff --git a/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs b/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs index 5ef4576d2f..f2acb87aa4 100644 --- a/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs +++ b/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action.Guild 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; @@ -38,9 +39,11 @@ public void Execute() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .ApplyGuild(appliedMemberAddress, guildAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.ApplyGuild(appliedMemberAddress, guildAddress); // These cases should fail because the member didn't apply the guild and // non-guild-master-addresses cannot accept the guild application. @@ -83,8 +86,10 @@ public void Execute() Signer = guildMasterAddress, }); - Assert.False(world.TryGetGuildApplication(appliedMemberAddress, out _)); - Assert.Equal(guildAddress, world.GetJoinedGuild(appliedMemberAddress)); + repository = new GuildRepository(world, new ActionContext()); + + Assert.False(repository.TryGetGuildApplication(appliedMemberAddress, out _)); + Assert.Equal(guildAddress, repository.GetJoinedGuild(appliedMemberAddress)); } } } diff --git a/.Lib9c.Tests/Action/Guild/ApplyGuildTest.cs b/.Lib9c.Tests/Action/Guild/ApplyGuildTest.cs index 654ec9c448..6979a335f7 100644 --- a/.Lib9c.Tests/Action/Guild/ApplyGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/ApplyGuildTest.cs @@ -7,7 +7,7 @@ namespace Lib9c.Tests.Action.Guild using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action.Guild; - using Nekoyume.Action.Loader; + using Nekoyume.Model.Guild; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.Module.Guild; @@ -39,15 +39,16 @@ public void Execute() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress); - IWorld bannedWorld = world.Ban(guildAddress, guildMasterAddress, agentAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.JoinGuild(guildAddress, guildMasterAddress); + repository.Ban(guildAddress, guildMasterAddress, agentAddress); // This case should fail because the agent is banned by the guild. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = bannedWorld, + PreviousState = repository.World, Signer = agentAddress, })); @@ -57,7 +58,8 @@ public void Execute() Signer = agentAddress, }); - Assert.True(world.TryGetGuildApplication(agentAddress, out var application)); + repository.UpdateWorld(world); + Assert.True(repository.TryGetGuildApplication(agentAddress, out var application)); Assert.Equal(guildAddress, application.GuildAddress); } } diff --git a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs index 3188be40ca..133d7296a3 100644 --- a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs +++ b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action.Guild 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; @@ -42,21 +43,22 @@ public void Ban_By_GuildMaster() var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - world = world.MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMemberAddress); - world = world.MakeGuild(otherGuildAddress, otherGuildMasterAddress) - .JoinGuild(otherGuildAddress, otherGuildMemberAddress); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.JoinGuild(guildAddress, guildMemberAddress); + repository.MakeGuild(otherGuildAddress, otherGuildMasterAddress); + repository.JoinGuild(otherGuildAddress, otherGuildMemberAddress); // Guild - Assert.False(world.IsBanned(guildAddress, guildMasterAddress)); - Assert.Equal(guildAddress, world.GetJoinedGuild(guildMasterAddress)); - Assert.False(world.IsBanned(guildAddress, guildMemberAddress)); - Assert.Equal(guildAddress, world.GetJoinedGuild(guildMemberAddress)); + Assert.False(repository.IsBanned(guildAddress, guildMasterAddress)); + Assert.Equal(guildAddress, repository.GetJoinedGuild(guildMasterAddress)); + Assert.False(repository.IsBanned(guildAddress, guildMemberAddress)); + Assert.Equal(guildAddress, repository.GetJoinedGuild(guildMemberAddress)); // Other guild - Assert.False(world.IsBanned(guildAddress, otherGuildMasterAddress)); - Assert.Equal(otherGuildAddress, world.GetJoinedGuild(otherGuildMasterAddress)); - Assert.False(world.IsBanned(guildAddress, otherGuildMemberAddress)); - Assert.Equal(otherGuildAddress, world.GetJoinedGuild(otherGuildMemberAddress)); + Assert.False(repository.IsBanned(guildAddress, otherGuildMasterAddress)); + Assert.Equal(otherGuildAddress, repository.GetJoinedGuild(otherGuildMasterAddress)); + Assert.False(repository.IsBanned(guildAddress, otherGuildMemberAddress)); + Assert.Equal(otherGuildAddress, repository.GetJoinedGuild(otherGuildMemberAddress)); var action = new BanGuildMember(guildMemberAddress); world = action.Execute(new ActionContext @@ -66,15 +68,15 @@ public void Ban_By_GuildMaster() }); // Guild - Assert.False(world.IsBanned(guildAddress, guildMasterAddress)); - Assert.Equal(guildAddress, world.GetJoinedGuild(guildMasterAddress)); - Assert.True(world.IsBanned(guildAddress, guildMemberAddress)); - Assert.Null(world.GetJoinedGuild(guildMemberAddress)); + Assert.False(repository.IsBanned(guildAddress, guildMasterAddress)); + Assert.Equal(guildAddress, repository.GetJoinedGuild(guildMasterAddress)); + Assert.True(repository.IsBanned(guildAddress, guildMemberAddress)); + Assert.Null(repository.GetJoinedGuild(guildMemberAddress)); // Other guild - Assert.False(world.IsBanned(guildAddress, otherGuildMasterAddress)); - Assert.Equal(otherGuildAddress, world.GetJoinedGuild(otherGuildMasterAddress)); - Assert.False(world.IsBanned(guildAddress, otherGuildMemberAddress)); - Assert.Equal(otherGuildAddress, world.GetJoinedGuild(otherGuildMemberAddress)); + Assert.False(repository.IsBanned(guildAddress, otherGuildMasterAddress)); + Assert.Equal(otherGuildAddress, repository.GetJoinedGuild(otherGuildMasterAddress)); + Assert.False(repository.IsBanned(guildAddress, otherGuildMemberAddress)); + Assert.Equal(otherGuildAddress, repository.GetJoinedGuild(otherGuildMemberAddress)); action = new BanGuildMember(otherGuildMasterAddress); world = action.Execute(new ActionContext @@ -84,15 +86,15 @@ public void Ban_By_GuildMaster() }); // Guild - Assert.False(world.IsBanned(guildAddress, guildMasterAddress)); - Assert.Equal(guildAddress, world.GetJoinedGuild(guildMasterAddress)); - Assert.True(world.IsBanned(guildAddress, guildMemberAddress)); - Assert.Null(world.GetJoinedGuild(guildMemberAddress)); + Assert.False(repository.IsBanned(guildAddress, guildMasterAddress)); + Assert.Equal(guildAddress, repository.GetJoinedGuild(guildMasterAddress)); + Assert.True(repository.IsBanned(guildAddress, guildMemberAddress)); + Assert.Null(repository.GetJoinedGuild(guildMemberAddress)); // Other guild - Assert.True(world.IsBanned(guildAddress, otherGuildMasterAddress)); - Assert.Equal(otherGuildAddress, world.GetJoinedGuild(otherGuildMasterAddress)); - Assert.False(world.IsBanned(guildAddress, otherGuildMemberAddress)); - Assert.Equal(otherGuildAddress, world.GetJoinedGuild(otherGuildMemberAddress)); + Assert.True(repository.IsBanned(guildAddress, otherGuildMasterAddress)); + Assert.Equal(otherGuildAddress, repository.GetJoinedGuild(otherGuildMasterAddress)); + Assert.False(repository.IsBanned(guildAddress, otherGuildMemberAddress)); + Assert.Equal(otherGuildAddress, repository.GetJoinedGuild(otherGuildMemberAddress)); action = new BanGuildMember(otherGuildMemberAddress); world = action.Execute(new ActionContext @@ -102,15 +104,15 @@ public void Ban_By_GuildMaster() }); // Guild - Assert.False(world.IsBanned(guildAddress, guildMasterAddress)); - Assert.Equal(guildAddress, world.GetJoinedGuild(guildMasterAddress)); - Assert.True(world.IsBanned(guildAddress, guildMemberAddress)); - Assert.Null(world.GetJoinedGuild(guildMemberAddress)); + Assert.False(repository.IsBanned(guildAddress, guildMasterAddress)); + Assert.Equal(guildAddress, repository.GetJoinedGuild(guildMasterAddress)); + Assert.True(repository.IsBanned(guildAddress, guildMemberAddress)); + Assert.Null(repository.GetJoinedGuild(guildMemberAddress)); // Other guild - Assert.True(world.IsBanned(guildAddress, otherGuildMasterAddress)); - Assert.Equal(otherGuildAddress, world.GetJoinedGuild(otherGuildMasterAddress)); - Assert.True(world.IsBanned(guildAddress, otherGuildMemberAddress)); - Assert.Equal(otherGuildAddress, world.GetJoinedGuild(otherGuildMemberAddress)); + Assert.True(repository.IsBanned(guildAddress, otherGuildMasterAddress)); + Assert.Equal(otherGuildAddress, repository.GetJoinedGuild(otherGuildMasterAddress)); + Assert.True(repository.IsBanned(guildAddress, otherGuildMemberAddress)); + Assert.Equal(otherGuildAddress, repository.GetJoinedGuild(otherGuildMemberAddress)); action = new BanGuildMember(guildMasterAddress); // GuildMaster cannot ban itself. @@ -136,15 +138,16 @@ public void Ban_By_GuildMember() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMemberAddress) - .JoinGuild(guildAddress, targetGuildMemberAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.JoinGuild(guildAddress, guildMemberAddress); + repository.JoinGuild(guildAddress, targetGuildMemberAddress); // GuildMember tries to ban other guild member. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMemberAddress, })); @@ -152,7 +155,7 @@ public void Ban_By_GuildMember() action = new BanGuildMember(guildMemberAddress); Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMemberAddress, })); @@ -160,7 +163,7 @@ public void Ban_By_GuildMember() // GuildMember tries to ban other not joined to its guild. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMemberAddress, })); } @@ -179,15 +182,16 @@ public void Ban_By_Other() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, targetGuildMemberAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.JoinGuild(guildAddress, targetGuildMemberAddress); // Other tries to ban GuildMember. var action = new BanGuildMember(targetGuildMemberAddress); Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = otherAddress, })); @@ -211,20 +215,22 @@ public void RejectGuildApplication() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .ApplyGuild(agentAddress, guildAddress); - Assert.True(world.TryGetGuildApplication(agentAddress, out _)); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.ApplyGuild(agentAddress, guildAddress); + Assert.True(repository.TryGetGuildApplication(agentAddress, out _)); var action = new BanGuildMember(agentAddress); world = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMasterAddress, }); - Assert.True(world.IsBanned(guildAddress, agentAddress)); - Assert.False(world.TryGetGuildApplication(agentAddress, out _)); + repository.UpdateWorld(world); + Assert.True(repository.IsBanned(guildAddress, agentAddress)); + Assert.False(repository.TryGetGuildApplication(agentAddress, out _)); } } } diff --git a/.Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs b/.Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs index 81e205a330..f6f2c1d670 100644 --- a/.Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs +++ b/.Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs @@ -8,6 +8,7 @@ namespace Lib9c.Tests.Action.Guild 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; @@ -39,34 +40,37 @@ public void Execute() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); Assert.Throws( () => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = signer, })); var otherAddress = AddressUtil.CreateAgentAddress(); - world = world.ApplyGuild(otherAddress, guildAddress); + repository.ApplyGuild(otherAddress, guildAddress); // It should fail because other agent applied the guild but the signer didn't apply. Assert.Throws( () => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = signer, })); - world = world.ApplyGuild(signer, guildAddress); + repository.ApplyGuild(signer, guildAddress); world = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = signer, }); - Assert.False(world.TryGetGuildApplication(signer, out _)); + repository.UpdateWorld(world); + + Assert.False(repository.TryGetGuildApplication(signer, out _)); } } } diff --git a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs index 4098a43ed2..ea5f700a49 100644 --- a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs @@ -8,6 +8,7 @@ namespace Lib9c.Tests.Action.Guild 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; @@ -68,9 +69,10 @@ public void Execute(AgentAddress guildMasterAddress, bool fail) Signer = guildMasterAddress, }); - var guildAddress = world.GetJoinedGuild(guildMasterAddress); + var repository = new GuildRepository(world, new ActionContext()); + var guildAddress = repository.GetJoinedGuild(guildMasterAddress); Assert.NotNull(guildAddress); - Assert.True(world.TryGetGuild(guildAddress.Value, out var guild)); + Assert.True(repository.TryGetGuild(guildAddress.Value, out var guild)); Assert.Equal(guildMasterAddress, guild.GuildMasterAddress); } } diff --git a/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs b/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs index 1a78310da1..664ece747b 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs @@ -11,6 +11,7 @@ namespace Lib9c.Tests.Action.Guild.Migration using Nekoyume.Action.Guild.Migration; using Nekoyume.Action.Guild.Migration.Controls; using Nekoyume.Extensions; + using Nekoyume.Model.Guild; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.Module.Guild; @@ -30,17 +31,18 @@ public void MigratePlanetariumPledgeToGuild_When_WithUnapprovedPledgeContract() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress) - .SetLegacyState(pledgeAddress, new List( - MeadConfig.PatronAddress.Serialize(), - false.Serialize(), // Unapproved - RequestPledge.DefaultRefillMead.Serialize())); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.JoinGuild(guildAddress, guildMasterAddress); + repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( + MeadConfig.PatronAddress.Serialize(), + false.Serialize(), // Unapproved + RequestPledge.DefaultRefillMead.Serialize()))); - Assert.Null(world.GetJoinedGuild(target)); + Assert.Null(repository.GetJoinedGuild(target)); Assert.Throws(() => - GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(world, target)); + GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(repository, target)); } [Fact] @@ -54,19 +56,20 @@ public void MigratePlanetariumPledgeToGuild_When_WithPledgeContract() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress) - .SetLegacyState(pledgeAddress, new List( - MeadConfig.PatronAddress.Serialize(), - true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize())); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.JoinGuild(guildAddress, guildMasterAddress); + repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( + MeadConfig.PatronAddress.Serialize(), + true.Serialize(), + RequestPledge.DefaultRefillMead.Serialize()))); - Assert.Null(world.GetJoinedGuild(target)); - world = GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(world, target); + Assert.Null(repository.GetJoinedGuild(target)); + GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(repository, target); - var joinedGuildAddress = Assert.IsType(world.GetJoinedGuild(target)); - Assert.True(world.TryGetGuild(joinedGuildAddress, out var guild)); + var joinedGuildAddress = Assert.IsType(repository.GetJoinedGuild(target)); + Assert.True(repository.TryGetGuild(joinedGuildAddress, out var guild)); Assert.Equal(GuildConfig.PlanetariumGuildOwner, guild.GuildMasterAddress); } @@ -80,13 +83,14 @@ public void MigratePlanetariumPledgeToGuild_When_WithoutPledgeContract() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.JoinGuild(guildAddress, guildMasterAddress); - Assert.Null(world.GetJoinedGuild(target)); + Assert.Null(repository.GetJoinedGuild(target)); Assert.Throws(() => - GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(world, target)); + GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(repository, target)); } [Fact] @@ -103,10 +107,10 @@ public void MigratePlanetariumPledgeToGuild_When_WithoutGuildYet() MeadConfig.PatronAddress.Serialize(), true.Serialize(), RequestPledge.DefaultRefillMead.Serialize())); - - Assert.Null(world.GetJoinedGuild(target)); + var repository = new GuildRepository(world, new ActionContext()); + Assert.Null(repository.GetJoinedGuild(target)); Assert.Throws(() => - GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(world, target)); + GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(repository, target)); } } } diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs index 06b5ce90cf..48a49675cc 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs @@ -13,6 +13,7 @@ namespace Lib9c.Tests.Action.Guild.Migration using Nekoyume.Action.Guild.Migration; using Nekoyume.Action.Loader; using Nekoyume.Extensions; + using Nekoyume.Model.Guild; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.Module.Guild; @@ -47,37 +48,40 @@ public void Execute_When_WithPledgeContract() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress) - .SetLegacyState(pledgeAddress, new List( + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.JoinGuild(guildAddress, guildMasterAddress); + repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( MeadConfig.PatronAddress.Serialize(), true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize())); + RequestPledge.DefaultRefillMead.Serialize()))); - Assert.Null(world.GetJoinedGuild(target)); + Assert.Null(repository.GetJoinedGuild(target)); var action = new MigratePledgeToGuild(target); // Migrate by other. IWorld newWorld = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = caller, }); - var joinedGuildAddress = Assert.IsType(newWorld.GetJoinedGuild(target)); - Assert.True(newWorld.TryGetGuild(joinedGuildAddress, out var guild)); + repository.UpdateWorld(newWorld); + var joinedGuildAddress = Assert.IsType(repository.GetJoinedGuild(target)); + Assert.True(repository.TryGetGuild(joinedGuildAddress, out var guild)); Assert.Equal(GuildConfig.PlanetariumGuildOwner, guild.GuildMasterAddress); // Migrate by itself. newWorld = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = target, }); - joinedGuildAddress = Assert.IsType(newWorld.GetJoinedGuild(target)); - Assert.True(newWorld.TryGetGuild(joinedGuildAddress, out guild)); + repository.UpdateWorld(newWorld); + joinedGuildAddress = Assert.IsType(repository.GetJoinedGuild(target)); + Assert.True(repository.TryGetGuild(joinedGuildAddress, out guild)); Assert.Equal(GuildConfig.PlanetariumGuildOwner, guild.GuildMasterAddress); } @@ -93,28 +97,29 @@ public void Execute_When_WithUnapprovedPledgeContract() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress) - .SetLegacyState(pledgeAddress, new List( - MeadConfig.PatronAddress.Serialize(), - false.Serialize(), - RequestPledge.DefaultRefillMead.Serialize())); - - Assert.Null(world.GetJoinedGuild(target)); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.JoinGuild(guildAddress, guildMasterAddress); + repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( + MeadConfig.PatronAddress.Serialize(), + false.Serialize(), + RequestPledge.DefaultRefillMead.Serialize()))); + + Assert.Null(repository.GetJoinedGuild(target)); var action = new MigratePledgeToGuild(target); // Migrate by other. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = caller, })); // Migrate by itself. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = target, })); } @@ -130,24 +135,25 @@ public void Execute_When_WithoutPledgeContract() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.JoinGuild(guildAddress, guildMasterAddress); - Assert.Null(world.GetJoinedGuild(target)); + Assert.Null(repository.GetJoinedGuild(target)); var action = new MigratePledgeToGuild(target); // Migrate by other. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = caller, })); // Migrate by itself. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = target, })); } @@ -163,21 +169,21 @@ public void Execute_When_WithoutGuildYet() MeadConfig.PatronAddress.Serialize(), true.Serialize(), RequestPledge.DefaultRefillMead.Serialize())); - - Assert.Null(world.GetJoinedGuild(target)); + var repository = new GuildRepository(world, new ActionContext()); + Assert.Null(repository.GetJoinedGuild(target)); var action = new MigratePledgeToGuild(target); // Migrate by other. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = caller, })); // Migrate by itself. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = target, })); } diff --git a/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs b/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs index bcc67adfda..dad2cc54a2 100644 --- a/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action.Guild 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; @@ -36,35 +37,37 @@ public void Execute() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); // This case should fail because guild master cannot quit the guild. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMasterAddress, })); // This case should fail because the agent is not a member of the guild. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = agentAddress, })); // Join the guild. - world = world.JoinGuild(guildAddress, agentAddress); - Assert.NotNull(world.GetJoinedGuild(agentAddress)); + repository.JoinGuild(guildAddress, agentAddress); + Assert.NotNull(repository.GetJoinedGuild(agentAddress)); // This case should fail because the agent is not a member of the guild. world = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = agentAddress, }); - Assert.Null(world.GetJoinedGuild(agentAddress)); + repository.UpdateWorld(world); + Assert.Null(repository.GetJoinedGuild(agentAddress)); } } } diff --git a/.Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs b/.Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs index c95b052dd1..02d002f2f9 100644 --- a/.Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs +++ b/.Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action.Guild 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; @@ -38,28 +39,29 @@ public void Execute() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .ApplyGuild(appliedMemberAddress, guildAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.ApplyGuild(appliedMemberAddress, guildAddress); // These cases should fail because the member didn't apply the guild and // non-guild-master-addresses cannot reject the guild application. Assert.Throws( () => new RejectGuildApplication(nonAppliedMemberAddress).Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMasterAddress, })); Assert.Throws( () => new RejectGuildApplication(nonAppliedMemberAddress).Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = appliedMemberAddress, })); Assert.Throws( () => new RejectGuildApplication(nonAppliedMemberAddress).Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = nonAppliedMemberAddress, })); @@ -67,24 +69,25 @@ public void Execute() Assert.Throws( () => new RejectGuildApplication(appliedMemberAddress).Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = appliedMemberAddress, })); Assert.Throws( () => new RejectGuildApplication(appliedMemberAddress).Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = nonAppliedMemberAddress, })); world = new RejectGuildApplication(appliedMemberAddress).Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMasterAddress, }); - Assert.False(world.TryGetGuildApplication(appliedMemberAddress, out _)); - Assert.Null(world.GetJoinedGuild(appliedMemberAddress)); + repository.UpdateWorld(world); + Assert.False(repository.TryGetGuildApplication(appliedMemberAddress, out _)); + Assert.Null(repository.GetJoinedGuild(appliedMemberAddress)); } } } diff --git a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs index fc6ca02420..ecf6705b08 100644 --- a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action.Guild 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; @@ -37,12 +38,13 @@ public void Execute_By_GuildMember() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMemberAddress, })); } @@ -59,17 +61,19 @@ public void Execute_By_GuildMaster() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); var changedWorld = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMasterAddress, }); - Assert.False(changedWorld.TryGetGuild(guildAddress, out _)); - Assert.Null(changedWorld.GetJoinedGuild(guildMasterAddress)); + repository.UpdateWorld(changedWorld); + Assert.False(repository.TryGetGuild(guildAddress, out _)); + Assert.Null(repository.GetJoinedGuild(guildMasterAddress)); } [Fact] @@ -85,12 +89,13 @@ public void Execute_By_Other() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = otherAddress, })); } @@ -108,19 +113,21 @@ public void ResetBannedAddresses() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .Ban(guildAddress, guildMasterAddress, bannedAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.Ban(guildAddress, guildMasterAddress, bannedAddress); - Assert.True(world.IsBanned(guildAddress, bannedAddress)); + Assert.True(repository.IsBanned(guildAddress, bannedAddress)); world = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMasterAddress, }); - Assert.False(world.IsBanned(guildAddress, bannedAddress)); + repository.UpdateWorld(world); + Assert.False(repository.IsBanned(guildAddress, bannedAddress)); } } } diff --git a/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs index 87a6facdc6..4fe7f7e912 100644 --- a/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs +++ b/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs @@ -8,6 +8,7 @@ namespace Lib9c.Tests.Action.Guild 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; @@ -41,21 +42,22 @@ public void Unban_By_GuildMember() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .JoinGuild(guildAddress, guildMemberAddress) - .JoinGuild(guildAddress, targetGuildMemberAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.JoinGuild(guildAddress, guildMemberAddress); + repository.JoinGuild(guildAddress, targetGuildMemberAddress); // GuildMember tries to ban other guild member. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMemberAddress, })); // GuildMember tries to ban itself. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = targetGuildMemberAddress, })); } @@ -73,21 +75,23 @@ public void Unban_By_GuildMaster() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .Ban(guildAddress, guildMasterAddress, targetGuildMemberAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.Ban(guildAddress, guildMasterAddress, targetGuildMemberAddress); - Assert.True(world.IsBanned(guildAddress, targetGuildMemberAddress)); - Assert.Null(world.GetJoinedGuild(targetGuildMemberAddress)); + Assert.True(repository.IsBanned(guildAddress, targetGuildMemberAddress)); + Assert.Null(repository.GetJoinedGuild(targetGuildMemberAddress)); world = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMasterAddress, }); - Assert.False(world.IsBanned(guildAddress, targetGuildMemberAddress)); - Assert.Null(world.GetJoinedGuild(targetGuildMemberAddress)); + repository.UpdateWorld(world); + Assert.False(repository.IsBanned(guildAddress, targetGuildMemberAddress)); + Assert.Null(repository.GetJoinedGuild(targetGuildMemberAddress)); } } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs index d4b708e02a..8344dde4d8 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -59,17 +59,18 @@ public void Execute() var blockHash = new BlockHash(Enumerable.Repeat((byte)0x01, BlockHash.Size).ToArray()); var timestamp = DateTimeOffset.UtcNow; var voteFlags = Enumerable.Range(0, 100).Select(i => i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null).ToArray(); - var bondedSet = world.GetValidatorList().GetBonded(); + var repository = new ValidatorRepository(world, context); + var bondedSet = repository.GetValidatorList().GetBonded(); var proposer = bondedSet.First(); - world = world.SetProposerInfo(new ProposerInfo(9L, proposer.OperatorAddress)); + repository.SetProposerInfo(new ProposerInfo(9L, proposer.OperatorAddress)); var votes = bondedSet.Select( (v, i) => new VoteMetadata( 9L, 0, blockHash, timestamp, v.PublicKey, v.Power, i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null) .Sign(i % 2 == 0 ? privateKeys.First(k => k.PublicKey.Equals(v.PublicKey)) : null)).ToImmutableArray(); var totalReward = ncg * 1000; - world = world.MintAsset(context, Addresses.RewardPool, totalReward); + world = repository.World.MintAsset(context, Addresses.RewardPool, totalReward); context = new ActionContext { @@ -99,10 +100,11 @@ var bonusProposerReward var proposerReward = baseProposerReward + bonusProposerReward; var remains = totalReward - proposerReward; + repository.UpdateWorld(world); foreach (var vote in votes) { - var validator = world.GetValidatorDelegatee(vote.ValidatorPublicKey.Address); + var validator = repository.GetValidatorDelegatee(vote.ValidatorPublicKey.Address); FungibleAssetValue rewardAllocated = (remains * vote.ValidatorPower!.Value).DivRem(totalPower).Quotient; diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index f2943964c1..7444a951aa 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -9,8 +9,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.State; using Nekoyume.Module; - using Nekoyume.Module.Delegation; - using Nekoyume.Module.ValidatorDelegation; + using Nekoyume.ValidatorDelegation; using Xunit; public class DelegateValidatorTest @@ -60,9 +59,10 @@ public void Execute() Signer = publicKey.Address, }); - var validator = world.GetValidatorDelegatee(validatorPublicKey.Address); - var bond = world.GetBond(validator, publicKey.Address); - var validatorList = world.GetValidatorList(); + var repository = new ValidatorRepository(world, context); + var validator = repository.GetValidatorDelegatee(validatorPublicKey.Address); + var bond = repository.GetBond(validator, publicKey.Address); + var validatorList = repository.GetValidatorList(); Assert.Contains(publicKey.Address, validator.Delegators); Assert.Equal(delegateFAV.RawValue, bond.Share); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs index aa52479e47..dd8b70fb03 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs @@ -9,8 +9,8 @@ namespace Lib9c.Tests.Action.ValidatorDelegation using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.State; using Nekoyume.Module; - using Nekoyume.Module.Delegation; using Nekoyume.Module.ValidatorDelegation; + using Nekoyume.ValidatorDelegation; using Xunit; public class PromoteValidatorTest @@ -52,9 +52,10 @@ public void Execute() Signer = publicKey.Address, }); - var validator = world.GetValidatorDelegatee(publicKey.Address); - var bond = world.GetBond(validator, publicKey.Address); - var validatorList = world.GetValidatorList(); + var repository = new ValidatorRepository(world, context); + var validator = repository.GetValidatorDelegatee(publicKey.Address); + var bond = repository.GetBond(validator, publicKey.Address); + var validatorList = repository.GetValidatorList(); Assert.Equal(publicKey.Address, Assert.Single(validator.Delegators)); Assert.Equal(fav.RawValue, bond.Share); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs index b4ba976483..8bd644754e 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs @@ -9,8 +9,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.State; using Nekoyume.Module; - using Nekoyume.Module.Delegation; - using Nekoyume.Module.ValidatorDelegation; + using Nekoyume.ValidatorDelegation; using Xunit; public class RedelegateValidatorTest @@ -59,8 +58,9 @@ public void Execute() Signer = dstPublicKey.Address, }); - var srcValidator = world.GetValidatorDelegatee(srcPublicKey.Address); - var bond = world.GetBond(srcValidator, srcPublicKey.Address); + var repository = new ValidatorRepository(world, context); + var srcValidator = repository.GetValidatorDelegatee(srcPublicKey.Address); + var bond = repository.GetBond(srcValidator, srcPublicKey.Address); var action = new RedelegateValidator(srcPublicKey.Address, dstPublicKey.Address, bond.Share); context = new ActionContext { @@ -71,10 +71,11 @@ public void Execute() world = action.Execute(context); - srcValidator = world.GetValidatorDelegatee(srcPublicKey.Address); - var dstValidator = world.GetValidatorDelegatee(dstPublicKey.Address); - var validatorList = world.GetValidatorList(); - var dstBond = world.GetBond(dstValidator, srcPublicKey.Address); + repository.UpdateWorld(world); + srcValidator = repository.GetValidatorDelegatee(srcPublicKey.Address); + var dstValidator = repository.GetValidatorDelegatee(dstPublicKey.Address); + var validatorList = repository.GetValidatorList(); + var dstBond = repository.GetBond(dstValidator, srcPublicKey.Address); Assert.Contains(srcPublicKey.Address, dstValidator.Delegators); Assert.Equal(dstValidator.Validator, Assert.Single(validatorList.Validators)); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 58641490d1..2a1b2ee2c9 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -9,8 +9,8 @@ namespace Lib9c.Tests.Action.ValidatorDelegation using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.State; using Nekoyume.Module; - using Nekoyume.Module.Delegation; using Nekoyume.Module.ValidatorDelegation; + using Nekoyume.ValidatorDelegation; using Xunit; public class UndelegateValidatorTest @@ -49,8 +49,9 @@ public void Execute() Signer = publicKey.Address, }); - var validator = world.GetValidatorDelegatee(publicKey.Address); - var bond = world.GetBond(validator, publicKey.Address); + var repository = new ValidatorRepository(world, context); + var validator = repository.GetValidatorDelegatee(publicKey.Address); + var bond = repository.GetBond(validator, publicKey.Address); var action = new UndelegateValidator(publicKey.Address, bond.Share); context = new ActionContext { @@ -61,21 +62,21 @@ public void Execute() world = action.Execute(context); - validator = world.GetValidatorDelegatee(publicKey.Address); - var validatorList = world.GetValidatorList(); + repository.UpdateWorld(world); + validator = repository.GetValidatorDelegatee(publicKey.Address); + var validatorList = repository.GetValidatorList(); Assert.Empty(validator.Delegators); Assert.Equal(BigInteger.Zero, validator.Validator.Power); Assert.Empty(validatorList.Validators); - context = new ActionContext + world = new ReleaseValidatorUnbondings().Execute(new ActionContext { PreviousState = world, Signer = publicKey.Address, BlockIndex = 1, - }; + }); - world = world.ReleaseUnbondings(context); Assert.Equal(gg * 100, world.GetBalance(publicKey.Address, gg)); } } diff --git a/.Lib9c.Tests/Delegation/DelegateeTest.cs b/.Lib9c.Tests/Delegation/DelegateeTest.cs index 54b21fec93..c7931dd20a 100644 --- a/.Lib9c.Tests/Delegation/DelegateeTest.cs +++ b/.Lib9c.Tests/Delegation/DelegateeTest.cs @@ -24,19 +24,17 @@ public void Ctor() Assert.Equal(address, delegatee.Address); Assert.Equal(DelegationFixture.TestCurrency, delegatee.DelegationCurrency); Assert.Equal(3, delegatee.UnbondingPeriod); - Assert.Equal(new byte[] { 0x01 }, delegatee.DelegateeId); } [Fact] - public void CtorWithBencoded() + public void GetSet() { - var address = new Address("0xe8327129891e1A0B2E3F0bfa295777912295942a"); + var repo = _fixture.Repository; var delegatee = _fixture.TestDelegatee1; var delegator = _fixture.TestDelegator1; delegatee.Bond(delegator, delegatee.DelegationCurrency * 10, 10L); - - var delegateeRecon = new TestDelegatee(address, delegatee.Bencoded, delegatee.Repository); - Assert.Equal(address, delegateeRecon.Address); + var delegateeRecon = repo.GetDelegatee(delegatee.Address); + Assert.Equal(delegatee.Address, delegateeRecon.Address); Assert.Equal(delegator.Address, Assert.Single(delegateeRecon.Delegators)); Assert.Equal(delegatee.TotalDelegated, delegateeRecon.TotalDelegated); Assert.Equal(delegatee.TotalShares, delegateeRecon.TotalShares); @@ -62,7 +60,7 @@ public void Bond() var totalBonding = testDelegatee.DelegationCurrency * 0; var bonding = testDelegatee.DelegationCurrency * 10; - var share = testDelegatee.ShareToBond(bonding); + var share = testDelegatee.ShareFromFAV(bonding); share1 += share; totalShare += share; totalBonding += bonding; @@ -76,7 +74,7 @@ public void Bond() Assert.Equal(totalBonding, testDelegatee.TotalDelegated); bonding = testDelegatee.DelegationCurrency * 20; - share = testDelegatee.ShareToBond(bonding); + share = testDelegatee.ShareFromFAV(bonding); share1 += share; totalShare += share; totalBonding += bonding; @@ -89,7 +87,7 @@ public void Bond() Assert.Equal(totalBonding, testDelegatee.TotalDelegated); bonding = testDelegatee.DelegationCurrency * 30; - share = testDelegatee.ShareToBond(bonding); + share = testDelegatee.ShareFromFAV(bonding); share2 += share; totalShare += share; totalBonding += bonding; @@ -142,14 +140,14 @@ public void Unbond() var totalDelegated = testDelegatee.DelegationCurrency * 0; var bonding = testDelegatee.DelegationCurrency * 100; - var share = testDelegatee.ShareToBond(bonding); + var share = testDelegatee.ShareFromFAV(bonding); share1 += share; totalShares += share; totalDelegated += bonding; testDelegatee.Bond(testDelegator1, bonding, 1L); bonding = testDelegatee.DelegationCurrency * 50; - share = testDelegatee.ShareToBond(bonding); + share = testDelegatee.ShareFromFAV(bonding); share2 += share; totalShares += share; totalDelegated += bonding; @@ -158,7 +156,7 @@ public void Unbond() var unbonding = share1 / 2; share1 -= unbonding; totalShares -= unbonding; - var unbondingFAV = testDelegatee.FAVToUnbond(unbonding); + var unbondingFAV = testDelegatee.FAVFromShare(unbonding); totalDelegated -= unbondingFAV; var unbondedFAV = testDelegatee.Unbond(testDelegator1, unbonding, 3L); var shareAfterUnbond = _fixture.Repository.GetBond(testDelegatee, testDelegator1.Address).Share; @@ -173,7 +171,7 @@ public void Unbond() unbonding = share2 / 2; share2 -= unbonding; totalShares -= unbonding; - unbondingFAV = testDelegatee.FAVToUnbond(unbonding); + unbondingFAV = testDelegatee.FAVFromShare(unbonding); totalDelegated -= unbondingFAV; unbondedFAV = testDelegatee.Unbond(testDelegator2, unbonding, 4L); shareAfterUnbond = _fixture.Repository.GetBond(testDelegatee, testDelegator2.Address).Share; @@ -186,7 +184,7 @@ public void Unbond() Assert.Equal(totalDelegated, testDelegatee.TotalDelegated); totalShares -= share1; - unbondingFAV = testDelegatee.FAVToUnbond(share1); + unbondingFAV = testDelegatee.FAVFromShare(share1); totalDelegated -= unbondingFAV; unbondedFAV = testDelegatee.Unbond(testDelegator1, share1, 5L); shareAfterUnbond = _fixture.Repository.GetBond(testDelegatee, testDelegator1.Address).Share; diff --git a/.Lib9c.Tests/Delegation/DelegationFixture.cs b/.Lib9c.Tests/Delegation/DelegationFixture.cs index 23ececeaeb..7e99291246 100644 --- a/.Lib9c.Tests/Delegation/DelegationFixture.cs +++ b/.Lib9c.Tests/Delegation/DelegationFixture.cs @@ -28,7 +28,7 @@ public DelegationFixture() BlockProtocolVersion = BlockMetadata.CurrentProtocolVersion, }; - Repository = new TestDelegationRepo(world, context); + Repository = new TestRepository(world, context); TestDelegator1 = new TestDelegator(new Address("0x0054E98312C47E7Fa0ABed45C23Fa187e31C373a"), Repository); TestDelegator2 = new TestDelegator(new Address("0x327CCff388255E9399207C3d5a09357D0BBc73dF"), Repository); TestDelegatee1 = new TestDelegatee(new Address("0x67A44E11506b8f0Bb625fEECccb205b33265Bb48"), Repository); @@ -37,7 +37,7 @@ public DelegationFixture() DummyDelegator1 = new DummyDelegator(new Address("0x0054E98312C47E7Fa0ABed45C23Fa187e31C373a"), Repository); } - public TestDelegationRepo Repository { get; } + public TestRepository Repository { get; } public TestDelegator TestDelegator1 { get; } diff --git a/.Lib9c.Tests/Delegation/DelegatorTest.cs b/.Lib9c.Tests/Delegation/DelegatorTest.cs index f8ce28889f..3d837b414a 100644 --- a/.Lib9c.Tests/Delegation/DelegatorTest.cs +++ b/.Lib9c.Tests/Delegation/DelegatorTest.cs @@ -24,15 +24,14 @@ public void Ctor() } [Fact] - public void CtorWithBencoded() + public void GetSet() { var repo = _fixture.Repository; var delegator = _fixture.TestDelegator1; var delegatee = _fixture.TestDelegatee1; repo.MintAsset(delegator.Address, delegatee.DelegationCurrency * 100); delegator.Delegate(delegatee, delegatee.DelegationCurrency * 10, 10L); - - var delegatorRecon = new TestDelegator(delegator.Address, delegator.Bencoded, delegator.Repository); + var delegatorRecon = repo.GetDelegator(delegator.Address); Assert.Equal(delegator.Address, delegatorRecon.Address); Assert.Equal(delegatee.Address, Assert.Single(delegatorRecon.Delegatees)); } @@ -48,7 +47,7 @@ public void Delegate() repo.MintAsset(delegator.Address, delegatorInitialBalance); var delegateFAV = delegatee1.DelegationCurrency * 10; - var delegateShare = delegatee1.ShareToBond(delegateFAV); + var delegateShare = delegatee1.ShareFromFAV(delegateFAV); delegator.Delegate(delegatee1, delegateFAV, 1L); var delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.DelegationCurrency); var delegateeBalance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.DelegationCurrency); @@ -62,7 +61,7 @@ public void Delegate() Assert.Equal(delegatee1.Address, Assert.Single(delegator.Delegatees)); var delegateFAV2 = delegatee1.DelegationCurrency * 20; - var delegateShare2 = delegatee1.ShareToBond(delegateFAV2); + var delegateShare2 = delegatee1.ShareFromFAV(delegateFAV2); delegator.Delegate(delegatee1, delegateFAV2, 2L); delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.DelegationCurrency); delegateeBalance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.DelegationCurrency); @@ -101,7 +100,7 @@ public void Undelegate() delegator.Delegate(delegatee, delegatingFAV, 10L); var initialShare = repo.GetBond(delegatee, delegator.Address).Share; var undelegatingShare = initialShare / 3; - var undelegatingFAV = delegatee.FAVToUnbond(undelegatingShare); + var undelegatingFAV = delegatee.FAVFromShare(undelegatingShare); delegator.Undelegate(delegatee, undelegatingShare, 10L); var delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.DelegationCurrency); var delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); @@ -119,13 +118,13 @@ public void Undelegate() var entriesByExpireHeight = Assert.Single(unbondLockIn.Entries); Assert.Equal(10L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); var entry = Assert.Single(entriesByExpireHeight.Value); - Assert.Equal(undelegatingFAV, entry.InitialLockInFAV); - Assert.Equal(undelegatingFAV, entry.LockInFAV); + Assert.Equal(undelegatingFAV, entry.InitialUnbondingFAV); + Assert.Equal(undelegatingFAV, entry.UnbondingFAV); Assert.Equal(10L, entry.CreationHeight); Assert.Equal(10L + delegatee.UnbondingPeriod, entry.ExpireHeight); undelegatingShare = repo.GetBond(delegatee, delegator.Address).Share; - var undelegatingFAV2 = delegatee.FAVToUnbond(undelegatingShare); + var undelegatingFAV2 = delegatee.FAVFromShare(undelegatingShare); delegator.Undelegate(delegatee, undelegatingShare, 12L); delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.DelegationCurrency); delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); @@ -149,15 +148,15 @@ public void Undelegate() entriesByExpireHeight = unbondLockIn.Entries.ElementAt(0); Assert.Equal(10L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); entry = Assert.Single(entriesByExpireHeight.Value); - Assert.Equal(undelegatingFAV, entry.InitialLockInFAV); - Assert.Equal(undelegatingFAV, entry.LockInFAV); + Assert.Equal(undelegatingFAV, entry.InitialUnbondingFAV); + Assert.Equal(undelegatingFAV, entry.UnbondingFAV); Assert.Equal(10L, entry.CreationHeight); Assert.Equal(10L + delegatee.UnbondingPeriod, entry.ExpireHeight); entriesByExpireHeight = unbondLockIn.Entries.ElementAt(1); Assert.Equal(12L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); entry = Assert.Single(entriesByExpireHeight.Value); - Assert.Equal(undelegatingFAV2, entry.InitialLockInFAV); - Assert.Equal(undelegatingFAV2, entry.LockInFAV); + Assert.Equal(undelegatingFAV2, entry.InitialUnbondingFAV); + Assert.Equal(undelegatingFAV2, entry.UnbondingFAV); Assert.Equal(12L, entry.CreationHeight); Assert.Equal(12L + delegatee.UnbondingPeriod, entry.ExpireHeight); Assert.Equal(delegatorInitialBalance - delegatingFAV, delegatorBalance); @@ -169,8 +168,8 @@ public void Undelegate() entriesByExpireHeight = Assert.Single(unbondLockIn.Entries); Assert.Equal(12L + delegatee.UnbondingPeriod, entriesByExpireHeight.Key); entry = Assert.Single(entriesByExpireHeight.Value); - Assert.Equal(undelegatingFAV2, entry.InitialLockInFAV); - Assert.Equal(undelegatingFAV2, entry.LockInFAV); + Assert.Equal(undelegatingFAV2, entry.InitialUnbondingFAV); + Assert.Equal(undelegatingFAV2, entry.UnbondingFAV); Assert.Equal(12L, entry.CreationHeight); Assert.Equal(12L + delegatee.UnbondingPeriod, entry.ExpireHeight); Assert.Equal(delegatorInitialBalance - delegatingFAV + undelegatingFAV, delegatorBalance); @@ -198,8 +197,8 @@ public void Redelegate() Assert.Equal(delegatee1.Address, Assert.Single(delegator.Delegatees)); var initialShare = repo.GetBond(delegatee1, delegator.Address).Share; var redelegatingShare = initialShare / 3; - var redelegatingFAV = delegatee1.FAVToUnbond(redelegatingShare); - var redelegatedDstShare = delegatee2.ShareToBond(redelegatingFAV); + var redelegatingFAV = delegatee1.FAVFromShare(redelegatingShare); + var redelegatedDstShare = delegatee2.ShareFromFAV(redelegatingFAV); delegator.Redelegate(delegatee1, delegatee2, redelegatingShare, 10L); var delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.DelegationCurrency); var delegatee1Balance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.DelegationCurrency); @@ -223,15 +222,15 @@ public void Redelegate() var entriesByExpireHeight = Assert.Single(rebondGrace.Entries); Assert.Equal(10L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); var entry = Assert.Single(entriesByExpireHeight.Value); - Assert.Equal(delegatee2.Address, entry.RebondeeAddress); - Assert.Equal(redelegatingFAV, entry.InitialGraceFAV); - Assert.Equal(redelegatingFAV, entry.GraceFAV); + Assert.Equal(delegatee2.Address, entry.UnbondeeAddress); + Assert.Equal(redelegatingFAV, entry.InitialUnbondingFAV); + Assert.Equal(redelegatingFAV, entry.UnbondingFAV); Assert.Equal(10L, entry.CreationHeight); Assert.Equal(10L + delegatee1.UnbondingPeriod, entry.ExpireHeight); var redelegatingShare2 = repo.GetBond(delegatee1, delegator.Address).Share; - var redelegatingFAV2 = delegatee1.FAVToUnbond(redelegatingShare2); - var redelegatedDstShare2 = delegatee2.ShareToBond(redelegatingFAV2); + var redelegatingFAV2 = delegatee1.FAVFromShare(redelegatingShare2); + var redelegatedDstShare2 = delegatee2.ShareFromFAV(redelegatingFAV2); delegator.Redelegate(delegatee1, delegatee2, redelegatingShare2, 12L); delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee1.DelegationCurrency); delegatee1Balance = repo.World.GetBalance(delegatee1.DelegationPoolAddress, delegatee1.DelegationCurrency); @@ -259,17 +258,17 @@ public void Redelegate() entriesByExpireHeight = rebondGrace.Entries.ElementAt(0); Assert.Equal(10L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); entry = Assert.Single(entriesByExpireHeight.Value); - Assert.Equal(delegatee2.Address, entry.RebondeeAddress); - Assert.Equal(redelegatingFAV, entry.InitialGraceFAV); - Assert.Equal(redelegatingFAV, entry.GraceFAV); + Assert.Equal(delegatee2.Address, entry.UnbondeeAddress); + Assert.Equal(redelegatingFAV, entry.InitialUnbondingFAV); + Assert.Equal(redelegatingFAV, entry.UnbondingFAV); Assert.Equal(10L, entry.CreationHeight); Assert.Equal(10L + delegatee1.UnbondingPeriod, entry.ExpireHeight); entriesByExpireHeight = rebondGrace.Entries.ElementAt(1); Assert.Equal(12L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); entry = Assert.Single(entriesByExpireHeight.Value); - Assert.Equal(delegatee2.Address, entry.RebondeeAddress); - Assert.Equal(redelegatingFAV2, entry.InitialGraceFAV); - Assert.Equal(redelegatingFAV2, entry.GraceFAV); + Assert.Equal(delegatee2.Address, entry.UnbondeeAddress); + Assert.Equal(redelegatingFAV2, entry.InitialUnbondingFAV); + Assert.Equal(redelegatingFAV2, entry.UnbondingFAV); Assert.Equal(12L, entry.CreationHeight); Assert.Equal(12L + delegatee1.UnbondingPeriod, entry.ExpireHeight); @@ -277,9 +276,9 @@ public void Redelegate() entriesByExpireHeight = Assert.Single(rebondGrace.Entries); Assert.Equal(12L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); entry = Assert.Single(entriesByExpireHeight.Value); - Assert.Equal(delegatee2.Address, entry.RebondeeAddress); - Assert.Equal(redelegatingFAV2, entry.InitialGraceFAV); - Assert.Equal(redelegatingFAV2, entry.GraceFAV); + Assert.Equal(delegatee2.Address, entry.UnbondeeAddress); + Assert.Equal(redelegatingFAV2, entry.InitialUnbondingFAV); + Assert.Equal(redelegatingFAV2, entry.UnbondingFAV); Assert.Equal(12L, entry.CreationHeight); Assert.Equal(12L + delegatee1.UnbondingPeriod, entry.ExpireHeight); diff --git a/.Lib9c.Tests/Delegation/DummyDelegatee.cs b/.Lib9c.Tests/Delegation/DummyDelegatee.cs index 7eeea0519e..3b0bb5ea51 100644 --- a/.Lib9c.Tests/Delegation/DummyDelegatee.cs +++ b/.Lib9c.Tests/Delegation/DummyDelegatee.cs @@ -1,7 +1,5 @@ -using System.Numerics; using Lib9c.Tests.Delegation; using Libplanet.Crypto; -using Libplanet.Types.Assets; using Nekoyume.Delegation; public sealed class DummyDelegatee : Delegatee @@ -11,19 +9,17 @@ public DummyDelegatee(Address address, IDelegationRepository repository) { } - public override Currency DelegationCurrency => DelegationFixture.TestCurrency; - - public override Currency RewardCurrency => DelegationFixture.TestCurrency; - - public override Address DelegationPoolAddress => DelegationFixture.FixedPoolAddress; - - public override long UnbondingPeriod => 3; - - public override byte[] DelegateeId => new byte[] { 0x02 }; - - public override int MaxUnbondLockInEntries => 5; - - public override int MaxRebondGraceEntries => 5; - - public override BigInteger SlashFactor => 1; + public DummyDelegatee(Address address, Address accountAddress, DummyRepository repository) + : base( + address, + accountAddress, + DelegationFixture.TestCurrency, + DelegationFixture.TestCurrency, + address, + 3, + 5, + 5, + repository) + { + } } diff --git a/.Lib9c.Tests/Delegation/DummyDelegator.cs b/.Lib9c.Tests/Delegation/DummyDelegator.cs index 19da999654..bbf5c8e128 100644 --- a/.Lib9c.Tests/Delegation/DummyDelegator.cs +++ b/.Lib9c.Tests/Delegation/DummyDelegator.cs @@ -7,4 +7,9 @@ public DummyDelegator(Address address, IDelegationRepository repository) : base(address, repository) { } + + public DummyDelegator(Address address, Address accountAddress, DummyRepository repo) + : base(address, accountAddress, address, repo) + { + } } diff --git a/.Lib9c.Tests/Delegation/DummyRepository.cs b/.Lib9c.Tests/Delegation/DummyRepository.cs new file mode 100644 index 0000000000..2fe2481f89 --- /dev/null +++ b/.Lib9c.Tests/Delegation/DummyRepository.cs @@ -0,0 +1,61 @@ +#nullable enable +namespace Nekoyume.Delegation +{ + using Libplanet.Action; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Types.Assets; + using Nekoyume.Action; + + public class DummyRepository : DelegationRepository + { + public DummyRepository(IWorld world, IActionContext context) + : base( + world: world, + context: context, + delegateeAccountAddress: new Address("1000000000000000000000000000000000000000/"), + delegatorAccountAddress: new Address("1000000000000000000000000000000000000001"), + delegateeMetadataAccountAddress: new Address("0000000000000000000000000000000000000002"), + delegatorMetadataAccountAddress: new Address("0000000000000000000000000000000000000003"), + bondAccountAddress: new Address("0000000000000000000000000000000000000004"), + unbondLockInAccountAddress: new Address("0000000000000000000000000000000000000005"), + rebondGraceAccountAddress: new Address("0000000000000000000000000000000000000006"), + unbondingSetAccountAddress: new Address("0000000000000000000000000000000000000007"), + lumpSumRewardRecordAccountAddress: new Address("0000000000000000000000000000000000000008")) + { + } + + public override DummyDelegatee GetDelegatee(Address address) + { + try + { + return new DummyDelegatee(address, this); + } + catch (FailedLoadStateException) + { + return new DummyDelegatee(address, DelegateeAccountAddress, this); + } + } + + public override DummyDelegator GetDelegator(Address address) + { + try + { + return new DummyDelegator(address, this); + } + catch (FailedLoadStateException) + { + return new DummyDelegator(address, DelegatorAccountAddress, this); + } + } + + public override void SetDelegatee(IDelegatee delegatee) + => SetDelegateeMetadata(((DummyDelegatee)delegatee).Metadata); + + public override void SetDelegator(IDelegator delegator) + => SetDelegatorMetadata(((DummyDelegator)delegator).Metadata); + + public void MintAsset(Address recipient, FungibleAssetValue value) + => previousWorld = previousWorld.MintAsset(actionContext, recipient, value); + } +} diff --git a/.Lib9c.Tests/Delegation/TestDelegatee.cs b/.Lib9c.Tests/Delegation/TestDelegatee.cs index 0492b05280..078d1267d9 100644 --- a/.Lib9c.Tests/Delegation/TestDelegatee.cs +++ b/.Lib9c.Tests/Delegation/TestDelegatee.cs @@ -1,37 +1,27 @@ namespace Lib9c.Tests.Delegation { - using System.Numerics; - using Bencodex.Types; using Libplanet.Crypto; - using Libplanet.Types.Assets; using Nekoyume.Delegation; public sealed class TestDelegatee : Delegatee { - public TestDelegatee(Address address, IDelegationRepository repository) + public TestDelegatee(Address address, TestRepository repository) : base(address, repository) { } - public TestDelegatee(Address address, IValue bencoded, IDelegationRepository repository) - : base(address, bencoded, repository) + public TestDelegatee(Address address, Address accountAddress, TestRepository repository) + : base( + address, + accountAddress, + DelegationFixture.TestCurrency, + DelegationFixture.TestCurrency, + address, + 3, + 5, + 5, + repository) { } - - public override Currency DelegationCurrency => DelegationFixture.TestCurrency; - - public override Currency RewardCurrency => DelegationFixture.TestCurrency; - - public override long UnbondingPeriod => 3; - - public override Address DelegationPoolAddress => DeriveAddress(PoolId); - - public override byte[] DelegateeId => new byte[] { 0x01 }; - - public override int MaxUnbondLockInEntries => 5; - - public override int MaxRebondGraceEntries => 5; - - public override BigInteger SlashFactor => 1; } } diff --git a/.Lib9c.Tests/Delegation/TestDelegationRepo.cs b/.Lib9c.Tests/Delegation/TestDelegationRepo.cs deleted file mode 100644 index 3458a47409..0000000000 --- a/.Lib9c.Tests/Delegation/TestDelegationRepo.cs +++ /dev/null @@ -1,166 +0,0 @@ -#nullable enable -namespace Nekoyume.Delegation -{ - using System; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - - public class TestDelegationRepo : IDelegationRepository - { - private readonly Address bondAddress = Addresses.Bond; - private readonly Address unbondLockInAddress = Addresses.UnbondLockIn; - private readonly Address rebondGraceAddress = Addresses.RebondGrace; - private readonly Address unbondingSetAddress = Addresses.UnbondingSet; - private readonly Address lumpSumRewardsRecordAddress = Addresses.LumpSumRewardsRecord; - - private IWorld _world; - private IActionContext _context; - private IAccount _bond; - private IAccount _unbondLockIn; - private IAccount _rebondGrace; - private IAccount _unbondingSet; - private IAccount _lumpSumRewardsRecord; - - public TestDelegationRepo(IWorld world, IActionContext context) - { - _world = world; - _context = context; - _bond = world.GetAccount(bondAddress); - _unbondLockIn = world.GetAccount(unbondLockInAddress); - _rebondGrace = world.GetAccount(rebondGraceAddress); - _unbondingSet = world.GetAccount(unbondingSetAddress); - _lumpSumRewardsRecord = world.GetAccount(lumpSumRewardsRecordAddress); - } - - public IWorld World => _world - .SetAccount(bondAddress, _bond) - .SetAccount(unbondLockInAddress, _unbondLockIn) - .SetAccount(rebondGraceAddress, _rebondGrace) - .SetAccount(unbondingSetAddress, _unbondingSet) - .SetAccount(lumpSumRewardsRecordAddress, _lumpSumRewardsRecord); - - public Bond GetBond(IDelegatee delegatee, Address delegatorAddress) - { - Address address = delegatee.BondAddress(delegatorAddress); - IValue? value = _bond.GetState(address); - return value is IValue bencoded - ? new Bond(address, bencoded) - : new Bond(address); - } - - public UnbondLockIn GetUnbondLockIn(IDelegatee delegatee, Address delegatorAddress) - { - Address address = delegatee.UnbondLockInAddress(delegatorAddress); - IValue? value = _unbondLockIn.GetState(address); - return value is IValue bencoded - ? new UnbondLockIn(address, delegatee.MaxUnbondLockInEntries, bencoded, this) - : new UnbondLockIn(address, delegatee.MaxUnbondLockInEntries, delegatee.DelegationPoolAddress, delegatorAddress, this); - } - - public UnbondLockIn GetUnlimitedUnbondLockIn(Address address) - { - IValue? value = _unbondLockIn.GetState(address); - return value is IValue bencoded - ? new UnbondLockIn(address, int.MaxValue, bencoded, this) - : throw new InvalidOperationException("UnbondLockIn not found."); - } - - public RebondGrace GetRebondGrace(IDelegatee delegatee, Address delegatorAddress) - { - Address address = delegatee.RebondGraceAddress(delegatorAddress); - IValue? value = _rebondGrace.GetState(address); - return value is IValue bencoded - ? new RebondGrace(address, delegatee.MaxRebondGraceEntries, bencoded, this) - : new RebondGrace(address, delegatee.MaxRebondGraceEntries, this); - } - - public RebondGrace GetUnlimitedRebondGrace(Address address) - { - IValue? value = _rebondGrace.GetState(address); - return value is IValue bencoded - ? new RebondGrace(address, int.MaxValue, bencoded, this) - : throw new InvalidOperationException("RebondGrace not found."); - } - - public UnbondingSet GetUnbondingSet() - => _unbondingSet.GetState(UnbondingSet.Address) is IValue bencoded - ? new UnbondingSet(bencoded, this) - : new UnbondingSet(this); - - public LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height) - { - Address address = delegatee.LumpSumRewardsRecordAddress(height); - IValue? value = _lumpSumRewardsRecord.GetState(address); - return value is IValue bencoded - ? new LumpSumRewardsRecord(address, bencoded) - : null; - } - - public LumpSumRewardsRecord? GetCurrentLumpSumRewardsRecord(IDelegatee delegatee) - { - Address address = delegatee.CurrentLumpSumRewardsRecordAddress(); - IValue? value = _lumpSumRewardsRecord.GetState(address); - return value is IValue bencoded - ? new LumpSumRewardsRecord(address, bencoded) - : null; - } - - public FungibleAssetValue GetBalance(Address address, Currency currency) - => _world.GetBalance(address, currency); - - public void SetBond(Bond bond) - { - _bond = bond.IsEmpty - ? _bond.RemoveState(bond.Address) - : _bond.SetState(bond.Address, bond.Bencoded); - } - - public void SetUnbondLockIn(UnbondLockIn unbondLockIn) - { - _unbondLockIn = unbondLockIn.IsEmpty - ? _unbondLockIn.RemoveState(unbondLockIn.Address) - : _unbondLockIn.SetState(unbondLockIn.Address, unbondLockIn.Bencoded); - } - - public void SetRebondGrace(RebondGrace rebondGrace) - { - _rebondGrace = rebondGrace.IsEmpty - ? _rebondGrace.RemoveState(rebondGrace.Address) - : _rebondGrace.SetState(rebondGrace.Address, rebondGrace.Bencoded); - } - - public void SetUnbondingSet(UnbondingSet unbondingSet) - { - _unbondingSet = unbondingSet.IsEmpty - ? _unbondingSet.RemoveState(UnbondingSet.Address) - : _unbondingSet.SetState(UnbondingSet.Address, unbondingSet.Bencoded); - } - - public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) - { - _lumpSumRewardsRecord = _lumpSumRewardsRecord.SetState( - lumpSumRewardsRecord.Address, lumpSumRewardsRecord.Bencoded); - } - - public void AddLumpSumRewards(IDelegatee delegatee, long height, FungibleAssetValue reward) - { - LumpSumRewardsRecord record = GetCurrentLumpSumRewardsRecord(delegatee) - ?? new LumpSumRewardsRecord( - delegatee.CurrentLumpSumRewardsRecordAddress(), - height, - delegatee.TotalShares, - delegatee.RewardCurrency); - record = record.AddLumpSumRewards(reward); - SetLumpSumRewardsRecord(record); - } - - public void TransferAsset(Address sender, Address recipient, FungibleAssetValue value) - => _world = _world.TransferAsset(_context, sender, recipient, value); - - public void MintAsset(Address recipient, FungibleAssetValue value) - => _world = _world.MintAsset(_context, recipient, value); - } -} diff --git a/.Lib9c.Tests/Delegation/TestDelegator.cs b/.Lib9c.Tests/Delegation/TestDelegator.cs index 0dfef5a92f..20fbade1f1 100644 --- a/.Lib9c.Tests/Delegation/TestDelegator.cs +++ b/.Lib9c.Tests/Delegation/TestDelegator.cs @@ -1,18 +1,17 @@ namespace Lib9c.Tests.Delegation { - using Bencodex.Types; using Libplanet.Crypto; using Nekoyume.Delegation; public sealed class TestDelegator : Delegator { - public TestDelegator(Address address, IDelegationRepository repo) + public TestDelegator(Address address, TestRepository repo) : base(address, repo) { } - public TestDelegator(Address address, IValue bencoded, IDelegationRepository repo) - : base(address, bencoded, repo) + public TestDelegator(Address address, Address accountAddress, TestRepository repo) + : base(address, accountAddress, address, repo) { } } diff --git a/.Lib9c.Tests/Delegation/TestRepository.cs b/.Lib9c.Tests/Delegation/TestRepository.cs new file mode 100644 index 0000000000..befff670a2 --- /dev/null +++ b/.Lib9c.Tests/Delegation/TestRepository.cs @@ -0,0 +1,62 @@ +#nullable enable +namespace Nekoyume.Delegation +{ + using Lib9c.Tests.Delegation; + using Libplanet.Action; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Types.Assets; + using Nekoyume.Action; + + public class TestRepository : DelegationRepository + { + public TestRepository(IWorld world, IActionContext context) + : base( + world: world, + context: context, + delegateeAccountAddress: new Address("0000000000000000000000000000000000000000"), + delegatorAccountAddress: new Address("0000000000000000000000000000000000000001"), + delegateeMetadataAccountAddress: new Address("0000000000000000000000000000000000000002"), + delegatorMetadataAccountAddress: new Address("0000000000000000000000000000000000000003"), + bondAccountAddress: new Address("0000000000000000000000000000000000000004"), + unbondLockInAccountAddress: new Address("0000000000000000000000000000000000000005"), + rebondGraceAccountAddress: new Address("0000000000000000000000000000000000000006"), + unbondingSetAccountAddress: new Address("0000000000000000000000000000000000000007"), + lumpSumRewardRecordAccountAddress: new Address("0000000000000000000000000000000000000008")) + { + } + + public override TestDelegatee GetDelegatee(Address address) + { + try + { + return new TestDelegatee(address, this); + } + catch (FailedLoadStateException) + { + return new TestDelegatee(address, DelegateeAccountAddress, this); + } + } + + public override TestDelegator GetDelegator(Address address) + { + try + { + return new TestDelegator(address, this); + } + catch (FailedLoadStateException) + { + return new TestDelegator(address, DelegatorAccountAddress, this); + } + } + + public override void SetDelegatee(IDelegatee delegatee) + => SetDelegateeMetadata(((TestDelegatee)delegatee).Metadata); + + public override void SetDelegator(IDelegator delegator) + => SetDelegatorMetadata(((TestDelegator)delegator).Metadata); + + public void MintAsset(Address recipient, FungibleAssetValue value) + => previousWorld = previousWorld.MintAsset(actionContext, recipient, value); + } +} diff --git a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.cs b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.cs index 0ab88739e5..ba32f7ae5f 100644 --- a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.cs +++ b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.cs @@ -1,7 +1,10 @@ namespace Lib9c.Tests.Model.Guild { using System.Threading.Tasks; + using Lib9c.Tests.Action; using Lib9c.Tests.Util; + using Libplanet.Action.State; + using Libplanet.Mocks; using Nekoyume.TypedAddress; using VerifyTests; using VerifyXunit; @@ -18,9 +21,12 @@ public GuildParticipantTest() [Fact] public Task Snapshot() { + IWorld world = new World(MockUtil.MockModernWorldState); + var repository = new Nekoyume.Model.Guild.GuildRepository(world, new ActionContext()); var guild = new Nekoyume.Model.Guild.GuildParticipant( new AgentAddress("0xB52B7F66B8464986f56053d82F0D80cA412A6F33"), - new GuildAddress("0xd928ae87311dead490c986c24cc23c37eff892f2")); + new GuildAddress("0xd928ae87311dead490c986c24cc23c37eff892f2"), + repository); return Verifier.Verify(guild.Bencoded); } @@ -28,12 +34,15 @@ public Task Snapshot() [Fact] public void Serialization() { + IWorld world = new World(MockUtil.MockModernWorldState); + var repository = new Nekoyume.Model.Guild.GuildRepository(world, new ActionContext()); var guildParticipant = new Nekoyume.Model.Guild.GuildParticipant( AddressUtil.CreateAgentAddress(), - AddressUtil.CreateGuildAddress()); + AddressUtil.CreateGuildAddress(), + repository); var newGuildParticipant = new Nekoyume.Model.Guild.GuildParticipant( - guildParticipant.AgentAddress, guildParticipant.Bencoded); + guildParticipant.Address, guildParticipant.Bencoded, repository); Assert.Equal(guildParticipant.GuildAddress, newGuildParticipant.GuildAddress); } diff --git a/.Lib9c.Tests/Model/Guild/GuildTest.cs b/.Lib9c.Tests/Model/Guild/GuildTest.cs index 84e153d1fa..d10e329c43 100644 --- a/.Lib9c.Tests/Model/Guild/GuildTest.cs +++ b/.Lib9c.Tests/Model/Guild/GuildTest.cs @@ -1,7 +1,10 @@ namespace Lib9c.Tests.Model.Guild { using System.Threading.Tasks; + using Lib9c.Tests.Action; using Lib9c.Tests.Util; + using Libplanet.Action.State; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume.TypedAddress; using VerifyTests; @@ -19,9 +22,13 @@ public GuildTest() [Fact] public Task Snapshot() { + IWorld world = new World(MockUtil.MockModernWorldState); + var repository = new Nekoyume.Model.Guild.GuildRepository(world, new ActionContext()); var guild = new Nekoyume.Model.Guild.Guild( + AddressUtil.CreateAgentAddress(), new AgentAddress("0xd928ae87311dead490c986c24cc23c37eff892f2"), - Currency.Legacy("NCG", 2, null)); + Currency.Legacy("NCG", 2, null), + repository); return Verifier.Verify(guild.Bencoded); } @@ -29,10 +36,18 @@ public Task Snapshot() [Fact] public void Serialization() { + IWorld world = new World(MockUtil.MockModernWorldState); + var repository = new Nekoyume.Model.Guild.GuildRepository(world, new ActionContext()); + var guildAddress = AddressUtil.CreateAgentAddress(); var guild = new Nekoyume.Model.Guild.Guild( + guildAddress, AddressUtil.CreateAgentAddress(), - Currency.Legacy("NCG", 2, null)); - var newGuild = new Nekoyume.Model.Guild.Guild(guild.Bencoded, Currency.Legacy("NCG", 2, null)); + Currency.Legacy("NCG", 2, null), + repository); + var newGuild = new Nekoyume.Model.Guild.Guild( + guildAddress, + guild.Bencoded, + repository); Assert.Equal(guild.GuildMasterAddress, newGuild.GuildMasterAddress); } diff --git a/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs b/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs index 7d9df89dc1..9dbd733c65 100644 --- a/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs +++ b/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs @@ -11,6 +11,7 @@ namespace Lib9c.Tests.PolicyAction.Tx.Begin using Nekoyume.Action; using Nekoyume.Action.Guild; using Nekoyume.Extensions; + using Nekoyume.Model.Guild; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.Module.Guild; @@ -41,25 +42,27 @@ public void Execute_When_WithPledgeContract() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress) - .SetLegacyState(pledgeAddress, new List( - MeadConfig.PatronAddress.Serialize(), - true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize())); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.JoinGuild(guildAddress, guildMasterAddress); + repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( + MeadConfig.PatronAddress.Serialize(), + true.Serialize(), + RequestPledge.DefaultRefillMead.Serialize()))); - Assert.Null(world.GetJoinedGuild(agentAddress)); + Assert.Null(repository.GetJoinedGuild(agentAddress)); var action = new AutoJoinGuild(); world = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = agentAddress, IsPolicyAction = true, }); - var joinedGuildAddress = Assert.IsType(world.GetJoinedGuild(agentAddress)); - Assert.True(world.TryGetGuild(joinedGuildAddress, out var guild)); + repository.UpdateWorld(world); + var joinedGuildAddress = Assert.IsType(repository.GetJoinedGuild(agentAddress)); + Assert.True(repository.TryGetGuild(joinedGuildAddress, out var guild)); Assert.Equal(GuildConfig.PlanetariumGuildOwner, guild.GuildMasterAddress); } @@ -73,20 +76,22 @@ public void Execute_When_WithoutPledgeContract() var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress); + repository.JoinGuild(guildAddress, guildMasterAddress); - Assert.Null(world.GetJoinedGuild(agentAddress)); + Assert.Null(repository.GetJoinedGuild(agentAddress)); var action = new AutoJoinGuild(); world = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = agentAddress, IsPolicyAction = true, }); - Assert.Null(world.GetJoinedGuild(agentAddress)); + repository.UpdateWorld(world); + Assert.Null(repository.GetJoinedGuild(agentAddress)); } [Fact] @@ -100,16 +105,18 @@ public void Execute_When_WithoutGuildYet() true.Serialize(), RequestPledge.DefaultRefillMead.Serialize())); - Assert.Null(world.GetJoinedGuild(agentAddress)); + var repository = new GuildRepository(world, new ActionContext()); + Assert.Null(repository.GetJoinedGuild(agentAddress)); var action = new AutoJoinGuild(); world = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = agentAddress, IsPolicyAction = true, }); - Assert.Null(world.GetJoinedGuild(agentAddress)); + repository.UpdateWorld(world); + Assert.Null(repository.GetJoinedGuild(agentAddress)); } } } diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index 67b1a0ca14..e4fa660d62 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -17,6 +17,7 @@ using Libplanet.Types.Blocks; using Libplanet.Types.Tx; using Nekoyume.PolicyAction.Tx.Begin; +using Nekoyume.Action.ValidatorDelegation; #if UNITY_EDITOR || UNITY_STANDALONE using UniRx; @@ -139,8 +140,16 @@ internal IBlockPolicy GetPolicy( // FIXME: Slight inconsistency due to pre-existing delegate. return new BlockPolicy( policyActionsRegistry: new PolicyActionsRegistry( - beginBlockActions: ImmutableArray.Empty, - endBlockActions: new IAction[] { new RewardGold() }.ToImmutableArray(), + beginBlockActions: new IAction[] { + new SlashValidator(), + new AllocateReward(), + }.ToImmutableArray(), + endBlockActions: new IAction[] { + new UpdateValidators(), + new RecordProposer(), + new RewardGold(), + new ReleaseValidatorUnbondings(), + }.ToImmutableArray(), beginTxActions: ImmutableArray.Empty, endTxActions: ImmutableArray.Empty), blockInterval: BlockInterval, diff --git a/Lib9c/Action/Delegate/ReleaseUnbondings.cs b/Lib9c/Action/Delegate/ReleaseUnbondings.cs deleted file mode 100644 index 449e32ed38..0000000000 --- a/Lib9c/Action/Delegate/ReleaseUnbondings.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using Bencodex.Types; -using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Module.Delegation; - -namespace Nekoyume.Action.Delegate -{ - public class ReleaseUnbondings : ActionBase - { - public override IValue PlainValue => Null.Value; - - public override void LoadPlainValue(IValue plainValue) - { - throw new InvalidOperationException("Policy action shouldn't be serialized."); - } - - public override IWorld Execute(IActionContext context) - { - if(!context.IsPolicyAction) - { - throw new InvalidOperationException( - "This action must be called when it is a policy action."); - } - - var world = context.PreviousState; - - return world.ReleaseUnbondings(context); - } - } -} diff --git a/Lib9c/Action/Guild/AcceptGuildApplication.cs b/Lib9c/Action/Guild/AcceptGuildApplication.cs index 58fe29468e..30878f8e1d 100644 --- a/Lib9c/Action/Guild/AcceptGuildApplication.cs +++ b/Lib9c/Action/Guild/AcceptGuildApplication.cs @@ -3,6 +3,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.Module.Guild; using Nekoyume.TypedAddress; @@ -49,9 +50,11 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new GuildRepository(world, context); var signer = context.GetAgentAddress(); - return world.AcceptGuildApplication(signer, Target); + repository.AcceptGuildApplication(context, signer, Target); + return repository.World; } } } diff --git a/Lib9c/Action/Guild/ApplyGuild.cs b/Lib9c/Action/Guild/ApplyGuild.cs index e56cf72721..a1b3305595 100644 --- a/Lib9c/Action/Guild/ApplyGuild.cs +++ b/Lib9c/Action/Guild/ApplyGuild.cs @@ -3,6 +3,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.Module.Guild; using Nekoyume.TypedAddress; @@ -49,11 +50,13 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new GuildRepository(world, context); var signer = context.GetAgentAddress(); // TODO: Do something related with ConsensusPower delegation. - return world.ApplyGuild(signer, GuildAddress); + repository.ApplyGuild(signer, GuildAddress); + return repository.World; } } } diff --git a/Lib9c/Action/Guild/BanGuildMember.cs b/Lib9c/Action/Guild/BanGuildMember.cs index c5c878d1a5..897a251109 100644 --- a/Lib9c/Action/Guild/BanGuildMember.cs +++ b/Lib9c/Action/Guild/BanGuildMember.cs @@ -3,6 +3,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.Module.Guild; using Nekoyume.TypedAddress; @@ -48,16 +49,17 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new GuildRepository(world, context); var signer = context.GetAgentAddress(); - if (world.GetJoinedGuild(signer) is not { } guildAddress) + if (repository.GetJoinedGuild(signer) is not { } guildAddress) { throw new InvalidOperationException("The signer does not have a guild."); } - world = world.Ban(guildAddress, signer, Target); + repository.Ban(guildAddress, signer, Target); - return world; + return repository.World; } } } diff --git a/Lib9c/Action/Guild/CancelGuildApplication.cs b/Lib9c/Action/Guild/CancelGuildApplication.cs index e9a78ede23..b5f4258bf3 100644 --- a/Lib9c/Action/Guild/CancelGuildApplication.cs +++ b/Lib9c/Action/Guild/CancelGuildApplication.cs @@ -3,6 +3,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.Module.Guild; namespace Nekoyume.Action.Guild @@ -33,9 +34,11 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new GuildRepository(world, context); var signer = context.GetAgentAddress(); - return world.CancelGuildApplication(signer); + repository.CancelGuildApplication(signer); + return repository.World; } } } diff --git a/Lib9c/Action/Guild/MakeGuild.cs b/Lib9c/Action/Guild/MakeGuild.cs index e9093291de..3bba2f2fbc 100644 --- a/Lib9c/Action/Guild/MakeGuild.cs +++ b/Lib9c/Action/Guild/MakeGuild.cs @@ -4,6 +4,7 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.Module.Guild; using Nekoyume.TypedAddress; @@ -33,6 +34,7 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new GuildRepository(world, context); var random = context.GetRandom(); // TODO: Remove this check when to deliver features to users. @@ -45,7 +47,8 @@ public override IWorld Execute(IActionContext context) var guildAddress = new GuildAddress(random.GenerateAddress()); var signer = context.GetAgentAddress(); - return world.MakeGuild(guildAddress, signer); + repository.MakeGuild(guildAddress, signer); + return repository.World; } } } diff --git a/Lib9c/Action/Guild/Migration/Controls/GuildMigrationCtrl.cs b/Lib9c/Action/Guild/Migration/Controls/GuildMigrationCtrl.cs index 721c7635fb..f082e759f0 100644 --- a/Lib9c/Action/Guild/Migration/Controls/GuildMigrationCtrl.cs +++ b/Lib9c/Action/Guild/Migration/Controls/GuildMigrationCtrl.cs @@ -1,12 +1,10 @@ using Bencodex.Types; -using Libplanet.Action.State; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.Module.Guild; -using Nekoyume.PolicyAction.Tx.Begin; using Nekoyume.TypedAddress; -using Serilog; namespace Nekoyume.Action.Guild.Migration.Controls { @@ -20,15 +18,15 @@ public static class GuildMigrationCtrl /// /// /// Migration to guild from pledge failed. - public static IWorld MigratePlanetariumPledgeToGuild(IWorld world, AgentAddress target) + public static void MigratePlanetariumPledgeToGuild(GuildRepository repository, AgentAddress target) { - if (world.GetJoinedGuild(GuildConfig.PlanetariumGuildOwner) is not + if (repository.GetJoinedGuild(GuildConfig.PlanetariumGuildOwner) is not { } planetariumGuildAddress) { throw new GuildMigrationFailedException("Planetarium guild is not found."); } - if (!world.TryGetGuild(planetariumGuildAddress, out var planetariumGuild)) + if (!repository.TryGetGuild(planetariumGuildAddress, out var planetariumGuild)) { throw new GuildMigrationFailedException("Planetarium guild is not found."); } @@ -38,7 +36,7 @@ public static IWorld MigratePlanetariumPledgeToGuild(IWorld world, AgentAddress throw new GuildMigrationFailedException("Unexpected guild master."); } - if (world.GetJoinedGuild(target) is not null) + if (repository.GetJoinedGuild(target) is not null) { throw new GuildMigrationFailedException("Already joined to other guild."); } @@ -49,14 +47,14 @@ public static IWorld MigratePlanetariumPledgeToGuild(IWorld world, AgentAddress // [0] = PatronAddress // [1] = IsApproved // [2] = Mead amount to refill. - if (!world.TryGetLegacyState(pledgeAddress, out List list) || list.Count < 3 || + if (!repository.World.TryGetLegacyState(pledgeAddress, out List list) || list.Count < 3 || list[0] is not Binary || list[0].ToAddress() != MeadConfig.PatronAddress || list[1] is not Boolean approved || !approved) { throw new GuildMigrationFailedException("Unexpected pledge structure."); } - return world.JoinGuild(planetariumGuildAddress, target); + repository.JoinGuild(planetariumGuildAddress, target); } } } diff --git a/Lib9c/Action/Guild/Migration/MigratePledgeToGuild.cs b/Lib9c/Action/Guild/Migration/MigratePledgeToGuild.cs index e282df29e2..4dace3ab1c 100644 --- a/Lib9c/Action/Guild/Migration/MigratePledgeToGuild.cs +++ b/Lib9c/Action/Guild/Migration/MigratePledgeToGuild.cs @@ -3,6 +3,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Nekoyume.Action.Guild.Migration.Controls; +using Nekoyume.Model.Guild; using Nekoyume.TypedAddress; namespace Nekoyume.Action.Guild.Migration @@ -54,8 +55,10 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new GuildRepository(world, context); - return GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(world, Target); + GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(repository, Target); + return repository.World; } } } diff --git a/Lib9c/Action/Guild/QuitGuild.cs b/Lib9c/Action/Guild/QuitGuild.cs index dd3b24a7b2..d330ed86f8 100644 --- a/Lib9c/Action/Guild/QuitGuild.cs +++ b/Lib9c/Action/Guild/QuitGuild.cs @@ -3,6 +3,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.Module.Guild; namespace Nekoyume.Action.Guild @@ -32,11 +33,13 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new GuildRepository(world, context); var signer = context.GetAgentAddress(); // TODO: Do something to return 'Power' token; + repository.LeaveGuild(signer); - return world.LeaveGuild(signer); + return repository.World; } } } diff --git a/Lib9c/Action/Guild/RejectGuildApplication.cs b/Lib9c/Action/Guild/RejectGuildApplication.cs index aaa242a31b..7242531d55 100644 --- a/Lib9c/Action/Guild/RejectGuildApplication.cs +++ b/Lib9c/Action/Guild/RejectGuildApplication.cs @@ -3,6 +3,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.Module.Guild; using Nekoyume.TypedAddress; @@ -49,9 +50,11 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new GuildRepository(world, context); var signer = context.GetAgentAddress(); - return world.RejectGuildApplication(signer, Target); + repository.RejectGuildApplication(signer, Target); + return repository.World; } } } diff --git a/Lib9c/Action/Guild/RemoveGuild.cs b/Lib9c/Action/Guild/RemoveGuild.cs index 2817185ae0..65fbcc21c6 100644 --- a/Lib9c/Action/Guild/RemoveGuild.cs +++ b/Lib9c/Action/Guild/RemoveGuild.cs @@ -3,6 +3,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.Module.Guild; namespace Nekoyume.Action.Guild @@ -35,11 +36,13 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new GuildRepository(world, context); var signer = context.GetAgentAddress(); // TODO: Do something to return 'Power' token; - return world.RemoveGuild(signer); + repository.RemoveGuild(signer); + return repository.World; } } } diff --git a/Lib9c/Action/Guild/UnbanGuildMember.cs b/Lib9c/Action/Guild/UnbanGuildMember.cs index 1fde604998..b4641099ff 100644 --- a/Lib9c/Action/Guild/UnbanGuildMember.cs +++ b/Lib9c/Action/Guild/UnbanGuildMember.cs @@ -5,6 +5,7 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.Module.Guild; namespace Nekoyume.Action.Guild @@ -49,14 +50,16 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new GuildRepository(world, context); var signer = context.GetAgentAddress(); - if (world.GetJoinedGuild(signer) is not { } guildAddress) + if (repository.GetJoinedGuild(signer) is not { } guildAddress) { throw new InvalidOperationException("The signer does not join any guild."); } - return world.Unban(guildAddress, signer, Target); + repository.Unban(guildAddress, signer, Target); + return repository.World; } } } diff --git a/Lib9c/Action/RewardGold.cs b/Lib9c/Action/RewardGold.cs index 5e9baa39d8..70e3a4b21f 100644 --- a/Lib9c/Action/RewardGold.cs +++ b/Lib9c/Action/RewardGold.cs @@ -297,7 +297,7 @@ public IWorld MinerReward(IActionContext ctx, IWorld states) states = states.TransferAsset( ctx, GoldCurrencyState.Address, - ctx.Miner, + Addresses.RewardPool, miningReward ); } diff --git a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs index 99cba0db77..94ad66667d 100644 --- a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs +++ b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs @@ -16,8 +16,6 @@ public class AllocateReward : ActionBase { public const string TypeIdentifier = "distribute_validators"; - private const string TargetKey = "t"; - public AllocateReward() { } @@ -31,55 +29,54 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { var world = context.PreviousState; + var repository = new ValidatorRepository(world, context); var rewardCurrency = world.GetGoldCurrency(); + var proposerInfo = repository.GetProposerInfo(); - var proposerInfo = world.GetProposerInfo(); - - world = DistributeProposerReward( - world, + DistributeProposerReward( + repository, context, rewardCurrency, proposerInfo, context.LastCommit.Votes); - world = DistributeValidatorReward( - world, + DistributeValidatorReward( + repository, context, rewardCurrency, context.LastCommit.Votes); - var communityFund = world.GetBalance(Addresses.RewardPool, rewardCurrency); + var communityFund = repository.GetBalance(Addresses.RewardPool, rewardCurrency); if (communityFund.Sign > 0) { - world = world.TransferAsset( - context, + repository.TransferAsset( Addresses.RewardPool, Addresses.CommunityPool, communityFund); } - return world; + return repository.World; } - internal static IWorld DistributeProposerReward( - IWorld states, + internal static void DistributeProposerReward( + ValidatorRepository repository, IActionContext ctx, Currency rewardCurrency, ProposerInfo proposerInfo, IEnumerable lastVotes) { - FungibleAssetValue blockReward = states.GetBalance( + FungibleAssetValue blockReward = repository.GetBalance( Addresses.RewardPool, rewardCurrency); if (proposerInfo.BlockIndex != ctx.BlockIndex - 1) { - return states; + return; } if (blockReward.Sign <= 0) { - return states; + return; } BigInteger votePowerNumerator @@ -99,7 +96,7 @@ BigInteger votePowerDenominator if (votePowerDenominator == BigInteger.Zero) { - return states; + return; } var baseProposerReward @@ -110,30 +107,26 @@ var bonusProposerReward .DivRem(votePowerDenominator * ValidatorDelegatee.BonusProposerRewardDenominator).Quotient; FungibleAssetValue proposerReward = baseProposerReward + bonusProposerReward; - states = states.TransferAsset( - ctx, + repository.TransferAsset( Addresses.RewardPool, proposerInfo.Proposer, proposerReward); - - return states; } - internal static IWorld DistributeValidatorReward( - IWorld states, + internal static void DistributeValidatorReward( + ValidatorRepository repository, IActionContext ctx, Currency rewardCurrency, IEnumerable lastVotes) { long blockHeight = ctx.BlockIndex; - var repo = new ValidatorRepository(states, ctx); FungibleAssetValue rewardToAllocate - = states.GetBalance(Addresses.RewardPool, rewardCurrency); + = repository.GetBalance(Addresses.RewardPool, rewardCurrency); if (rewardToAllocate.Sign <= 0) { - return states; + return; } BigInteger validatorSetPower @@ -143,7 +136,7 @@ BigInteger validatorSetPower if (validatorSetPower == BigInteger.Zero) { - return states; + return; } foreach (Vote vote in lastVotes) @@ -153,8 +146,8 @@ BigInteger validatorSetPower continue; } - if (!states.TryGetValidatorDelegatee( - vote.ValidatorPublicKey.Address, repo, out var validatorDelegatee)) + if (!repository.TryGetValidatorDelegatee( + vote.ValidatorPublicKey.Address, out var validatorDelegatee)) { continue; } @@ -172,11 +165,7 @@ BigInteger validatorSetPower validatorSetPower, Addresses.RewardPool, blockHeight); - - states = validatorDelegatee.Repository.World; } - - return states; } } } diff --git a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs index 2a83384be3..1e3d89eaa3 100644 --- a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs @@ -4,6 +4,7 @@ using Libplanet.Action; using Libplanet.Crypto; using Nekoyume.Module.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; namespace Nekoyume.Action.ValidatorDelegation { @@ -11,8 +12,6 @@ public class ClaimRewardValidator : ActionBase { public const string TypeIdentifier = "claim_reward_validator"; - private const string TargetKey = "t"; - public ClaimRewardValidator() { } public ClaimRewardValidator(Address validatorDelegatee) @@ -45,8 +44,10 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new ValidatorRepository(world, context); + repository.ClaimRewardValidator(context, ValidatorDelegatee); - return world.ClaimRewardValidator(context, ValidatorDelegatee); + return repository.World; } } } diff --git a/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs index c42dea8efd..0be03656a5 100644 --- a/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs @@ -5,6 +5,7 @@ using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Module.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; namespace Nekoyume.Action.ValidatorDelegation { @@ -12,8 +13,6 @@ public class DelegateValidator : ActionBase { public const string TypeIdentifier = "delegate_validator"; - private const string TargetKey = "t"; - public DelegateValidator() { } public DelegateValidator(Address validatorDelegatee, FungibleAssetValue fav) @@ -51,8 +50,10 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new ValidatorRepository(world, context); + repository.DelegateValidator(context, ValidatorDelegatee, FAV); - return world.DelegateValidator(context, ValidatorDelegatee, FAV); + return repository.World; } } } diff --git a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs index ba6593c347..3c3215e04b 100644 --- a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs @@ -5,6 +5,7 @@ using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Module.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; namespace Nekoyume.Action.ValidatorDelegation { @@ -12,8 +13,6 @@ public class PromoteValidator : ActionBase { public const string TypeIdentifier = "promote_validator"; - private const string TargetKey = "t"; - public PromoteValidator() { } public PromoteValidator(PublicKey publicKey, FungibleAssetValue fav) @@ -51,10 +50,12 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new ValidatorRepository(world, context); + + repository.CreateValidatorDelegatee(context, PublicKey); + repository.DelegateValidator(context, context.Signer, FAV); - return world - .CreateValidatorDelegatee(context, PublicKey) - .DelegateValidator(context, context.Signer, FAV); + return repository.World; } } } diff --git a/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs index d1f354f63e..dc94c194ba 100644 --- a/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs @@ -5,6 +5,7 @@ using Libplanet.Action; using Libplanet.Crypto; using Nekoyume.Module.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; namespace Nekoyume.Action.ValidatorDelegation { @@ -12,8 +13,6 @@ public class RedelegateValidator : ActionBase { public const string TypeIdentifier = "redelegate_validator"; - private const string TargetKey = "t"; - public RedelegateValidator() { } public RedelegateValidator( @@ -57,8 +56,10 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new ValidatorRepository(world, context); + repository.RedelegateValidator(context, SrcValidatorDelegatee, DstValidatorDelegatee, Share); - return world.RedelegateValidator(context, SrcValidatorDelegatee, DstValidatorDelegatee, Share); + return repository.World; } } } diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs new file mode 100644 index 0000000000..d4e125354c --- /dev/null +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -0,0 +1,50 @@ +using System; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Crypto; +using Nekoyume.ValidatorDelegation; + +namespace Nekoyume.Action.ValidatorDelegation +{ + public sealed class ReleaseValidatorUnbondings : ActionBase + { + public const string TypeIdentifier = "release_validator_unbondings"; + + public ReleaseValidatorUnbondings() { } + + public ReleaseValidatorUnbondings(Address validatorDelegatee) + { + } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", Null.Value); + + public override void LoadPlainValue(IValue plainValue) + { + if (plainValue is not Dictionary root || + !root.TryGetValue((Text)"values", out var rawValues) || + rawValues is not Null) + { + throw new InvalidCastException(); + } + } + + public override IWorld Execute(IActionContext context) + { + GasTracer.UseGas(0L); + + var world = context.PreviousState; + var repository = new ValidatorRepository(world, context); + var unbondings = repository.GetUnbondingSet().UnbondingsToRelease(context.BlockIndex); + + foreach (var unbonding in unbondings) + { + unbonding.Release(context.BlockIndex); + } + + return repository.World; + } + } +} diff --git a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs new file mode 100644 index 0000000000..f172305afb --- /dev/null +++ b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs @@ -0,0 +1,87 @@ +using System; +using System.Numerics; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Crypto; +using Libplanet.Types.Consensus; +using Libplanet.Types.Evidence; +using Nekoyume.ValidatorDelegation; +using System.Linq; +using Nekoyume.Module.ValidatorDelegation; + +namespace Nekoyume.Action.ValidatorDelegation +{ + public class SlashValidator : ActionBase + { + public const string TypeIdentifier = "slash_validator"; + + public SlashValidator() { } + + public SlashValidator(Address validatorDelegatee) + { + } + + public static BigInteger DuplicateVoteSlashFactor => 10; + + public static BigInteger LivenessSlashFactor => 10; + + public static long AbstainJailTime => 10L; + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", Null.Value); + + public override void LoadPlainValue(IValue plainValue) + { + if (plainValue is not Dictionary root || + !root.TryGetValue((Text)"values", out var rawValues) || + rawValues is not Null) + { + throw new InvalidCastException(); + } + } + + public override IWorld Execute(IActionContext context) + { + GasTracer.UseGas(0L); + + var world = context.PreviousState; + var repository = new ValidatorRepository(world, context); + + var abstainHistory = repository.GetAbstainHistory(); + var abstainsToSlash = abstainHistory.FindToSlashAndAdd( + context.LastCommit.Votes.Where(vote => vote.Flag == VoteFlag.Null) + .Select(vote => vote.ValidatorPublicKey), + context.BlockIndex); + + foreach (var abstain in abstainsToSlash) + { + var validatorDelegatee = repository.GetValidatorDelegatee(abstain.Address); + validatorDelegatee.Slash(LivenessSlashFactor, context.BlockIndex, context.BlockIndex); + validatorDelegatee.Jail(context.BlockIndex + AbstainJailTime, context.BlockIndex); + } + + foreach (var evidence in context.Evidence) + { + switch (evidence) + { + case DuplicateVoteEvidence e: + if (e.Height > context.BlockIndex) + { + throw new Exception("Evidence height is greater than block index."); + } + + var validatorDelegatee = repository.GetValidatorDelegatee(e.TargetAddress); + validatorDelegatee.Slash(DuplicateVoteSlashFactor, e.Height, context.BlockIndex); + validatorDelegatee.Tombstone(); + break; + default: + break; + } + } + + return repository.World; + } + } +} diff --git a/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs index 805a3e4ab6..1a9cf765b0 100644 --- a/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs @@ -5,6 +5,7 @@ using Libplanet.Action; using Libplanet.Crypto; using Nekoyume.Module.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; namespace Nekoyume.Action.ValidatorDelegation { @@ -12,8 +13,6 @@ public class UndelegateValidator : ActionBase { public const string TypeIdentifier = "undelegate_validator"; - private const string TargetKey = "t"; - public UndelegateValidator() { } public UndelegateValidator(Address validatorDelegatee, BigInteger share) @@ -51,8 +50,10 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var repository = new ValidatorRepository(world, context); + repository.UndelegateValidator(context, ValidatorDelegatee, Share); - return world.UndelegateValidator(context, ValidatorDelegatee, Share); + return repository.World; } } } diff --git a/Lib9c/Action/ValidatorDelegation/UnjailValidator.cs b/Lib9c/Action/ValidatorDelegation/UnjailValidator.cs new file mode 100644 index 0000000000..2f485d1fad --- /dev/null +++ b/Lib9c/Action/ValidatorDelegation/UnjailValidator.cs @@ -0,0 +1,41 @@ +using System; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Nekoyume.ValidatorDelegation; + +namespace Nekoyume.Action.ValidatorDelegation +{ + public class UnjailValidator : ActionBase + { + public const string TypeIdentifier = "unjail_validator"; + + public UnjailValidator() { } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", Null.Value); + + public override void LoadPlainValue(IValue plainValue) + { + if (plainValue is not Dictionary root || + !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 delegatee = repository.GetValidatorDelegatee(context.Signer); + delegatee.Unjail(context.BlockIndex); + + return repository.World; + } + } +} diff --git a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs new file mode 100644 index 0000000000..25dbd8175f --- /dev/null +++ b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs @@ -0,0 +1,39 @@ +using System; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Types.Consensus; +using Nekoyume.ValidatorDelegation; + +namespace Nekoyume.Action.ValidatorDelegation +{ + public sealed class UpdateValidators : ActionBase + { + const string TypeIdentifier = "update_validators"; + + public UpdateValidators() { } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", Null.Value); + + public override void LoadPlainValue(IValue plainValue) + { + if (plainValue is not Dictionary root || + !root.TryGetValue((Text)"values", out var rawValues) || + rawValues is not Null) + { + throw new InvalidCastException(); + } + } + + public override IWorld Execute(IActionContext context) + { + var world = context.PreviousState; + var repository = new ValidatorRepository(world, context); + var validators = repository.GetValidatorList(); + + return world.SetValidatorSet(new ValidatorSet(validators.GetBonded())); + } + } +} diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index 2394c074f2..f25fdb2f0f 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -94,40 +94,48 @@ public static Address GetGuildBanAccountAddress(Address guildAddress) => public static readonly Address EmptyAccountAddress = new("ffffffffffffffffffffffffffffffffffffffff"); - #endregion - - #region Delegation - /// /// An address of an account having . /// - public static readonly Address UnbondingSet + public static readonly Address GuildUnbondingSet = new Address("0000000000000000000000000000000000000300"); /// - /// An address of an account having . + /// An address of an account having . /// - public static readonly Address Bond + public static readonly Address GuildMetadata = new Address("0000000000000000000000000000000000000301"); /// - /// An address of an account having . + /// An address of an account having . /// - public static readonly Address UnbondLockIn + public static readonly Address GuildParticipantMetadata = new Address("0000000000000000000000000000000000000302"); /// - /// An address of an account having . + /// An address of an account having . /// - public static readonly Address RebondGrace + public static readonly Address GuildBond = new Address("0000000000000000000000000000000000000303"); /// - /// An address of an account having . + /// An address of an account having . /// - public static readonly Address LumpSumRewardsRecord + public static readonly Address GuildUnbondLockIn = new Address("0000000000000000000000000000000000000304"); + /// + /// An address of an account having . + /// + public static readonly Address GuildRebondGrace + = new Address("0000000000000000000000000000000000000305"); + + /// + /// An address of an account having . + /// + public static readonly Address GuildLumpSumRewardsRecord + = new Address("0000000000000000000000000000000000000306"); + #endregion #region Validator @@ -162,6 +170,54 @@ public static readonly Address ValidatorDelegator public static readonly Address CommunityPool = new Address("0000000000000000000000000000000000000499"); + /// + /// An address of an account having . + /// + public static readonly Address ValidatorUnbondingSet + = new Address("0000000000000000000000000000000000000300"); + + /// + /// An address of an account having . + /// + public static readonly Address ValidatorDelegateeMetadata + = new Address("0000000000000000000000000000000000000301"); + + /// + /// An address of an account having . + /// + public static readonly Address ValidatorDelegatorMetadata + = new Address("0000000000000000000000000000000000000302"); + + /// + /// An address of an account having . + /// + public static readonly Address ValidatorBond + = new Address("0000000000000000000000000000000000000303"); + + /// + /// An address of an account having . + /// + public static readonly Address ValidatorUnbondLockIn + = new Address("0000000000000000000000000000000000000304"); + + /// + /// An address of an account having . + /// + public static readonly Address ValidatorRebondGrace + = new Address("0000000000000000000000000000000000000305"); + + /// + /// An address of an account having . + /// + public static readonly Address ValidatorLumpSumRewardsRecord + = new Address("0000000000000000000000000000000000000306"); + + /// + /// An address of an account having . + /// + public static readonly Address AbstainHistory + = new Address("0000000000000000000000000000000000000307"); + #endregion public static Address GetSheetAddress() where T : ISheet => GetSheetAddress(typeof(T).Name); diff --git a/Lib9c/Delegation/Bond.cs b/Lib9c/Delegation/Bond.cs index a2479c8c63..3f208dc5e0 100644 --- a/Lib9c/Delegation/Bond.cs +++ b/Lib9c/Delegation/Bond.cs @@ -10,7 +10,7 @@ namespace Nekoyume.Delegation public sealed class Bond : IBencodable, IEquatable { public Bond(Address address) - : this(address, BigInteger.Zero, 0) + : this(address, BigInteger.Zero, null) { } @@ -20,11 +20,14 @@ public Bond(Address address, IValue bencoded) } public Bond(Address address, List bencoded) - : this(address, (Integer)bencoded[0], (Integer)bencoded[1]) + : this( + address, + (Integer)bencoded[0], + bencoded[1] is Integer share ? share : null) { } - private Bond(Address address, BigInteger share, long lastDistributeHeight) + private Bond(Address address, BigInteger share, long? lastDistributeHeight) { if (share.Sign < 0) { @@ -51,13 +54,15 @@ private Bond(Address address, BigInteger share, long lastDistributeHeight) public BigInteger Share { get; } - public long LastDistributeHeight { get; } + public long? LastDistributeHeight { get; } - public bool IsEmpty => Share.IsZero; + public bool IsEmpty => Share.IsZero && LastDistributeHeight is null; public List Bencoded => List.Empty .Add(Share) - .Add(LastDistributeHeight); + .Add(LastDistributeHeight.HasValue + ? new Integer(LastDistributeHeight.Value) + : Null.Value); IValue IBencodable.Bencoded => Bencoded; @@ -102,7 +107,7 @@ internal Bond SubtractShare(BigInteger share) internal Bond UpdateLastDistributeHeight(long height) { - if (height <= LastDistributeHeight) + if (LastDistributeHeight.HasValue && LastDistributeHeight >= height) { throw new ArgumentOutOfRangeException( nameof(height), @@ -112,5 +117,10 @@ internal Bond UpdateLastDistributeHeight(long height) return new Bond(Address, Share, height); } + + internal Bond ClearLastDistributeHeight() + { + return new Bond(Address, Share, null); + } } } diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 3f8e888bd3..fb394156fd 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -2,10 +2,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Numerics; -using System.Security.Cryptography; -using Bencodex; using Bencodex.Types; using Libplanet.Crypto; using Libplanet.Types.Assets; @@ -16,142 +13,141 @@ public abstract class Delegatee : IDelegatee where T : Delegator where TSelf : Delegatee { - protected readonly byte[] BondId = new byte[] { 0x42 }; // `B` - protected readonly byte[] UnbondLockInId = new byte[] { 0x55 }; // `U` - protected readonly byte[] RebondGraceId = new byte[] { 0x52 }; // `R` - protected readonly byte[] LumpSumRewardsRecordId = new byte[] { 0x4c }; // `L` - protected readonly byte[] RewardCollectorId = new byte[] { 0x63 }; // `c` - protected readonly byte[] RewardDistributorId = new byte[] { 0x64 }; // `d` - protected readonly byte[] PoolId = new byte[] { 0x70 }; // `p` - - private readonly IDelegationRepository? _repository; - private ImmutableSortedSet _unbondingRefs; - - public Delegatee(Address address, IDelegationRepository? repository = null) + public Delegatee( + Address address, + Address accountAddress, + Currency delegationCurrency, + Currency rewardCurrency, + Address delegationPoolAddress, + long unbondingPeriod, + int maxUnbondLockInEntries, + int maxRebondGraceEntries, + IDelegationRepository repository) + : this( + new DelegateeMetadata( + address, + accountAddress, + delegationCurrency, + rewardCurrency, + delegationPoolAddress, + unbondingPeriod, + maxUnbondLockInEntries, + maxRebondGraceEntries), + repository) { - Address = address; - Delegators = ImmutableSortedSet
.Empty; - TotalDelegated = DelegationCurrency * 0; - TotalShares = BigInteger.Zero; - _unbondingRefs = ImmutableSortedSet.Empty; - _repository = repository; } - public Delegatee(Address address, IValue bencoded, IDelegationRepository? repository = null) - : this(address, (List)bencoded, repository) + public Delegatee( + Address address, + IDelegationRepository repository) + : this(repository.GetDelegateeMetadata(address), repository) { } - public Delegatee(Address address, List bencoded, IDelegationRepository? repository = null) - : this( - address, - ((List)bencoded[0]).Select(item => new Address(item)), - new FungibleAssetValue(bencoded[1]), - (Integer)bencoded[2], - ((List)bencoded[3]).Select(item => new UnbondingRef(item)), - repository) + private Delegatee(DelegateeMetadata metadata, IDelegationRepository repository) { + Metadata = metadata; + Repository = repository; } - private Delegatee( - Address address, - IEnumerable
delegators, - FungibleAssetValue totalDelegated, - BigInteger totalShares, - IEnumerable unbondingRefs, - IDelegationRepository? repository) - { - if (!totalDelegated.Currency.Equals(DelegationCurrency)) - { - throw new InvalidOperationException("Invalid currency."); - } + public event EventHandler? DelegationChanged; - if (totalDelegated.Sign < 0) - { - throw new ArgumentOutOfRangeException( - nameof(totalDelegated), - totalDelegated, - "Total delegated must be non-negative."); - } + public event EventHandler? Enjailed; - if (totalShares.Sign < 0) - { - throw new ArgumentOutOfRangeException( - nameof(totalShares), - totalShares, - "Total shares must be non-negative."); - } + public event EventHandler? Unjailed; - Address = address; - Delegators = delegators.ToImmutableSortedSet(); - TotalDelegated = totalDelegated; - TotalShares = totalShares; - _unbondingRefs = unbondingRefs.ToImmutableSortedSet(); - _repository = repository; - } + public DelegateeMetadata Metadata { get; } - public Address Address { get; } + public IDelegationRepository Repository { get; } - public abstract Currency DelegationCurrency { get; } + public Address Address => Metadata.DelegateeAddress; - public abstract Currency RewardCurrency { get; } + public Address AccountAddress => Metadata.DelegateeAccountAddress; - public abstract Address DelegationPoolAddress { get; } + public Address MetadataAddress => Metadata.Address; - public abstract long UnbondingPeriod { get; } + public Currency DelegationCurrency => Metadata.DelegationCurrency; - public abstract byte[] DelegateeId { get; } + public Currency RewardCurrency => Metadata.RewardCurrency; - public abstract int MaxUnbondLockInEntries { get; } + public Address DelegationPoolAddress => Metadata.DelegationPoolAddress; - public abstract int MaxRebondGraceEntries { get; } + public long UnbondingPeriod => Metadata.UnbondingPeriod; - public abstract BigInteger SlashFactor { get; } + public int MaxUnbondLockInEntries => Metadata.MaxUnbondLockInEntries; - public Address RewardCollectorAddress => DeriveAddress(RewardCollectorId); + public int MaxRebondGraceEntries => Metadata.MaxRebondGraceEntries; - public Address RewardDistributorAddress => DeriveAddress(RewardDistributorId); + public Address RewardCollectorAddress => Metadata.RewardCollectorAddress; - public ImmutableSortedSet
Delegators { get; private set; } + public Address RewardDistributorAddress => Metadata.RewardDistributorAddress; - public FungibleAssetValue TotalDelegated { get; private set; } + public ImmutableSortedSet
Delegators => Metadata.Delegators; - public BigInteger TotalShares { get; private set; } + public FungibleAssetValue TotalDelegated => Metadata.TotalDelegatedFAV; - public IDelegationRepository? Repository => _repository; + public BigInteger TotalShares => Metadata.TotalShares; - public virtual List Bencoded => List.Empty - .Add(new List(Delegators.Select(delegator => delegator.Bencoded))) - .Add(TotalDelegated.Serialize()) - .Add(TotalShares) - .Add(new List(_unbondingRefs.Select(unbondingRef => unbondingRef.Bencoded))); + public bool Jailed => Metadata.Jailed; - IValue IBencodable.Bencoded => Bencoded; + public long JailedUntil => Metadata.JailedUntil; - public BigInteger ShareToBond(FungibleAssetValue fav) - => TotalShares.IsZero - ? fav.RawValue - : TotalShares * fav.RawValue / TotalDelegated.RawValue; + public bool Tombstoned => Metadata.Tombstoned; - public FungibleAssetValue FAVToUnbond(BigInteger share) - => TotalShares == share - ? TotalDelegated - : (TotalDelegated * share).DivRem(TotalShares).Quotient; + public List MetadataBencoded => Metadata.Bencoded; - BigInteger IDelegatee.Bond( - IDelegator delegator, FungibleAssetValue fav, long height) + public BigInteger ShareFromFAV(FungibleAssetValue fav) + => Metadata.ShareFromFAV(fav); + + public FungibleAssetValue FAVFromShare(BigInteger share) + => Metadata.FAVFromShare(share); + + public BigInteger Bond(IDelegator delegator, FungibleAssetValue fav, long height) => Bond((T)delegator, fav, height); - FungibleAssetValue IDelegatee.Unbond( - IDelegator delegator, BigInteger share, long height) + public FungibleAssetValue Unbond(IDelegator delegator, BigInteger share, long height) => Unbond((T)delegator, share, height); - void IDelegatee.DistributeReward(IDelegator delegator, long height) + public void DistributeReward(IDelegator delegator, long height) => DistributeReward((T)delegator, height); + public void Jail(long releaseHeight, long height) + { + Metadata.JailUntil(releaseHeight); + Repository.SetDelegateeMetadata(Metadata); + Enjailed?.Invoke(this, height); + } + + public void Unjail(long height) + { + Metadata.Unjail(height); + Repository.SetDelegateeMetadata(Metadata); + Unjailed?.Invoke(this, height); + } + + public void Tombstone() + { + Metadata.Tombstone(); + Repository.SetDelegateeMetadata(Metadata); + } + + public Address BondAddress(Address delegatorAddress) + => Metadata.BondAddress(delegatorAddress); + + public Address UnbondLockInAddress(Address delegatorAddress) + => Metadata.UnbondLockInAddress(delegatorAddress); + + public Address RebondGraceAddress(Address delegatorAddress) + => Metadata.RebondGraceAddress(delegatorAddress); + + public Address CurrentLumpSumRewardsRecordAddress() + => Metadata.CurrentLumpSumRewardsRecordAddress(); + + public Address LumpSumRewardsRecordAddress(long height) + => Metadata.LumpSumRewardsRecordAddress(height); + public virtual BigInteger Bond(T delegator, FungibleAssetValue fav, long height) { - CannotMutateRelationsWithoutRepository(delegator); DistributeReward(delegator, height); if (!fav.Currency.Equals(DelegationCurrency)) @@ -160,21 +156,25 @@ public virtual BigInteger Bond(T delegator, FungibleAssetValue fav, long height) "Cannot bond with invalid currency."); } - Bond bond = _repository!.GetBond(this, delegator.Address); - BigInteger share = ShareToBond(fav); + Bond bond = Repository.GetBond(this, delegator.Address); + BigInteger share = ShareFromFAV(fav); bond = bond.AddShare(share); - Delegators = Delegators.Add(delegator.Address); - TotalShares += share; - TotalDelegated += fav; - _repository.SetBond(bond); + Metadata.AddDelegator(delegator.Address); + Metadata.AddShare(share); + Metadata.AddDelegatedFAV(fav); + Repository.SetBond(bond); StartNewRewardPeriod(height); + Repository.SetDelegateeMetadata(Metadata); + DelegationChanged?.Invoke(this, height); return share; } - public virtual FungibleAssetValue Unbond(T delegator, BigInteger share, long height) + BigInteger IDelegatee.Bond(IDelegator delegator, FungibleAssetValue fav, long height) + => Bond((T)delegator, fav, height); + + public FungibleAssetValue Unbond(T delegator, BigInteger share, long height) { - CannotMutateRelationsWithoutRepository(delegator); DistributeReward(delegator, height); if (TotalShares.IsZero || TotalDelegated.RawValue.IsZero) { @@ -182,140 +182,113 @@ public virtual FungibleAssetValue Unbond(T delegator, BigInteger share, long hei "Cannot unbond without bonding."); } - Bond bond = _repository!.GetBond(this, delegator.Address); - FungibleAssetValue fav = FAVToUnbond(share); + Bond bond = Repository!.GetBond(this, delegator.Address); + FungibleAssetValue fav = FAVFromShare(share); bond = bond.SubtractShare(share); if (bond.Share.IsZero) { - Delegators = Delegators.Remove(delegator.Address); + bond = bond.ClearLastDistributeHeight(); + Metadata.RemoveDelegator(delegator.Address); } - TotalShares -= share; - TotalDelegated -= fav; - _repository.SetBond(bond); + Metadata.RemoveShare(share); + Metadata.RemoveDelegatedFAV(fav); + Repository.SetBond(bond); StartNewRewardPeriod(height); + Repository.SetDelegateeMetadata(Metadata); + DelegationChanged?.Invoke(this, height); return fav; } - public virtual void DistributeReward(T delegator, long height) + FungibleAssetValue IDelegatee.Unbond(IDelegator delegator, BigInteger share, long height) + => Unbond((T)delegator, share, height); + + public void DistributeReward(T delegator, long height) { - CannotMutateRelationsWithoutRepository(delegator); - BigInteger share = _repository!.GetBond(this, delegator.Address).Share; - IEnumerable lumpSumRewardsRecords = - GetLumpSumRewardsRecords(delegator.LastRewardHeight); - FungibleAssetValue reward = CalculateReward(share, lumpSumRewardsRecords); - if (reward.Sign > 0) + Bond bond = Repository.GetBond(this, delegator.Address); + BigInteger share = bond.Share; + + if (!share.IsZero && bond.LastDistributeHeight.HasValue) { - _repository.TransferAsset(RewardDistributorAddress, delegator.Address, reward); + IEnumerable lumpSumRewardsRecords = + GetLumpSumRewardsRecords(bond.LastDistributeHeight); + FungibleAssetValue reward = CalculateReward(share, lumpSumRewardsRecords); + if (reward.Sign > 0) + { + Repository.TransferAsset(RewardDistributorAddress, delegator.Address, reward); + } } - delegator.UpdateLastRewardHeight(height); + bond = bond.UpdateLastDistributeHeight(height); + Repository.SetBond(bond); } - public virtual void CollectRewards(long height) + void IDelegatee.DistributeReward(IDelegator delegator, long height) + => DistributeReward((T)delegator, height); + + public void CollectRewards(long height) { - CannotMutateRelationsWithoutRepository(); - FungibleAssetValue rewards = _repository!.GetBalance(RewardCollectorAddress, RewardCurrency); - _repository!.AddLumpSumRewards(this, height, rewards); - _repository!.TransferAsset(RewardCollectorAddress, RewardDistributorAddress, rewards); + FungibleAssetValue rewards = Repository.GetBalance(RewardCollectorAddress, RewardCurrency); + Repository.AddLumpSumRewards(this, height, rewards); + Repository.TransferAsset(RewardCollectorAddress, RewardDistributorAddress, rewards); } - public void Slash(long infractionHeight) + public void Slash(BigInteger slashFactor, long infractionHeight, long height) { - CannotMutateRelationsWithoutRepository(); - foreach (var item in _unbondingRefs) + FungibleAssetValue? fav = null; + foreach (var item in Metadata.UnbondingRefs) { - var unbonding = UnbondingFactory.GetUnbondingFromRef(item, _repository) - .Slash(SlashFactor, infractionHeight); + var unbonding = UnbondingFactory.GetUnbondingFromRef(item, Repository); + + unbonding = unbonding.Slash(slashFactor, infractionHeight, height, out var slashedFAV); + + if (slashedFAV.HasValue) + { + fav = fav.HasValue + ? fav.Value + slashedFAV.Value + : slashedFAV.Value; + } if (unbonding.IsEmpty) { - RemoveUnbondingRef(item); + Metadata.RemoveUnbondingRef(item); } switch (unbonding) { case UnbondLockIn unbondLockIn: - _repository!.SetUnbondLockIn(unbondLockIn); + Repository.SetUnbondLockIn(unbondLockIn); break; case RebondGrace rebondGrace: - _repository!.SetRebondGrace(rebondGrace); + Repository.SetRebondGrace(rebondGrace); break; default: throw new InvalidOperationException("Invalid unbonding type."); } } - } - public void AddUnbondingRef(UnbondingRef unbondingRef) - { - _unbondingRefs = _unbondingRefs.Add(unbondingRef); - } + if (fav.HasValue) + { + Metadata.RemoveDelegatedFAV(fav.Value); + } - public void RemoveUnbondingRef(UnbondingRef unbondingRef) - { - _unbondingRefs = _unbondingRefs.Remove(unbondingRef); + Repository.SetDelegateeMetadata(Metadata); + DelegationChanged?.Invoke(this, height); } - public virtual Address BondAddress(Address delegatorAddress) - => DeriveAddress(BondId, delegatorAddress); - - public virtual Address UnbondLockInAddress(Address delegatorAddress) - => DeriveAddress(UnbondLockInId, delegatorAddress); - - public virtual Address RebondGraceAddress(Address delegatorAddress) - => DeriveAddress(RebondGraceId, delegatorAddress); - - public virtual Address CurrentLumpSumRewardsRecordAddress() - => DeriveAddress(LumpSumRewardsRecordId); - - public virtual Address LumpSumRewardsRecordAddress(long height) - => DeriveAddress(LumpSumRewardsRecordId, BitConverter.GetBytes(height)); - - - public override bool Equals(object? obj) - => obj is IDelegatee other && Equals(other); + void IDelegatee.Slash(BigInteger slashFactor, long infractionHeight, long height) + => Slash(slashFactor, infractionHeight, height); - public virtual bool Equals(IDelegatee? other) - => ReferenceEquals(this, other) - || (other is Delegatee delegatee - && (GetType() != delegatee.GetType()) - && Address.Equals(delegatee.Address) - && DelegationCurrency.Equals(delegatee.DelegationCurrency) - && RewardCurrency.Equals(delegatee.RewardCurrency) - && DelegationPoolAddress.Equals(delegatee.DelegationPoolAddress) - && UnbondingPeriod == delegatee.UnbondingPeriod - && RewardCollectorAddress.Equals(delegatee.RewardCollectorAddress) - && RewardDistributorAddress.Equals(delegatee.RewardDistributorAddress) - && Delegators.SequenceEqual(delegatee.Delegators) - && TotalDelegated.Equals(delegatee.TotalDelegated) - && TotalShares.Equals(delegatee.TotalShares) - && _unbondingRefs.SequenceEqual(delegatee._unbondingRefs) - && DelegateeId.SequenceEqual(delegatee.DelegateeId)); + public void AddUnbondingRef(UnbondingRef reference) + => Metadata.AddUnbondingRef(reference); - public override int GetHashCode() - => Address.GetHashCode(); - - protected Address DeriveAddress(byte[] typeId, Address address) - => DeriveAddress(typeId, address.ByteArray); - - protected Address DeriveAddress(byte[] typeId, IEnumerable? bytes = null) - { - byte[] hashed; - using (HMACSHA1 hmac = new(DelegateeId.Concat(typeId).ToArray())) - { - hashed = hmac.ComputeHash( - Address.ByteArray.Concat(bytes ?? Array.Empty()).ToArray()); - } - - return new Address(hashed); - } + public void RemoveUnbondingRef(UnbondingRef reference) + => Metadata.RemoveUnbondingRef(reference); private void StartNewRewardPeriod(long height) { - CannotMutateRelationsWithoutRepository(); - LumpSumRewardsRecord? currentRecord = _repository!.GetCurrentLumpSumRewardsRecord(this); + LumpSumRewardsRecord? currentRecord = Repository.GetCurrentLumpSumRewardsRecord(this); long? lastStartHeight = null; if (currentRecord is LumpSumRewardsRecord lastRecord) { @@ -329,11 +302,11 @@ private void StartNewRewardPeriod(long height) RewardCurrency, currentRecord.LastStartHeight); - _repository.SetLumpSumRewardsRecord(currentRecord); + Repository.SetLumpSumRewardsRecord(currentRecord); return; } - _repository.SetLumpSumRewardsRecord( + Repository.SetLumpSumRewardsRecord( lastRecord.MoveAddress( LumpSumRewardsRecordAddress(lastRecord.StartHeight))); } @@ -345,7 +318,7 @@ private void StartNewRewardPeriod(long height) RewardCurrency, lastStartHeight); - _repository.SetLumpSumRewardsRecord(newRecord); + Repository.SetLumpSumRewardsRecord(newRecord); } private FungibleAssetValue CalculateReward( @@ -382,10 +355,9 @@ private FungibleAssetValue CalculateReward( private List GetLumpSumRewardsRecords(long? lastRewardHeight) { - CannotMutateRelationsWithoutRepository(); List records = new(); if (lastRewardHeight is null - || !(_repository!.GetCurrentLumpSumRewardsRecord(this) is LumpSumRewardsRecord record)) + || !(Repository.GetCurrentLumpSumRewardsRecord(this) is LumpSumRewardsRecord record)) { return records; } @@ -399,31 +371,12 @@ private List GetLumpSumRewardsRecords(long? lastRewardHeig break; } - record = _repository.GetLumpSumRewardsRecord(this, lastStartHeight) + record = Repository.GetLumpSumRewardsRecord(this, lastStartHeight) ?? throw new InvalidOperationException( $"Lump sum rewards record for #{lastStartHeight} is missing"); } return records; } - - private void CannotMutateRelationsWithoutRepository(T delegator) - { - CannotMutateRelationsWithoutRepository(); - if (!_repository!.Equals(delegator.Repository)) - { - throw new InvalidOperationException( - "Cannot mutate with different repository."); - } - } - - private void CannotMutateRelationsWithoutRepository() - { - if (_repository is null) - { - throw new InvalidOperationException( - "Cannot mutate without repository."); - } - } } } diff --git a/Lib9c/Delegation/DelegateeMetadata.cs b/Lib9c/Delegation/DelegateeMetadata.cs new file mode 100644 index 0000000000..52ad219d19 --- /dev/null +++ b/Lib9c/Delegation/DelegateeMetadata.cs @@ -0,0 +1,307 @@ +#nullable enable +using Bencodex; +using Bencodex.Types; +using Libplanet.Crypto; +using Libplanet.Types.Assets; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Numerics; + +namespace Nekoyume.Delegation +{ + public class DelegateeMetadata : IDelegateeMetadata + { + private Address? _address; + + public DelegateeMetadata( + Address delegateeAddress, + Address delegateeAccountAddress, + Currency delegationCurrency, + Currency rewardCurrency, + Address delegationPoolAddress, + long unbondingPeriod, + int maxUnbondLockInEntries, + int maxRebondGraceEntries) + : this( + delegateeAddress, + delegateeAccountAddress, + delegationCurrency, + rewardCurrency, + delegationPoolAddress, + unbondingPeriod, + maxUnbondLockInEntries, + maxRebondGraceEntries, + ImmutableSortedSet
.Empty, + delegationCurrency * 0, + BigInteger.Zero, + false, + -1L, + false, + ImmutableSortedSet.Empty) + { + } + + public DelegateeMetadata( + Address delegateeAddress, + Address delegateeAccountAddress, + IValue bencoded) + : this(delegateeAddress, delegateeAccountAddress, (List)bencoded) + { + } + + public DelegateeMetadata( + Address address, + Address accountAddress, + List bencoded) + : this( + address, + accountAddress, + new Currency(bencoded[0]), + new Currency(bencoded[1]), + new Address(bencoded[2]), + (Integer)bencoded[3], + (Integer)bencoded[4], + (Integer)bencoded[5], + ((List)bencoded[6]).Select(item => new Address(item)), + new FungibleAssetValue(bencoded[7]), + (Integer)bencoded[8], + (Bencodex.Types.Boolean)bencoded[9], + (Integer)bencoded[10], + (Bencodex.Types.Boolean)bencoded[11], + ((List)bencoded[12]).Select(item => new UnbondingRef(item))) + { + } + + private DelegateeMetadata( + Address delegateeAddress, + Address delegateeAccountAddress, + Currency delegationCurrency, + Currency rewardCurrency, + Address delegationPoolAddress, + long unbondingPeriod, + int maxUnbondLockInEntries, + int maxRebondGraceEntries, + IEnumerable
delegators, + FungibleAssetValue totalDelegated, + BigInteger totalShares, + bool jailed, + long jailedUntil, + bool tombstoned, + IEnumerable unbondingRefs) + { + if (!totalDelegated.Currency.Equals(delegationCurrency)) + { + throw new InvalidOperationException("Invalid currency."); + } + + if (totalDelegated.Sign < 0) + { + throw new ArgumentOutOfRangeException( + nameof(totalDelegated), + totalDelegated, + "Total delegated must be non-negative."); + } + + if (totalShares.Sign < 0) + { + throw new ArgumentOutOfRangeException( + nameof(totalShares), + totalShares, + "Total shares must be non-negative."); + } + + DelegateeAddress = delegateeAddress; + DelegateeAccountAddress = delegateeAccountAddress; + DelegationCurrency = delegationCurrency; + RewardCurrency = rewardCurrency; + DelegationPoolAddress = delegationPoolAddress; + UnbondingPeriod = unbondingPeriod; + MaxUnbondLockInEntries = maxUnbondLockInEntries; + MaxRebondGraceEntries = maxRebondGraceEntries; + Delegators = delegators.ToImmutableSortedSet(); + TotalDelegatedFAV = totalDelegated; + TotalShares = totalShares; + Jailed = jailed; + JailedUntil = jailedUntil; + Tombstoned = tombstoned; + UnbondingRefs = unbondingRefs.ToImmutableSortedSet(); + } + + public Address DelegateeAddress { get; } + + public Address DelegateeAccountAddress { get; } + + public Address Address + => _address ??= DelegationAddress.DelegateeMetadataAddress( + DelegateeAddress, + DelegateeAccountAddress); + + public Currency DelegationCurrency { get; } + + public Currency RewardCurrency { get; } + + public Address DelegationPoolAddress { get; } + + public long UnbondingPeriod { get; } + + public int MaxUnbondLockInEntries { get; } + + public int MaxRebondGraceEntries { get; } + + public Address RewardCollectorAddress + => DelegationAddress.RewardCollectorAddress(Address); + + public Address RewardDistributorAddress + => DelegationAddress.RewardDistributorAddress(Address); + + public ImmutableSortedSet
Delegators { get; private set; } + + public FungibleAssetValue TotalDelegatedFAV { get; private set; } + + public BigInteger TotalShares { get; private set; } + + public bool Jailed { get; private set; } + + public long JailedUntil { get; private set; } + + public bool Tombstoned { get; private set; } + + public ImmutableSortedSet UnbondingRefs { get; private set; } + + public List Bencoded => List.Empty + .Add(DelegationCurrency.Serialize()) + .Add(RewardCurrency.Serialize()) + .Add(DelegationPoolAddress.Bencoded) + .Add(UnbondingPeriod) + .Add(MaxUnbondLockInEntries) + .Add(MaxRebondGraceEntries) + .Add(new List(Delegators.Select(delegator => delegator.Bencoded))) + .Add(TotalDelegatedFAV.Serialize()) + .Add(TotalShares) + .Add(Jailed) + .Add(JailedUntil) + .Add(Tombstoned) + .Add(new List(UnbondingRefs.Select(unbondingRef => unbondingRef.Bencoded))); + + IValue IBencodable.Bencoded => Bencoded; + + public BigInteger ShareFromFAV(FungibleAssetValue fav) + => TotalShares.IsZero + ? fav.RawValue + : TotalShares * fav.RawValue / TotalDelegatedFAV.RawValue; + + public FungibleAssetValue FAVFromShare(BigInteger share) + => TotalShares == share + ? TotalDelegatedFAV + : (TotalDelegatedFAV * share).DivRem(TotalShares).Quotient; + + public void AddDelegator(Address delegatorAddress) + { + Delegators = Delegators.Add(delegatorAddress); + } + + public void RemoveDelegator(Address delegatorAddress) + { + Delegators = Delegators.Remove(delegatorAddress); + } + + public void AddDelegatedFAV(FungibleAssetValue fav) + { + TotalDelegatedFAV += fav; + } + + public void RemoveDelegatedFAV(FungibleAssetValue fav) + { + TotalDelegatedFAV -= fav; + } + + public void AddShare(BigInteger share) + { + TotalShares += share; + } + + public void RemoveShare(BigInteger share) + { + TotalShares -= share; + } + + public void JailUntil(long height) + { + JailedUntil = height; + Jailed = true; + } + + public void Unjail(long height) + { + if (Tombstoned) + { + throw new InvalidOperationException("Cannot unjail tombstoned delegatee."); + } + + if (JailedUntil > height) + { + throw new InvalidOperationException("Cannot unjail before jailed until."); + } + + JailedUntil = -1L; + Jailed = false; + } + + public void Tombstone() + { + JailUntil(long.MaxValue); + Tombstoned = true; + } + + public void AddUnbondingRef(UnbondingRef unbondingRef) + { + UnbondingRefs = UnbondingRefs.Add(unbondingRef); + } + + public void RemoveUnbondingRef(UnbondingRef unbondingRef) + { + UnbondingRefs = UnbondingRefs.Remove(unbondingRef); + } + + public Address BondAddress(Address delegatorAddress) + => DelegationAddress.BondAddress(Address, delegatorAddress); + + public Address UnbondLockInAddress(Address delegatorAddress) + => DelegationAddress.UnbondLockInAddress(Address, delegatorAddress); + + public virtual Address RebondGraceAddress(Address delegatorAddress) + => DelegationAddress.RebondGraceAddress(Address, delegatorAddress); + + public virtual Address CurrentLumpSumRewardsRecordAddress() + => DelegationAddress.CurrentLumpSumRewardsRecordAddress(Address); + + public virtual Address LumpSumRewardsRecordAddress(long height) + => DelegationAddress.LumpSumRewardsRecordAddress(Address, height); + + public override bool Equals(object? obj) + => obj is IDelegateeMetadata other && Equals(other); + + public virtual bool Equals(IDelegateeMetadata? other) + => ReferenceEquals(this, other) + || (other is DelegateeMetadata delegatee + && (GetType() != delegatee.GetType()) + && DelegateeAddress.Equals(delegatee.DelegateeAddress) + && DelegateeAccountAddress.Equals(delegatee.DelegateeAccountAddress) + && DelegationCurrency.Equals(delegatee.DelegationCurrency) + && RewardCurrency.Equals(delegatee.RewardCurrency) + && DelegationPoolAddress.Equals(delegatee.DelegationPoolAddress) + && UnbondingPeriod == delegatee.UnbondingPeriod + && RewardCollectorAddress.Equals(delegatee.RewardCollectorAddress) + && RewardDistributorAddress.Equals(delegatee.RewardDistributorAddress) + && Delegators.SequenceEqual(delegatee.Delegators) + && TotalDelegatedFAV.Equals(delegatee.TotalDelegatedFAV) + && TotalShares.Equals(delegatee.TotalShares) + && Jailed == delegatee.Jailed + && UnbondingRefs.SequenceEqual(delegatee.UnbondingRefs)); + + public override int GetHashCode() + => DelegateeAddress.GetHashCode(); + } +} diff --git a/Lib9c/Delegation/DelegationAddress.cs b/Lib9c/Delegation/DelegationAddress.cs new file mode 100644 index 0000000000..d57f91b332 --- /dev/null +++ b/Lib9c/Delegation/DelegationAddress.cs @@ -0,0 +1,147 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using Libplanet.Crypto; + +namespace Nekoyume.Delegation +{ + public static class DelegationAddress + { + public static Address DelegateeMetadataAddress( + Address delegateeAddress, Address delegateeAccountAddress) + => DeriveAddress( + DelegationElementType.DelegateeMetadata, + delegateeAddress, + delegateeAccountAddress.ByteArray); + + public static Address DelegatorMetadataAddress( + Address delegatorAddress, Address delegatorAccountAddress) + => DeriveAddress( + DelegationElementType.DelegatorMetadata, + delegatorAddress, + delegatorAccountAddress.ByteArray); + + public static Address BondAddress( + Address delegateeAddress, Address delegateeAccountAddress, Address delegatorAddress) + => DeriveAddress( + DelegationElementType.Bond, + DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress), + delegatorAddress.ByteArray); + + public static Address BondAddress( + Address delegateeMetadataAddress, Address delegatorAddress) + => DeriveAddress( + DelegationElementType.Bond, + delegateeMetadataAddress, + delegatorAddress.ByteArray); + + public static Address UnbondLockInAddress( + Address delegateeAddress, Address delegateeAccountAddress, Address delegatorAddress) + => DeriveAddress( + DelegationElementType.UnbondLockIn, + DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress), + delegatorAddress.ByteArray); + + public static Address UnbondLockInAddress( + Address delegateeMetadataAddress, Address delegatorAddress) + => DeriveAddress( + DelegationElementType.UnbondLockIn, + delegateeMetadataAddress, + delegatorAddress.ByteArray); + + public static Address RebondGraceAddress( + Address delegateeAddress, Address delegateeAccountAddress, Address delegatorAddress) + => DeriveAddress( + DelegationElementType.RebondGrace, + DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress), + delegatorAddress.ByteArray); + + public static Address RebondGraceAddress( + Address delegateeMetadataAddress, Address delegatorAddress) + => DeriveAddress( + DelegationElementType.RebondGrace, + delegateeMetadataAddress, + delegatorAddress.ByteArray); + + public static Address CurrentLumpSumRewardsRecordAddress( + Address delegateeAddress, Address delegateeAccountAddress) + => DeriveAddress( + DelegationElementType.LumpSumRewardsRecord, + DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress)); + + public static Address CurrentLumpSumRewardsRecordAddress( + Address delegateeMetadataAddress) + => DeriveAddress( + DelegationElementType.LumpSumRewardsRecord, + delegateeMetadataAddress); + + public static Address LumpSumRewardsRecordAddress( + Address delegateeAddress, Address delegateeAccountAddress, long height) + => DeriveAddress( + DelegationElementType.LumpSumRewardsRecord, + DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress), + BitConverter.GetBytes(height)); + + public static Address LumpSumRewardsRecordAddress( + Address delegateeMetadataAddress, long height) + => DeriveAddress( + DelegationElementType.LumpSumRewardsRecord, + delegateeMetadataAddress, + BitConverter.GetBytes(height)); + + public static Address RewardCollectorAddress( + Address delegateeAddress, Address delegateeAccountAddress) + => DeriveAddress( + DelegationElementType.RewardCollector, + DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress)); + + public static Address RewardCollectorAddress( + Address delegateeMetadataAddress) + => DeriveAddress( + DelegationElementType.RewardCollector, + delegateeMetadataAddress); + + public static Address RewardDistributorAddress( + Address delegateeAddress, Address delegateeAccountAddress) + => DeriveAddress( + DelegationElementType.RewardDistributor, + DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress)); + + public static Address RewardDistributorAddress( + Address delegateeMetadataAddress) + => DeriveAddress( + DelegationElementType.RewardDistributor, + delegateeMetadataAddress); + + private static Address DeriveAddress( + DelegationElementType identifier, + Address address, + IEnumerable? bytes = null) + { + byte[] hashed; + using (HMACSHA1 hmac = new( + BitConverter.GetBytes((int)identifier).ToArray())) + { + hashed = hmac.ComputeHash( + address.ByteArray.Concat(bytes ?? Array.Empty()).ToArray()); + } + + return new Address(hashed); + } + + private enum DelegationElementType + { + DelegateeMetadata, + DelegatorMetadata, + Bond, + UnbondLockIn, + RebondGrace, + LumpSumRewardsRecord, + RewardCollector, + RewardDistributor, + DelegationPool, + } + } +} diff --git a/Lib9c/Delegation/DelegationRepository.cs b/Lib9c/Delegation/DelegationRepository.cs index ba37e801c3..8a909878d4 100644 --- a/Lib9c/Delegation/DelegationRepository.cs +++ b/Lib9c/Delegation/DelegationRepository.cs @@ -1,151 +1,236 @@ #nullable enable -using System; using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; +using Nekoyume.Action; namespace Nekoyume.Delegation { - public class DelegationRepository : IDelegationRepository + public abstract class DelegationRepository : IDelegationRepository { - private readonly Address bondAddress = Addresses.Bond; - private readonly Address unbondLockInAddress = Addresses.UnbondLockIn; - private readonly Address rebondGraceAddress = Addresses.RebondGrace; - private readonly Address unbondingSetAddress = Addresses.UnbondingSet; - private readonly Address lumpSumRewardsRecordAddress = Addresses.LumpSumRewardsRecord; - - private IWorld _world; - private IActionContext _context; - private IAccount _bond; - private IAccount _unbondLockIn; - private IAccount _rebondGrace; - private IAccount _unbondingSet; - private IAccount _lumpSumRewardsRecord; - - public DelegationRepository(IWorld world, IActionContext context) - { - _world = world; - _context = context; - _bond = world.GetAccount(bondAddress); - _unbondLockIn = world.GetAccount(unbondLockInAddress); - _rebondGrace = world.GetAccount(rebondGraceAddress); - _unbondingSet = world.GetAccount(unbondingSetAddress); - _lumpSumRewardsRecord = world.GetAccount(lumpSumRewardsRecordAddress); - } - - public virtual IWorld World => _world - .SetAccount(bondAddress, _bond) - .SetAccount(unbondLockInAddress, _unbondLockIn) - .SetAccount(rebondGraceAddress, _rebondGrace) - .SetAccount(unbondingSetAddress, _unbondingSet) - .SetAccount(lumpSumRewardsRecordAddress, _lumpSumRewardsRecord); - - public virtual Bond GetBond(IDelegatee delegatee, Address delegatorAddress) + protected IWorld previousWorld; + protected IActionContext actionContext; + protected IAccount delegateeAccount; + protected IAccount delegatorAccount; + protected IAccount delegateeMetadataAccount; + protected IAccount delegatorMetadataAccount; + protected IAccount bondAccount; + protected IAccount unbondLockInAccount; + protected IAccount rebondGraceAccount; + protected IAccount unbondingSetAccount; + protected IAccount lumpSumRewardsRecordAccount; + + public DelegationRepository( + IWorld world, + IActionContext context, + Address delegateeAccountAddress, + Address delegatorAccountAddress, + Address delegateeMetadataAccountAddress, + Address delegatorMetadataAccountAddress, + Address bondAccountAddress, + Address unbondLockInAccountAddress, + Address rebondGraceAccountAddress, + Address unbondingSetAccountAddress, + Address lumpSumRewardRecordAccountAddress) + { + previousWorld = world; + actionContext = context; + DelegateeAccountAddress = delegateeAccountAddress; + DelegatorAccountAddress = delegatorAccountAddress; + DelegateeMetadataAccountAddress = delegateeMetadataAccountAddress; + DelegatorMetadataAccountAddress = delegatorMetadataAccountAddress; + BondAccountAddress = bondAccountAddress; + UnbondLockInAccountAddress = unbondLockInAccountAddress; + RebondGraceAccountAddress = rebondGraceAccountAddress; + UnbondingSetAccountAddress = unbondingSetAccountAddress; + LumpSumRewardsRecordAccountAddress = lumpSumRewardRecordAccountAddress; + + delegateeAccount = world.GetAccount(DelegateeAccountAddress); + delegatorAccount = world.GetAccount(DelegatorAccountAddress); + delegateeMetadataAccount = world.GetAccount(DelegateeMetadataAccountAddress); + delegatorMetadataAccount = world.GetAccount(DelegatorMetadataAccountAddress); + bondAccount = world.GetAccount(BondAccountAddress); + unbondLockInAccount = world.GetAccount(UnbondLockInAccountAddress); + rebondGraceAccount = world.GetAccount(RebondGraceAccountAddress); + unbondingSetAccount = world.GetAccount(UnbondingSetAccountAddress); + lumpSumRewardsRecordAccount = world.GetAccount(LumpSumRewardsRecordAccountAddress); + } + + public virtual IWorld World => previousWorld + .SetAccount(DelegateeAccountAddress, delegateeAccount) + .SetAccount(DelegatorAccountAddress, delegatorAccount) + .SetAccount(DelegateeMetadataAccountAddress, delegateeMetadataAccount) + .SetAccount(DelegatorMetadataAccountAddress, delegatorMetadataAccount) + .SetAccount(BondAccountAddress, bondAccount) + .SetAccount(UnbondLockInAccountAddress, unbondLockInAccount) + .SetAccount(RebondGraceAccountAddress, rebondGraceAccount) + .SetAccount(UnbondingSetAccountAddress, unbondingSetAccount) + .SetAccount(LumpSumRewardsRecordAccountAddress, lumpSumRewardsRecordAccount); + + public Address DelegateeAccountAddress { get; } + + public Address DelegatorAccountAddress { get; } + + private Address DelegateeMetadataAccountAddress { get; } + + private Address DelegatorMetadataAccountAddress { get; } + + private Address BondAccountAddress { get; } + + private Address UnbondLockInAccountAddress { get; } + + private Address RebondGraceAccountAddress { get; } + + private Address UnbondingSetAccountAddress { get; } + + private Address LumpSumRewardsRecordAccountAddress { get; } + + public abstract IDelegatee GetDelegatee(Address address); + + public abstract IDelegator GetDelegator(Address address); + + public abstract void SetDelegatee(IDelegatee delegatee); + + public abstract void SetDelegator(IDelegator delegator); + + public DelegateeMetadata GetDelegateeMetadata(Address delegateeAddress) + { + IValue? value = delegateeMetadataAccount.GetState( + DelegationAddress.DelegateeMetadataAddress(delegateeAddress, DelegateeAccountAddress)); + return value is IValue bencoded + ? new DelegateeMetadata(delegateeAddress, DelegateeAccountAddress, bencoded) + : throw new FailedLoadStateException("DelegateeMetadata not found."); + } + + public DelegatorMetadata GetDelegatorMetadata(Address delegatorAddress) + { + IValue? value = delegatorMetadataAccount.GetState( + DelegationAddress.DelegatorMetadataAddress(delegatorAddress, DelegatorAccountAddress)); + return value is IValue bencoded + ? new DelegatorMetadata(delegatorAddress, DelegatorAccountAddress, bencoded) + : throw new FailedLoadStateException("DelegatorMetadata not found."); + } + + public Bond GetBond(IDelegatee delegatee, Address delegatorAddress) { Address address = delegatee.BondAddress(delegatorAddress); - IValue? value = _bond.GetState(address); + IValue? value = bondAccount.GetState(address); return value is IValue bencoded ? new Bond(address, bencoded) : new Bond(address); } - public virtual UnbondLockIn GetUnbondLockIn(IDelegatee delegatee, Address delegatorAddress) + public UnbondLockIn GetUnbondLockIn(IDelegatee delegatee, Address delegatorAddress) { Address address = delegatee.UnbondLockInAddress(delegatorAddress); - IValue? value = _unbondLockIn.GetState(address); + IValue? value = unbondLockInAccount.GetState(address); return value is IValue bencoded ? new UnbondLockIn(address, delegatee.MaxUnbondLockInEntries, bencoded, this) - : new UnbondLockIn(address, delegatee.MaxUnbondLockInEntries, delegatee.DelegationPoolAddress, delegatorAddress, this); + : new UnbondLockIn(address, delegatee.MaxUnbondLockInEntries, delegatee.Address, delegatorAddress, this); } - public virtual UnbondLockIn GetUnlimitedUnbondLockIn(Address address) + public UnbondLockIn GetUnlimitedUnbondLockIn(Address address) { - IValue? value = _unbondLockIn.GetState(address); + IValue? value = unbondLockInAccount.GetState(address); return value is IValue bencoded ? new UnbondLockIn(address, int.MaxValue, bencoded, this) - : throw new InvalidOperationException("UnbondLockIn not found."); + : throw new FailedLoadStateException("UnbondLockIn not found."); } - public virtual RebondGrace GetRebondGrace(IDelegatee delegatee, Address delegatorAddress) + public RebondGrace GetRebondGrace(IDelegatee delegatee, Address delegatorAddress) { Address address = delegatee.RebondGraceAddress(delegatorAddress); - IValue? value = _rebondGrace.GetState(address); + IValue? value = rebondGraceAccount.GetState(address); return value is IValue bencoded ? new RebondGrace(address, delegatee.MaxRebondGraceEntries, bencoded, this) : new RebondGrace(address, delegatee.MaxRebondGraceEntries, this); } - public virtual RebondGrace GetUnlimitedRebondGrace(Address address) + public RebondGrace GetUnlimitedRebondGrace(Address address) { - IValue? value = _rebondGrace.GetState(address); + IValue? value = rebondGraceAccount.GetState(address); return value is IValue bencoded ? new RebondGrace(address, int.MaxValue, bencoded, this) - : throw new InvalidOperationException("RebondGrace not found."); + : throw new FailedLoadStateException("RebondGrace not found."); } - public virtual UnbondingSet GetUnbondingSet() - => _unbondingSet.GetState(UnbondingSet.Address) is IValue bencoded + public UnbondingSet GetUnbondingSet() + => unbondingSetAccount.GetState(UnbondingSet.Address) is IValue bencoded ? new UnbondingSet(bencoded, this) : new UnbondingSet(this); - public virtual LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height) + public LumpSumRewardsRecord? GetLumpSumRewardsRecord(IDelegatee delegatee, long height) { Address address = delegatee.LumpSumRewardsRecordAddress(height); - IValue? value = _lumpSumRewardsRecord.GetState(address); + IValue? value = lumpSumRewardsRecordAccount.GetState(address); return value is IValue bencoded ? new LumpSumRewardsRecord(address, bencoded) : null; } - public virtual LumpSumRewardsRecord? GetCurrentLumpSumRewardsRecord(IDelegatee delegatee) + public LumpSumRewardsRecord? GetCurrentLumpSumRewardsRecord(IDelegatee delegatee) { Address address = delegatee.CurrentLumpSumRewardsRecordAddress(); - IValue? value = _lumpSumRewardsRecord.GetState(address); + IValue? value = lumpSumRewardsRecordAccount.GetState(address); return value is IValue bencoded ? new LumpSumRewardsRecord(address, bencoded) : null; } - public virtual FungibleAssetValue GetBalance(Address address, Currency currency) - => _world.GetBalance(address, currency); + public FungibleAssetValue GetBalance(Address address, Currency currency) + => previousWorld.GetBalance(address, currency); - public virtual void SetBond(Bond bond) + public void SetDelegateeMetadata(DelegateeMetadata delegateeMetadata) { - _bond = bond.IsEmpty - ? _bond.RemoveState(bond.Address) - : _bond.SetState(bond.Address, bond.Bencoded); + delegateeMetadataAccount + = delegateeMetadataAccount.SetState( + delegateeMetadata.Address, delegateeMetadata.Bencoded); } - public virtual void SetUnbondLockIn(UnbondLockIn unbondLockIn) + public void SetDelegatorMetadata(DelegatorMetadata delegatorMetadata) { - _unbondLockIn = unbondLockIn.IsEmpty - ? _unbondLockIn.RemoveState(unbondLockIn.Address) - : _unbondLockIn.SetState(unbondLockIn.Address, unbondLockIn.Bencoded); + delegatorMetadataAccount + = delegatorMetadataAccount.SetState( + delegatorMetadata.Address, delegatorMetadata.Bencoded); } - public virtual void SetRebondGrace(RebondGrace rebondGrace) + public void SetBond(Bond bond) { - _rebondGrace = rebondGrace.IsEmpty - ? _rebondGrace.RemoveState(rebondGrace.Address) - : _rebondGrace.SetState(rebondGrace.Address, rebondGrace.Bencoded); + bondAccount = bond.IsEmpty + ? bondAccount.RemoveState(bond.Address) + : bondAccount.SetState(bond.Address, bond.Bencoded); } - public virtual void SetUnbondingSet(UnbondingSet unbondingSet) + public void SetUnbondLockIn(UnbondLockIn unbondLockIn) { - _unbondingSet = unbondingSet.IsEmpty - ? _unbondingSet.RemoveState(UnbondingSet.Address) - : _unbondingSet.SetState(UnbondingSet.Address, unbondingSet.Bencoded); + unbondLockInAccount = unbondLockIn.IsEmpty + ? unbondLockInAccount.RemoveState(unbondLockIn.Address) + : unbondLockInAccount.SetState(unbondLockIn.Address, unbondLockIn.Bencoded); } - public virtual void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) + public void SetRebondGrace(RebondGrace rebondGrace) { - _lumpSumRewardsRecord = _lumpSumRewardsRecord.SetState( + rebondGraceAccount = rebondGrace.IsEmpty + ? rebondGraceAccount.RemoveState(rebondGrace.Address) + : rebondGraceAccount.SetState(rebondGrace.Address, rebondGrace.Bencoded); + } + + public void SetUnbondingSet(UnbondingSet unbondingSet) + { + unbondingSetAccount = unbondingSet.IsEmpty + ? unbondingSetAccount.RemoveState(UnbondingSet.Address) + : unbondingSetAccount.SetState(UnbondingSet.Address, unbondingSet.Bencoded); + } + + public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) + { + lumpSumRewardsRecordAccount = lumpSumRewardsRecordAccount.SetState( lumpSumRewardsRecord.Address, lumpSumRewardsRecord.Bencoded); } - public virtual void AddLumpSumRewards(IDelegatee delegatee, long height, FungibleAssetValue rewards) + public void AddLumpSumRewards(IDelegatee delegatee, long height, FungibleAssetValue rewards) { LumpSumRewardsRecord record = GetCurrentLumpSumRewardsRecord(delegatee) ?? new LumpSumRewardsRecord( @@ -158,6 +243,20 @@ record = record.AddLumpSumRewards(rewards); } public void TransferAsset(Address sender, Address recipient, FungibleAssetValue value) - => _world = _world.TransferAsset(_context, sender, recipient, value); + => previousWorld = previousWorld.TransferAsset(actionContext, sender, recipient, value); + + public virtual void UpdateWorld(IWorld world) + { + previousWorld = world; + delegateeAccount = world.GetAccount(DelegateeAccountAddress); + delegatorAccount = world.GetAccount(DelegatorAccountAddress); + delegateeMetadataAccount = world.GetAccount(DelegateeMetadataAccountAddress); + delegatorMetadataAccount = world.GetAccount(DelegatorMetadataAccountAddress); + bondAccount = world.GetAccount(BondAccountAddress); + unbondLockInAccount = world.GetAccount(UnbondLockInAccountAddress); + rebondGraceAccount = world.GetAccount(RebondGraceAccountAddress); + unbondingSetAccount = world.GetAccount(UnbondingSetAccountAddress); + lumpSumRewardsRecordAccount = world.GetAccount(LumpSumRewardsRecordAccountAddress); + } } } diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index efd0a83ea2..2b233fb404 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -1,9 +1,7 @@ #nullable enable using System; using System.Collections.Immutable; -using System.Linq; using System.Numerics; -using Bencodex; using Bencodex.Types; using Libplanet.Crypto; using Libplanet.Types.Assets; @@ -14,89 +12,71 @@ public abstract class Delegator : IDelegator where T : Delegatee where TSelf : Delegator { - private readonly IDelegationRepository? _repository; - - public Delegator(Address address, IDelegationRepository? repository = null) - : this(address, ImmutableSortedSet
.Empty, null, repository) - { - } - - public Delegator(Address address, IValue bencoded, IDelegationRepository? repository = null) - : this(address, (List)bencoded, repository) - { - } - - public Delegator(Address address, List bencoded, IDelegationRepository? repository = null) + public Delegator( + Address address, + Address accountAddress, + Address delegationPoolAddress, + IDelegationRepository repository) : this( - address, - ((List)bencoded[0]).Select(item => new Address(item)).ToImmutableSortedSet(), - bencoded[1] is Integer lastRewardHeight ? lastRewardHeight : null, - repository) + new DelegatorMetadata( + address, + accountAddress, + delegationPoolAddress), + repository) { } - private Delegator( + public Delegator( Address address, - ImmutableSortedSet
delegatees, - long? lastRewardHeight, - IDelegationRepository? repository) + IDelegationRepository repository) + : this(repository.GetDelegatorMetadata(address), repository) { - Address = address; - Delegatees = delegatees; - LastRewardHeight = lastRewardHeight; - _repository = repository; } - public Address Address { get; } - - public ImmutableSortedSet
Delegatees { get; private set; } + private Delegator(DelegatorMetadata metadata, IDelegationRepository repository) + { + Metadata = metadata; + Repository = repository; + } - public long? LastRewardHeight { get; private set; } + public DelegatorMetadata Metadata { get; } - public IDelegationRepository? Repository => _repository; + public IDelegationRepository Repository { get; } - public virtual List Bencoded - => List.Empty - .Add(new List(Delegatees.Select(a => a.Bencoded))) - .Add(Null.Value); + public Address Address => Metadata.DelegatorAddress; - IValue IBencodable.Bencoded => Bencoded; + public Address AccountAddress => Metadata.DelegatorAccountAddress; - void IDelegator.Delegate( - IDelegatee delegatee, FungibleAssetValue fav, long height) - => Delegate((T)delegatee, fav, height); + public Address MetadataAddress => Metadata.Address; - void IDelegator.Undelegate( - IDelegatee delegatee, BigInteger share, long height) - => Undelegate((T)delegatee, share, height); + public Address DelegationPoolAddress => Metadata.DelegationPoolAddress; - void IDelegator.Redelegate( - IDelegatee srcDelegatee, IDelegatee dstDelegatee, BigInteger share, long height) - => Redelegate((T)srcDelegatee, (T)dstDelegatee, share, height); + public ImmutableSortedSet
Delegatees => Metadata.Delegatees; - void IDelegator.ClaimReward( - IDelegatee delegatee, long height) - => ClaimReward((T)delegatee, height); + public List MetadataBencoded => Metadata.Bencoded; public virtual void Delegate( T delegatee, FungibleAssetValue fav, long height) { - CannotMutateRelationsWithoutRepository(delegatee); if (fav.Sign <= 0) { throw new ArgumentOutOfRangeException( nameof(fav), fav, "Fungible asset value must be positive."); } - delegatee.Bond((TSelf)this, fav, height); - Delegatees = Delegatees.Add(delegatee.Address); - _repository!.TransferAsset(Address, delegatee.DelegationPoolAddress, fav); + delegatee.Bond(this, fav, height); + Metadata.AddDelegatee(delegatee.Address); + Repository.TransferAsset(DelegationPoolAddress, delegatee.DelegationPoolAddress, fav); + Repository.SetDelegator(this); } - public virtual void Undelegate( + void IDelegator.Delegate( + IDelegatee delegatee, FungibleAssetValue fav, long height) + => Delegate((T)delegatee, fav, height); + + public void Undelegate( T delegatee, BigInteger share, long height) { - CannotMutateRelationsWithoutRepository(delegatee); if (share.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -109,34 +89,38 @@ public virtual void Undelegate( nameof(height), height, "Height must be positive."); } - UnbondLockIn unbondLockIn = _repository!.GetUnbondLockIn(delegatee, Address); + UnbondLockIn unbondLockIn = Repository.GetUnbondLockIn(delegatee, Address); if (unbondLockIn.IsFull) { throw new InvalidOperationException("Undelegation is full."); } - FungibleAssetValue fav = delegatee.Unbond((TSelf)this, share, height); + FungibleAssetValue fav = delegatee.Unbond(this, share, height); unbondLockIn = unbondLockIn.LockIn( fav, height, height + delegatee.UnbondingPeriod); if (!delegatee.Delegators.Contains(Address)) { - Delegatees = Delegatees.Remove(delegatee.Address); + Metadata.RemoveDelegatee(delegatee.Address); } delegatee.AddUnbondingRef(UnbondingFactory.ToReference(unbondLockIn)); - _repository.SetUnbondLockIn(unbondLockIn); - _repository.SetUnbondingSet( - _repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); + Repository.SetUnbondLockIn(unbondLockIn); + Repository.SetUnbondingSet( + Repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); + Repository.SetDelegator(this); } - public virtual void Redelegate( + void IDelegator.Undelegate( + IDelegatee delegatee, BigInteger share, long height) + => Undelegate((T)delegatee, share, height); + + + public void Redelegate( T srcDelegatee, T dstDelegatee, BigInteger share, long height) { - CannotMutateRelationsWithoutRepository(srcDelegatee); - CannotMutateRelationsWithoutRepository(dstDelegatee); if (share.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -150,10 +134,10 @@ public virtual void Redelegate( } FungibleAssetValue fav = srcDelegatee.Unbond( - (TSelf)this, share, height); + this, share, height); dstDelegatee.Bond( - (TSelf)this, fav, height); - RebondGrace srcRebondGrace = _repository!.GetRebondGrace(srcDelegatee, Address).Grace( + this, fav, height); + RebondGrace srcRebondGrace = Repository.GetRebondGrace(srcDelegatee, Address).Grace( dstDelegatee.Address, fav, height, @@ -161,22 +145,26 @@ public virtual void Redelegate( if (!srcDelegatee.Delegators.Contains(Address)) { - Delegatees = Delegatees.Remove(srcDelegatee.Address); + Metadata.RemoveDelegatee(srcDelegatee.Address); } - Delegatees = Delegatees.Add(dstDelegatee.Address); + Metadata.AddDelegatee(dstDelegatee.Address); srcDelegatee.AddUnbondingRef(UnbondingFactory.ToReference(srcRebondGrace)); - _repository.SetRebondGrace(srcRebondGrace); - _repository.SetUnbondingSet( - _repository.GetUnbondingSet().SetUnbonding(srcRebondGrace)); + Repository.SetRebondGrace(srcRebondGrace); + Repository.SetUnbondingSet( + Repository.GetUnbondingSet().SetUnbonding(srcRebondGrace)); + Repository.SetDelegator(this); } - public virtual void CancelUndelegate( + void IDelegator.Redelegate( + IDelegatee srcDelegatee, IDelegatee dstDelegatee, BigInteger share, long height) + => Redelegate((T)srcDelegatee, (T)dstDelegatee, share, height); + + public void CancelUndelegate( T delegatee, FungibleAssetValue fav, long height) { - CannotMutateRelationsWithoutRepository(delegatee); if (fav.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -189,70 +177,40 @@ public virtual void CancelUndelegate( nameof(height), height, "Height must be positive."); } - UnbondLockIn unbondLockIn = _repository!.GetUnbondLockIn(delegatee, Address); + UnbondLockIn unbondLockIn = Repository.GetUnbondLockIn(delegatee, Address); if (unbondLockIn.IsFull) { throw new InvalidOperationException("Undelegation is full."); } - delegatee.Bond((TSelf)this, fav, height); + delegatee.Bond(this, fav, height); unbondLockIn = unbondLockIn.Cancel(fav, height); - Delegatees = Delegatees.Add(delegatee.Address); + Metadata.AddDelegatee(delegatee.Address); if (unbondLockIn.IsEmpty) { delegatee.RemoveUnbondingRef(UnbondingFactory.ToReference(unbondLockIn)); } - _repository.SetUnbondLockIn(unbondLockIn); - _repository.SetUnbondingSet( - _repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); - } - - public virtual void ClaimReward( - T delegatee, long height) - { - CannotMutateRelationsWithoutRepository(delegatee); - delegatee.DistributeReward((TSelf)this, height); - } - - public void UpdateLastRewardHeight(long height) - { - LastRewardHeight = height; + Repository.SetUnbondLockIn(unbondLockIn); + Repository.SetUnbondingSet( + Repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); + Repository.SetDelegator(this); } - public override bool Equals(object? obj) - => obj is IDelegator other && Equals(other); - - public virtual bool Equals(IDelegator? other) - => ReferenceEquals(this, other) - || (other is Delegator delegator - && GetType() != delegator.GetType() - && Address.Equals(delegator.Address) - && Delegatees.SequenceEqual(delegator.Delegatees) - && LastRewardHeight == delegator.LastRewardHeight); - - public override int GetHashCode() - => Address.GetHashCode(); + void IDelegator.CancelUndelegate( + IDelegatee delegatee, FungibleAssetValue fav, long height) + => CancelUndelegate((T)delegatee, fav, height); - private void CannotMutateRelationsWithoutRepository(T delegatee) + public void ClaimReward( + T delegatee, long height) { - CannotMutateRelationsWithoutRepository(); - if (!_repository!.Equals(delegatee.Repository)) - { - throw new InvalidOperationException( - "Cannot mutate with different repository."); - } + delegatee.DistributeReward(this, height); + Repository.SetDelegator(this); } - private void CannotMutateRelationsWithoutRepository() - { - if (_repository is null) - { - throw new InvalidOperationException( - "Cannot mutate without repository."); - } - } + void IDelegator.ClaimReward(IDelegatee delegatee, long height) + => ClaimReward((T)delegatee, height); } } diff --git a/Lib9c/Delegation/DelegatorMetadata.cs b/Lib9c/Delegation/DelegatorMetadata.cs new file mode 100644 index 0000000000..bd95e15334 --- /dev/null +++ b/Lib9c/Delegation/DelegatorMetadata.cs @@ -0,0 +1,102 @@ +#nullable enable +using System; +using System.Collections.Immutable; +using System.Linq; +using Bencodex; +using Bencodex.Types; +using Libplanet.Crypto; + +namespace Nekoyume.Delegation +{ + public class DelegatorMetadata : IDelegatorMetadata + { + private Address? _address; + + public DelegatorMetadata( + Address address, + Address accountAddress, + Address delegationPoolAddress) + : this( + address, + accountAddress, + delegationPoolAddress, + ImmutableSortedSet
.Empty) + { + } + + public DelegatorMetadata( + Address address, + Address accountAddress, + IValue bencoded) + : this(address, accountAddress, (List)bencoded) + { + } + + public DelegatorMetadata( + Address address, + Address accountAddress, + List bencoded) + : this( + address, + accountAddress, + new Address(bencoded[0]), + ((List)bencoded[1]).Select(item => new Address(item)).ToImmutableSortedSet()) + { + } + + private DelegatorMetadata( + Address address, + Address accountAddress, + Address delegationPoolAddress, + ImmutableSortedSet
delegatees) + { + DelegatorAddress = address; + DelegatorAccountAddress = accountAddress; + DelegationPoolAddress = delegationPoolAddress; + Delegatees = delegatees; + } + + public Address DelegatorAddress { get; } + + public Address DelegatorAccountAddress { get; } + + public Address Address + => _address ??= DelegationAddress.DelegatorMetadataAddress( + DelegatorAddress, + DelegatorAccountAddress); + + public Address DelegationPoolAddress { get; } + + public ImmutableSortedSet
Delegatees { get; private set; } + + public List Bencoded + => List.Empty + .Add(DelegationPoolAddress.Bencoded) + .Add(new List(Delegatees.Select(a => a.Bencoded))); + + IValue IBencodable.Bencoded => Bencoded; + + public void AddDelegatee(Address delegatee) + { + Delegatees = Delegatees.Add(delegatee); + } + + public void RemoveDelegatee(Address delegatee) + { + Delegatees = Delegatees.Remove(delegatee); + } + + public override bool Equals(object? obj) + => obj is IDelegator other && Equals(other); + + public virtual bool Equals(IDelegator? other) + => ReferenceEquals(this, other) + || (other is DelegatorMetadata delegator + && GetType() != delegator.GetType() + && DelegatorAddress.Equals(delegator.DelegatorAddress) + && Delegatees.SequenceEqual(delegator.Delegatees)); + + public override int GetHashCode() + => DelegatorAddress.GetHashCode(); + } +} diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index 93e5275135..fc0d71b45c 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -8,10 +8,12 @@ namespace Nekoyume.Delegation { - public interface IDelegatee : IBencodable, IEquatable + public interface IDelegatee { Address Address { get; } + Address AccountAddress { get; } + Currency DelegationCurrency { get; } Currency RewardCurrency { get; } @@ -24,8 +26,6 @@ public interface IDelegatee : IBencodable, IEquatable int MaxRebondGraceEntries { get; } - BigInteger SlashFactor { get; } - Address RewardCollectorAddress { get; } Address RewardDistributorAddress { get; } @@ -36,6 +36,16 @@ public interface IDelegatee : IBencodable, IEquatable BigInteger TotalShares { get; } + bool Jailed { get; } + + long JailedUntil { get; } + + bool Tombstoned { get; } + + BigInteger ShareFromFAV(FungibleAssetValue fav); + + FungibleAssetValue FAVFromShare(BigInteger share); + BigInteger Bond(IDelegator delegator, FungibleAssetValue fav, long height); FungibleAssetValue Unbond(IDelegator delegator, BigInteger share, long height); @@ -44,7 +54,13 @@ public interface IDelegatee : IBencodable, IEquatable void CollectRewards(long height); - void Slash(long infractionHeight); + void Slash(BigInteger slashFactor, long infractionHeight, long height); + + void Jail(long releaseHeight, long height); + + void Unjail(long height); + + void Tombstone(); Address BondAddress(Address delegatorAddress); @@ -55,5 +71,7 @@ public interface IDelegatee : IBencodable, IEquatable Address CurrentLumpSumRewardsRecordAddress(); Address LumpSumRewardsRecordAddress(long height); + + event EventHandler? DelegationChanged; } } diff --git a/Lib9c/Delegation/IDelegateeMetadata.cs b/Lib9c/Delegation/IDelegateeMetadata.cs new file mode 100644 index 0000000000..98ead5073d --- /dev/null +++ b/Lib9c/Delegation/IDelegateeMetadata.cs @@ -0,0 +1,51 @@ +#nullable enable +using System; +using System.Collections.Immutable; +using System.Numerics; +using Bencodex; +using Libplanet.Crypto; +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public interface IDelegateeMetadata : IBencodable + { + Address DelegateeAddress { get; } + + Address DelegateeAccountAddress { get; } + + Address Address { get; } + + Currency DelegationCurrency { get; } + + Currency RewardCurrency { get; } + + Address DelegationPoolAddress { get; } + + long UnbondingPeriod { get; } + + int MaxUnbondLockInEntries { get; } + + int MaxRebondGraceEntries { get; } + + Address RewardCollectorAddress { get; } + + Address RewardDistributorAddress { get; } + + ImmutableSortedSet
Delegators { get; } + + FungibleAssetValue TotalDelegatedFAV { get; } + + BigInteger TotalShares { get; } + + bool Jailed { get; } + + long JailedUntil { get; } + + bool Tombstoned { get; } + + BigInteger ShareFromFAV(FungibleAssetValue fav); + + FungibleAssetValue FAVFromShare(BigInteger share); + } +} diff --git a/Lib9c/Delegation/IDelegationRepository.cs b/Lib9c/Delegation/IDelegationRepository.cs index b71df158bb..340f9a7c4d 100644 --- a/Lib9c/Delegation/IDelegationRepository.cs +++ b/Lib9c/Delegation/IDelegationRepository.cs @@ -7,8 +7,20 @@ namespace Nekoyume.Delegation { public interface IDelegationRepository { + Address DelegateeAccountAddress { get; } + + Address DelegatorAccountAddress { get; } + IWorld World { get; } + IDelegatee GetDelegatee(Address address); + + IDelegator GetDelegator(Address address); + + DelegateeMetadata GetDelegateeMetadata(Address delegateeAddress); + + DelegatorMetadata GetDelegatorMetadata(Address delegatorAddress); + Bond GetBond(IDelegatee delegatee, Address delegatorAddress); UnbondLockIn GetUnbondLockIn(IDelegatee delegatee, Address delegatorAddress); @@ -27,6 +39,14 @@ public interface IDelegationRepository FungibleAssetValue GetBalance(Address address, Currency currency); + void SetDelegatee(IDelegatee delegatee); + + void SetDelegator(IDelegator delegator); + + void SetDelegateeMetadata(DelegateeMetadata delegateeMetadata); + + void SetDelegatorMetadata(DelegatorMetadata delegatorMetadata); + void SetBond(Bond bond); void SetUnbondLockIn(UnbondLockIn unbondLockIn); diff --git a/Lib9c/Delegation/IDelegator.cs b/Lib9c/Delegation/IDelegator.cs index 6d09a920b0..4b42ecb2b3 100644 --- a/Lib9c/Delegation/IDelegator.cs +++ b/Lib9c/Delegation/IDelegator.cs @@ -2,16 +2,19 @@ using System; using System.Collections.Immutable; using System.Numerics; -using Bencodex; using Libplanet.Crypto; using Libplanet.Types.Assets; namespace Nekoyume.Delegation { - public interface IDelegator : IBencodable, IEquatable + public interface IDelegator { Address Address { get; } + Address AccountAddress { get; } + + Address DelegationPoolAddress { get; } + ImmutableSortedSet
Delegatees { get; } void Delegate( @@ -30,6 +33,11 @@ void Redelegate( BigInteger share, long height); + void CancelUndelegate( + IDelegatee delegatee, + FungibleAssetValue fav, + long height); + void ClaimReward( IDelegatee delegatee, long height); diff --git a/Lib9c/Delegation/IDelegatorMetadata.cs b/Lib9c/Delegation/IDelegatorMetadata.cs new file mode 100644 index 0000000000..c2a15deba7 --- /dev/null +++ b/Lib9c/Delegation/IDelegatorMetadata.cs @@ -0,0 +1,23 @@ +using System.Collections.Immutable; +using Bencodex; +using Libplanet.Crypto; + +namespace Nekoyume.Delegation +{ + public interface IDelegatorMetadata : IBencodable + { + Address DelegatorAddress { get; } + + Address DelegatorAccountAddress { get; } + + Address Address { get; } + + Address DelegationPoolAddress { get; } + + ImmutableSortedSet
Delegatees { get; } + + public void AddDelegatee(Address delegatee); + + public void RemoveDelegatee(Address delegatee); + } +} diff --git a/Lib9c/Delegation/IUnbonding.cs b/Lib9c/Delegation/IUnbonding.cs index d858c16936..4248733506 100644 --- a/Lib9c/Delegation/IUnbonding.cs +++ b/Lib9c/Delegation/IUnbonding.cs @@ -1,5 +1,6 @@ using System.Numerics; using Libplanet.Crypto; +using Libplanet.Types.Assets; namespace Nekoyume.Delegation { @@ -15,6 +16,10 @@ public interface IUnbonding IUnbonding Release(long height); - IUnbonding Slash(BigInteger slashFactor, long infractionHeight); + IUnbonding Slash( + BigInteger slashFactor, + long infractionHeight, + long height, + out FungibleAssetValue? slashedFAV); } } diff --git a/Lib9c/Delegation/IUnbondingEntry.cs b/Lib9c/Delegation/IUnbondingEntry.cs deleted file mode 100644 index 01cf2d802c..0000000000 --- a/Lib9c/Delegation/IUnbondingEntry.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Nekoyume.Delegation -{ - public interface IUnbondingEntry - { - long ExpireHeight { get; } - } -} diff --git a/Lib9c/Delegation/LumpSumRewardsRecord.cs b/Lib9c/Delegation/LumpSumRewardsRecord.cs index 753aecfba0..ef32497f8b 100644 --- a/Lib9c/Delegation/LumpSumRewardsRecord.cs +++ b/Lib9c/Delegation/LumpSumRewardsRecord.cs @@ -103,7 +103,7 @@ public LumpSumRewardsRecord AddLumpSumRewards(FungibleAssetValue rewards) LastStartHeight); public FungibleAssetValue RewardsDuringPeriod(BigInteger share) - => (LumpSumRewards * share).DivRem(TotalShares, out _); + => (LumpSumRewards * share).DivRem(TotalShares).Quotient; public override bool Equals(object? obj) => obj is LumpSumRewardsRecord other && Equals(other); diff --git a/Lib9c/Delegation/RebondGrace.cs b/Lib9c/Delegation/RebondGrace.cs index d1c0bc199d..f8ab128390 100644 --- a/Lib9c/Delegation/RebondGrace.cs +++ b/Lib9c/Delegation/RebondGrace.cs @@ -13,8 +13,8 @@ namespace Nekoyume.Delegation { public sealed class RebondGrace : IUnbonding, IBencodable, IEquatable { - private static readonly IComparer _entryComparer - = new RebondGraceEntryComparer(); + private static readonly IComparer _entryComparer + = new UnbondingEntry.Comparer(); private readonly IDelegationRepository? _repository; @@ -22,7 +22,7 @@ public RebondGrace(Address address, int maxEntries, IDelegationRepository? repos : this( address, maxEntries, - ImmutableSortedDictionary>.Empty, + ImmutableSortedDictionary>.Empty, repository) { } @@ -37,9 +37,9 @@ public RebondGrace(Address address, int maxEntries, List bencoded, IDelegationRe address, maxEntries, bencoded.Select(kv => kv is List list - ? new KeyValuePair>( + ? new KeyValuePair>( (Integer)list[0], - ((List)list[1]).Select(e => new RebondGraceEntry(e)).ToImmutableList()) + ((List)list[1]).Select(e => new UnbondingEntry(e)).ToImmutableList()) : throw new InvalidCastException( $"Unable to cast object of type '{kv.GetType()}' to type '{typeof(List)}'.")) .ToImmutableSortedDictionary(), @@ -50,7 +50,7 @@ public RebondGrace(Address address, int maxEntries, List bencoded, IDelegationRe public RebondGrace( Address address, int maxEntries, - IEnumerable entries, + IEnumerable entries, IDelegationRepository? repository = null) : this(address, maxEntries, repository) { @@ -63,7 +63,7 @@ public RebondGrace( private RebondGrace( Address address, int maxEntries, - ImmutableSortedDictionary> entries, + ImmutableSortedDictionary> entries, IDelegationRepository? repository) { if (maxEntries < 0) @@ -84,6 +84,10 @@ private RebondGrace( public int MaxEntries { get; } + public Address DelegateeAddress { get; } + + public Address DelegatorAddress { get; } + public IDelegationRepository? Repository => _repository; public long LowestExpireHeight => Entries.First().Key; @@ -93,9 +97,9 @@ private RebondGrace( public bool IsEmpty => Entries.IsEmpty; // TODO: Use better custom collection type - public ImmutableSortedDictionary> Entries { get; } + public ImmutableSortedDictionary> Entries { get; } - public ImmutableArray FlattenedEntries + public ImmutableArray FlattenedEntries => Entries.Values.SelectMany(e => e).ToImmutableArray(); public List Bencoded @@ -135,16 +139,57 @@ public RebondGrace Release(long height) IUnbonding IUnbonding.Release(long height) => Release(height); - public RebondGrace Slash(BigInteger slashFactor, long infractionHeight) - => UpdateEntries(Entries.TakeWhile(e => e.Key >= infractionHeight) - .Select(kv => KeyValuePair.Create( - kv.Key, - kv.Value.Select(v => v.Slash(slashFactor, infractionHeight)).ToImmutableList())) - .Concat(Entries.SkipWhile(e => e.Key >= infractionHeight)) - .ToImmutableSortedDictionary()); + public RebondGrace Slash( + BigInteger slashFactor, + long infractionHeight, + long height, + out FungibleAssetValue? slashedFAV) + { + CannotMutateRelationsWithoutRepository(); + + var slashed = new SortedDictionary(); + var updatedEntries = Entries; + var entriesToSlash = Entries.TakeWhile(e => e.Key >= infractionHeight); + foreach (var (expireHeight, entries) in entriesToSlash) + { + ImmutableList slashedEntries = ImmutableList.Empty; + foreach (var entry in entries) + { + var slashedEntry = entry.Slash(slashFactor, infractionHeight, out var slashedSingle); + int index = slashedEntries.BinarySearch(slashedEntry, _entryComparer); + slashedEntries = slashedEntries.Insert(index < 0 ? ~index : index, slashedEntry); + if (slashed.TryGetValue(entry.UnbondeeAddress, out var value)) + { + slashed[entry.UnbondeeAddress] = value + slashedSingle; + } + else + { + slashed[entry.UnbondeeAddress] = slashedSingle; + } + } + + updatedEntries = Entries.SetItem(expireHeight, slashedEntries); + } + + slashedFAV = null; + foreach (var (address, slashedEach) in slashed) + { + var delegatee = _repository!.GetDelegatee(address); + var delegator = _repository!.GetDelegator(DelegatorAddress); + delegatee.Unbond(delegator, delegatee.ShareFromFAV(slashedEach), height); + slashedFAV = slashedFAV.HasValue ? slashedFAV + slashedEach : slashedEach; + } + + + return UpdateEntries(updatedEntries); + } - IUnbonding IUnbonding.Slash(BigInteger slashFactor, long infractionHeight) - => Slash(slashFactor, infractionHeight); + IUnbonding IUnbonding.Slash( + BigInteger slashFactor, + long infractionHeight, + long height, + out FungibleAssetValue? slashedFAV) + => Slash(slashFactor, infractionHeight, height, out slashedFAV); public override bool Equals(object? obj) => obj is RebondGrace other && Equals(other); @@ -172,11 +217,11 @@ internal RebondGrace Grace( } return AddEntry( - new RebondGraceEntry( + new UnbondingEntry( rebondeeAddress, initialGraceFAV, creationHeight, expireHeight)); } - private RebondGrace AddEntry(RebondGraceEntry entry) + private RebondGrace AddEntry(UnbondingEntry entry) { if (IsFull) { @@ -194,11 +239,11 @@ private RebondGrace AddEntry(RebondGraceEntry entry) return UpdateEntries( Entries.Add( - entry.ExpireHeight, ImmutableList.Empty.Add(entry))); + entry.ExpireHeight, ImmutableList.Empty.Add(entry))); } private RebondGrace UpdateEntries( - ImmutableSortedDictionary> entries) + ImmutableSortedDictionary> entries) => new RebondGrace(Address, MaxEntries, entries, _repository); private void CannotMutateRelationsWithoutRepository() @@ -209,202 +254,5 @@ private void CannotMutateRelationsWithoutRepository() "Cannot mutate without repository."); } } - - public class RebondGraceEntry : IUnbondingEntry, IBencodable, IEquatable - { - private int? _cachedHashCode; - - public RebondGraceEntry( - Address rebondeeAddress, - FungibleAssetValue graceFAV, - long creationHeight, - long expireHeight) - : this(rebondeeAddress, graceFAV, graceFAV, creationHeight, expireHeight) - { - } - - public RebondGraceEntry(IValue bencoded) - : this((List)bencoded) - { - } - - private RebondGraceEntry(List bencoded) - : this( - new Address(bencoded[0]), - new FungibleAssetValue(bencoded[1]), - new FungibleAssetValue(bencoded[2]), - (Integer)bencoded[3], - (Integer)bencoded[4]) - { - } - - private RebondGraceEntry( - Address rebondeeAddress, - FungibleAssetValue initialGraceFAV, - FungibleAssetValue graceFAV, - long creationHeight, - long expireHeight) - { - if (initialGraceFAV.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(initialGraceFAV), - initialGraceFAV, - "The initial grace FAV must be greater than zero."); - } - - if (graceFAV.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(graceFAV), - graceFAV, - "The grace FAV must be greater than zero."); - } - - if (graceFAV > initialGraceFAV) - { - throw new ArgumentOutOfRangeException( - nameof(graceFAV), - graceFAV, - "The grace FAV must be less than or equal to the initial grace FAV."); - } - - if (creationHeight < 0) - { - throw new ArgumentOutOfRangeException( - nameof(creationHeight), - creationHeight, - "The creation height must be greater than or equal to zero."); - } - - if (expireHeight < creationHeight) - { - throw new ArgumentOutOfRangeException( - nameof(expireHeight), - expireHeight, - "The expire height must be greater than the creation height."); - } - - RebondeeAddress = rebondeeAddress; - InitialGraceFAV = initialGraceFAV; - GraceFAV = graceFAV; - CreationHeight = creationHeight; - ExpireHeight = expireHeight; - } - - public Address RebondeeAddress { get; } - - public FungibleAssetValue InitialGraceFAV { get; } - - public FungibleAssetValue GraceFAV { get; } - - public long CreationHeight { get; } - - public long ExpireHeight { get; } - - public List Bencoded => List.Empty - .Add(RebondeeAddress.Bencoded) - .Add(InitialGraceFAV.Serialize()) - .Add(GraceFAV.Serialize()) - .Add(CreationHeight) - .Add(ExpireHeight); - - IValue IBencodable.Bencoded => Bencoded; - - public override bool Equals(object? obj) - => obj is RebondGraceEntry other && Equals(other); - - public bool Equals(RebondGraceEntry? other) - => ReferenceEquals(this, other) - || (other is RebondGraceEntry rebondGraceEntry - && RebondeeAddress.Equals(rebondGraceEntry.RebondeeAddress) - && InitialGraceFAV.Equals(rebondGraceEntry.InitialGraceFAV) - && GraceFAV.Equals(rebondGraceEntry.GraceFAV) - && CreationHeight == rebondGraceEntry.CreationHeight - && ExpireHeight == rebondGraceEntry.ExpireHeight); - - public override int GetHashCode() - { - if (_cachedHashCode is int cached) - { - return cached; - } - - int hash = HashCode.Combine( - RebondeeAddress, - InitialGraceFAV, - GraceFAV, - CreationHeight, - ExpireHeight); - - _cachedHashCode = hash; - return hash; - } - - public RebondGraceEntry Slash(BigInteger slashFactor, long infractionHeight) - { - if (CreationHeight > infractionHeight || - ExpireHeight < infractionHeight) - { - throw new ArgumentOutOfRangeException( - nameof(infractionHeight), - infractionHeight, - "The infraction height must be between in creation height and expire height of entry."); - } - - return new RebondGraceEntry( - RebondeeAddress, - GraceFAV - GraceFAV.DivRem(slashFactor).Quotient, - CreationHeight, - ExpireHeight); - } - } - - public class RebondGraceEntryComparer : IComparer - { - public int Compare(RebondGraceEntry? x, RebondGraceEntry? y) - { - if (ReferenceEquals(x, y)) - { - return 0; - } - - if (x is null) - { - return -1; - } - - if (y is null) - { - return 1; - } - - int comparison = x.ExpireHeight.CompareTo(y.ExpireHeight); - if (comparison != 0) - { - return comparison; - } - - comparison = x.CreationHeight.CompareTo(y.CreationHeight); - if (comparison != 0) - { - return comparison; - } - - comparison = -x.InitialGraceFAV.CompareTo(y.InitialGraceFAV); - if (comparison != 0) - { - return comparison; - } - - comparison = -x.GraceFAV.CompareTo(y.GraceFAV); - if (comparison != 0) - { - return comparison; - } - - return x.RebondeeAddress.CompareTo(y.RebondeeAddress); - } - } } } diff --git a/Lib9c/Delegation/UnbondLockIn.cs b/Lib9c/Delegation/UnbondLockIn.cs index 99ce47f55b..8ce441b322 100644 --- a/Lib9c/Delegation/UnbondLockIn.cs +++ b/Lib9c/Delegation/UnbondLockIn.cs @@ -13,23 +13,23 @@ namespace Nekoyume.Delegation { public sealed class UnbondLockIn : IUnbonding, IBencodable, IEquatable { - private static readonly IComparer _entryComparer - = new UnbondLockInEntryComparer(); + private static readonly IComparer _entryComparer + = new UnbondingEntry.Comparer(); private readonly IDelegationRepository? _repository; public UnbondLockIn( Address address, int maxEntries, - Address sender, - Address recipient, + Address delegateeAddress, + Address delegatorAddress, IDelegationRepository? repository) : this( address, maxEntries, - sender, - recipient, - ImmutableSortedDictionary>.Empty, + delegateeAddress, + delegatorAddress, + ImmutableSortedDictionary>.Empty, repository) { _repository = repository; @@ -49,9 +49,9 @@ public UnbondLockIn( new Address(bencoded[0]), new Address(bencoded[1]), ((List)bencoded[2]).Select(kv => kv is List list - ? new KeyValuePair>( + ? new KeyValuePair>( (Integer)list[0], - ((List)list[1]).Select(e => new UnbondLockInEntry(e)).ToImmutableList()) + ((List)list[1]).Select(e => new UnbondingEntry(e)).ToImmutableList()) : throw new InvalidCastException( $"Unable to cast object of type '{kv.GetType()}' " + $"to type '{typeof(List)}'.")) @@ -63,11 +63,16 @@ public UnbondLockIn( public UnbondLockIn( Address address, int maxEntries, - Address sender, - Address recipient, - IEnumerable entries, + Address delegateeAddress, + Address delegatorAddress, + IEnumerable entries, IDelegationRepository? repository = null) - : this(address, maxEntries, sender, recipient, repository) + : this( + address, + maxEntries, + delegateeAddress, + delegatorAddress, + repository) { foreach (var entry in entries) { @@ -78,9 +83,9 @@ public UnbondLockIn( private UnbondLockIn( Address address, int maxEntries, - Address sender, - Address recipient, - ImmutableSortedDictionary> entries, + Address delegateeAddress, + Address delegatorAddress, + ImmutableSortedDictionary> entries, IDelegationRepository? repository) { if (maxEntries < 0) @@ -94,8 +99,8 @@ private UnbondLockIn( Address = address; MaxEntries = maxEntries; Entries = entries; - Sender = sender; - Recipient = recipient; + DelegateeAddress = delegateeAddress; + DelegatorAddress = delegatorAddress; _repository = repository; } @@ -103,12 +108,12 @@ private UnbondLockIn( public int MaxEntries { get; } - public Address Sender { get; } + public Address DelegateeAddress { get; } - public Address Recipient { get; } + public Address DelegatorAddress { get; } // TODO: Use better custom collection type - public ImmutableSortedDictionary> Entries { get; } + public ImmutableSortedDictionary> Entries { get; } public long LowestExpireHeight => Entries.First().Key; @@ -116,13 +121,13 @@ private UnbondLockIn( public bool IsEmpty => Entries.IsEmpty; - public ImmutableArray FlattenedEntries + public ImmutableArray FlattenedEntries => Entries.Values.SelectMany(e => e).ToImmutableArray(); public List Bencoded => List.Empty - .Add(Sender.Bencoded) - .Add(Recipient.Bencoded) + .Add(DelegateeAddress.Bencoded) + .Add(DelegatorAddress.Bencoded) .Add(new List( Entries.Select( sortedDict => new List( @@ -149,7 +154,7 @@ public UnbondLockIn Release(long height) if (expireHeight <= height) { FungibleAssetValue entriesFAV = entries - .Select(e => e.LockInFAV) + .Select(e => e.UnbondingFAV) .Aggregate((accum, next) => accum + next); releasingFAV = releasingFAV.HasValue ? releasingFAV.Value + entriesFAV @@ -165,7 +170,12 @@ public UnbondLockIn Release(long height) if (releasingFAV.HasValue) { - _repository!.TransferAsset(Sender, Recipient, releasingFAV.Value); + var delegateeMetadata = _repository!.GetDelegateeMetadata(DelegateeAddress); + var delegatorMetadata = _repository.GetDelegatorMetadata(DelegatorAddress); + _repository!.TransferAsset( + delegateeMetadata.DelegationPoolAddress, + delegatorMetadata.DelegationPoolAddress, + releasingFAV.Value); } return UpdateEntries(updatedEntries); @@ -173,16 +183,40 @@ public UnbondLockIn Release(long height) IUnbonding IUnbonding.Release(long height) => Release(height); - public UnbondLockIn Slash(BigInteger slashFactor, long infractionHeight) - => UpdateEntries(Entries.TakeWhile(e => e.Key >= infractionHeight) - .Select(kv => KeyValuePair.Create( - kv.Key, - kv.Value.Select(v => v.Slash(slashFactor, infractionHeight)).ToImmutableList())) - .Concat(Entries.SkipWhile(e => e.Key >= infractionHeight)) - .ToImmutableSortedDictionary()); + public UnbondLockIn Slash( + BigInteger slashFactor, + long infractionHeight, + long height, + out FungibleAssetValue? slashedFAV) + { + slashedFAV = null; + var updatedEntries = Entries; + var entriesToSlash = Entries.TakeWhile(e => e.Key >= infractionHeight); + foreach (var (expireHeight, entries) in entriesToSlash) + { + ImmutableList slashedEntries = ImmutableList.Empty; + foreach (var entry in entries) + { + var slashedEntry = entry.Slash(slashFactor, infractionHeight, out var slashedSingle); + int index = slashedEntries.BinarySearch(slashedEntry, _entryComparer); + slashedEntries = slashedEntries.Insert(index < 0 ? ~index : index, slashedEntry); + slashedFAV = slashedFAV.HasValue + ? slashedFAV.Value + slashedSingle + : slashedSingle; + } + + updatedEntries = Entries.SetItem(expireHeight, slashedEntries); + } + + return UpdateEntries(updatedEntries); + } - IUnbonding IUnbonding.Slash(BigInteger slashFactor, long infractionHeight) - => Slash(slashFactor, infractionHeight); + IUnbonding IUnbonding.Slash( + BigInteger slashFactor, + long infractionHeight, + long height, + out FungibleAssetValue? slashedFAV) + => Slash(slashFactor, infractionHeight, height, out slashedFAV); public override bool Equals(object? obj) => obj is UnbondLockIn other && Equals(other); @@ -192,8 +226,8 @@ public bool Equals(UnbondLockIn? other) || (other is UnbondLockIn unbondLockIn && Address.Equals(unbondLockIn.Address) && MaxEntries == unbondLockIn.MaxEntries - && Sender.Equals(unbondLockIn.Sender) - && Recipient.Equals(unbondLockIn.Recipient) + && DelegateeAddress.Equals(unbondLockIn.DelegateeAddress) + && DelegatorAddress.Equals(unbondLockIn.DelegatorAddress) && FlattenedEntries.SequenceEqual(unbondLockIn.FlattenedEntries)); public override int GetHashCode() @@ -207,7 +241,7 @@ internal UnbondLockIn LockIn( throw new ArgumentException("The expire height must be greater than the creation height."); } - return AddEntry(new UnbondLockInEntry(lockInFAV, creationHeight, expireHeight)); + return AddEntry(new UnbondingEntry(DelegateeAddress, lockInFAV, creationHeight, expireHeight)); } internal UnbondLockIn Cancel(FungibleAssetValue cancellingFAV, long height) @@ -248,9 +282,9 @@ internal UnbondLockIn Cancel(FungibleAssetValue cancellingFAV, long height) break; } - if (entry.value.LockInFAV <= cancellingFAV) + if (entry.value.UnbondingFAV <= cancellingFAV) { - cancellingFAV -= entry.value.LockInFAV; + cancellingFAV -= entry.value.UnbondingFAV; updatedEntries = updatedEntries.SetItem( expireHeight, updatedEntries[expireHeight].RemoveAt(entry.index)); @@ -258,7 +292,7 @@ internal UnbondLockIn Cancel(FungibleAssetValue cancellingFAV, long height) else { var cancelledEntry = entry.value.Cancel(cancellingFAV); - cancellingFAV -= entry.value.LockInFAV; + cancellingFAV -= entry.value.UnbondingFAV; updatedEntries = updatedEntries.SetItem( expireHeight, updatedEntries[expireHeight].SetItem(entry.index, cancelledEntry)); @@ -278,14 +312,14 @@ internal FungibleAssetValue Cancellable(long height) => Entries .Where(kv => kv.Key > height) .SelectMany(kv => kv.Value) - .Select(e => e.LockInFAV) + .Select(e => e.UnbondingFAV) .Aggregate((accum, next) => accum + next); private UnbondLockIn UpdateEntries( - ImmutableSortedDictionary> entries) - => new UnbondLockIn(Address, MaxEntries, Sender, Recipient, entries, _repository); + ImmutableSortedDictionary> entries) + => new UnbondLockIn(Address, MaxEntries, DelegateeAddress, DelegatorAddress, entries, _repository); - private UnbondLockIn AddEntry(UnbondLockInEntry entry) + private UnbondLockIn AddEntry(UnbondingEntry entry) { if (IsFull) { @@ -303,7 +337,7 @@ private UnbondLockIn AddEntry(UnbondLockInEntry entry) return UpdateEntries( Entries.Add( - entry.ExpireHeight, ImmutableList.Empty.Add(entry))); + entry.ExpireHeight, ImmutableList.Empty.Add(entry))); } private void CannotMutateRelationsWithoutRepository() @@ -314,209 +348,5 @@ private void CannotMutateRelationsWithoutRepository() "Cannot mutate without repository."); } } - - public class UnbondLockInEntry : IUnbondingEntry, IBencodable, IEquatable - { - private int? _cachedHashCode; - - public UnbondLockInEntry( - FungibleAssetValue lockInFAV, - long creationHeight, - long expireHeight) - : this(lockInFAV, lockInFAV, creationHeight, expireHeight) - { - } - - public UnbondLockInEntry(IValue bencoded) - : this((List)bencoded) - { - } - - private UnbondLockInEntry(List bencoded) - : this( - new FungibleAssetValue(bencoded[0]), - new FungibleAssetValue(bencoded[1]), - (Integer)bencoded[2], - (Integer)bencoded[3]) - { - } - - private UnbondLockInEntry( - FungibleAssetValue initialLockInFAV, - FungibleAssetValue lockInFAV, - long creationHeight, - long expireHeight) - { - if (initialLockInFAV.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(initialLockInFAV), - initialLockInFAV, - "The initial lock-in FAV must be greater than zero."); - } - - if (lockInFAV.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(lockInFAV), - lockInFAV, - "The lock-in FAV must be greater than zero."); - } - - if (lockInFAV > initialLockInFAV) - { - throw new ArgumentOutOfRangeException( - nameof(lockInFAV), - lockInFAV, - "The lock-in FAV must be less than or equal to the initial lock-in FAV."); - } - - if (creationHeight < 0) - { - throw new ArgumentOutOfRangeException( - nameof(creationHeight), - creationHeight, - "The creation height must be greater than or equal to zero."); - } - - if (expireHeight < creationHeight) - { - throw new ArgumentOutOfRangeException( - nameof(expireHeight), - expireHeight, - "The expire height must be greater than the creation height."); - } - - InitialLockInFAV = initialLockInFAV; - LockInFAV = lockInFAV; - CreationHeight = creationHeight; - ExpireHeight = expireHeight; - } - - public FungibleAssetValue InitialLockInFAV { get; } - - public FungibleAssetValue LockInFAV { get; } - - public long CreationHeight { get; } - - public long ExpireHeight { get; } - - public List Bencoded => List.Empty - .Add(InitialLockInFAV.Serialize()) - .Add(LockInFAV.Serialize()) - .Add(CreationHeight) - .Add(ExpireHeight); - - IValue IBencodable.Bencoded => Bencoded; - - public override bool Equals(object? obj) - => obj is UnbondLockInEntry other && Equals(other); - - public bool Equals(UnbondLockInEntry? other) - => ReferenceEquals(this, other) - || (other is UnbondLockInEntry unbondLockInEntry - && InitialLockInFAV.Equals(unbondLockInEntry.InitialLockInFAV) - && LockInFAV.Equals(unbondLockInEntry.LockInFAV) - && CreationHeight == unbondLockInEntry.CreationHeight - && ExpireHeight == unbondLockInEntry.ExpireHeight); - - public override int GetHashCode() - { - if (_cachedHashCode is int cached) - { - return cached; - } - - int hash = HashCode.Combine( - InitialLockInFAV, - LockInFAV, - CreationHeight, - ExpireHeight); - - _cachedHashCode = hash; - return hash; - } - - public UnbondLockInEntry Slash(BigInteger slashFactor, long infractionHeight) - { - if (CreationHeight > infractionHeight || - ExpireHeight < infractionHeight) - { - throw new ArgumentOutOfRangeException( - nameof(infractionHeight), - infractionHeight, - "The infraction height must be between in creation height and expire height of entry."); - } - - return new UnbondLockInEntry( - InitialLockInFAV, - LockInFAV - LockInFAV.DivRem(slashFactor).Quotient, - CreationHeight, - ExpireHeight); - } - - internal UnbondLockInEntry Cancel(FungibleAssetValue cancellingFAV) - { - if (cancellingFAV.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(cancellingFAV), - cancellingFAV, - "The cancelling FAV must be greater than zero."); - } - - if (LockInFAV <= cancellingFAV) - { - throw new InvalidOperationException("Cannot cancel more than locked-in FAV."); - } - - return new UnbondLockInEntry( - InitialLockInFAV - cancellingFAV, - LockInFAV - cancellingFAV, - CreationHeight, - ExpireHeight); - } - } - - public class UnbondLockInEntryComparer : IComparer - { - public int Compare(UnbondLockInEntry? x, UnbondLockInEntry? y) - { - if (ReferenceEquals(x, y)) - { - return 0; - } - - if (x is null) - { - return -1; - } - - if (y is null) - { - return 1; - } - - int comparison = x.ExpireHeight.CompareTo(y.ExpireHeight); - if (comparison != 0) - { - return comparison; - } - - comparison = x.CreationHeight.CompareTo(y.CreationHeight); - if (comparison != 0) - { - return comparison; - } - - comparison = -x.InitialLockInFAV.CompareTo(y.InitialLockInFAV); - if (comparison != 0) - { - return comparison; - } - - return -x.LockInFAV.CompareTo(y.LockInFAV); - } - } } } diff --git a/Lib9c/Delegation/UnbondingEntry.cs b/Lib9c/Delegation/UnbondingEntry.cs new file mode 100644 index 0000000000..e1f786146e --- /dev/null +++ b/Lib9c/Delegation/UnbondingEntry.cs @@ -0,0 +1,238 @@ +#nullable enable +using System; +using System.Numerics; +using System.Collections.Generic; +using Bencodex.Types; +using Bencodex; +using Libplanet.Crypto; +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + public class UnbondingEntry : IBencodable, IEquatable + { + private int? _cachedHashCode; + + public UnbondingEntry( + Address unbondeeAddress, + FungibleAssetValue unbondingFAV, + long creationHeight, + long expireHeight) + : this(unbondeeAddress, unbondingFAV, unbondingFAV, creationHeight, expireHeight) + { + } + + public UnbondingEntry(IValue bencoded) + : this((List)bencoded) + { + } + + private UnbondingEntry(List bencoded) + : this( + new Address(bencoded[0]), + new FungibleAssetValue(bencoded[1]), + new FungibleAssetValue(bencoded[2]), + (Integer)bencoded[3], + (Integer)bencoded[4]) + { + } + + public UnbondingEntry( + Address unbondeeAddress, + FungibleAssetValue initialUnbondingFAV, + FungibleAssetValue unbondingFAV, + long creationHeight, + long expireHeight) + { + if (initialUnbondingFAV.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(initialUnbondingFAV), + initialUnbondingFAV, + "The initial unbonding FAV must be greater than zero."); + } + + if (unbondingFAV.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(unbondingFAV), + unbondingFAV, + "The unbonding FAV must be greater than zero."); + } + + if (unbondingFAV > initialUnbondingFAV) + { + throw new ArgumentOutOfRangeException( + nameof(unbondingFAV), + unbondingFAV, + "The unbonding FAV must be less than or equal to the initial unbonding FAV."); + } + + if (creationHeight < 0) + { + throw new ArgumentOutOfRangeException( + nameof(creationHeight), + creationHeight, + "The creation height must be greater than or equal to zero."); + } + + if (expireHeight < creationHeight) + { + throw new ArgumentOutOfRangeException( + nameof(expireHeight), + expireHeight, + "The expire height must be greater than the creation height."); + } + + UnbondeeAddress = unbondeeAddress; + InitialUnbondingFAV = initialUnbondingFAV; + UnbondingFAV = unbondingFAV; + CreationHeight = creationHeight; + ExpireHeight = expireHeight; + } + + public Address UnbondeeAddress { get; } + + public FungibleAssetValue InitialUnbondingFAV { get; } + + public FungibleAssetValue UnbondingFAV { get; } + + public long CreationHeight { get; } + + public long ExpireHeight { get; } + + public List Bencoded => List.Empty + .Add(UnbondeeAddress.Bencoded) + .Add(InitialUnbondingFAV.Serialize()) + .Add(UnbondingFAV.Serialize()) + .Add(CreationHeight) + .Add(ExpireHeight); + + IValue IBencodable.Bencoded => Bencoded; + + public override bool Equals(object? obj) + => obj is UnbondingEntry other && Equals(other); + + public bool Equals(UnbondingEntry? other) + => ReferenceEquals(this, other) + || (other is UnbondingEntry rebondGraceEntry + && UnbondeeAddress.Equals(rebondGraceEntry.UnbondeeAddress) + && InitialUnbondingFAV.Equals(rebondGraceEntry.InitialUnbondingFAV) + && UnbondingFAV.Equals(rebondGraceEntry.UnbondingFAV) + && CreationHeight == rebondGraceEntry.CreationHeight + && ExpireHeight == rebondGraceEntry.ExpireHeight); + + public override int GetHashCode() + { + if (_cachedHashCode is int cached) + { + return cached; + } + + int hash = HashCode.Combine( + UnbondeeAddress, + InitialUnbondingFAV, + UnbondingFAV, + CreationHeight, + ExpireHeight); + + _cachedHashCode = hash; + return hash; + } + + public UnbondingEntry Slash( + BigInteger slashFactor, long infractionHeight, out FungibleAssetValue slashedFAV) + { + if (CreationHeight > infractionHeight || + ExpireHeight < infractionHeight) + { + throw new ArgumentOutOfRangeException( + nameof(infractionHeight), + infractionHeight, + "The infraction height must be between in creation height and expire height of entry."); + } + + var favToSlash = InitialUnbondingFAV.DivRem(slashFactor).Quotient; + slashedFAV = favToSlash < UnbondingFAV + ? favToSlash + : UnbondingFAV; + + return new UnbondingEntry( + UnbondeeAddress, + InitialUnbondingFAV, + UnbondingFAV - slashedFAV, + CreationHeight, + ExpireHeight); + } + + internal UnbondingEntry Cancel(FungibleAssetValue cancellingFAV) + { + if (cancellingFAV.Sign <= 0) + { + throw new ArgumentOutOfRangeException( + nameof(cancellingFAV), + cancellingFAV, + "The cancelling FAV must be greater than zero."); + } + + if (UnbondingFAV <= cancellingFAV) + { + throw new InvalidOperationException("Cannot cancel more than unbonding FAV."); + } + + return new UnbondingEntry( + UnbondeeAddress, + InitialUnbondingFAV - cancellingFAV, + UnbondingFAV - cancellingFAV, + CreationHeight, + ExpireHeight); + } + + public class Comparer : IComparer + { + public int Compare(UnbondingEntry? x, UnbondingEntry? y) + { + if (ReferenceEquals(x, y)) + { + return 0; + } + + if (x is null) + { + return -1; + } + + if (y is null) + { + return 1; + } + + int comparison = x.ExpireHeight.CompareTo(y.ExpireHeight); + if (comparison != 0) + { + return comparison; + } + + comparison = x.CreationHeight.CompareTo(y.CreationHeight); + if (comparison != 0) + { + return comparison; + } + + comparison = -x.InitialUnbondingFAV.CompareTo(y.InitialUnbondingFAV); + if (comparison != 0) + { + return comparison; + } + + comparison = -x.UnbondingFAV.CompareTo(y.UnbondingFAV); + if (comparison != 0) + { + return comparison; + } + + return x.UnbondeeAddress.CompareTo(y.UnbondeeAddress); + } + } + } +} diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index dac87095c3..64a77e4451 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -1,5 +1,4 @@ using System; -using System.Numerics; using Bencodex; using Bencodex.Types; using Lib9c; @@ -19,31 +18,40 @@ public class Guild : Delegatee, IEquatable, IBen public readonly AgentAddress GuildMasterAddress; public Guild( - AgentAddress guildMasterAddress, Currency rewardCurrency) - : this(guildMasterAddress, rewardCurrency, null) - { - } - - public Guild(List list, Currency rewardCurrency) - : this(list, rewardCurrency, null) + Address address, + AgentAddress guildMasterAddress, + Currency rewardCurrency, + GuildRepository repository) + : base( + address: address, + accountAddress: Addresses.Guild, + delegationCurrency: Currencies.GuildGold, + rewardCurrency: rewardCurrency, + delegationPoolAddress: address, + unbondingPeriod: 75600L, + maxUnbondLockInEntries: 10, + maxRebondGraceEntries: 10, + repository: repository) { + GuildMasterAddress = guildMasterAddress; } public Guild( - AgentAddress guildMasterAddress, Currency rewardCurrency, IDelegationRepository repository) - : base(guildMasterAddress, repository) + Address address, + IValue bencoded, GuildRepository repository) + : this(address: address, bencoded: (List)bencoded, repository: repository) { - GuildMasterAddress = guildMasterAddress; - RewardCurrency = rewardCurrency; } - public Guild(List list, Currency rewardCurrency, IDelegationRepository repository) - : base(new Address(list[2]), list[3], repository) + public Guild( + Address address, List bencoded, GuildRepository repository) + : base( + address: address, + repository: repository) { - GuildMasterAddress = new AgentAddress(list[2]); - RewardCurrency = rewardCurrency; + GuildMasterAddress = new AgentAddress(bencoded[2]); - if (list[0] is not Text text || text != StateTypeName || list[1] is not Integer integer) + if (bencoded[0] is not Text text || text != StateTypeName || bencoded[1] is not Integer integer) { throw new InvalidCastException(); } @@ -54,27 +62,10 @@ public Guild(List list, Currency rewardCurrency, IDelegationRepository repositor } } - public override Currency DelegationCurrency => Currencies.GuildGold; - - public override Currency RewardCurrency { get; } - - public override Address DelegationPoolAddress => DeriveAddress(PoolId); - - public override long UnbondingPeriod => 75600L; - - public override byte[] DelegateeId => new byte[] { 0x47 }; // `G` - - public override int MaxUnbondLockInEntries => 10; - - public override int MaxRebondGraceEntries => 10; - - public override BigInteger SlashFactor => BigInteger.One; - - public override List Bencoded => List.Empty + public List Bencoded => List.Empty .Add(StateTypeName) .Add(StateVersion) - .Add(GuildMasterAddress.Bencoded) - .Add(base.Bencoded); + .Add(GuildMasterAddress.Bencoded); IValue IBencodable.Bencoded => Bencoded; @@ -83,7 +74,7 @@ public bool Equals(Guild other) if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return GuildMasterAddress.Equals(other.GuildMasterAddress) - && base.Equals(other); + && Metadata.Equals(other.Metadata); } public override bool Equals(object obj) diff --git a/Lib9c/Model/Guild/GuildParticipant.cs b/Lib9c/Model/Guild/GuildParticipant.cs index 5a440908ae..632343de0a 100644 --- a/Lib9c/Model/Guild/GuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildParticipant.cs @@ -4,6 +4,7 @@ using Libplanet.Crypto; using Nekoyume.Action; using Nekoyume.Delegation; +using Nekoyume.Model.State; using Nekoyume.TypedAddress; namespace Nekoyume.Model.Guild @@ -16,29 +17,37 @@ public class GuildParticipant : Delegator, IBencodable, public readonly GuildAddress GuildAddress; public GuildParticipant( - AgentAddress agentAddress, GuildAddress guildAddress) - : this(agentAddress, guildAddress, null) - { - } - - public GuildParticipant(AgentAddress agentAddress, List list) - : this(agentAddress, list, null) + Address address, + GuildAddress guildAddress, + GuildRepository repository) + : base( + address: address, + accountAddress: Addresses.GuildParticipant, + delegationPoolAddress: StakeState.DeriveAddress(address), + repository: repository) { + GuildAddress = guildAddress; } public GuildParticipant( - AgentAddress agentAddress, GuildAddress guildAddress, IDelegationRepository repository) - : base(agentAddress, repository) + Address address, + IValue bencoded, + GuildRepository repository) + : this(address, (List)bencoded, repository) { - GuildAddress = guildAddress; } - public GuildParticipant(AgentAddress agentAddress, List list, IDelegationRepository repository) - : base(agentAddress, list[3], repository) + public GuildParticipant( + Address address, + List bencoded, + GuildRepository repository) + : base( + address: address, + repository: repository) { - GuildAddress = new GuildAddress(list[2]); + GuildAddress = new GuildAddress(bencoded[2]); - if (list[0] is not Text text || text != StateTypeName || list[1] is not Integer integer) + if (bencoded[0] is not Text text || text != StateTypeName || bencoded[1] is not Integer integer) { throw new InvalidCastException(); } @@ -49,13 +58,10 @@ public GuildParticipant(AgentAddress agentAddress, List list, IDelegationReposit } } - public AgentAddress AgentAddress => new AgentAddress(Address); - - public override List Bencoded => List.Empty + public List Bencoded => List.Empty .Add(StateTypeName) .Add(StateVersion) - .Add(GuildAddress.Bencoded) - .Add(base.Bencoded); + .Add(GuildAddress.Bencoded); IValue IBencodable.Bencoded => Bencoded; @@ -64,7 +70,7 @@ public bool Equals(GuildParticipant other) if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return GuildAddress.Equals(other.GuildAddress) - && base.Equals(other); + && Metadata.Equals(other.Metadata); } public override bool Equals(object obj) @@ -77,7 +83,7 @@ public override bool Equals(object obj) public override int GetHashCode() { - return Address.GetHashCode(); + return GuildAddress.GetHashCode(); } } } diff --git a/Lib9c/Model/Guild/GuildRepository.cs b/Lib9c/Model/Guild/GuildRepository.cs new file mode 100644 index 0000000000..f4627b3565 --- /dev/null +++ b/Lib9c/Model/Guild/GuildRepository.cs @@ -0,0 +1,79 @@ +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Crypto; +using Nekoyume.Action; +using Nekoyume.Delegation; +using Nekoyume.TypedAddress; + +namespace Nekoyume.Model.Guild +{ + public class GuildRepository : DelegationRepository + { + public GuildRepository(IWorld world, IActionContext context) + : base( + world: world, + context: context, + 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 Guild GetGuild(Address address) + => delegateeAccount.GetState(address) is IValue bencoded + ? new Guild( + new AgentAddress(address), + bencoded, + this) + : throw new FailedLoadStateException("Guild does not exist."); + + public override IDelegatee GetDelegatee(Address address) + => GetGuild(address); + + + public GuildParticipant GetGuildParticipant(Address address) + => delegatorAccount.GetState(address) is IValue bencoded + ? new GuildParticipant(address, bencoded, this) + : throw new FailedLoadStateException("Delegator does not exist."); + + public override IDelegator GetDelegator(Address address) + => GetGuildParticipant(address); + + public void SetGuild(Guild guild) + { + delegateeAccount = delegateeAccount.SetState( + guild.Address, guild.Bencoded); + SetDelegateeMetadata(guild.Metadata); + } + + public override void SetDelegatee(IDelegatee delegatee) + => SetGuild(delegatee as Guild); + + public void SetGuildParticipant(GuildParticipant guildParticipant) + { + delegatorAccount = delegatorAccount.SetState( + guildParticipant.Address, guildParticipant.Bencoded); + SetDelegatorMetadata(guildParticipant.Metadata); + } + + public override void SetDelegator(IDelegator delegator) + => SetGuildParticipant(delegator as GuildParticipant); + + public void RemoveGuild(Address guildAddress) + { + delegatorAccount = delegatorAccount.RemoveState(guildAddress); + } + + public void RemoveGuildParticipant(Address guildParticipantAddress) + { + delegatorAccount = delegatorAccount.RemoveState(guildParticipantAddress); + } + } +} diff --git a/Lib9c/Module/Delegation/BondModule.cs b/Lib9c/Module/Delegation/BondModule.cs deleted file mode 100644 index 122512fa71..0000000000 --- a/Lib9c/Module/Delegation/BondModule.cs +++ /dev/null @@ -1,43 +0,0 @@ -#nullable enable -using System; -using Bencodex.Types; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Delegation; - -namespace Nekoyume.Module.Delegation -{ - public static class BondModule - { - public static Bond GetBond( - this IWorldState world, IDelegatee delegatee, Address delegatorAddress) - => GetBond(world, delegatee.BondAddress(delegatorAddress)); - - public static Bond GetBond(this IWorldState world, Address address) - => TryGetBond(world, address, out var bond) - ? bond! - : throw new InvalidOperationException("Bond not found"); - - public static bool TryGetBond( - this IWorldState world, Address address, out Bond? bond) - { - try - { - var value = world.GetAccountState(Addresses.Bond).GetState(address); - if (!(value is List list)) - { - bond = null; - return false; - } - - bond = new Bond(address, list); - return true; - } - catch - { - bond = null; - return false; - } - } - } -} diff --git a/Lib9c/Module/Delegation/LumpSumRewardsRecordModule.cs b/Lib9c/Module/Delegation/LumpSumRewardsRecordModule.cs deleted file mode 100644 index 2e6275e1d4..0000000000 --- a/Lib9c/Module/Delegation/LumpSumRewardsRecordModule.cs +++ /dev/null @@ -1,48 +0,0 @@ -#nullable enable -using System; -using Bencodex.Types; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Delegation; - -namespace Nekoyume.Module.Delegation -{ - public static class LumpSumRewardsRecordModule - { - public static LumpSumRewardsRecord GetCurrentLumpSumRewardsRecord( - this IWorldState world, IDelegatee delegatee) - => GetLumpSumRewardsRecord(world, delegatee.CurrentLumpSumRewardsRecordAddress()); - - public static LumpSumRewardsRecord GetLumpSumRewardsRecord( - this IWorldState world, IDelegatee delegatee, long height) - => GetLumpSumRewardsRecord(world, delegatee.LumpSumRewardsRecordAddress(height)); - - public static LumpSumRewardsRecord GetLumpSumRewardsRecord( - this IWorldState world, Address address) - => TryGetLumpSumRewardsRecord(world, address, out var lumpSumRewardsRecord) - ? lumpSumRewardsRecord! - : throw new InvalidOperationException("Failed to get LumpSumRewardsRecord."); - - public static bool TryGetLumpSumRewardsRecord( - this IWorldState world, Address address, out LumpSumRewardsRecord? lumpSumRewardsRecord) - { - try - { - var value = world.GetAccountState(Addresses.LumpSumRewardsRecord).GetState(address); - if (!(value is List list)) - { - lumpSumRewardsRecord = null; - return false; - } - - lumpSumRewardsRecord = new LumpSumRewardsRecord(address, list); - return true; - } - catch - { - lumpSumRewardsRecord = null; - return false; - } - } - } -} diff --git a/Lib9c/Module/Delegation/RebondGraceModule.cs b/Lib9c/Module/Delegation/RebondGraceModule.cs deleted file mode 100644 index adda5816ff..0000000000 --- a/Lib9c/Module/Delegation/RebondGraceModule.cs +++ /dev/null @@ -1,49 +0,0 @@ -#nullable enable -using System; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Delegation; - -namespace Nekoyume.Module.Delegation -{ - public static class RebondGraceModule - { - public static RebondGrace GetRebondGrace( - this IWorldState world, IDelegatee delegatee, Address delegatorAddress) - => GetRebondGrace( - world, - delegatee.RebondGraceAddress(delegatorAddress), - delegatee.MaxRebondGraceEntries); - - public static RebondGrace GetRebondGrace( - this IWorldState world, Address address, int maxEntries) - => TryGetRebondGrace(world, address, maxEntries, out var rebondGrace) - ? rebondGrace! - : throw new InvalidOperationException("RebondGrace not found"); - - public static bool TryGetRebondGrace( - this IWorldState world, - Address address, - int maxEntries, - out RebondGrace? rebondGrace) - { - try - { - var value = world.GetAccountState(Addresses.RebondGrace).GetState(address); - if (!(value is Bencodex.Types.List list)) - { - rebondGrace = null; - return false; - } - - rebondGrace = new RebondGrace(address, maxEntries, list); - return true; - } - catch - { - rebondGrace = null; - return false; - } - } - } -} diff --git a/Lib9c/Module/Delegation/UnbondLockInModule.cs b/Lib9c/Module/Delegation/UnbondLockInModule.cs deleted file mode 100644 index 0b6a3a950a..0000000000 --- a/Lib9c/Module/Delegation/UnbondLockInModule.cs +++ /dev/null @@ -1,50 +0,0 @@ -#nullable enable -using System; -using Bencodex.Types; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Delegation; - -namespace Nekoyume.Module.Delegation -{ - public static class UnbondLockInModule - { - public static UnbondLockIn GetUnbondLockIn( - this IWorldState world, IDelegatee delegatee, Address delegatorAddress) - => GetUnbondLockIn( - world, - delegatee.UnbondLockInAddress(delegatorAddress), - delegatee.MaxUnbondLockInEntries); - - public static UnbondLockIn GetUnbondLockIn( - this IWorldState world, Address address, int maxEntries) - => TryGetUnbondLockIn(world, address, maxEntries, out var unbondLockIn) - ? unbondLockIn! - : throw new InvalidOperationException("UnbondLockIn not found"); - - public static bool TryGetUnbondLockIn( - this IWorldState world, - Address address, - int maxEntries, - out UnbondLockIn? unbondLockIn) - { - try - { - var value = world.GetAccountState(Addresses.UnbondLockIn).GetState(address); - if (!(value is List list)) - { - unbondLockIn = null; - return false; - } - - unbondLockIn = new UnbondLockIn(address, maxEntries, list); - return true; - } - catch - { - unbondLockIn = null; - return false; - } - } - } -} diff --git a/Lib9c/Module/Guild/GuildApplicationModule.cs b/Lib9c/Module/Guild/GuildApplicationModule.cs index 3698c2f446..355cb649cd 100644 --- a/Lib9c/Module/Guild/GuildApplicationModule.cs +++ b/Lib9c/Module/Guild/GuildApplicationModule.cs @@ -2,8 +2,7 @@ using System; using System.Diagnostics.CodeAnalysis; using Bencodex.Types; -using Libplanet.Action.State; -using Libplanet.Crypto; +using Libplanet.Action; using Nekoyume.Action; using Nekoyume.Extensions; using Nekoyume.Model.Guild; @@ -13,9 +12,10 @@ namespace Nekoyume.Module.Guild { public static class GuildApplicationModule { - public static Model.Guild.GuildApplication GetGuildApplication(this IWorldState worldState, AgentAddress agentAddress) + public static Model.Guild.GuildApplication GetGuildApplication( + this GuildRepository repository, AgentAddress agentAddress) { - var value = worldState.GetAccountState(Addresses.GuildApplication).GetState(agentAddress); + var value = repository.World.GetAccountState(Addresses.GuildApplication).GetState(agentAddress); if (value is List list) { return new Model.Guild.GuildApplication(list); @@ -24,12 +24,12 @@ public static Model.Guild.GuildApplication GetGuildApplication(this IWorldState throw new FailedLoadStateException("There is no such guild."); } - public static bool TryGetGuildApplication(this IWorldState worldState, + public static bool TryGetGuildApplication(this GuildRepository repository, AgentAddress agentAddress, [NotNullWhen(true)] out Model.Guild.GuildApplication? guildApplication) { try { - guildApplication = GetGuildApplication(worldState, agentAddress); + guildApplication = repository.GetGuildApplication(agentAddress); return true; } catch @@ -39,50 +39,48 @@ public static bool TryGetGuildApplication(this IWorldState worldState, } } - public static IWorld ApplyGuild( - this IWorld world, AgentAddress signer, GuildAddress guildAddress) + public static void ApplyGuild( + this GuildRepository repository, AgentAddress signer, GuildAddress guildAddress) { - if (world.GetJoinedGuild(signer) is not null) + if (repository.GetJoinedGuild(signer) is not null) { throw new InvalidOperationException("The signer is already joined in a guild."); } // NOTE: Check there is such guild. - if (!world.TryGetGuild(guildAddress, out _)) + if (!repository.TryGetGuild(guildAddress, out _)) { throw new InvalidOperationException("The guild does not exist."); } - if (world.IsBanned(guildAddress, signer)) + if (repository.IsBanned(guildAddress, signer)) { throw new InvalidOperationException("The signer is banned from the guild."); } - return world.MutateAccount(Addresses.GuildApplication, - account => - account.SetState(signer, new GuildApplication(guildAddress).Bencoded)); + repository.SetGuildApplication(signer, new GuildApplication(guildAddress)); } - public static IWorld CancelGuildApplication( - this IWorld world, AgentAddress agentAddress) + public static void CancelGuildApplication( + this GuildRepository repository, AgentAddress agentAddress) { - if (!world.TryGetGuildApplication(agentAddress, out _)) + if (!repository.TryGetGuildApplication(agentAddress, out _)) { throw new InvalidOperationException("It may not apply any guild."); } - return world.RemoveGuildApplication(agentAddress); + repository.RemoveGuildApplication(agentAddress); } - public static IWorld AcceptGuildApplication( - this IWorld world, AgentAddress signer, AgentAddress target) + public static void AcceptGuildApplication( + this GuildRepository repository, IActionContext context, AgentAddress signer, AgentAddress target) { - if (!world.TryGetGuildApplication(target, out var guildApplication)) + if (!repository.TryGetGuildApplication(target, out var guildApplication)) { throw new InvalidOperationException("It may not apply any guild."); } - if (!world.TryGetGuild(guildApplication.GuildAddress, out var guild)) + if (!repository.TryGetGuild(guildApplication.GuildAddress, out var guild)) { throw new InvalidOperationException( "There is no such guild now. It may be removed. Please cancel and apply another guild."); @@ -93,21 +91,21 @@ public static IWorld AcceptGuildApplication( throw new InvalidOperationException("It may not be a guild master."); } - return world.RemoveGuildApplication(target) - .JoinGuild(guildApplication.GuildAddress, target); + repository.RemoveGuildApplication(target); + repository.JoinGuild(guildApplication.GuildAddress, target); } #pragma warning disable S4144 - public static IWorld RejectGuildApplication( + public static void RejectGuildApplication( #pragma warning restore S4144 - this IWorld world, AgentAddress signer, AgentAddress target) + this GuildRepository repository, AgentAddress signer, AgentAddress target) { - if (!world.TryGetGuildApplication(target, out var guildApplication)) + if (!repository.TryGetGuildApplication(target, out var guildApplication)) { throw new InvalidOperationException("It may not apply any guild."); } - if (!world.TryGetGuild(guildApplication.GuildAddress, out var guild)) + if (!repository.TryGetGuild(guildApplication.GuildAddress, out var guild)) { throw new InvalidOperationException( "There is no such guild now. It may be removed. Please cancel and apply another guild."); @@ -118,14 +116,25 @@ public static IWorld RejectGuildApplication( throw new InvalidOperationException("It may not be a guild master."); } - return world.RemoveGuildApplication(target); + repository.RemoveGuildApplication(target); } - private static IWorld RemoveGuildApplication(this IWorld world, AgentAddress agentAddress) + public static void SetGuildApplication( + this GuildRepository repository, AgentAddress agentAddress, GuildApplication guildApplication) { - return world.MutateAccount(Addresses.GuildApplication, - account => account.RemoveState(agentAddress)); + repository.UpdateWorld( + repository.World.MutateAccount( + Addresses.GuildApplication, + account => account.SetState(agentAddress, guildApplication.Bencoded))); } + private static void RemoveGuildApplication( + this GuildRepository repository, AgentAddress agentAddress) + { + repository.UpdateWorld( + repository.World.MutateAccount( + Addresses.GuildApplication, + account => account.RemoveState(agentAddress))); + } } } diff --git a/Lib9c/Module/Guild/GuildBanModule.cs b/Lib9c/Module/Guild/GuildBanModule.cs index bcb189d2f0..fd5122fc84 100644 --- a/Lib9c/Module/Guild/GuildBanModule.cs +++ b/Lib9c/Module/Guild/GuildBanModule.cs @@ -1,8 +1,9 @@ using System; +using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; -using Libplanet.Store.Trie; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.TypedAddress; using Boolean = Bencodex.Types.Boolean; @@ -10,10 +11,10 @@ namespace Nekoyume.Module.Guild { public static class GuildBanModule { - public static bool IsBanned(this IWorldState worldState, GuildAddress guildAddress, Address agentAddress) + public static bool IsBanned(this GuildRepository repository, GuildAddress guildAddress, Address agentAddress) { var accountAddress = Addresses.GetGuildBanAccountAddress(guildAddress); - var value = worldState.GetAccountState(accountAddress) + var value = repository.World.GetAccountState(accountAddress) .GetState(agentAddress); if (value is Boolean boolean) { @@ -28,9 +29,9 @@ public static bool IsBanned(this IWorldState worldState, GuildAddress guildAddre return false; } - public static IWorld Ban(this IWorld world, GuildAddress guildAddress, AgentAddress signer, AgentAddress target) + public static void Ban(this GuildRepository repository, GuildAddress guildAddress, AgentAddress signer, AgentAddress target) { - if (!world.TryGetGuild(guildAddress, out var guild)) + if (!repository.TryGetGuild(guildAddress, out var guild)) { throw new InvalidOperationException("There is no such guild."); } @@ -45,22 +46,25 @@ public static IWorld Ban(this IWorld world, GuildAddress guildAddress, AgentAddr throw new InvalidOperationException("The guild master cannot be banned."); } - if (world.TryGetGuildApplication(target, out var guildApplication) && guildApplication.GuildAddress == guildAddress) + if (repository.TryGetGuildApplication(target, out var guildApplication) && guildApplication.GuildAddress == guildAddress) { - world = world.RejectGuildApplication(signer, target); + repository.RejectGuildApplication(signer, target); } - if (world.GetJoinedGuild(target) == guildAddress) + if (repository.GetJoinedGuild(target) == guildAddress) { - world = world.LeaveGuild(target); + repository.LeaveGuild(target); } - return world.MutateAccount(Addresses.GetGuildBanAccountAddress(guildAddress), account => account.SetState(target, (Boolean)true)); + repository.UpdateWorld( + repository.World.MutateAccount( + Addresses.GetGuildBanAccountAddress(guildAddress), + account => account.SetState(target, (Boolean)true))); } - public static IWorld Unban(this IWorld world, GuildAddress guildAddress, AgentAddress signer, Address target) + public static void Unban(this GuildRepository repository, GuildAddress guildAddress, AgentAddress signer, Address target) { - if (!world.TryGetGuild(guildAddress, out var guild)) + if (!repository.TryGetGuild(guildAddress, out var guild)) { throw new InvalidOperationException("There is no such guild."); } @@ -70,17 +74,22 @@ public static IWorld Unban(this IWorld world, GuildAddress guildAddress, AgentAd throw new InvalidOperationException("The signer is not a guild master."); } - if (!world.IsBanned(guildAddress, target)) + if (!repository.IsBanned(guildAddress, target)) { throw new InvalidOperationException("The target is not banned."); } - return world.MutateAccount(Addresses.GetGuildBanAccountAddress(guildAddress), - account => account.RemoveState(target)); + repository.UpdateWorld( + repository.World.MutateAccount( + Addresses.GetGuildBanAccountAddress(guildAddress), + account => account.RemoveState(target))); } - public static IWorld RemoveBanList(this IWorld world, GuildAddress guildAddress) => - world.SetAccount(Addresses.GetGuildBanAccountAddress(guildAddress), GetEmptyAccount(world)); + public static void RemoveBanList(this GuildRepository repository, GuildAddress guildAddress) => + repository.UpdateWorld( + repository.World.SetAccount( + Addresses.GetGuildBanAccountAddress(guildAddress), + GetEmptyAccount(repository.World))); private static IAccount GetEmptyAccount(this IWorld world) { diff --git a/Lib9c/Module/Guild/GuildMemberCounterModule.cs b/Lib9c/Module/Guild/GuildMemberCounterModule.cs index 52cfc90a02..ed60dfc1b3 100644 --- a/Lib9c/Module/Guild/GuildMemberCounterModule.cs +++ b/Lib9c/Module/Guild/GuildMemberCounterModule.cs @@ -2,17 +2,18 @@ using System; using System.Numerics; using Bencodex.Types; -using Libplanet.Action.State; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.TypedAddress; namespace Nekoyume.Module.Guild { public static class GuildMemberCounterModule { - public static BigInteger GetGuildMemberCount(this IWorldState world, GuildAddress guildAddress) + public static BigInteger GetGuildMemberCount( + this GuildRepository repository, GuildAddress guildAddress) { - var account = world.GetAccountState(Addresses.GuildMemberCounter); + var account = repository.World.GetAccountState(Addresses.GuildMemberCounter); return account.GetState(guildAddress) switch { Integer i => i.Value, @@ -21,9 +22,12 @@ public static BigInteger GetGuildMemberCount(this IWorldState world, GuildAddres }; } - public static IWorld IncreaseGuildMemberCount(this IWorld world, GuildAddress guildAddress) + public static GuildRepository IncreaseGuildMemberCount( + this GuildRepository repository, GuildAddress guildAddress) { - return world.MutateAccount(Addresses.GuildMemberCounter, account => + repository.UpdateWorld( + repository.World.MutateAccount( + Addresses.GuildMemberCounter, account => { BigInteger count = account.GetState(guildAddress) switch { @@ -33,12 +37,17 @@ public static IWorld IncreaseGuildMemberCount(this IWorld world, GuildAddress gu }; return account.SetState(guildAddress, (Integer)(count + 1)); - }); + })); + + return repository; } - public static IWorld DecreaseGuildMemberCount(this IWorld world, GuildAddress guildAddress) + public static GuildRepository DecreaseGuildMemberCount( + this GuildRepository repository, GuildAddress guildAddress) { - return world.MutateAccount(Addresses.GuildMemberCounter, account => + repository.UpdateWorld( + repository.World.MutateAccount( + Addresses.GuildMemberCounter, account => { BigInteger count = account.GetState(guildAddress) switch { @@ -54,7 +63,9 @@ public static IWorld DecreaseGuildMemberCount(this IWorld world, GuildAddress gu } return account.SetState(guildAddress, (Integer)(count - 1)); - }); + })); + + return repository; } } } diff --git a/Lib9c/Module/Guild/GuildModule.cs b/Lib9c/Module/Guild/GuildModule.cs index f3228e4aed..aa9dcabd50 100644 --- a/Lib9c/Module/Guild/GuildModule.cs +++ b/Lib9c/Module/Guild/GuildModule.cs @@ -1,65 +1,24 @@ #nullable enable using System; using System.Diagnostics.CodeAnalysis; -using Bencodex.Types; +using Lib9c; using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Action; -using Nekoyume.Delegation; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.TypedAddress; namespace Nekoyume.Module.Guild { public static class GuildModule { - public static Model.Guild.Guild GetGuild(this IWorldState worldState, GuildAddress guildAddress) - { - var value = worldState.GetAccountState(Addresses.Guild).GetState(guildAddress); - if (value is List list) - { - return new Model.Guild.Guild(list, worldState.GetGoldCurrency()); - } - - throw new FailedLoadStateException("There is no such guild."); - } - - public static Model.Guild.Guild GetGuild( - this IWorldState worldState, GuildAddress guildAddress, IDelegationRepository repository) - { - var value = worldState.GetAccountState(Addresses.Guild).GetState(guildAddress); - if (value is List list) - { - return new Model.Guild.Guild(list, worldState.GetGoldCurrency(), repository); - } - - throw new FailedLoadStateException("There is no such guild."); - } - - public static bool TryGetGuild(this IWorldState worldState, - GuildAddress guildAddress, [NotNullWhen(true)] out Model.Guild.Guild? guild) - { - try - { - guild = GetGuild(worldState, guildAddress); - return true; - } - catch - { - guild = null; - return false; - } - } - public static bool TryGetGuild( - this IWorldState worldState, + this GuildRepository repository, GuildAddress guildAddress, - IDelegationRepository repository, [NotNullWhen(true)] out Model.Guild.Guild? guild) { try { - guild = GetGuild(worldState, guildAddress, repository); + guild = repository.GetGuild(guildAddress); return true; } catch @@ -69,33 +28,39 @@ public static bool TryGetGuild( } } - public static IWorld MakeGuild(this IWorld world, GuildAddress guildAddress, AgentAddress signer) + public static GuildRepository MakeGuild( + this GuildRepository repository, + GuildAddress guildAddress, + AgentAddress signer) { - if (world.GetJoinedGuild(signer) is not null) + if (repository.GetJoinedGuild(signer) is not null) { throw new InvalidOperationException("The signer already has a guild."); } - if (world.TryGetGuild(guildAddress, out _)) + if (repository.TryGetGuild(guildAddress, out _)) { throw new InvalidOperationException("Duplicated guild address. Please retry."); } - return world.MutateAccount(Addresses.Guild, - account => - account.SetState(guildAddress, - new Model.Guild.Guild(signer, world.GetGoldCurrency()).Bencoded)) - .JoinGuild(guildAddress, signer); + var guild = new Model.Guild.Guild( + guildAddress, signer, Currencies.GuildGold, repository); + repository.SetGuild(guild); + repository.JoinGuild(guildAddress, signer); + + return repository; } - public static IWorld RemoveGuild(this IWorld world, AgentAddress signer) + public static GuildRepository RemoveGuild( + this GuildRepository repository, + AgentAddress signer) { - if (world.GetJoinedGuild(signer) is not { } guildAddress) + if (repository.GetJoinedGuild(signer) is not { } guildAddress) { throw new InvalidOperationException("The signer does not join any guild."); } - if (!world.TryGetGuild(guildAddress, out var guild)) + if (!repository.TryGetGuild(guildAddress, out var guild)) { throw new InvalidOperationException("There is no such guild."); } @@ -105,36 +70,30 @@ public static IWorld RemoveGuild(this IWorld world, AgentAddress signer) throw new InvalidOperationException("The signer is not a guild master."); } - if (world.GetGuildMemberCount(guildAddress) > 1) + if (repository.GetGuildMemberCount(guildAddress) > 1) { throw new InvalidOperationException("There are remained participants in the guild."); } - return world - .RawLeaveGuild(signer) - .MutateAccount(Addresses.Guild, account => account.RemoveState(guildAddress)) - .RemoveBanList(guildAddress); - } + repository.RawLeaveGuild(signer); + repository.UpdateWorld( + repository.World.MutateAccount( + Addresses.Guild, account => account.RemoveState(guildAddress))); + repository.RemoveBanList(guildAddress); - public static IWorld SetGuild(this IWorld world, Model.Guild.Guild guild) - => world.MutateAccount( - Addresses.Guild, - account => account.SetState(guild.Address, guild.Bencoded)); + return repository; + } - public static IWorld CollectRewardGuild( - this IWorld world, + public static GuildRepository CollectRewardGuild( + this GuildRepository repository, IActionContext context, GuildAddress guildAddress) { - var repo = new DelegationRepository(world, context); - - var guild = world.TryGetGuild(guildAddress, repo, out var g) - ? g - : throw new InvalidOperationException("The guild does not exist."); - + var guild = repository.GetGuild(guildAddress); guild.CollectRewards(context.BlockIndex); + repository.SetGuild(guild); - return repo.World; + return repository; } } } diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index 768808447f..4c9a422a7b 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -2,91 +2,87 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Numerics; -using Bencodex.Types; using Lib9c; using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Types.Assets; -using Nekoyume.Action; -using Nekoyume.Delegation; -using Nekoyume.Extensions; using Nekoyume.Model.Guild; -using Nekoyume.Module.Delegation; using Nekoyume.TypedAddress; namespace Nekoyume.Module.Guild { public static class GuildParticipantModule { + public static GuildRepository GetGuildRepository(this IWorld world, IActionContext context) + => new GuildRepository(world, context); + // Returns `null` when it didn't join any guild. // Returns `GuildAddress` when it joined a guild. - public static GuildAddress? GetJoinedGuild(this IWorldState worldState, AgentAddress agentAddress) + public static GuildAddress? GetJoinedGuild(this GuildRepository repository, AgentAddress agentAddress) { - return worldState.TryGetGuildParticipant(agentAddress, out var guildParticipant) + return repository.TryGetGuildParticipant(agentAddress, out var guildParticipant) ? guildParticipant.GuildAddress : null; } - public static IWorld JoinGuildWithDelegate( - this IWorld world, + public static GuildRepository JoinGuildWithDelegate( + this GuildRepository repository, IActionContext context, GuildAddress guildAddress) - => world + => repository .JoinGuild(guildAddress, new AgentAddress(context.Signer)) - .Delegate(context, guildAddress, world.GetBalance(context.Signer, Currencies.GuildGold)); + .Delegate(context, guildAddress, repository.World.GetBalance(context.Signer, Currencies.GuildGold)); - public static IWorld LeaveGuildWithUndelegate( - this IWorld world, + public static GuildRepository LeaveGuildWithUndelegate( + this GuildRepository repository, IActionContext context) { - var guild = world.GetJoinedGuild(new AgentAddress(context.Signer)) is GuildAddress guildAddr - ? world.GetGuild(guildAddr) + var guild = repository.GetJoinedGuild(new AgentAddress(context.Signer)) is GuildAddress guildAddr + ? repository.GetGuild(guildAddr) : throw new InvalidOperationException("The signer does not join any guild."); - return world - .Undelegate(context, guildAddr, world.GetBond(guild, context.Signer).Share) + return repository + .Undelegate(context, guildAddr, repository.GetBond(guild, context.Signer).Share) .LeaveGuild(new AgentAddress(context.Signer)); } - public static IWorld MoveGuildWithRedelegate( - this IWorld world, + public static GuildRepository MoveGuildWithRedelegate( + this GuildRepository repository, IActionContext context, GuildAddress srcGuildAddress, GuildAddress dstGuildAddress) { var agentAddress = new AgentAddress(context.Signer); - var srcGuild = world.GetGuild(srcGuildAddress); - return world - .Redelegate( - context, - srcGuildAddress, - dstGuildAddress, - world.GetBond(srcGuild, context.Signer).Share) - .LeaveGuild(agentAddress) - .JoinGuild(dstGuildAddress, agentAddress); + var srcGuild = repository.GetGuild(srcGuildAddress); + repository.Redelegate(context, srcGuildAddress, dstGuildAddress, repository.GetBond(srcGuild, agentAddress).Share); + repository.LeaveGuild(agentAddress); + repository.JoinGuild(dstGuildAddress, agentAddress); + + return repository; } - public static IWorld JoinGuild( - this IWorld world, + public static GuildRepository JoinGuild( + this GuildRepository repository, GuildAddress guildAddress, AgentAddress target) { - var guildParticipant = new Model.Guild.GuildParticipant(target, guildAddress); - return world.MutateAccount(Addresses.GuildParticipant, - account => account.SetState(target, guildParticipant.Bencoded)) - .IncreaseGuildMemberCount(guildAddress); + var guildParticipant = new Model.Guild.GuildParticipant(target, guildAddress, repository); + repository.SetGuildParticipant(guildParticipant); + repository.IncreaseGuildMemberCount(guildAddress); + + return repository; } - public static IWorld LeaveGuild( - this IWorld world, + public static GuildRepository LeaveGuild( + this GuildRepository repository, AgentAddress target) { - if (world.GetJoinedGuild(target) is not { } guildAddress) + if (repository.GetJoinedGuild(target) is not { } guildAddress) { throw new InvalidOperationException("The signer does not join any guild."); } - if (!world.TryGetGuild(guildAddress, out var guild)) + if (!repository.TryGetGuild(guildAddress, out var guild)) { throw new InvalidOperationException( "There is no such guild."); @@ -98,55 +94,30 @@ public static IWorld LeaveGuild( "The signer is a guild master. Guild master cannot quit the guild."); } - return RawLeaveGuild(world, target); + return repository.RawLeaveGuild(target); } - public static IWorld RawLeaveGuild(this IWorld world, AgentAddress target) + public static GuildRepository RawLeaveGuild(this GuildRepository repository, AgentAddress target) { - if (!world.TryGetGuildParticipant(target, out var guildParticipant)) + if (!repository.TryGetGuildParticipant(target, out var guildParticipant)) { throw new InvalidOperationException("It may not join any guild."); } - return world.RemoveGuildParticipant(target) - .DecreaseGuildMemberCount(guildParticipant.GuildAddress); - } - - private static Model.Guild.GuildParticipant GetGuildParticipant( - this IWorldState worldState, AgentAddress agentAddress) - { - var value = worldState.GetAccountState(Addresses.GuildParticipant) - .GetState(agentAddress); - if (value is List list) - { - return new Model.Guild.GuildParticipant(agentAddress, list); - } + repository.RemoveGuildParticipant(target); + repository.DecreaseGuildMemberCount(guildParticipant.GuildAddress); - throw new FailedLoadStateException("It may not join any guild."); + return repository; } - private static Model.Guild.GuildParticipant GetGuildParticipant( - this IWorldState worldState, - AgentAddress agentAddress, - IDelegationRepository repository) - { - var value = worldState.GetAccountState(Addresses.GuildParticipant) - .GetState(agentAddress); - if (value is List list) - { - return new Model.Guild.GuildParticipant(agentAddress, list, repository); - } - - throw new FailedLoadStateException("It may not join any guild."); - } - - private static bool TryGetGuildParticipant(this IWorldState worldState, + private static bool TryGetGuildParticipant( + this GuildRepository repository, AgentAddress agentAddress, [NotNullWhen(true)] out Model.Guild.GuildParticipant? guildParticipant) { try { - guildParticipant = GetGuildParticipant(worldState, agentAddress); + guildParticipant = repository.GetGuildParticipant(agentAddress); return true; } catch @@ -156,117 +127,61 @@ private static bool TryGetGuildParticipant(this IWorldState worldState, } } - private static bool TryGetGuildParticipant(this IWorldState worldState, - AgentAddress agentAddress, - IDelegationRepository repository, - [NotNullWhen(true)] out Model.Guild.GuildParticipant? guildParticipant) - { - try - { - guildParticipant = GetGuildParticipant(worldState, agentAddress, repository); - return true; - } - catch - { - guildParticipant = null; - return false; - } - } - - private static IWorld RemoveGuildParticipant(this IWorld world, AgentAddress agentAddress) - { - return world.MutateAccount(Addresses.GuildParticipant, - account => account.RemoveState(agentAddress)); - } - - private static IWorld Delegate( - this IWorld world, + private static GuildRepository Delegate( + this GuildRepository repository, IActionContext context, GuildAddress guildAddress, FungibleAssetValue fav) { - var repo = new DelegationRepository(world, context); - var agentAddress = new AgentAddress(context.Signer); - var guildParticipant = world.TryGetGuildParticipant(agentAddress, repo, out var p) - ? p - : throw new InvalidOperationException("The signer was not joined to any guild."); - var guild = world.TryGetGuild(guildAddress, repo, out var g) - ? g - : throw new InvalidOperationException("The guild does not exist."); + var guildParticipant = repository.GetGuildParticipant(agentAddress); + var guild = repository.GetGuild(guildAddress); guildParticipant.Delegate(guild, fav, context.BlockIndex); - return repo.World.SetGuild(guild).SetGuildParticipant(guildParticipant); + return repository; } - private static IWorld Undelegate( - this IWorld world, + private static GuildRepository Undelegate( + this GuildRepository repository, IActionContext context, GuildAddress guildAddress, BigInteger share) { - var repo = new DelegationRepository(world, context); - var agentAddress = new AgentAddress(context.Signer); - var guildParticipant = world.TryGetGuildParticipant(agentAddress, repo, out var p) - ? p - : throw new InvalidOperationException("The signer was not joined to any guild."); - var guild = world.TryGetGuild(guildAddress, repo, out var g) - ? g - : throw new InvalidOperationException("The guild does not exist."); + var guildParticipant = repository.GetGuildParticipant(agentAddress); + var guild = repository.GetGuild(guildAddress); guildParticipant.Undelegate(guild, share, context.BlockIndex); - return repo.World.SetGuild(guild).SetGuildParticipant(guildParticipant); + return repository; } - public static IWorld Redelegate( - this IWorld world, + public static GuildRepository Redelegate( + this GuildRepository repository, IActionContext context, GuildAddress srcGuildAddress, GuildAddress dstGuildAddress, BigInteger share) { - var repo = new DelegationRepository(world, context); - var agentAddress = new AgentAddress(context.Signer); - var guildParticipant = world.TryGetGuildParticipant(agentAddress, repo, out var p) - ? p - : throw new InvalidOperationException("The signer was not joined to any guild."); - var srcGuild = world.TryGetGuild(srcGuildAddress, repo, out var s) - ? s - : throw new InvalidOperationException("The guild does not exist."); - var dstGuild = world.TryGetGuild(dstGuildAddress, repo, out var d) - ? d - : throw new InvalidOperationException("The guild does not exist."); + var guildParticipant = repository.GetGuildParticipant(agentAddress); + var srcGuild = repository.GetGuild(srcGuildAddress); + var dstGuild = repository.GetGuild(dstGuildAddress); guildParticipant.Redelegate(srcGuild, dstGuild, share, context.BlockIndex); - return repo.World.SetGuild(srcGuild).SetGuild(dstGuild).SetGuildParticipant(guildParticipant); + return repository; } - private static IWorld ClaimReward( - this IWorld world, + private static GuildRepository ClaimReward( + this GuildRepository repository, IActionContext context, GuildAddress guildAddress) { - var repo = new DelegationRepository(world, context); - var agentAddress = new AgentAddress(context.Signer); - var guildParticipant = world.TryGetGuildParticipant(agentAddress, out var p) - ? p - : throw new InvalidOperationException("The signer was not joined to any guild."); - var guild = world.TryGetGuild(guildAddress, out var g) - ? g - : throw new InvalidOperationException("The guild does not exist."); + var guildParticipant = repository.GetGuildParticipant(agentAddress); + var guild = repository.GetGuild(guildAddress); guildParticipant.ClaimReward(guild, context.BlockIndex); - return repo.World.SetGuild(guild).SetGuildParticipant(guildParticipant); + return repository; } - - private static IWorld SetGuildParticipant( - this IWorld world, - GuildParticipant guildParticipant) - => world.MutateAccount( - Addresses.GuildParticipant, - account => account.SetState(guildParticipant.Address, guildParticipant.Bencoded)); } } diff --git a/Lib9c/Module/Delegation/UnbondingSetModule.cs b/Lib9c/Module/Guild/GuildUnbondingModule.cs similarity index 87% rename from Lib9c/Module/Delegation/UnbondingSetModule.cs rename to Lib9c/Module/Guild/GuildUnbondingModule.cs index e0d9027d0e..c29f1b9c16 100644 --- a/Lib9c/Module/Delegation/UnbondingSetModule.cs +++ b/Lib9c/Module/Guild/GuildUnbondingModule.cs @@ -1,16 +1,16 @@ -#nullable enable using System; using Libplanet.Action; using Libplanet.Action.State; using Nekoyume.Delegation; +using Nekoyume.Model.Guild; -namespace Nekoyume.Module.Delegation +namespace Nekoyume.Module.Guild { - public static class UnbondingSetModule + public static class GuildUnbondingModule { public static IWorld ReleaseUnbondings(this IWorld world, IActionContext context) { - var repository = new DelegationRepository(world, context); + var repository = new GuildRepository(world, context); var unbondingSet = repository.GetUnbondingSet(); var unbondings = unbondingSet.UnbondingsToRelease(context.BlockIndex); diff --git a/Lib9c/Module/ValidatorDelegation/AbstainHistoryModule.cs b/Lib9c/Module/ValidatorDelegation/AbstainHistoryModule.cs new file mode 100644 index 0000000000..3c51b46529 --- /dev/null +++ b/Lib9c/Module/ValidatorDelegation/AbstainHistoryModule.cs @@ -0,0 +1,37 @@ +using Nekoyume.Extensions; +using Nekoyume.ValidatorDelegation; + +namespace Nekoyume.Module.ValidatorDelegation +{ + public static class AbstainHistoryModule + { + public static AbstainHistory GetAbstainHistory( + this ValidatorRepository repository) + { + try + { + return new AbstainHistory( + repository.World + .GetAccount(Addresses.AbstainHistory) + .GetState(AbstainHistory.Address)); + } + catch + { + return new AbstainHistory(); + } + } + + public static ValidatorRepository SetAbstainHistory( + this ValidatorRepository repository, + AbstainHistory abstainHistory) + { + repository.UpdateWorld( + repository.World + .MutateAccount( + Addresses.AbstainHistory, + account => account.SetState(AbstainHistory.Address, abstainHistory.Bencoded))); + + return repository; + } + } +} diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs index a7b7dae168..afcd8aa1e9 100644 --- a/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs @@ -1,69 +1,22 @@ #nullable enable using System; using System.Diagnostics.CodeAnalysis; -using Bencodex.Types; using Libplanet.Action; -using Libplanet.Action.State; using Libplanet.Crypto; -using Nekoyume.Action; -using Nekoyume.Extensions; using Nekoyume.ValidatorDelegation; namespace Nekoyume.Module.ValidatorDelegation { public static class ValidatorDelegateeModule { - public static ValidatorDelegatee GetValidatorDelegatee(this IWorldState worldState, Address address) - => worldState.TryGetValidatorDelegatee(address, out var validatorDelegatee) - ? validatorDelegatee - : throw new FailedLoadStateException("There is no such validator delegatee"); - - public static ValidatorDelegatee GetValidatorDelegatee( - this IWorldState worldState, Address address, ValidatorRepository repository) - => worldState.TryGetValidatorDelegatee(address, repository, out var validatorDelegatee) - ? validatorDelegatee - : throw new FailedLoadStateException("There is no such validator delegatee"); - - public static bool TryGetValidatorDelegatee( - this IWorldState worldState, - Address address, - [NotNullWhen(true)] out ValidatorDelegatee? validatorDelegatee) - { - try - { - var value = worldState.GetAccountState(Addresses.ValidatorDelegatee).GetState(address); - if (!(value is List list)) - { - validatorDelegatee = null; - return false; - } - - validatorDelegatee = new ValidatorDelegatee(address, list, worldState.GetGoldCurrency()); - return true; - } - catch - { - validatorDelegatee = null; - return false; - } - } - public static bool TryGetValidatorDelegatee( - this IWorldState worldState, + this ValidatorRepository repository, Address address, - ValidatorRepository repository, [NotNullWhen(true)] out ValidatorDelegatee? validatorDelegatee) { try { - var value = worldState.GetAccountState(Addresses.ValidatorDelegatee).GetState(address); - if (!(value is List list)) - { - validatorDelegatee = null; - return false; - } - - validatorDelegatee = new ValidatorDelegatee(address, list, worldState.GetGoldCurrency(), repository); + validatorDelegatee = repository.GetValidatorDelegatee(address); return true; } catch @@ -73,12 +26,8 @@ public static bool TryGetValidatorDelegatee( } } - public static IWorld SetValidatorDelegatee(this IWorld world, ValidatorDelegatee validatorDelegatee) - => world.MutateAccount( - Addresses.ValidatorDelegatee, - account => account.SetState(validatorDelegatee.Address, validatorDelegatee.Bencoded)); - - public static IWorld CreateValidatorDelegatee(this IWorld world, IActionContext context, PublicKey publicKey) + public static ValidatorRepository CreateValidatorDelegatee( + this ValidatorRepository repository, IActionContext context, PublicKey publicKey) { var signer = context.Signer; @@ -87,16 +36,17 @@ public static IWorld CreateValidatorDelegatee(this IWorld world, IActionContext throw new ArgumentException("The public key does not match the signer."); } - if (world.TryGetValidatorDelegatee(context.Signer, out _)) + if (repository.TryGetValidatorDelegatee(context.Signer, out _)) { throw new InvalidOperationException("The signer already has a validator delegatee."); } - return world.MutateAccount( - Addresses.ValidatorDelegatee, - account => account.SetState( - signer, - new ValidatorDelegatee(signer, publicKey, world.GetGoldCurrency()).Bencoded)); + var validatorDelegatee = new ValidatorDelegatee( + signer, publicKey, repository.World.GetGoldCurrency(), repository); + + repository.SetValidatorDelegatee(validatorDelegatee); + + return repository; } } } diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs index c456401780..83fc3d8f7c 100644 --- a/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs @@ -1,140 +1,77 @@ #nullable enable using System.Diagnostics.CodeAnalysis; using System.Numerics; -using Bencodex.Types; using Libplanet.Action; -using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; -using Nekoyume.Extensions; using Nekoyume.ValidatorDelegation; namespace Nekoyume.Module.ValidatorDelegation { public static class ValidatorDelegatorModule { - public static IWorld DelegateValidator( - this IWorld world, + public static ValidatorRepository DelegateValidator( + this ValidatorRepository repository, IActionContext context, Address address, FungibleAssetValue fav) { - var repo = new ValidatorRepository(world, context); - - var validatorDelegator = world.GetValidatorDelegator(context.Signer, repo); - var validatorDelegatee = world.GetValidatorDelegatee(address, repo); + var validatorDelegator = repository.GetValidatorDelegator(context.Signer); + var validatorDelegatee = repository.GetValidatorDelegatee(address); validatorDelegator.Delegate(validatorDelegatee, fav, context.BlockIndex); - return repo.World - .SetValidatorDelegatee(validatorDelegatee) - .SetValidatorDelegator(validatorDelegator); + return repository; } - public static IWorld UndelegateValidator( - this IWorld world, + public static ValidatorRepository UndelegateValidator( + this ValidatorRepository repository, IActionContext context, Address address, BigInteger share) { - var repo = new ValidatorRepository(world, context); - - var validatorDelegator = world.GetValidatorDelegator(context.Signer, repo); - var validatorDelegatee = world.GetValidatorDelegatee(address, repo); + var validatorDelegator = repository.GetValidatorDelegator(context.Signer); + var validatorDelegatee = repository.GetValidatorDelegatee(address); validatorDelegator.Undelegate(validatorDelegatee, share, context.BlockIndex); - return repo.World - .SetValidatorDelegatee(validatorDelegatee) - .SetValidatorDelegator(validatorDelegator); + return repository; } - public static IWorld RedelegateValidator( - this IWorld world, + public static ValidatorRepository RedelegateValidator( + this ValidatorRepository repository, IActionContext context, Address srcAddress, Address dstAddress, BigInteger share) { - var repo = new ValidatorRepository(world, context); - - var validatorDelegator = world.GetValidatorDelegator(context.Signer, repo); - var srcValidatorDelegatee = world.GetValidatorDelegatee(srcAddress, repo); - var dstValidatorDelegatee = world.GetValidatorDelegatee(dstAddress, repo); + var validatorDelegator = repository.GetValidatorDelegator(context.Signer); + var srcValidatorDelegatee = repository.GetValidatorDelegatee(srcAddress); + var dstValidatorDelegatee = repository.GetValidatorDelegatee(dstAddress); validatorDelegator.Redelegate(srcValidatorDelegatee, dstValidatorDelegatee, share, context.BlockIndex); - return repo.World - .SetValidatorDelegatee(srcValidatorDelegatee) - .SetValidatorDelegatee(dstValidatorDelegatee) - .SetValidatorDelegator(validatorDelegator); + return repository; } - public static IWorld ClaimRewardValidator( - this IWorld world, + public static ValidatorRepository ClaimRewardValidator( + this ValidatorRepository repository, IActionContext context, Address address) { - var repo = new ValidatorRepository(world, context); - - var validatorDelegator = world.GetValidatorDelegator(context.Signer, repo); - var validatorDelegatee = world.GetValidatorDelegatee(address, repo); + var validatorDelegator = repository.GetValidatorDelegator(context.Signer); + var validatorDelegatee = repository.GetValidatorDelegatee(address); validatorDelegator.ClaimReward(validatorDelegatee, context.BlockIndex); - return repo.World - .SetValidatorDelegatee(validatorDelegatee) - .SetValidatorDelegator(validatorDelegator); - } - - public static ValidatorDelegator GetValidatorDelegator(this IWorldState worldState, Address address) - => worldState.TryGetValidatorDelegator(address, out var validatorDelegator) - ? validatorDelegator - : new ValidatorDelegator(address); - - public static ValidatorDelegator GetValidatorDelegator( - this IWorldState worldState, Address address, ValidatorRepository repository) - => worldState.TryGetValidatorDelegator(address, repository, out var validatorDelegator) - ? validatorDelegator - : new ValidatorDelegator(address, repository); - - public static bool TryGetValidatorDelegator( - this IWorldState worldState, - Address address, - [NotNullWhen(true)] out ValidatorDelegator? validatorDelegator) - { - try - { - var value = worldState.GetAccountState(Addresses.ValidatorDelegator).GetState(address); - if (!(value is List list)) - { - validatorDelegator = null; - return false; - } - - validatorDelegator = new ValidatorDelegator(address, list); - return true; - } - catch - { - validatorDelegator = null; - return false; - } + return repository; } public static bool TryGetValidatorDelegator( - this IWorldState worldState, + this ValidatorRepository repository, Address address, - ValidatorRepository repository, [NotNullWhen(true)] out ValidatorDelegator? validatorDelegator) { try { - var value = worldState.GetAccountState(Addresses.ValidatorDelegator).GetState(address); - if (!(value is List list)) - { - validatorDelegator = null; - return false; - } - - validatorDelegator = new ValidatorDelegator(address, list, repository); + validatorDelegator = repository.GetValidatorDelegator(address); return true; } catch @@ -143,10 +80,5 @@ public static bool TryGetValidatorDelegator( return false; } } - - private static IWorld SetValidatorDelegator(this IWorld world, ValidatorDelegator validatorDelegator) - => world.MutateAccount( - Addresses.ValidatorDelegator, - account => account.SetState(validatorDelegator.Address, validatorDelegator.Bencoded)); } } diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorListModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorListModule.cs index 01f315e54d..62f951a909 100644 --- a/Lib9c/Module/ValidatorDelegation/ValidatorListModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorListModule.cs @@ -1,9 +1,7 @@ #nullable enable using System.Diagnostics.CodeAnalysis; using Bencodex.Types; -using Libplanet.Action.State; using Libplanet.Crypto; -using Libplanet.Types.Consensus; using Nekoyume.Action; using Nekoyume.Extensions; using Nekoyume.ValidatorDelegation; @@ -12,19 +10,19 @@ namespace Nekoyume.Module.ValidatorDelegation { public static class ValidatorListModule { - public static ProposerInfo GetProposerInfo(this IWorldState worldState) - => worldState.TryGetProposerInfo(ProposerInfo.Address, out var proposerInfo) + public static ProposerInfo GetProposerInfo(this ValidatorRepository repository) + => repository.TryGetProposerInfo(ProposerInfo.Address, out var proposerInfo) ? proposerInfo : throw new FailedLoadStateException("There is no such proposer info"); public static bool TryGetProposerInfo( - this IWorldState worldState, + this ValidatorRepository repository, Address address, [NotNullWhen(true)] out ProposerInfo? proposerInfo) { try { - var value = worldState.GetAccountState(Addresses.ValidatorList).GetState(address); + var value = repository.World.GetAccountState(Addresses.ValidatorList).GetState(address); if (!(value is List list)) { proposerInfo = null; @@ -41,25 +39,28 @@ public static bool TryGetProposerInfo( } } - public static IWorld SetProposerInfo( - this IWorld world, + public static ValidatorRepository SetProposerInfo( + this ValidatorRepository repository, ProposerInfo proposerInfo) - => world.MutateAccount( + { + repository.UpdateWorld(repository.World.MutateAccount( Addresses.ValidatorList, - state => state.SetState(ProposerInfo.Address, proposerInfo.Bencoded)); + state => state.SetState(ProposerInfo.Address, proposerInfo.Bencoded))); + return repository; + } - public static ValidatorList GetValidatorList(this IWorldState worldState) - => worldState.TryGetValidatorList(out var validatorList) + public static ValidatorList GetValidatorList(this ValidatorRepository repository) + => repository.TryGetValidatorList(out var validatorList) ? validatorList : throw new FailedLoadStateException("There is no such validator list"); public static bool TryGetValidatorList( - this IWorldState worldState, + this ValidatorRepository repository, [NotNullWhen(true)] out ValidatorList? validatorList) { try { - var value = worldState.GetAccountState(Addresses.ValidatorList).GetState(ValidatorList.Address); + var value = repository.World.GetAccountState(Addresses.ValidatorList).GetState(ValidatorList.Address); if (!(value is List list)) { validatorList = null; diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorUnbondingModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorUnbondingModule.cs new file mode 100644 index 0000000000..7ce6fd0c45 --- /dev/null +++ b/Lib9c/Module/ValidatorDelegation/ValidatorUnbondingModule.cs @@ -0,0 +1,41 @@ +using System; +using Libplanet.Action; +using Nekoyume.Delegation; +using Nekoyume.ValidatorDelegation; + +namespace Nekoyume.Module.ValidatorDelegation +{ + public static class ValidatorUnbondingModule + { + public static ValidatorRepository ReleaseUnbondings( + this ValidatorRepository repository, IActionContext context) + { + var unbondingSet = repository.GetUnbondingSet(); + var unbondings = unbondingSet.UnbondingsToRelease(context.BlockIndex); + + IUnbonding released; + foreach (var unbonding in unbondings) + { + released = unbonding.Release(context.BlockIndex); + + switch (released) + { + case UnbondLockIn unbondLockIn: + repository.SetUnbondLockIn(unbondLockIn); + break; + case RebondGrace rebondGrace: + repository.SetRebondGrace(rebondGrace); + break; + default: + throw new ArgumentException("Invalid unbonding type."); + } + + unbondingSet = unbondingSet.SetUnbonding(released); + } + + repository.SetUnbondingSet(unbondingSet); + + return repository; + } + } +} diff --git a/Lib9c/PolicyAction/Tx/Begin/AutoJoinGuild.cs b/Lib9c/PolicyAction/Tx/Begin/AutoJoinGuild.cs index b1d7f4da52..0381f31805 100644 --- a/Lib9c/PolicyAction/Tx/Begin/AutoJoinGuild.cs +++ b/Lib9c/PolicyAction/Tx/Begin/AutoJoinGuild.cs @@ -6,6 +6,7 @@ using Nekoyume.Action.Guild.Migration; using Nekoyume.Action.Guild.Migration.Controls; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Serilog; namespace Nekoyume.PolicyAction.Tx.Begin @@ -31,11 +32,12 @@ public override IWorld Execute(IActionContext context) } var world = context.PreviousState; + var repository = new GuildRepository(world, context); var signer = context.GetAgentAddress(); try { - return GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(world, signer); + GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(repository, signer); } catch (GuildMigrationFailedException guildMigrationFailedException) { @@ -52,7 +54,7 @@ public override IWorld Execute(IActionContext context) e.Message); } - return world; + return repository.World; } } } diff --git a/Lib9c/ValidatorDelegation/AbstainHistory.cs b/Lib9c/ValidatorDelegation/AbstainHistory.cs new file mode 100644 index 0000000000..c90a987361 --- /dev/null +++ b/Lib9c/ValidatorDelegation/AbstainHistory.cs @@ -0,0 +1,93 @@ +#nullable enable +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Bencodex.Types; +using Libplanet.Crypto; + +namespace Nekoyume.ValidatorDelegation +{ + public sealed class AbstainHistory + { + private static readonly Comparer Comparer + = Comparer.Create((x, y) => x.Address.CompareTo(y.Address)); + + public AbstainHistory() + { + History = new SortedDictionary>(Comparer); + } + + public AbstainHistory(IValue bencoded) + : this((List)bencoded) + { + } + + public AbstainHistory(List bencoded) + { + History = new SortedDictionary>(Comparer); + foreach (var item in bencoded) + { + var list = (List)item; + var publicKey = new PublicKey(((Binary)list[0]).ToArray()); + var history = new List(); + foreach (var height in (List)list[1]) + { + history.Add((Integer)height); + } + History.Add(publicKey, history); + } + } + + public SortedDictionary> History { get; private set; } + + public static int WindowSize => 10; + + public static int MaxAbstainAllowance => 3; + + public static Address Address => new Address( + ImmutableArray.Create( + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41)); + + public IValue Bencoded + => new List( + History.Select(pair => + List.Empty + .Add(pair.Key.Format(true)) + .Add(new List(pair.Value)))); + + + public List FindToSlashAndAdd(IEnumerable abstainList, long height) + { + var lowerBound = height - WindowSize; + var toSlashList = new List(); + foreach (var abstain in abstainList) + { + if (History.TryGetValue(abstain, out var history)) + { + history.Add(height); + if (history.Count(abstainHeight => abstainHeight > lowerBound) > MaxAbstainAllowance) + { + toSlashList.Add(abstain); + History.Remove(abstain); + } + } + else + { + History.Add(abstain, new List() { height }); + } + } + + foreach (var history in History) + { + history.Value.RemoveAll(abstainHeight => abstainHeight < lowerBound); + if (history.Value.Count == 0) + { + History.Remove(history.Key); + } + } + + return toSlashList; + } + } +} diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index 2e7e6bb53d..c50b33aa40 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -12,66 +12,70 @@ namespace Nekoyume.ValidatorDelegation { - public class ValidatorDelegatee : Delegatee, IEquatable, IBencodable + public sealed class ValidatorDelegatee + : Delegatee, IEquatable, IBencodable { - public ValidatorDelegatee(Address address, PublicKey publicKey, Currency rewardCurrency, ValidatorRepository? repository = null) - : base(address, repository) + public ValidatorDelegatee( + Address address, + PublicKey publicKey, + Currency rewardCurrency, + ValidatorRepository repository) + : base( + address: address, + accountAddress: repository.DelegateeAccountAddress, + delegationCurrency: ValidatorDelegationCurrency, + rewardCurrency: rewardCurrency, + delegationPoolAddress: UnbondedPoolAddress, + unbondingPeriod: ValidatorUnbondingPeriod, + maxUnbondLockInEntries: ValidatorMaxUnbondLockInEntries, + maxRebondGraceEntries: ValidatorMaxRebondGraceEntries, + repository: repository) { if (!address.Equals(publicKey.Address)) { throw new ArgumentException("The address and the public key do not match."); } - Publickey = publicKey; + PublicKey = publicKey; IsBonded = false; - RewardCurrency = rewardCurrency; + DelegationChanged += OnDelegationChanged; + Enjailed += OnEnjailed; + Unjailed += OnUnjailed; } - public ValidatorDelegatee(Address address, IValue bencoded, Currency rewardCurrency, ValidatorRepository? repository = null) - : this(address, (List)bencoded, rewardCurrency, repository) + public ValidatorDelegatee( + Address address, + IValue bencoded, + ValidatorRepository repository) + : this( + address: address, + bencoded: (List)bencoded, + repository: repository) { } - public ValidatorDelegatee(Address address, List bencoded, Currency rewardCurrency, ValidatorRepository? repository = null) - : base(address, bencoded[0], repository) + public ValidatorDelegatee( + Address address, + List bencoded, + ValidatorRepository repository) + : base( + address: address, + repository: repository) { - Publickey = new PublicKey(((Binary)bencoded[1]).ByteArray); - IsBonded = (Bencodex.Types.Boolean)bencoded[2]; - RewardCurrency = rewardCurrency; + PublicKey = new PublicKey(((Binary)bencoded[0]).ByteArray); + IsBonded = (Bencodex.Types.Boolean)bencoded[1]; + DelegationChanged += OnDelegationChanged; } - public override byte[] DelegateeId => new byte[] { 0x56 }; // `V` + public static Currency ValidatorDelegationCurrency => Currencies.GuildGold; - public override Address DelegationPoolAddress => IsBonded - ? BondedPoolAddress - : UnbondedPoolAddress; + public static long ValidatorUnbondingPeriod => 0L; - public override Currency DelegationCurrency => Currencies.GuildGold; + public static int ValidatorMaxUnbondLockInEntries => 10; - public override Currency RewardCurrency { get; } + public static int ValidatorMaxRebondGraceEntries => 10; - public override long UnbondingPeriod => 0L; - - public override int MaxUnbondLockInEntries => 10; - - public override int MaxRebondGraceEntries => 10; - - public override BigInteger SlashFactor => 10; - - public override List Bencoded => List.Empty - .Add(base.Bencoded) - .Add(Publickey.Format(true)) - .Add(IsBonded); - - IValue IBencodable.Bencoded => Bencoded; - - public PublicKey Publickey { get; } - - public bool IsBonded { get; private set; } - - public BigInteger Power => TotalDelegated.RawValue; - - public Validator Validator => new(Publickey, Power); + public static FungibleAssetValue MinSelfDelegation => ValidatorDelegationCurrency * 10; public static BigInteger BaseProposerRewardNumerator => 1; @@ -89,28 +93,19 @@ public ValidatorDelegatee(Address address, List bencoded, Currency rewardCurrenc public static double CommissionMaxChangeRate => 0.01; - public override BigInteger Bond(ValidatorDelegator delegator, FungibleAssetValue fav, long height) - { - BigInteger share = base.Bond(delegator, fav, height); - ValidatorRepository repo = (ValidatorRepository)Repository!; - repo.SetValidatorList(repo.GetValidatorList().SetValidator(Validator)); - return share; - } + public List Bencoded => List.Empty + .Add(PublicKey.Format(true)) + .Add(IsBonded); - public override FungibleAssetValue Unbond(ValidatorDelegator delegator, BigInteger share, long height) - { - FungibleAssetValue fav = base.Unbond(delegator, share, height); - ValidatorRepository repo = (ValidatorRepository)Repository!; + IValue IBencodable.Bencoded => Bencoded; - if (Validator.Power.IsZero) - { - repo.SetValidatorList(repo.GetValidatorList().RemoveValidator(Validator.PublicKey)); - return fav; - } + public PublicKey PublicKey { get; } - repo.SetValidatorList(repo.GetValidatorList().SetValidator(Validator)); - return fav; - } + public bool IsBonded { get; private set; } + + public BigInteger Power => TotalDelegated.RawValue; + + public Validator Validator => new(PublicKey, Power); public void AllocateReward( FungibleAssetValue rewardToAllocate, @@ -119,7 +114,7 @@ public void AllocateReward( Address RewardSource, long height) { - ValidatorRepository repo = (ValidatorRepository)Repository!; + ValidatorRepository repository = (ValidatorRepository)Repository; FungibleAssetValue rewardAllocated = (rewardToAllocate * validatorPower).DivRem(validatorSetPower).Quotient; @@ -127,24 +122,62 @@ FungibleAssetValue commission = (rewardAllocated * CommissionNumerator).DivRem(CommissionDenominator).Quotient; FungibleAssetValue delegationRewards = rewardAllocated - commission; - repo.TransferAsset(RewardSource, Address, commission); - repo.TransferAsset(RewardSource, RewardCollectorAddress, delegationRewards); + repository.TransferAsset(RewardSource, Address, commission); + repository.TransferAsset(RewardSource, RewardCollectorAddress, delegationRewards); CollectRewards(height); } + public void OnDelegationChanged(object? sender, long height) + { + ValidatorRepository repository = (ValidatorRepository)Repository; + + if (Jailed) + { + return; + } + + if (Validator.Power.IsZero) + { + repository.SetValidatorList(repository.GetValidatorList().RemoveValidator(Validator.PublicKey)); + } + else + { + repository.SetValidatorList(repository.GetValidatorList().SetValidator(Validator)); + } + + var selfDelegation = FAVFromShare(repository.GetBond(this, Address).Share); + if (MinSelfDelegation > selfDelegation) + { + Jail(height, height); + } + } + + public void OnEnjailed(object? sender, long height) + { + ValidatorRepository repository = (ValidatorRepository)Repository; + repository.SetValidatorList(repository.GetValidatorList().RemoveValidator(Validator.PublicKey)); + } + + public void OnUnjailed(object? sender, long height) + { + ValidatorRepository repository = (ValidatorRepository)Repository; + repository.SetValidatorList(repository.GetValidatorList().SetValidator(Validator)); + } + public bool Equals(ValidatorDelegatee? other) - => base.Equals(other) - && Publickey.Equals(other.Publickey) - && IsBonded == other.IsBonded; + => other is ValidatorDelegatee validatorDelegatee + && Metadata.Equals(validatorDelegatee.Metadata) + && PublicKey.Equals(validatorDelegatee.PublicKey) + && IsBonded == validatorDelegatee.IsBonded; - public override bool Equals(IDelegatee? other) + public bool Equals(IDelegatee? other) => Equals(other as ValidatorDelegatee); public override bool Equals(object? obj) => Equals(obj as ValidatorDelegatee); public override int GetHashCode() - => base.GetHashCode(); + => HashCode.Combine(Address, AccountAddress); public static Address BondedPoolAddress => new Address( ImmutableArray.Create( diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegator.cs b/Lib9c/ValidatorDelegation/ValidatorDelegator.cs index 9e9f6ec0d5..bdaf71f9e2 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegator.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegator.cs @@ -1,25 +1,34 @@ #nullable enable -using Bencodex.Types; +using System; using Libplanet.Crypto; using Nekoyume.Delegation; namespace Nekoyume.ValidatorDelegation { - public class ValidatorDelegator : Delegator + public sealed class ValidatorDelegator : Delegator, IEquatable { - public ValidatorDelegator(Address address, ValidatorRepository? repository = null) - : base(address, repository) + public ValidatorDelegator( + Address address, + Address delegationPoolAddress, + ValidatorRepository repository) + : base( + address: address, + accountAddress: repository.DelegatorAccountAddress, + delegationPoolAddress: delegationPoolAddress, + repository: repository) { } - public ValidatorDelegator(Address address, IValue bencoded, ValidatorRepository? repository = null) - : base(address, bencoded, repository) + public ValidatorDelegator( + Address address, + ValidatorRepository repository) + : base( + address: address, + repository: repository) { } - public ValidatorDelegator(Address address, List bencoded, ValidatorRepository? repository = null) - : base(address, bencoded, repository) - { - } + public bool Equals(ValidatorDelegator? other) + => Metadata.Equals(other?.Metadata); } } diff --git a/Lib9c/ValidatorDelegation/ValidatorList.cs b/Lib9c/ValidatorDelegation/ValidatorList.cs index a2348d4c75..cea8741dd6 100644 --- a/Lib9c/ValidatorDelegation/ValidatorList.cs +++ b/Lib9c/ValidatorDelegation/ValidatorList.cs @@ -9,7 +9,7 @@ namespace Nekoyume.ValidatorDelegation { - public class ValidatorList : IBencodable + public sealed class ValidatorList : IBencodable { private static readonly IComparer _reversedComparer = Comparer.Create((y, x) => new ValidatorComparer().Compare(x, y)); diff --git a/Lib9c/ValidatorDelegation/ValidatorRepository.cs b/Lib9c/ValidatorDelegation/ValidatorRepository.cs index 35cc3949e5..498893aa24 100644 --- a/Lib9c/ValidatorDelegation/ValidatorRepository.cs +++ b/Lib9c/ValidatorDelegation/ValidatorRepository.cs @@ -1,21 +1,32 @@ #nullable enable -using Libplanet.Action.State; -using Libplanet.Action; -using Nekoyume.Delegation; using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.State; using Libplanet.Crypto; -using static Nekoyume.Model.WorldInformation; +using Nekoyume.Action; +using Nekoyume.Delegation; namespace Nekoyume.ValidatorDelegation { - public class ValidatorRepository : DelegationRepository + public sealed class ValidatorRepository : DelegationRepository { private readonly Address validatorListAddress = Addresses.ValidatorList; private IAccount _validatorList; public ValidatorRepository(IWorld world, IActionContext context) - : base(world, context) + : base( + world, + context, + Addresses.ValidatorDelegatee, + Addresses.ValidatorDelegator, + Addresses.ValidatorDelegateeMetadata, + Addresses.ValidatorDelegatorMetadata, + Addresses.ValidatorBond, + Addresses.ValidatorUnbondLockIn, + Addresses.ValidatorRebondGrace, + Addresses.ValidatorUnbondingSet, + Addresses.ValidatorLumpSumRewardsRecord) { _validatorList = world.GetAccount(validatorListAddress); } @@ -23,6 +34,32 @@ public ValidatorRepository(IWorld world, IActionContext context) public override IWorld World => base.World .SetAccount(validatorListAddress, _validatorList); + public ValidatorDelegatee GetValidatorDelegatee(Address address) + => delegateeAccount.GetState(address) is IValue bencoded + ? new ValidatorDelegatee( + address, + bencoded, + this) + : throw new FailedLoadStateException("Delegatee does not exist."); + + public override IDelegatee GetDelegatee(Address address) + => GetValidatorDelegatee(address); + + public ValidatorDelegator GetValidatorDelegator(Address address) + { + try + { + return new ValidatorDelegator(address, this); + } + catch (FailedLoadStateException) + { + return new ValidatorDelegator(address, address, this); + } + } + + public override IDelegator GetDelegator(Address address) + => GetValidatorDelegator(address); + public ValidatorList GetValidatorList() { IValue? value = _validatorList.GetState(ValidatorList.Address); @@ -31,9 +68,34 @@ public ValidatorList GetValidatorList() : new ValidatorList(); } + public void SetValidatorDelegatee(ValidatorDelegatee validatorDelegatee) + { + delegateeAccount = delegateeAccount.SetState( + validatorDelegatee.Address, validatorDelegatee.Bencoded); + SetDelegateeMetadata(validatorDelegatee.Metadata); + } + + public override void SetDelegatee(IDelegatee delegatee) + => SetValidatorDelegatee((ValidatorDelegatee)delegatee); + + public void SetValidatorDelegator(ValidatorDelegator validatorDelegator) + { + SetDelegatorMetadata(validatorDelegator.Metadata); + } + + public override void SetDelegator(IDelegator delegator) + => SetValidatorDelegator((ValidatorDelegator)delegator); + public void SetValidatorList(ValidatorList validatorList) { - _validatorList = _validatorList.SetState(ValidatorList.Address, validatorList.Bencoded); + _validatorList = _validatorList.SetState( + ValidatorList.Address, validatorList.Bencoded); + } + + public override void UpdateWorld(IWorld world) + { + base.UpdateWorld(world); + _validatorList = world.GetAccount(validatorListAddress); } } } From a68ba984d0400910795620e18d8e580272f83dfe Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 16 Sep 2024 00:51:59 +0900 Subject: [PATCH 038/206] test: fix tests --- .../Guild/AcceptGuildApplicationTest.cs | 3 +- .Lib9c.Tests/Action/Guild/ApplyGuildTest.cs | 7 ++-- .../Action/Guild/BanGuildMemberTest.cs | 11 +++--- .../Migration/MigratePledgeToGuildTest.cs | 16 +++++---- .Lib9c.Tests/Action/RewardGoldTest.cs | 2 +- .Lib9c.Tests/Delegation/DelegateeTest.cs | 16 ++++----- .Lib9c.Tests/Delegation/DelegationFixture.cs | 26 +++++++++----- .Lib9c.Tests/Delegation/DelegatorTest.cs | 24 ++++++------- .Lib9c.Tests/Delegation/DummyDelegatee.cs | 20 +++++------ .Lib9c.Tests/Delegation/DummyRepository.cs | 2 +- ...GuildParticipantTest.Snapshot.received.txt | 34 +++++++++++++++++++ .../Model/Guild/GuildParticipantTest.cs | 1 + .../Guild/GuildTest.Snapshot.received.txt | 34 +++++++++++++++++++ .Lib9c.Tests/Model/Guild/GuildTest.cs | 1 + Lib9c/Model/Guild/GuildRepository.cs | 5 --- 15 files changed, 142 insertions(+), 60 deletions(-) create mode 100644 .Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.received.txt create mode 100644 .Lib9c.Tests/Model/Guild/GuildTest.Snapshot.received.txt diff --git a/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs b/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs index f2acb87aa4..5b5663b795 100644 --- a/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs +++ b/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs @@ -44,6 +44,7 @@ public void Execute() var repository = new GuildRepository(world, new ActionContext()); repository.MakeGuild(guildAddress, guildMasterAddress); repository.ApplyGuild(appliedMemberAddress, guildAddress); + world = repository.World; // These cases should fail because the member didn't apply the guild and // non-guild-master-addresses cannot accept the guild application. @@ -86,7 +87,7 @@ public void Execute() Signer = guildMasterAddress, }); - repository = new GuildRepository(world, new ActionContext()); + repository.UpdateWorld(world); Assert.False(repository.TryGetGuildApplication(appliedMemberAddress, out _)); Assert.Equal(guildAddress, repository.GetJoinedGuild(appliedMemberAddress)); diff --git a/.Lib9c.Tests/Action/Guild/ApplyGuildTest.cs b/.Lib9c.Tests/Action/Guild/ApplyGuildTest.cs index 6979a335f7..07c0c831dc 100644 --- a/.Lib9c.Tests/Action/Guild/ApplyGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/ApplyGuildTest.cs @@ -43,18 +43,19 @@ public void Execute() var repository = new GuildRepository(world, new ActionContext()); repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, guildMasterAddress); - repository.Ban(guildAddress, guildMasterAddress, agentAddress); + var bannedRepository = new GuildRepository(repository.World, new ActionContext()); + bannedRepository.Ban(guildAddress, guildMasterAddress, agentAddress); // This case should fail because the agent is banned by the guild. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = repository.World, + PreviousState = bannedRepository.World, Signer = agentAddress, })); world = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = agentAddress, }); diff --git a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs index 133d7296a3..385a0e4c9c 100644 --- a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs +++ b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs @@ -63,11 +63,12 @@ public void Ban_By_GuildMaster() var action = new BanGuildMember(guildMemberAddress); world = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMasterAddress, }); // Guild + repository.UpdateWorld(world); Assert.False(repository.IsBanned(guildAddress, guildMasterAddress)); Assert.Equal(guildAddress, repository.GetJoinedGuild(guildMasterAddress)); Assert.True(repository.IsBanned(guildAddress, guildMemberAddress)); @@ -81,11 +82,12 @@ public void Ban_By_GuildMaster() action = new BanGuildMember(otherGuildMasterAddress); world = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMasterAddress, }); // Guild + repository.UpdateWorld(world); Assert.False(repository.IsBanned(guildAddress, guildMasterAddress)); Assert.Equal(guildAddress, repository.GetJoinedGuild(guildMasterAddress)); Assert.True(repository.IsBanned(guildAddress, guildMemberAddress)); @@ -99,11 +101,12 @@ public void Ban_By_GuildMaster() action = new BanGuildMember(otherGuildMemberAddress); world = action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMasterAddress, }); // Guild + repository.UpdateWorld(world); Assert.False(repository.IsBanned(guildAddress, guildMasterAddress)); Assert.Equal(guildAddress, repository.GetJoinedGuild(guildMasterAddress)); Assert.True(repository.IsBanned(guildAddress, guildMemberAddress)); @@ -118,7 +121,7 @@ public void Ban_By_GuildMaster() // GuildMaster cannot ban itself. Assert.Throws(() => action.Execute(new ActionContext { - PreviousState = world, + PreviousState = repository.World, Signer = guildMasterAddress, })); } diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs index 48a49675cc..8c8eef9d16 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs @@ -52,7 +52,9 @@ public void Execute_When_WithPledgeContract() var repository = new GuildRepository(world, new ActionContext()); repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, guildMasterAddress); - repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( + repository.UpdateWorld(repository.World.SetLegacyState( + pledgeAddress, + new List( MeadConfig.PatronAddress.Serialize(), true.Serialize(), RequestPledge.DefaultRefillMead.Serialize()))); @@ -67,9 +69,9 @@ public void Execute_When_WithPledgeContract() Signer = caller, }); - repository.UpdateWorld(newWorld); - var joinedGuildAddress = Assert.IsType(repository.GetJoinedGuild(target)); - Assert.True(repository.TryGetGuild(joinedGuildAddress, out var guild)); + var newRepository = new GuildRepository(newWorld, new ActionContext()); + var joinedGuildAddress = Assert.IsType(newRepository.GetJoinedGuild(target)); + Assert.True(newRepository.TryGetGuild(joinedGuildAddress, out var guild)); Assert.Equal(GuildConfig.PlanetariumGuildOwner, guild.GuildMasterAddress); // Migrate by itself. @@ -79,9 +81,9 @@ public void Execute_When_WithPledgeContract() Signer = target, }); - repository.UpdateWorld(newWorld); - joinedGuildAddress = Assert.IsType(repository.GetJoinedGuild(target)); - Assert.True(repository.TryGetGuild(joinedGuildAddress, out guild)); + newRepository.UpdateWorld(newWorld); + joinedGuildAddress = Assert.IsType(newRepository.GetJoinedGuild(target)); + Assert.True(newRepository.TryGetGuild(joinedGuildAddress, out guild)); Assert.Equal(GuildConfig.PlanetariumGuildOwner, guild.GuildMasterAddress); } diff --git a/.Lib9c.Tests/Action/RewardGoldTest.cs b/.Lib9c.Tests/Action/RewardGoldTest.cs index 63df7d4cf9..4fdce90e2e 100644 --- a/.Lib9c.Tests/Action/RewardGoldTest.cs +++ b/.Lib9c.Tests/Action/RewardGoldTest.cs @@ -467,7 +467,7 @@ void AssertMinerReward(int blockIndex, string expected) { ctx.BlockIndex = blockIndex; IWorld delta = action.MinerReward(ctx, _baseState); - Assert.Equal(FungibleAssetValue.Parse(currency, expected), delta.GetBalance(miner, currency)); + Assert.Equal(FungibleAssetValue.Parse(currency, expected), delta.GetBalance(Addresses.RewardPool, currency)); } // Before halving (10 / 2^0 = 10) diff --git a/.Lib9c.Tests/Delegation/DelegateeTest.cs b/.Lib9c.Tests/Delegation/DelegateeTest.cs index c7931dd20a..a8edff2fe9 100644 --- a/.Lib9c.Tests/Delegation/DelegateeTest.cs +++ b/.Lib9c.Tests/Delegation/DelegateeTest.cs @@ -20,7 +20,7 @@ public DelegateeTest() public void Ctor() { var address = new Address("0xe8327129891e1A0B2E3F0bfa295777912295942a"); - var delegatee = new TestDelegatee(address, _fixture.Repository); + var delegatee = new TestDelegatee(address, _fixture.TestRepository.DelegateeAccountAddress, _fixture.TestRepository); Assert.Equal(address, delegatee.Address); Assert.Equal(DelegationFixture.TestCurrency, delegatee.DelegationCurrency); Assert.Equal(3, delegatee.UnbondingPeriod); @@ -29,7 +29,7 @@ public void Ctor() [Fact] public void GetSet() { - var repo = _fixture.Repository; + var repo = _fixture.TestRepository; var delegatee = _fixture.TestDelegatee1; var delegator = _fixture.TestDelegator1; delegatee.Bond(delegator, delegatee.DelegationCurrency * 10, 10L); @@ -66,7 +66,7 @@ public void Bond() totalBonding += bonding; var bondedShare = testDelegatee.Bond(testDelegator1, bonding, 10L); - var bondedShare1 = _fixture.Repository.GetBond(testDelegatee, testDelegator1.Address).Share; + var bondedShare1 = _fixture.TestRepository.GetBond(testDelegatee, testDelegator1.Address).Share; Assert.Equal(testDelegator1.Address, Assert.Single(testDelegatee.Delegators)); Assert.Equal(share, bondedShare); Assert.Equal(share1, bondedShare1); @@ -79,7 +79,7 @@ public void Bond() totalShare += share; totalBonding += bonding; bondedShare = testDelegatee.Bond(testDelegator1, bonding, 20L); - bondedShare1 = _fixture.Repository.GetBond(testDelegatee, testDelegator1.Address).Share; + bondedShare1 = _fixture.TestRepository.GetBond(testDelegatee, testDelegator1.Address).Share; Assert.Equal(testDelegator1.Address, Assert.Single(testDelegatee.Delegators)); Assert.Equal(share, bondedShare); Assert.Equal(share1, bondedShare1); @@ -92,7 +92,7 @@ public void Bond() totalShare += share; totalBonding += bonding; bondedShare = testDelegatee.Bond(testDelegator2, bonding, 30L); - var bondedShare2 = _fixture.Repository.GetBond(testDelegatee, testDelegator2.Address).Share; + var bondedShare2 = _fixture.TestRepository.GetBond(testDelegatee, testDelegator2.Address).Share; Assert.Equal(2, testDelegatee.Delegators.Count); Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); Assert.Contains(testDelegator2.Address, testDelegatee.Delegators); @@ -159,7 +159,7 @@ public void Unbond() var unbondingFAV = testDelegatee.FAVFromShare(unbonding); totalDelegated -= unbondingFAV; var unbondedFAV = testDelegatee.Unbond(testDelegator1, unbonding, 3L); - var shareAfterUnbond = _fixture.Repository.GetBond(testDelegatee, testDelegator1.Address).Share; + var shareAfterUnbond = _fixture.TestRepository.GetBond(testDelegatee, testDelegator1.Address).Share; Assert.Equal(2, testDelegatee.Delegators.Count); Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); Assert.Contains(testDelegator2.Address, testDelegatee.Delegators); @@ -174,7 +174,7 @@ public void Unbond() unbondingFAV = testDelegatee.FAVFromShare(unbonding); totalDelegated -= unbondingFAV; unbondedFAV = testDelegatee.Unbond(testDelegator2, unbonding, 4L); - shareAfterUnbond = _fixture.Repository.GetBond(testDelegatee, testDelegator2.Address).Share; + shareAfterUnbond = _fixture.TestRepository.GetBond(testDelegatee, testDelegator2.Address).Share; Assert.Equal(2, testDelegatee.Delegators.Count); Assert.Contains(testDelegator1.Address, testDelegatee.Delegators); Assert.Contains(testDelegator2.Address, testDelegatee.Delegators); @@ -187,7 +187,7 @@ public void Unbond() unbondingFAV = testDelegatee.FAVFromShare(share1); totalDelegated -= unbondingFAV; unbondedFAV = testDelegatee.Unbond(testDelegator1, share1, 5L); - shareAfterUnbond = _fixture.Repository.GetBond(testDelegatee, testDelegator1.Address).Share; + shareAfterUnbond = _fixture.TestRepository.GetBond(testDelegatee, testDelegator1.Address).Share; Assert.Equal(testDelegator2.Address, Assert.Single(testDelegatee.Delegators)); Assert.Equal(unbondingFAV, unbondedFAV); Assert.Equal(BigInteger.Zero, shareAfterUnbond); diff --git a/.Lib9c.Tests/Delegation/DelegationFixture.cs b/.Lib9c.Tests/Delegation/DelegationFixture.cs index 7e99291246..7f9b712514 100644 --- a/.Lib9c.Tests/Delegation/DelegationFixture.cs +++ b/.Lib9c.Tests/Delegation/DelegationFixture.cs @@ -28,16 +28,26 @@ public DelegationFixture() BlockProtocolVersion = BlockMetadata.CurrentProtocolVersion, }; - Repository = new TestRepository(world, context); - TestDelegator1 = new TestDelegator(new Address("0x0054E98312C47E7Fa0ABed45C23Fa187e31C373a"), Repository); - TestDelegator2 = new TestDelegator(new Address("0x327CCff388255E9399207C3d5a09357D0BBc73dF"), Repository); - TestDelegatee1 = new TestDelegatee(new Address("0x67A44E11506b8f0Bb625fEECccb205b33265Bb48"), Repository); - TestDelegatee2 = new TestDelegatee(new Address("0xea1C4eedEfC99691DEfc6eF2753FAfa8C17F4584"), Repository); - DummyDelegatee1 = new DummyDelegatee(new Address("0x67A44E11506b8f0Bb625fEECccb205b33265Bb48"), Repository); - DummyDelegator1 = new DummyDelegator(new Address("0x0054E98312C47E7Fa0ABed45C23Fa187e31C373a"), Repository); + TestRepository = new TestRepository(world, context); + TestDelegator1 = new TestDelegator( + new Address("0x0054E98312C47E7Fa0ABed45C23Fa187e31C373a"), TestRepository.DelegatorAccountAddress, TestRepository); + TestDelegator2 = new TestDelegator( + new Address("0x327CCff388255E9399207C3d5a09357D0BBc73dF"), TestRepository.DelegatorAccountAddress, TestRepository); + TestDelegatee1 = new TestDelegatee( + new Address("0x67A44E11506b8f0Bb625fEECccb205b33265Bb48"), TestRepository.DelegateeAccountAddress, TestRepository); + TestDelegatee2 = new TestDelegatee( + new Address("0xea1C4eedEfC99691DEfc6eF2753FAfa8C17F4584"), TestRepository.DelegateeAccountAddress, TestRepository); + + DummyRepository = new DummyRepository(world, context); + DummyDelegatee1 = new DummyDelegatee( + new Address("0x67A44E11506b8f0Bb625fEECccb205b33265Bb48"), DummyRepository.DelegateeAccountAddress, DummyRepository); + DummyDelegator1 = new DummyDelegator( + new Address("0x0054E98312C47E7Fa0ABed45C23Fa187e31C373a"), DummyRepository.DelegateeAccountAddress, DummyRepository); } - public TestRepository Repository { get; } + public TestRepository TestRepository { get; } + + public DummyRepository DummyRepository { get; } public TestDelegator TestDelegator1 { get; } diff --git a/.Lib9c.Tests/Delegation/DelegatorTest.cs b/.Lib9c.Tests/Delegation/DelegatorTest.cs index 3d837b414a..a0427a5c02 100644 --- a/.Lib9c.Tests/Delegation/DelegatorTest.cs +++ b/.Lib9c.Tests/Delegation/DelegatorTest.cs @@ -18,7 +18,7 @@ public DelegatorTest() public void Ctor() { var address = new Address("0x070e5719767CfB86712C31F5AB0072c48959d862"); - var delegator = new TestDelegator(address, _fixture.Repository); + var delegator = new TestDelegator(address, _fixture.TestRepository.DelegatorAccountAddress, _fixture.TestRepository); Assert.Equal(address, delegator.Address); Assert.Empty(delegator.Delegatees); } @@ -26,7 +26,7 @@ public void Ctor() [Fact] public void GetSet() { - var repo = _fixture.Repository; + var repo = _fixture.TestRepository; var delegator = _fixture.TestDelegator1; var delegatee = _fixture.TestDelegatee1; repo.MintAsset(delegator.Address, delegatee.DelegationCurrency * 100); @@ -39,7 +39,7 @@ public void GetSet() [Fact] public void Delegate() { - var repo = _fixture.Repository; + var repo = _fixture.TestRepository; var delegator = _fixture.TestDelegator1; var delegatee1 = _fixture.TestDelegatee1; var delegatee2 = _fixture.TestDelegatee2; @@ -91,13 +91,13 @@ public void Delegate() [Fact] public void Undelegate() { - var repo = _fixture.Repository; + var repo = _fixture.TestRepository; var delegator = _fixture.TestDelegator1; var delegatee = _fixture.TestDelegatee1; var delegatorInitialBalance = delegatee.DelegationCurrency * 100; repo.MintAsset(delegator.Address, delegatorInitialBalance); var delegatingFAV = delegatee.DelegationCurrency * 10; - delegator.Delegate(delegatee, delegatingFAV, 10L); + delegator.Delegate(delegatee, delegatingFAV, 9L); var initialShare = repo.GetBond(delegatee, delegator.Address).Share; var undelegatingShare = initialShare / 3; var undelegatingFAV = delegatee.FAVFromShare(undelegatingShare); @@ -186,7 +186,7 @@ public void Undelegate() [Fact] public void Redelegate() { - var repo = _fixture.Repository; + var repo = _fixture.TestRepository; var delegator = _fixture.TestDelegator1; var delegatee1 = _fixture.TestDelegatee1; var delegatee2 = _fixture.TestDelegatee2; @@ -289,7 +289,7 @@ public void Redelegate() [Fact] public void RewardOnDelegate() { - var repo = _fixture.Repository; + var repo = _fixture.TestRepository; var delegator1 = _fixture.TestDelegator1; var delegator2 = _fixture.TestDelegator2; var delegatee = _fixture.TestDelegatee1; @@ -345,7 +345,7 @@ public void RewardOnDelegate() [Fact] public void RewardOnUndelegate() { - var repo = _fixture.Repository; + var repo = _fixture.TestRepository; var delegator1 = _fixture.TestDelegator1; var delegator2 = _fixture.TestDelegator2; var delegatee = _fixture.TestDelegatee1; @@ -354,9 +354,9 @@ public void RewardOnUndelegate() repo.MintAsset(delegator2.Address, delegatorInitialBalance); var reward = delegatee.DelegationCurrency * 100; - repo.MintAsset(delegatee.RewardDistributorAddress, reward); + repo.MintAsset(delegatee.RewardCollectorAddress, reward); // EndBlock after delegatee's reward - repo.AddLumpSumRewards(delegatee, 10L, reward); + delegatee.CollectRewards(10L); var delegatingFAV1 = delegatee.DelegationCurrency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 10L); @@ -402,7 +402,7 @@ public void RewardOnUndelegate() [Fact] public void RewardOnRedelegate() { - var repo = _fixture.Repository; + var repo = _fixture.TestRepository; var delegator1 = _fixture.TestDelegator1; var delegator2 = _fixture.TestDelegator2; var delegatee = _fixture.TestDelegatee1; @@ -460,7 +460,7 @@ public void RewardOnRedelegate() [Fact] public void RewardOnClaim() { - var repo = _fixture.Repository; + var repo = _fixture.TestRepository; var delegator1 = _fixture.TestDelegator1; var delegator2 = _fixture.TestDelegator2; var delegatee = _fixture.TestDelegatee1; diff --git a/.Lib9c.Tests/Delegation/DummyDelegatee.cs b/.Lib9c.Tests/Delegation/DummyDelegatee.cs index 3b0bb5ea51..03c00d4feb 100644 --- a/.Lib9c.Tests/Delegation/DummyDelegatee.cs +++ b/.Lib9c.Tests/Delegation/DummyDelegatee.cs @@ -10,16 +10,16 @@ public DummyDelegatee(Address address, IDelegationRepository repository) } public DummyDelegatee(Address address, Address accountAddress, DummyRepository repository) - : base( - address, - accountAddress, - DelegationFixture.TestCurrency, - DelegationFixture.TestCurrency, - address, - 3, - 5, - 5, - repository) + : base( + address, + accountAddress, + DelegationFixture.TestCurrency, + DelegationFixture.TestCurrency, + address, + 3, + 5, + 5, + repository) { } } diff --git a/.Lib9c.Tests/Delegation/DummyRepository.cs b/.Lib9c.Tests/Delegation/DummyRepository.cs index 2fe2481f89..11af6050e1 100644 --- a/.Lib9c.Tests/Delegation/DummyRepository.cs +++ b/.Lib9c.Tests/Delegation/DummyRepository.cs @@ -13,7 +13,7 @@ public DummyRepository(IWorld world, IActionContext context) : base( world: world, context: context, - delegateeAccountAddress: new Address("1000000000000000000000000000000000000000/"), + delegateeAccountAddress: new Address("1000000000000000000000000000000000000000"), delegatorAccountAddress: new Address("1000000000000000000000000000000000000001"), delegateeMetadataAccountAddress: new Address("0000000000000000000000000000000000000002"), delegatorMetadataAccountAddress: new Address("0000000000000000000000000000000000000003"), diff --git a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.received.txt b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.received.txt new file mode 100644 index 0000000000..6f640eb219 --- /dev/null +++ b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.received.txt @@ -0,0 +1,34 @@ +[ + { + EncodingLength: 21, + Kind: Text, + Value: guild_participant + }, + { + EncodingLength: 3, + Kind: Integer, + Value: 1 + }, + [ + 217, + 40, + 174, + 135, + 49, + 29, + 234, + 212, + 144, + 201, + 134, + 194, + 76, + 194, + 60, + 55, + 239, + 248, + 146, + 242 + ] +] \ No newline at end of file diff --git a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.cs b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.cs index ba32f7ae5f..ca96122f0f 100644 --- a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.cs +++ b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.cs @@ -40,6 +40,7 @@ public void Serialization() AddressUtil.CreateAgentAddress(), AddressUtil.CreateGuildAddress(), repository); + repository.SetGuildParticipant(guildParticipant); var newGuildParticipant = new Nekoyume.Model.Guild.GuildParticipant( guildParticipant.Address, guildParticipant.Bencoded, repository); diff --git a/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.received.txt b/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.received.txt new file mode 100644 index 0000000000..4cb9594719 --- /dev/null +++ b/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.received.txt @@ -0,0 +1,34 @@ +[ + { + EncodingLength: 8, + Kind: Text, + Value: guild + }, + { + EncodingLength: 3, + Kind: Integer, + Value: 1 + }, + [ + 217, + 40, + 174, + 135, + 49, + 29, + 234, + 212, + 144, + 201, + 134, + 194, + 76, + 194, + 60, + 55, + 239, + 248, + 146, + 242 + ] +] \ No newline at end of file diff --git a/.Lib9c.Tests/Model/Guild/GuildTest.cs b/.Lib9c.Tests/Model/Guild/GuildTest.cs index d10e329c43..b0ff757ef2 100644 --- a/.Lib9c.Tests/Model/Guild/GuildTest.cs +++ b/.Lib9c.Tests/Model/Guild/GuildTest.cs @@ -44,6 +44,7 @@ public void Serialization() AddressUtil.CreateAgentAddress(), Currency.Legacy("NCG", 2, null), repository); + repository.SetGuild(guild); var newGuild = new Nekoyume.Model.Guild.Guild( guildAddress, guild.Bencoded, diff --git a/Lib9c/Model/Guild/GuildRepository.cs b/Lib9c/Model/Guild/GuildRepository.cs index f4627b3565..aba15d7d25 100644 --- a/Lib9c/Model/Guild/GuildRepository.cs +++ b/Lib9c/Model/Guild/GuildRepository.cs @@ -66,11 +66,6 @@ public void SetGuildParticipant(GuildParticipant guildParticipant) public override void SetDelegator(IDelegator delegator) => SetGuildParticipant(delegator as GuildParticipant); - public void RemoveGuild(Address guildAddress) - { - delegatorAccount = delegatorAccount.RemoveState(guildAddress); - } - public void RemoveGuildParticipant(Address guildParticipantAddress) { delegatorAccount = delegatorAccount.RemoveState(guildParticipantAddress); From a588cac648eff668aeb3a81e606b0da121684387 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 16 Sep 2024 10:13:57 +0900 Subject: [PATCH 039/206] test: revert bencoded fixtures --- ...GuildParticipantTest.Snapshot.verified.txt | 6 ---- .../Guild/GuildTest.Snapshot.verified.txt | 31 ------------------- 2 files changed, 37 deletions(-) diff --git a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.verified.txt b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.verified.txt index 9e3bf7e99a..1483b43646 100644 --- a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.verified.txt +++ b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.verified.txt @@ -30,11 +30,5 @@ 248, 146, 242 - ], - [ - [], - { - EncodingLength: 1 - } ] ] diff --git a/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt b/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt index 5617d65487..14587b1dd9 100644 --- a/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt +++ b/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt @@ -30,36 +30,5 @@ 248, 146, 242 - ], - [ - [], - [ - { - Bencodex.Types.Text "decimalPlaces": [ - 18 - ], - Bencodex.Types.Text "minters": { - EncodingLength: 1 - }, - Bencodex.Types.Text "ticker": { - EncodingLength: 14, - Kind: Text, - Value: GUILD_GOLD - }, - Bencodex.Types.Text "totalSupplyTrackable": { - EncodingLength: 1, - Kind: Boolean, - Value: true - } - }, - { - EncodingLength: 3, - Kind: Integer - } - ], - { - EncodingLength: 3, - Kind: Integer - } ] ] From 85485dcce401b7b9b2bbcf183a60bfb246ada8fa Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sat, 21 Sep 2024 10:49:35 +0900 Subject: [PATCH 040/206] fix: Fix delegation actions --- .../ValidatorDelegation/AllocateReward.cs | 34 ++++++++++--------- .../ClaimRewardValidator.cs | 1 + .../ValidatorDelegation/DelegateValidator.cs | 1 + .../ValidatorDelegation/PromoteValidator.cs | 1 + .../RedelegateValidator.cs | 3 +- .../ReleaseValidatorUnbondings.cs | 13 +------ .../ValidatorDelegation/SlashValidator.cs | 20 +++-------- .../UndelegateValidator.cs | 3 +- .../ValidatorDelegation/UnjailValidator.cs | 3 +- .../ValidatorDelegation/UpdateValidators.cs | 13 +------ 10 files changed, 33 insertions(+), 59 deletions(-) diff --git a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs index 94ad66667d..3e83ab2946 100644 --- a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs +++ b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs @@ -9,18 +9,17 @@ using Nekoyume.ValidatorDelegation; using Nekoyume.Module.ValidatorDelegation; using Nekoyume.Module; +using Libplanet.Types.Blocks; namespace Nekoyume.Action.ValidatorDelegation { public class AllocateReward : ActionBase { - public const string TypeIdentifier = "distribute_validators"; - public AllocateReward() { } - public override IValue PlainValue => Dictionary.Empty; + public override IValue PlainValue => Null.Value; public override void LoadPlainValue(IValue plainValue) { @@ -33,19 +32,22 @@ public override IWorld Execute(IActionContext context) var rewardCurrency = world.GetGoldCurrency(); var proposerInfo = repository.GetProposerInfo(); - DistributeProposerReward( - repository, - context, - rewardCurrency, - proposerInfo, - context.LastCommit.Votes); - - DistributeValidatorReward( - repository, - context, - rewardCurrency, - context.LastCommit.Votes); - + if (context.LastCommit is BlockCommit lastCommit) + { + DistributeProposerReward( + repository, + context, + rewardCurrency, + proposerInfo, + lastCommit.Votes); + + DistributeValidatorReward( + repository, + context, + rewardCurrency, + lastCommit.Votes); + } + var communityFund = repository.GetBalance(Addresses.RewardPool, rewardCurrency); if (communityFund.Sign > 0) diff --git a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs index 1e3d89eaa3..0fb110ad7d 100644 --- a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs @@ -8,6 +8,7 @@ namespace Nekoyume.Action.ValidatorDelegation { + [ActionType(TypeIdentifier)] public class ClaimRewardValidator : ActionBase { public const string TypeIdentifier = "claim_reward_validator"; diff --git a/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs index 0be03656a5..b0613a90b9 100644 --- a/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs @@ -9,6 +9,7 @@ namespace Nekoyume.Action.ValidatorDelegation { + [ActionType(TypeIdentifier)] public class DelegateValidator : ActionBase { public const string TypeIdentifier = "delegate_validator"; diff --git a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs index 3c3215e04b..d4b76ceed5 100644 --- a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs @@ -9,6 +9,7 @@ namespace Nekoyume.Action.ValidatorDelegation { + [ActionType(TypeIdentifier)] public class PromoteValidator : ActionBase { public const string TypeIdentifier = "promote_validator"; diff --git a/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs index dc94c194ba..21f5423464 100644 --- a/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs @@ -9,7 +9,8 @@ namespace Nekoyume.Action.ValidatorDelegation { - public class RedelegateValidator : ActionBase + [ActionType(TypeIdentifier)] + public sealed class RedelegateValidator : ActionBase { public const string TypeIdentifier = "redelegate_validator"; diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs index d4e125354c..5d1250c3e6 100644 --- a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -1,4 +1,3 @@ -using System; using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Action; @@ -9,26 +8,16 @@ namespace Nekoyume.Action.ValidatorDelegation { public sealed class ReleaseValidatorUnbondings : ActionBase { - public const string TypeIdentifier = "release_validator_unbondings"; - public ReleaseValidatorUnbondings() { } public ReleaseValidatorUnbondings(Address validatorDelegatee) { } - public override IValue PlainValue => Dictionary.Empty - .Add("type_id", TypeIdentifier) - .Add("values", Null.Value); + public override IValue PlainValue => Null.Value; public override void LoadPlainValue(IValue plainValue) { - if (plainValue is not Dictionary root || - !root.TryGetValue((Text)"values", out var rawValues) || - rawValues is not Null) - { - throw new InvalidCastException(); - } } public override IWorld Execute(IActionContext context) diff --git a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs index f172305afb..4807765364 100644 --- a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs @@ -3,7 +3,6 @@ using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Action; -using Libplanet.Crypto; using Libplanet.Types.Consensus; using Libplanet.Types.Evidence; using Nekoyume.ValidatorDelegation; @@ -12,13 +11,9 @@ namespace Nekoyume.Action.ValidatorDelegation { - public class SlashValidator : ActionBase + public sealed class SlashValidator : ActionBase { - public const string TypeIdentifier = "slash_validator"; - - public SlashValidator() { } - - public SlashValidator(Address validatorDelegatee) + public SlashValidator() { } @@ -28,18 +23,10 @@ public SlashValidator(Address validatorDelegatee) public static long AbstainJailTime => 10L; - public override IValue PlainValue => Dictionary.Empty - .Add("type_id", TypeIdentifier) - .Add("values", Null.Value); + public override IValue PlainValue => Null.Value; public override void LoadPlainValue(IValue plainValue) { - if (plainValue is not Dictionary root || - !root.TryGetValue((Text)"values", out var rawValues) || - rawValues is not Null) - { - throw new InvalidCastException(); - } } public override IWorld Execute(IActionContext context) @@ -54,6 +41,7 @@ public override IWorld Execute(IActionContext context) context.LastCommit.Votes.Where(vote => vote.Flag == VoteFlag.Null) .Select(vote => vote.ValidatorPublicKey), context.BlockIndex); + repository.SetAbstainHistory(abstainHistory); foreach (var abstain in abstainsToSlash) { diff --git a/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs index 1a9cf765b0..d6a4af1018 100644 --- a/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs @@ -9,7 +9,8 @@ namespace Nekoyume.Action.ValidatorDelegation { - public class UndelegateValidator : ActionBase + [ActionType(TypeIdentifier)] + public sealed class UndelegateValidator : ActionBase { public const string TypeIdentifier = "undelegate_validator"; diff --git a/Lib9c/Action/ValidatorDelegation/UnjailValidator.cs b/Lib9c/Action/ValidatorDelegation/UnjailValidator.cs index 2f485d1fad..a232dcc717 100644 --- a/Lib9c/Action/ValidatorDelegation/UnjailValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/UnjailValidator.cs @@ -6,7 +6,8 @@ namespace Nekoyume.Action.ValidatorDelegation { - public class UnjailValidator : ActionBase + [ActionType(TypeIdentifier)] + public sealed class UnjailValidator : ActionBase { public const string TypeIdentifier = "unjail_validator"; diff --git a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs index 25dbd8175f..6595be1e52 100644 --- a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs +++ b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs @@ -1,4 +1,3 @@ -using System; using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Action; @@ -9,22 +8,12 @@ namespace Nekoyume.Action.ValidatorDelegation { public sealed class UpdateValidators : ActionBase { - const string TypeIdentifier = "update_validators"; - public UpdateValidators() { } - public override IValue PlainValue => Dictionary.Empty - .Add("type_id", TypeIdentifier) - .Add("values", Null.Value); + public override IValue PlainValue => Null.Value; public override void LoadPlainValue(IValue plainValue) { - if (plainValue is not Dictionary root || - !root.TryGetValue((Text)"values", out var rawValues) || - rawValues is not Null) - { - throw new InvalidCastException(); - } } public override IWorld Execute(IActionContext context) From a567ed5ca0bd4965679dcf182ba209af1b1b8457 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sat, 21 Sep 2024 10:52:01 +0900 Subject: [PATCH 041/206] feat: Initialize with initial delegation --- .Lib9c.Tests/Action/InitializeStatesTest.cs | 22 +++++- .Lib9c.Tests/Policy/BlockPolicyTest.cs | 81 +++++++++++++++------ .Lib9c.Tests/TestHelper/BlockChainHelper.cs | 3 + Lib9c.Utils/BlockHelper.cs | 18 +---- Lib9c/Action/InitializeStates.cs | 28 ++++++- 5 files changed, 111 insertions(+), 41 deletions(-) diff --git a/.Lib9c.Tests/Action/InitializeStatesTest.cs b/.Lib9c.Tests/Action/InitializeStatesTest.cs index 7af13e7d5f..e11e96a892 100644 --- a/.Lib9c.Tests/Action/InitializeStatesTest.cs +++ b/.Lib9c.Tests/Action/InitializeStatesTest.cs @@ -9,6 +9,7 @@ namespace Lib9c.Tests.Action using Libplanet.Crypto; using Libplanet.Mocks; using Libplanet.Types.Assets; + using Libplanet.Types.Consensus; using Nekoyume; using Nekoyume.Action; using Nekoyume.Model; @@ -43,8 +44,10 @@ public void Execute() var privateKey = new PrivateKey(); (ActivationKey activationKey, PendingActivationState pendingActivation) = ActivationKey.Create(privateKey, nonce); + var validatorSet = new ValidatorSet(new List { new (privateKey.PublicKey, 10) }); var action = new InitializeStates( + validatorSet: validatorSet, rankingState: new RankingState0(), shopState: new ShopState(), tableSheets: _sheets, @@ -105,8 +108,10 @@ public void ExecuteWithAuthorizedMinersState() var privateKey = new PrivateKey(); (ActivationKey activationKey, PendingActivationState pendingActivation) = ActivationKey.Create(privateKey, nonce); + var validatorSet = new ValidatorSet(new List { new (privateKey.PublicKey, 10) }); var action = new InitializeStates( + validatorSet: validatorSet, rankingState: new RankingState0(), shopState: new ShopState(), tableSheets: _sheets, @@ -162,8 +167,10 @@ public void ExecuteWithActivateAdminKey() (ActivationKey activationKey, PendingActivationState pendingActivation) = ActivationKey.Create(privateKey, nonce); var adminAddress = new Address("F9A15F870701268Bd7bBeA6502eB15F4997f32f9"); + var validatorSet = new ValidatorSet(new List { new (privateKey.PublicKey, 10) }); var action = new InitializeStates( + validatorSet: validatorSet, rankingState: new RankingState0(), shopState: new ShopState(), tableSheets: _sheets, @@ -213,8 +220,10 @@ public void ExecuteWithCredits() "山田太郎", } ); + var validatorSet = new ValidatorSet(new List { new (minterKey.PublicKey, 10) }); var action = new InitializeStates( + validatorSet: validatorSet, rankingState: new RankingState0(), shopState: new ShopState(), tableSheets: _sheets, @@ -260,8 +269,10 @@ public void ExecuteWithoutAdminState() var privateKey = new PrivateKey(); (ActivationKey activationKey, PendingActivationState pendingActivation) = ActivationKey.Create(privateKey, nonce); + var validatorSet = new ValidatorSet(new List { new (privateKey.PublicKey, 10) }); var action = new InitializeStates( + validatorSet: validatorSet, rankingState: new RankingState0(), shopState: new ShopState(), tableSheets: _sheets, @@ -303,8 +314,10 @@ public void ExecuteWithoutInitialSupply() #pragma warning restore CS0618 var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; var privateKey = new PrivateKey(); + var validatorSet = new ValidatorSet(new List { new (privateKey.PublicKey, 10) }); var action = new InitializeStates( + validatorSet: validatorSet, rankingState: new RankingState0(), shopState: new ShopState(), tableSheets: _sheets, @@ -317,15 +330,14 @@ public void ExecuteWithoutInitialSupply() pendingActivationStates: Array.Empty() ); - var genesisState = action.Execute(new ActionContext() + // Cannot execute InitializeStates without initial supply, due to validator delegation. + Assert.Throws(() => action.Execute(new ActionContext() { BlockIndex = 0, Miner = default, Signer = minterKey.Address, PreviousState = new World(MockUtil.MockModernWorldState), - }); - - Assert.Equal(0 * ncg, genesisState.GetBalance(GoldCurrencyState.Address, ncg)); + })); } [Fact] @@ -343,8 +355,10 @@ public void ExecuteWithAssetMinters() #pragma warning restore CS0618 var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; var adminAddress = new Address("F9A15F870701268Bd7bBeA6502eB15F4997f32f9"); + var validatorSet = new ValidatorSet(new List { new (minterKey.PublicKey, 10) }); var action = new InitializeStates( + validatorSet: validatorSet, rankingState: new RankingState0(), shopState: new ShopState(), tableSheets: _sheets, diff --git a/.Lib9c.Tests/Policy/BlockPolicyTest.cs b/.Lib9c.Tests/Policy/BlockPolicyTest.cs index 7283abe1c1..2ac7b9e174 100644 --- a/.Lib9c.Tests/Policy/BlockPolicyTest.cs +++ b/.Lib9c.Tests/Policy/BlockPolicyTest.cs @@ -24,10 +24,10 @@ namespace Lib9c.Tests using Nekoyume; using Nekoyume.Action; using Nekoyume.Action.Loader; + using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Blockchain.Policy; using Nekoyume.Model; using Nekoyume.Model.State; - using Nekoyume.Module; using Xunit; public class BlockPolicyTest @@ -70,10 +70,9 @@ public void ValidateNextBlockTx_Mead() }, }; Block genesis = MakeGenesisBlock( + new ValidatorSet(new List { new (adminPrivateKey.PublicKey, 1000) }), adminAddress, ImmutableHashSet
.Empty, - initialValidators: new Dictionary - { { adminPrivateKey.PublicKey, BigInteger.One } }, actionBases: new[] { mint, mint2 }, privateKey: adminPrivateKey ); @@ -163,10 +162,9 @@ public void BlockCommitFromNonValidator() IBlockPolicy policy = blockPolicySource.GetPolicy(null, null, null, null); IStagePolicy stagePolicy = new VolatileStagePolicy(); Block genesis = MakeGenesisBlock( + new ValidatorSet(new List { new (adminPrivateKey.PublicKey, 1000) }), adminAddress, - ImmutableHashSet.Create(adminAddress), - initialValidators: new Dictionary - { { adminPrivateKey.PublicKey, BigInteger.One } } + ImmutableHashSet.Create(adminAddress) ); using var store = new DefaultStore(null); using var stateStore = new TrieStateStore(new DefaultKeyValueStore(null)); @@ -208,6 +206,7 @@ public void MustNotIncludeBlockActionAtTransaction() maxTransactionsPerSignerPerBlockPolicy: null); IStagePolicy stagePolicy = new VolatileStagePolicy(); Block genesis = MakeGenesisBlock( + new ValidatorSet(new List { new (adminPrivateKey.PublicKey, 1000) }), adminAddress, ImmutableHashSet.Create(adminAddress), new AuthorizedMinersState( @@ -260,6 +259,7 @@ public void EarnMiningGoldWhenSuccessMining() maxTransactionsPerSignerPerBlockPolicy: null); IStagePolicy stagePolicy = new VolatileStagePolicy(); Block genesis = MakeGenesisBlock( + new ValidatorSet(new List { new (adminPrivateKey.PublicKey, 1000) }), adminAddress, ImmutableHashSet.Create(adminAddress), new AuthorizedMinersState( @@ -267,7 +267,6 @@ public void EarnMiningGoldWhenSuccessMining() 5, 10 ), - new Dictionary { { adminPrivateKey.PublicKey, BigInteger.One } }, pendingActivations: new[] { ps } ); @@ -293,12 +292,53 @@ public void EarnMiningGoldWhenSuccessMining() ); Block block = blockChain.ProposeBlock(adminPrivateKey); - blockChain.Append(block, GenerateBlockCommit(block, adminPrivateKey)); - FungibleAssetValue actualBalance = blockChain + BigInteger power = blockChain.GetNextWorldState().GetValidatorSet().GetValidator(adminPrivateKey.PublicKey).Power; + BlockCommit commit = GenerateBlockCommit(block, adminPrivateKey, power); + // Since it's a block right after the Genesis, the reward is 0. + blockChain.Append(block, commit); + + var mint = new PrepareRewardAssets + { + RewardPoolAddress = adminAddress, + Assets = new List + { + 10 * Currencies.Mead, + }, + }; + + blockChain.MakeTransaction( + adminPrivateKey, + new ActionBase[] { mint, } + ); + block = blockChain.ProposeBlock(adminPrivateKey, commit); + power = blockChain.GetNextWorldState().GetValidatorSet().GetValidator(adminPrivateKey.PublicKey).Power; + commit = GenerateBlockCommit(block, adminPrivateKey, power); + // First Reward : Proposer base reward 10 * 0.01, proposer bonus reward 10 * 0.04, Commission 9.5 * 0.1 + // Total 0.5 + 0.95 = 1.45 + blockChain.Append(block, commit); + + var actualBalance = blockChain + .GetNextWorldState() + .GetBalance(adminAddress, _currency); + var expectedBalance = new FungibleAssetValue(_currency, 1, 45); + Assert.Equal(expectedBalance, actualBalance); + + blockChain.MakeTransaction( + adminPrivateKey, + new ActionBase[] { new ClaimRewardValidator(adminAddress), } + ); + + block = blockChain.ProposeBlock(adminPrivateKey, commit); + power = blockChain.GetNextWorldState().GetValidatorSet().GetValidator(adminPrivateKey.PublicKey).Power; + commit = GenerateBlockCommit(block, adminPrivateKey, power); + // First + Second Reward : Total reward of two blocks : 10 * 2 = 20 + blockChain.Append(block, commit); + + actualBalance = blockChain .GetNextWorldState() .GetBalance(adminAddress, _currency); - FungibleAssetValue expectedBalance = new FungibleAssetValue(_currency, 10, 0); - Assert.True(expectedBalance.Equals(actualBalance)); + expectedBalance = new FungibleAssetValue(_currency, 20, 0); + Assert.Equal(expectedBalance, actualBalance); } [Fact] @@ -317,10 +357,9 @@ public void ValidateNextBlockWithManyTransactions() IStagePolicy stagePolicy = new VolatileStagePolicy(); Block genesis = MakeGenesisBlock( + validators: new ValidatorSet(new List { new (adminPublicKey, 1000) }), adminPublicKey.Address, - ImmutableHashSet
.Empty, - initialValidators: new Dictionary - { { adminPrivateKey.PublicKey, BigInteger.One } }); + ImmutableHashSet
.Empty); using var store = new DefaultStore(null); var stateStore = new TrieStateStore(new MemoryKeyValueStore()); @@ -429,12 +468,12 @@ public void ValidateNextBlockWithManyTransactionsPerSigner() .Default .Add(new SpannedSubPolicy(2, null, null, 5))); IStagePolicy stagePolicy = new VolatileStagePolicy(); + var validatorSet = new ValidatorSet(new List { new (adminPrivateKey.PublicKey, 1000) }); Block genesis = MakeGenesisBlock( + validatorSet, adminPublicKey.Address, - ImmutableHashSet
.Empty, - initialValidators: new Dictionary - { { adminPrivateKey.PublicKey, BigInteger.One } }); + ImmutableHashSet
.Empty); using var store = new DefaultStore(null); var stateStore = new TrieStateStore(new MemoryKeyValueStore()); @@ -535,7 +574,7 @@ List GenerateTransactions(int count) Assert.True(blockChain.ContainsBlock(block3.Hash)); } - private BlockCommit GenerateBlockCommit(Block block, PrivateKey key) + private BlockCommit GenerateBlockCommit(Block block, PrivateKey key, BigInteger? power = null) { PrivateKey privateKey = key; return block.Index != 0 @@ -549,16 +588,16 @@ private BlockCommit GenerateBlockCommit(Block block, PrivateKey key) block.Hash, DateTimeOffset.UtcNow, privateKey.PublicKey, - null, + power, VoteFlag.PreCommit).Sign(privateKey))) : null; } private Block MakeGenesisBlock( + ValidatorSet validators, Address adminAddress, IImmutableSet
activatedAddresses, AuthorizedMinersState authorizedMinersState = null, - Dictionary initialValidators = null, DateTimeOffset? timestamp = null, PendingActivationState[] pendingActivations = null, IEnumerable actionBases = null, @@ -574,13 +613,13 @@ private Block MakeGenesisBlock( var sheets = TableSheetsImporter.ImportSheets(); return BlockHelper.ProposeGenesisBlock( + validators, sheets, new GoldDistribution[0], pendingActivations, new AdminState(adminAddress, 1500000), authorizedMinersState: authorizedMinersState, activatedAccounts: activatedAddresses, - initialValidators: initialValidators, isActivateAdminAddress: false, credits: null, privateKey: privateKey ?? _privateKey, diff --git a/.Lib9c.Tests/TestHelper/BlockChainHelper.cs b/.Lib9c.Tests/TestHelper/BlockChainHelper.cs index e119d8df0c..9957d84982 100644 --- a/.Lib9c.Tests/TestHelper/BlockChainHelper.cs +++ b/.Lib9c.Tests/TestHelper/BlockChainHelper.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.TestHelper using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; + using System.Numerics; using Lib9c.DevExtensions.Action; using Lib9c.Renderers; using Lib9c.Tests.Action; @@ -17,6 +18,7 @@ namespace Lib9c.Tests.TestHelper using Libplanet.Store.Trie; using Libplanet.Types.Assets; using Libplanet.Types.Blocks; + using Libplanet.Types.Consensus; using Nekoyume; using Nekoyume.Action; using Nekoyume.Action.Loader; @@ -74,6 +76,7 @@ public static Block MakeGenesisBlock( var sheets = TableSheetsImporter.ImportSheets(); return BlockHelper.ProposeGenesisBlock( + new ValidatorSet(new List { new (privateKey.PublicKey, BigInteger.One) }), sheets, new GoldDistribution[0], pendingActivations, diff --git a/Lib9c.Utils/BlockHelper.cs b/Lib9c.Utils/BlockHelper.cs index 58c0c58986..b2e58007e1 100644 --- a/Lib9c.Utils/BlockHelper.cs +++ b/Lib9c.Utils/BlockHelper.cs @@ -2,10 +2,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Numerics; -using Bencodex.Types; using Libplanet.Action; -using Libplanet.Action.Sys; using Libplanet.Blockchain; using Libplanet.Crypto; using Libplanet.Store; @@ -25,13 +22,13 @@ namespace Nekoyume public static class BlockHelper { public static Block ProposeGenesisBlock( + ValidatorSet validatorSet, IDictionary tableSheets, GoldDistribution[] goldDistributions, PendingActivationState[] pendingActivationStates, AdminState? adminState = null, AuthorizedMinersState? authorizedMinersState = null, IImmutableSet
? activatedAccounts = null, - Dictionary? initialValidators = null, bool isActivateAdminAddress = false, IEnumerable? credits = null, PrivateKey? privateKey = null, @@ -50,7 +47,6 @@ public static Block ProposeGenesisBlock( redeemCodeListSheet.Set(tableSheets[nameof(RedeemCodeListSheet)]); privateKey ??= new PrivateKey(); - initialValidators ??= new Dictionary(); activatedAccounts ??= ImmutableHashSet
.Empty; #pragma warning disable CS0618 // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 @@ -59,6 +55,7 @@ public static Block ProposeGenesisBlock( var initialStatesAction = new InitializeStates ( + validatorSet: validatorSet, rankingState: new RankingState0(), shopState: new ShopState(), tableSheets: (Dictionary) tableSheets, @@ -80,16 +77,7 @@ public static Block ProposeGenesisBlock( { initialStatesAction, }; - IEnumerable systemActions = new IAction[] - { - new Initialize( - states: ImmutableDictionary.Create(), - validatorSet: new ValidatorSet( - initialValidators.Select(validator => - new Validator(validator.Key, validator.Value)).ToList() - ) - ), - }; + IEnumerable systemActions = new IAction[] { }; if (!(actionBases is null)) { actions.AddRange(actionBases); diff --git a/Lib9c/Action/InitializeStates.cs b/Lib9c/Action/InitializeStates.cs index 6b7b1a1a66..eec637f7d6 100644 --- a/Lib9c/Action/InitializeStates.cs +++ b/Lib9c/Action/InitializeStates.cs @@ -6,9 +6,12 @@ using Lib9c.Abstractions; using Libplanet.Action; using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Types.Assets; +using Libplanet.Types.Consensus; using Nekoyume.Model.State; using Nekoyume.Module; -using Libplanet.Crypto; +using Nekoyume.ValidatorDelegation; namespace Nekoyume.Action { @@ -33,6 +36,7 @@ namespace Nekoyume.Action [ActionType("initialize_states")] public class InitializeStates : GameAction, IInitializeStatesV1 { + public IValue ValidatorSet { get; set; } = Null.Value; public Dictionary Ranking { get; set; } = Dictionary.Empty; public Dictionary Shop { get; set; } = Dictionary.Empty; public Dictionary TableSheets { get; set; } @@ -75,6 +79,7 @@ public InitializeStates() } public InitializeStates( + ValidatorSet validatorSet, RankingState0 rankingState, ShopState shopState, Dictionary tableSheets, @@ -89,6 +94,7 @@ public InitializeStates( CreditsState creditsState = null, ISet
assetMinters = null) { + ValidatorSet = validatorSet.Bencoded; Ranking = (Dictionary)rankingState.Serialize(); Shop = (Dictionary)shopState.Serialize(); TableSheets = tableSheets; @@ -192,6 +198,24 @@ public override IWorld Execute(IActionContext context) ); } + var repository = new ValidatorRepository(states, context); + var validatorSet = new ValidatorSet(ValidatorSet); + foreach (var validator in validatorSet.Validators) + { + var validatorDelegatee = new ValidatorDelegatee( + validator.OperatorAddress, validator.PublicKey, repository.World.GetGoldCurrency(), repository); + var delegationFAV = FungibleAssetValue.FromRawValue( + validatorDelegatee.DelegationCurrency, validator.Power); + var validatorDelegator = repository.GetValidatorDelegator(validator.OperatorAddress); + + repository.SetValidatorDelegatee(validatorDelegatee); + repository.TransferAsset( + GoldCurrencyState.Address, validatorDelegator.DelegationPoolAddress, delegationFAV); + validatorDelegator.Delegate(validatorDelegatee, delegationFAV, context.BlockIndex); + + states = repository.World; + } + return states; } @@ -200,6 +224,7 @@ protected override IImmutableDictionary PlainValueInternal get { var rv = ImmutableDictionary.Empty + .Add("validator_set", ValidatorSet) .Add("ranking_state", Ranking) .Add("shop_state", Shop) .Add("table_sheets", @@ -241,6 +266,7 @@ protected override IImmutableDictionary PlainValueInternal protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) { + ValidatorSet = plainValue["validator_set"]; Ranking = (Dictionary) plainValue["ranking_state"]; Shop = (Dictionary) plainValue["shop_state"]; TableSheets = ((Dictionary) plainValue["table_sheets"]) From 18a2dae4b18bf16e657f7c50239515001931a6a5 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sat, 21 Sep 2024 10:52:52 +0900 Subject: [PATCH 042/206] feat: Use NCG as delegation currency --- .../ValidatorDelegation/AllocateRewardTest.cs | 16 +++++++++++----- .../ValidatorDelegation/DelegateValidatorTest.cs | 12 +++++++++--- .../ValidatorDelegation/PromoteValidatorTest.cs | 9 ++++++--- .../RedelegateValidatorTest.cs | 4 +++- .../UndelegateValidatorTest.cs | 5 +++-- Lib9c/ValidatorDelegation/ValidatorDelegatee.cs | 7 ++++--- Lib9c/ValidatorDelegation/ValidatorRepository.cs | 1 + 7 files changed, 37 insertions(+), 17 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs index 8344dde4d8..5544bf7bab 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -37,7 +37,9 @@ public void Execute() IWorld world = new World(MockUtil.MockModernWorldState); var context = new ActionContext { }; var ncg = Currency.Uncapped("NCG", 2, null); - var gg = Currencies.GuildGold; + // TODO: Use Currencies.GuildGold when it's available. + // var gg = Currencies.GuildGold; + var gg = ncg; var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); @@ -72,6 +74,9 @@ public void Execute() var totalReward = ncg * 1000; world = repository.World.MintAsset(context, Addresses.RewardPool, totalReward); + // TODO: Remove this after delegation currency has been changed into GuildGold. + var initialFAVs = votes.Select(vote => world.GetBalance(vote.ValidatorPublicKey.Address, ncg)).ToArray(); + context = new ActionContext { BlockIndex = 10L, @@ -102,8 +107,9 @@ var bonusProposerReward var remains = totalReward - proposerReward; repository.UpdateWorld(world); - foreach (var vote in votes) + foreach (var (vote, index) in votes.Select((v, i) => (v, i))) { + var initialFAV = initialFAVs[index]; var validator = repository.GetValidatorDelegatee(vote.ValidatorPublicKey.Address); FungibleAssetValue rewardAllocated @@ -114,7 +120,7 @@ FungibleAssetValue commission if (vote.Flag == VoteFlag.Null) { - Assert.Equal(ncg * 0, world.GetBalance(vote.ValidatorPublicKey.Address, ncg)); + Assert.Equal(initialFAV, world.GetBalance(vote.ValidatorPublicKey.Address, ncg)); Assert.Equal(ncg * 0, world.GetBalance(validator.RewardDistributorAddress, ncg)); continue; } @@ -122,12 +128,12 @@ FungibleAssetValue commission if (vote.ValidatorPublicKey.Equals(proposer.PublicKey)) { Assert.Equal( - proposerReward + commission, + proposerReward + commission + initialFAV, world.GetBalance(vote.ValidatorPublicKey.Address, ncg)); } else { - Assert.Equal(commission, world.GetBalance(vote.ValidatorPublicKey.Address, ncg)); + Assert.Equal(commission + initialFAV, world.GetBalance(vote.ValidatorPublicKey.Address, ncg)); } Assert.Equal(rewardAllocated - commission, world.GetBalance(validator.RewardDistributorAddress, ncg)); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index 7444a951aa..5ad8613c00 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -35,7 +35,9 @@ public void Execute() IWorld world = new World(MockUtil.MockModernWorldState); var context = new ActionContext { }; var ncg = Currency.Uncapped("NCG", 2, null); - var gg = Currencies.GuildGold; + // TODO: Use Currencies.GuildGold when it's available. + // var gg = Currencies.GuildGold; + var gg = ncg; var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); @@ -77,7 +79,9 @@ public void CannotDelegateWithInvalidCurrency() IWorld world = new World(MockUtil.MockModernWorldState); var context = new ActionContext { }; var ncg = Currency.Uncapped("NCG", 2, null); - var gg = Currencies.GuildGold; + // TODO: Use Currencies.GuildGold when it's available. + // var gg = Currencies.GuildGold; + var gg = ncg; var invalid = Currency.Uncapped("invalid", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world @@ -109,7 +113,9 @@ public void CannotDelegateWithInsufficientBalance() IWorld world = new World(MockUtil.MockModernWorldState); var context = new ActionContext { }; var ncg = Currency.Uncapped("NCG", 2, null); - var gg = Currencies.GuildGold; + // TODO: Use Currencies.GuildGold when it's available. + // var gg = Currencies.GuildGold; + var gg = ncg; var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs index dd8b70fb03..7006fcb4e1 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs @@ -9,7 +9,6 @@ namespace Lib9c.Tests.Action.ValidatorDelegation using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.State; using Nekoyume.Module; - using Nekoyume.Module.ValidatorDelegation; using Nekoyume.ValidatorDelegation; using Xunit; @@ -36,7 +35,9 @@ public void Execute() IWorld world = new World(MockUtil.MockModernWorldState); var context = new ActionContext { }; var ncg = Currency.Uncapped("NCG", 2, null); - var gg = Currencies.GuildGold; + // TODO: Use Currencies.GuildGold when it's available. + // var gg = Currencies.GuildGold; + var gg = ncg; var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); @@ -117,7 +118,9 @@ public void CannotPromoteWithInsufficientBalance() IWorld world = new World(MockUtil.MockModernWorldState); var context = new ActionContext { }; var ncg = Currency.Uncapped("NCG", 2, null); - var gg = Currencies.GuildGold; + // TODO: Use Currencies.GuildGold when it's available. + // var gg = Currencies.GuildGold; + var gg = ncg; var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs index 8bd644754e..1f5ed59d0e 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs @@ -36,7 +36,9 @@ public void Execute() IWorld world = new World(MockUtil.MockModernWorldState); var context = new ActionContext { }; var ncg = Currency.Uncapped("NCG", 2, null); - var gg = Currencies.GuildGold; + // TODO: Use Currencies.GuildGold when it's available. + // var gg = Currencies.GuildGold; + var gg = ncg; var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 2a1b2ee2c9..3a9afdd9a9 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -9,7 +9,6 @@ namespace Lib9c.Tests.Action.ValidatorDelegation using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.State; using Nekoyume.Module; - using Nekoyume.Module.ValidatorDelegation; using Nekoyume.ValidatorDelegation; using Xunit; @@ -35,7 +34,9 @@ public void Execute() IWorld world = new World(MockUtil.MockModernWorldState); var context = new ActionContext { }; var ncg = Currency.Uncapped("NCG", 2, null); - var gg = Currencies.GuildGold; + // TODO: Use Currencies.GuildGold when it's available. + // var gg = Currencies.GuildGold; + var gg = ncg; var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index c50b33aa40..d1ad320b47 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -15,6 +15,7 @@ namespace Nekoyume.ValidatorDelegation public sealed class ValidatorDelegatee : Delegatee, IEquatable, IBencodable { + // TODO: After guild-PoS implemented, delegation currency have to be changed into guild gold. public ValidatorDelegatee( Address address, PublicKey publicKey, @@ -23,7 +24,7 @@ public ValidatorDelegatee( : base( address: address, accountAddress: repository.DelegateeAccountAddress, - delegationCurrency: ValidatorDelegationCurrency, + delegationCurrency: rewardCurrency, rewardCurrency: rewardCurrency, delegationPoolAddress: UnbondedPoolAddress, unbondingPeriod: ValidatorUnbondingPeriod, @@ -75,8 +76,6 @@ public ValidatorDelegatee( public static int ValidatorMaxRebondGraceEntries => 10; - public static FungibleAssetValue MinSelfDelegation => ValidatorDelegationCurrency * 10; - public static BigInteger BaseProposerRewardNumerator => 1; public static BigInteger BaseProposerRewardDenominator => 100; @@ -107,6 +106,8 @@ public ValidatorDelegatee( public Validator Validator => new(PublicKey, Power); + public FungibleAssetValue MinSelfDelegation => DelegationCurrency * 10; + public void AllocateReward( FungibleAssetValue rewardToAllocate, BigInteger validatorPower, diff --git a/Lib9c/ValidatorDelegation/ValidatorRepository.cs b/Lib9c/ValidatorDelegation/ValidatorRepository.cs index 498893aa24..84858cf0bd 100644 --- a/Lib9c/ValidatorDelegation/ValidatorRepository.cs +++ b/Lib9c/ValidatorDelegation/ValidatorRepository.cs @@ -53,6 +53,7 @@ public ValidatorDelegator GetValidatorDelegator(Address address) } catch (FailedLoadStateException) { + // TODO: delegationPoolAddress have to be changed after guild system is implemented. return new ValidatorDelegator(address, address, this); } } From 895571492875c81e2cddd9839b56d10a3bc2fb60 Mon Sep 17 00:00:00 2001 From: s2quake Date: Fri, 20 Sep 2024 14:55:27 +0900 Subject: [PATCH 043/206] fix: Fix an issue with Unjail and Slash method --- Lib9c/Delegation/DelegateeMetadata.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Lib9c/Delegation/DelegateeMetadata.cs b/Lib9c/Delegation/DelegateeMetadata.cs index 52ad219d19..a7438c3e32 100644 --- a/Lib9c/Delegation/DelegateeMetadata.cs +++ b/Lib9c/Delegation/DelegateeMetadata.cs @@ -235,12 +235,17 @@ public void JailUntil(long height) public void Unjail(long height) { + if (!Jailed) + { + throw new InvalidOperationException("Cannot unjail non-jailed delegatee."); + } + if (Tombstoned) { throw new InvalidOperationException("Cannot unjail tombstoned delegatee."); } - if (JailedUntil > height) + if (JailedUntil >= height) { throw new InvalidOperationException("Cannot unjail before jailed until."); } From fff6d7c7ec68e3c73029f08de2fa6f6d1bfebcf3 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 23 Sep 2024 11:44:05 +0900 Subject: [PATCH 044/206] fix: Update missing sealed access modifier --- Lib9c/Action/ValidatorDelegation/AllocateReward.cs | 2 +- Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs | 2 +- Lib9c/Action/ValidatorDelegation/DelegateValidator.cs | 2 +- Lib9c/Action/ValidatorDelegation/PromoteValidator.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs index 3e83ab2946..5e91b11a48 100644 --- a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs +++ b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs @@ -13,7 +13,7 @@ namespace Nekoyume.Action.ValidatorDelegation { - public class AllocateReward : ActionBase + public sealed class AllocateReward : ActionBase { public AllocateReward() { diff --git a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs index 0fb110ad7d..91f471ecba 100644 --- a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs @@ -9,7 +9,7 @@ namespace Nekoyume.Action.ValidatorDelegation { [ActionType(TypeIdentifier)] - public class ClaimRewardValidator : ActionBase + public sealed class ClaimRewardValidator : ActionBase { public const string TypeIdentifier = "claim_reward_validator"; diff --git a/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs index b0613a90b9..16c57c4b22 100644 --- a/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs @@ -10,7 +10,7 @@ namespace Nekoyume.Action.ValidatorDelegation { [ActionType(TypeIdentifier)] - public class DelegateValidator : ActionBase + public sealed class DelegateValidator : ActionBase { public const string TypeIdentifier = "delegate_validator"; diff --git a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs index d4b76ceed5..6a4c08ef66 100644 --- a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs @@ -10,7 +10,7 @@ namespace Nekoyume.Action.ValidatorDelegation { [ActionType(TypeIdentifier)] - public class PromoteValidator : ActionBase + public sealed class PromoteValidator : ActionBase { public const string TypeIdentifier = "promote_validator"; From 7999c682e0c03346fc5f0343ef0d932f92a5268d Mon Sep 17 00:00:00 2001 From: ilgyu Date: Tue, 24 Sep 2024 00:39:14 +0900 Subject: [PATCH 045/206] feat: Use custom commission percentage --- .../ValidatorDelegation/AllocateRewardTest.cs | 12 +- .../SetValidatorCommissionTest.cs | 135 ++++++++++++++++++ .../ValidatorDelegation/AllocateReward.cs | 8 +- .../SetValidatorCommission.cs | 59 ++++++++ .../ValidatorDelegation/ValidatorDelegatee.cs | 45 ++++-- .../ValidatorRepository.cs | 8 ++ 6 files changed, 244 insertions(+), 23 deletions(-) create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs create mode 100644 Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs index 5544bf7bab..0f875581c9 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -97,11 +97,11 @@ public void Execute() (accum, next) => accum + (BigInteger)next.ValidatorPower! * (next.Flag == VoteFlag.PreCommit ? 1 : 0)); var baseProposerReward - = (totalReward * ValidatorDelegatee.BaseProposerRewardNumerator) - .DivRem(ValidatorDelegatee.BaseProposerRewardDenominator).Quotient; + = (totalReward * ValidatorDelegatee.BaseProposerRewardPercentage) + .DivRem(100).Quotient; var bonusProposerReward - = (totalReward * preCommitPower * ValidatorDelegatee.BonusProposerRewardNumerator) - .DivRem(totalPower * ValidatorDelegatee.BonusProposerRewardDenominator).Quotient; + = (totalReward * preCommitPower * ValidatorDelegatee.BonusProposerRewardPercentage) + .DivRem(totalPower * 100).Quotient; var proposerReward = baseProposerReward + bonusProposerReward; var remains = totalReward - proposerReward; @@ -115,8 +115,8 @@ var bonusProposerReward FungibleAssetValue rewardAllocated = (remains * vote.ValidatorPower!.Value).DivRem(totalPower).Quotient; FungibleAssetValue commission - = (rewardAllocated * ValidatorDelegatee.CommissionNumerator) - .DivRem(ValidatorDelegatee.CommissionDenominator).Quotient; + = (rewardAllocated * validator.CommissionPercentage) + .DivRem(100).Quotient; if (vote.Flag == VoteFlag.Null) { diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs new file mode 100644 index 0000000000..b4281a3bed --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs @@ -0,0 +1,135 @@ +namespace Lib9c.Tests.Action.ValidatorDelegation +{ + using System; + using System.Linq; + using System.Numerics; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.Model.State; + using Nekoyume.Module; + using Nekoyume.ValidatorDelegation; + using Xunit; + + public class SetValidatorCommissionTest + { + [Fact] + public void Serialization() + { + var address = new PrivateKey().Address; + BigInteger commissionPercentage = 10; + var action = new SetValidatorCommission(address, commissionPercentage); + var plainValue = action.PlainValue; + + var deserialized = new SetValidatorCommission(); + deserialized.LoadPlainValue(plainValue); + Assert.Equal(address, deserialized.ValidatorDelegatee); + Assert.Equal(commissionPercentage, deserialized.CommissionPercentage); + } + + [Fact] + public void Execute() + { + IWorld world = new World(MockUtil.MockModernWorldState); + var context = new ActionContext { }; + var ncg = Currency.Uncapped("NCG", 2, null); + // TODO: Use Currencies.GuildGold when it's available. + // var gg = Currencies.GuildGold; + var gg = ncg; + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + var validatorPublicKey = new PrivateKey().PublicKey; + world = world.MintAsset(context, validatorPublicKey.Address, gg * 100); + var promoteFAV = gg * 10; + world = new PromoteValidator(validatorPublicKey, promoteFAV).Execute(new ActionContext + { + PreviousState = world, + Signer = validatorPublicKey.Address, + }); + + world = new SetValidatorCommission(validatorPublicKey.Address, 11).Execute(new ActionContext + { + PreviousState = world, + Signer = validatorPublicKey.Address, + }); + + var repository = new ValidatorRepository(world, context); + var validator = repository.GetValidatorDelegatee(validatorPublicKey.Address); + Assert.Equal(11, validator.CommissionPercentage); + } + + [Fact] + public void CannotExceedMaxPercentage() + { + IWorld world = new World(MockUtil.MockModernWorldState); + var context = new ActionContext { }; + var ncg = Currency.Uncapped("NCG", 2, null); + // TODO: Use Currencies.GuildGold when it's available. + // var gg = Currencies.GuildGold; + var gg = ncg; + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + var validatorPublicKey = new PrivateKey().PublicKey; + world = world.MintAsset(context, validatorPublicKey.Address, gg * 100); + var promoteFAV = gg * 10; + world = new PromoteValidator(validatorPublicKey, promoteFAV).Execute(new ActionContext + { + PreviousState = world, + Signer = validatorPublicKey.Address, + }); + + foreach (var percentage in Enumerable.Range(11, 10)) + { + world = new SetValidatorCommission(validatorPublicKey.Address, percentage).Execute(new ActionContext + { + PreviousState = world, + Signer = validatorPublicKey.Address, + }); + } + + Assert.Throws( + () => new SetValidatorCommission(validatorPublicKey.Address, 31).Execute(new ActionContext + { + PreviousState = world, + Signer = validatorPublicKey.Address, + })); + } + + [Fact] + public void CannotExceedMaxChange() + { + IWorld world = new World(MockUtil.MockModernWorldState); + var context = new ActionContext { }; + var ncg = Currency.Uncapped("NCG", 2, null); + // TODO: Use Currencies.GuildGold when it's available. + // var gg = Currencies.GuildGold; + var gg = ncg; + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + var validatorPublicKey = new PrivateKey().PublicKey; + world = world.MintAsset(context, validatorPublicKey.Address, gg * 100); + var promoteFAV = gg * 10; + world = new PromoteValidator(validatorPublicKey, promoteFAV).Execute(new ActionContext + { + PreviousState = world, + Signer = validatorPublicKey.Address, + }); + + Assert.Throws( + () => new SetValidatorCommission(validatorPublicKey.Address, 12).Execute(new ActionContext + { + PreviousState = world, + Signer = validatorPublicKey.Address, + })); + } + } +} diff --git a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs index 5e91b11a48..9c3fa300b4 100644 --- a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs +++ b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs @@ -102,11 +102,11 @@ BigInteger votePowerDenominator } var baseProposerReward - = (blockReward * ValidatorDelegatee.BaseProposerRewardNumerator) - .DivRem(ValidatorDelegatee.BaseProposerRewardDenominator).Quotient; + = (blockReward * ValidatorDelegatee.BaseProposerRewardPercentage) + .DivRem(100).Quotient; var bonusProposerReward - = (blockReward * votePowerNumerator * ValidatorDelegatee.BonusProposerRewardNumerator) - .DivRem(votePowerDenominator * ValidatorDelegatee.BonusProposerRewardDenominator).Quotient; + = (blockReward * votePowerNumerator * ValidatorDelegatee.BonusProposerRewardPercentage) + .DivRem(votePowerDenominator * 100).Quotient; FungibleAssetValue proposerReward = baseProposerReward + bonusProposerReward; repository.TransferAsset( diff --git a/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs b/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs new file mode 100644 index 0000000000..de623d60df --- /dev/null +++ b/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs @@ -0,0 +1,59 @@ +using System; +using System.Numerics; +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 SetValidatorCommission : ActionBase + { + public const string TypeIdentifier = "set_validator_commission"; + + public SetValidatorCommission() { } + + public SetValidatorCommission(Address validatorDelegatee, BigInteger commissionPercentage) + { + ValidatorDelegatee = validatorDelegatee; + CommissionPercentage = commissionPercentage; + } + + public Address ValidatorDelegatee { get; private set; } + + public BigInteger CommissionPercentage { get; private set; } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", List.Empty + .Add(ValidatorDelegatee.Bencoded) + .Add(CommissionPercentage)); + + 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(); + } + + ValidatorDelegatee = new Address(values[0]); + CommissionPercentage = (Integer)values[1]; + } + + public override IWorld Execute(IActionContext context) + { + GasTracer.UseGas(1); + + var world = context.PreviousState; + var repository = new ValidatorRepository(world, context); + repository.SetCommissionPercentage(ValidatorDelegatee, CommissionPercentage); + + return repository.World; + } + } +} diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index d1ad320b47..22a8c7eb30 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -39,6 +39,7 @@ public ValidatorDelegatee( PublicKey = publicKey; IsBonded = false; + CommissionPercentage = DefaultCommissionPercentage; DelegationChanged += OnDelegationChanged; Enjailed += OnEnjailed; Unjailed += OnUnjailed; @@ -65,6 +66,7 @@ public ValidatorDelegatee( { PublicKey = new PublicKey(((Binary)bencoded[0]).ByteArray); IsBonded = (Bencodex.Types.Boolean)bencoded[1]; + CommissionPercentage = (Integer)bencoded[2]; DelegationChanged += OnDelegationChanged; } @@ -76,25 +78,22 @@ public ValidatorDelegatee( public static int ValidatorMaxRebondGraceEntries => 10; - public static BigInteger BaseProposerRewardNumerator => 1; + public static BigInteger BaseProposerRewardPercentage => 1; - public static BigInteger BaseProposerRewardDenominator => 100; + public static BigInteger BonusProposerRewardPercentage => 4; - public static BigInteger BonusProposerRewardNumerator => 4; + public static BigInteger DefaultCommissionPercentage => 10; - public static BigInteger BonusProposerRewardDenominator => 100; + public static BigInteger MaxCommissionPercentage => 20; - public static BigInteger CommissionNumerator => 1; + public static BigInteger CommissionPercentageMaxChange => 1; - public static BigInteger CommissionDenominator => 10; - - public static double CommissionMaxRate => 0.2; - - public static double CommissionMaxChangeRate => 0.01; + public BigInteger CommissionPercentage { get; private set; } public List Bencoded => List.Empty .Add(PublicKey.Format(true)) - .Add(IsBonded); + .Add(IsBonded) + .Add(CommissionPercentage); IValue IBencodable.Bencoded => Bencoded; @@ -120,7 +119,7 @@ public void AllocateReward( FungibleAssetValue rewardAllocated = (rewardToAllocate * validatorPower).DivRem(validatorSetPower).Quotient; FungibleAssetValue commission - = (rewardAllocated * CommissionNumerator).DivRem(CommissionDenominator).Quotient; + = (rewardAllocated * CommissionPercentage).DivRem(100).Quotient; FungibleAssetValue delegationRewards = rewardAllocated - commission; repository.TransferAsset(RewardSource, Address, commission); @@ -128,6 +127,25 @@ FungibleAssetValue commission CollectRewards(height); } + public void SetCommissionPercentage(BigInteger percentage) + { + if (percentage < 0 || percentage > MaxCommissionPercentage) + { + throw new ArgumentOutOfRangeException( + nameof(percentage), + $"The commission percentage must be between 0 and {MaxCommissionPercentage}."); + } + + if (BigInteger.Abs(CommissionPercentage - percentage) > CommissionPercentageMaxChange) + { + throw new ArgumentOutOfRangeException( + nameof(percentage), + $"The commission percentage can be changed by at most {CommissionPercentageMaxChange}."); + } + + CommissionPercentage = percentage; + } + public void OnDelegationChanged(object? sender, long height) { ValidatorRepository repository = (ValidatorRepository)Repository; @@ -169,7 +187,8 @@ public bool Equals(ValidatorDelegatee? other) => other is ValidatorDelegatee validatorDelegatee && Metadata.Equals(validatorDelegatee.Metadata) && PublicKey.Equals(validatorDelegatee.PublicKey) - && IsBonded == validatorDelegatee.IsBonded; + && IsBonded == validatorDelegatee.IsBonded + && CommissionPercentage == validatorDelegatee.CommissionPercentage; public bool Equals(IDelegatee? other) => Equals(other as ValidatorDelegatee); diff --git a/Lib9c/ValidatorDelegation/ValidatorRepository.cs b/Lib9c/ValidatorDelegation/ValidatorRepository.cs index 84858cf0bd..1f69355e5e 100644 --- a/Lib9c/ValidatorDelegation/ValidatorRepository.cs +++ b/Lib9c/ValidatorDelegation/ValidatorRepository.cs @@ -5,6 +5,7 @@ using Libplanet.Crypto; using Nekoyume.Action; using Nekoyume.Delegation; +using System.Numerics; namespace Nekoyume.ValidatorDelegation { @@ -93,6 +94,13 @@ public void SetValidatorList(ValidatorList validatorList) ValidatorList.Address, validatorList.Bencoded); } + public void SetCommissionPercentage(Address address, BigInteger commissionPercentage) + { + ValidatorDelegatee validatorDelegatee = GetValidatorDelegatee(address); + validatorDelegatee.SetCommissionPercentage(commissionPercentage); + SetValidatorDelegatee(validatorDelegatee); + } + public override void UpdateWorld(IWorld world) { base.UpdateWorld(world); From 2edb960f7bdb7851532981d6b61ab9c00b3a03d1 Mon Sep 17 00:00:00 2001 From: s2quake Date: Tue, 24 Sep 2024 10:58:02 +0900 Subject: [PATCH 046/206] test: Add test for PromoteValidatorTest --- .../PromoteValidatorTest.cs | 27 ++++++- .../ValidatorDelegationTestBase.cs | 76 +++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs index 7006fcb4e1..100161608d 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs @@ -12,7 +12,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation using Nekoyume.ValidatorDelegation; using Xunit; - public class PromoteValidatorTest + public class PromoteValidatorTest : ValidatorDelegationTestBase { [Fact] public void Serialization() @@ -135,5 +135,30 @@ public void CannotPromoteWithInsufficientBalance() Signer = publicKey.Address, })); } + + [Fact] + public void Promote_PromotedValidator_Throw() + { + // Given + var world = World; + var validatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, NCG * 10, blockHeight++); + + // When + var promoteValidator = new PromoteValidator( + validatorPrivateKey.PublicKey, NCG * 10); + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = validatorPrivateKey.Address, + }; + + // Then + Assert.Throws( + () => promoteValidator.Execute(actionContext)); + } } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs new file mode 100644 index 0000000000..008daea725 --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -0,0 +1,76 @@ +#nullable enable +namespace Lib9c.Tests.Action.ValidatorDelegation +{ + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Linq; + using System.Numerics; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Libplanet.Types.Blocks; + using Libplanet.Types.Consensus; + using Libplanet.Types.Evidence; + using Nekoyume; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.Model.State; + using Nekoyume.Module; + using Nekoyume.ValidatorDelegation; + + public class ValidatorDelegationTestBase + { + protected static readonly Currency NCG = Currency.Uncapped("NCG", 2, null); + + public ValidatorDelegationTestBase() + { + var world = new World(MockUtil.MockModernWorldState); + var goldCurrencyState = new GoldCurrencyState(NCG); + World = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + } + + protected static BlockHash EmptyBlockHash { get; } + = new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x01)); + + protected PrivateKey AdminKey { get; } = new PrivateKey(); + + protected IWorld World { get; } + + protected static T[] GetRandomArray(int length, Func creator) + => Enumerable.Range(0, length).Select(creator).ToArray(); + + protected static IWorld MintAsset( + IWorld world, + PrivateKey delegatorPrivateKey, + FungibleAssetValue amount, + long blockHeight) + { + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight, + }; + return world.MintAsset(actionContext, delegatorPrivateKey.Address, amount); + } + + protected static IWorld EnsureValidatorToBePromoted( + IWorld world, + PrivateKey validatorPrivateKey, + FungibleAssetValue amount, + long blockHeight) + { + var validatorPublicKey = validatorPrivateKey.PublicKey; + var promoteValidator = new PromoteValidator(validatorPublicKey, amount); + var actionContext = new ActionContext + { + PreviousState = MintAsset( + world, validatorPrivateKey, amount, blockHeight), + Signer = validatorPublicKey.Address, + BlockIndex = blockHeight, + }; + return promoteValidator.Execute(actionContext); + } + } +} From a08a08135ca1c4ade9a75122a33186e9d2c15a93 Mon Sep 17 00:00:00 2001 From: s2quake Date: Tue, 24 Sep 2024 11:03:17 +0900 Subject: [PATCH 047/206] test: Add infraction-related test code --- .../ValidatorDelegation/SlashValidatorTest.cs | 172 +++++++++++++ .../UnjailValidatorTest.cs | 148 +++++++++++ .../ValidatorDelegationTestBase.cs | 229 ++++++++++++++++++ 3 files changed, 549 insertions(+) create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs new file mode 100644 index 0000000000..5c9b77f849 --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs @@ -0,0 +1,172 @@ +#nullable enable +namespace Lib9c.Tests.Action.ValidatorDelegation +{ + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Numerics; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Types.Blocks; + using Libplanet.Types.Consensus; + using Libplanet.Types.Evidence; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.ValidatorDelegation; + using Xunit; + + public class SlashValidatorTest : ValidatorDelegationTestBase + { + [Fact] + public void Serialization() + { + var action = new SlashValidator(); + var plainValue = action.PlainValue; + + var deserialized = new SlashValidator(); + deserialized.LoadPlainValue(plainValue); + } + + [Fact] + public void Execute() + { + // Given + const int length = 10; + var world = World; + var validatorPrivateKey = new PrivateKey(); + var validatorGold = NCG * 10; + var deletatorPrivateKeys = GetRandomArray(length, _ => new PrivateKey()); + var delegatorNCGs = GetRandomArray( + length, i => NCG * Random.Shared.Next(10, 100)); + var blockHeight = 1L; + var actionContext = new ActionContext { }; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, NCG * 10, blockHeight++); + world = EnsureDelegatorsToBeBond( + world, deletatorPrivateKeys, validatorPrivateKey, delegatorNCGs, blockHeight++); + + // When + var validatorSet = new ValidatorSet(new List + { + new (validatorPrivateKey.PublicKey, new BigInteger(1000)), + }); + var vote1 = new VoteMetadata( + height: blockHeight - 1, + round: 0, + blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x01)), + timestamp: DateTimeOffset.UtcNow, + validatorPublicKey: validatorPrivateKey.PublicKey, + validatorPower: BigInteger.One, + flag: VoteFlag.PreCommit).Sign(validatorPrivateKey); + var vote2 = new VoteMetadata( + height: blockHeight - 1, + round: 0, + blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x02)), + timestamp: DateTimeOffset.UtcNow, + validatorPublicKey: validatorPrivateKey.PublicKey, + validatorPower: BigInteger.One, + flag: VoteFlag.PreCommit).Sign(validatorPrivateKey); + var evidence = new DuplicateVoteEvidence( + vote1, + vote2, + validatorSet, + vote1.Timestamp); + var lastCommit = new BlockCommit( + height: blockHeight - 1, + round: 0, + blockHash: EmptyBlockHash, + ImmutableArray.Create(vote1)); + var slashValidator = new SlashValidator(); + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Evidence = new List { evidence }, + LastCommit = lastCommit, + }; + world = slashValidator.Execute(actionContext); + + // Then + var balance = world.GetBalance(validatorPrivateKey.Address, NCG); + var repository = new ValidatorRepository(world, actionContext); + var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); + + Assert.True(delegatee.Jailed); + Assert.Equal(long.MaxValue, delegatee.JailedUntil); + Assert.True(delegatee.Tombstoned); + } + + [Fact] + public void Jail_By_Abstain() + { + // Given + var world = World; + var validatorPrivateKey = new PrivateKey(); + var actionContext = new ActionContext { }; + var blockHeight = 1L; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, NCG * 10, blockHeight++); + + // When + for (var i = 0L; i <= AbstainHistory.MaxAbstainAllowance; i++) + { + var vote = CreateNullVote(validatorPrivateKey, blockHeight - 1); + var lastCommit = new BlockCommit( + height: blockHeight - 1, + round: 0, + blockHash: vote.BlockHash, + ImmutableArray.Create(vote)); + world = ExecuteSlashValidator( + world, validatorPrivateKey.PublicKey, lastCommit, blockHeight++); + } + + // Then + var repository = new ValidatorRepository(world, actionContext); + var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); + Assert.True(delegatee.Jailed); + Assert.False(delegatee.Tombstoned); + } + + [Fact] + public void Jail_JailedDelegatee_Nothing_Happens_Test() + { + // Given + var world = World; + var validatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + var actionContext = new ActionContext(); + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, NCG * 10, blockHeight++); + world = EnsureValidatorToBeTombstoned(world, validatorPrivateKey, blockHeight++); + + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee( + validatorPrivateKey.Address); + var expectedJailed = expectedDelegatee.Jailed; + var evidence = CreateDuplicateVoteEvidence(validatorPrivateKey, blockHeight - 1); + var lastCommit = new BlockCommit( + height: blockHeight - 1, + round: 0, + blockHash: EmptyBlockHash, + ImmutableArray.Create(evidence.VoteRef)); + var slashValidator = new SlashValidator(); + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight + SlashValidator.AbstainJailTime, + Signer = validatorPrivateKey.Address, + Evidence = new List { evidence }, + LastCommit = lastCommit, + }; + world = slashValidator.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepository.GetValidatorDelegatee( + validatorPrivateKey.Address); + var actualJailed = actualDelegatee.Jailed; + + Assert.Equal(expectedJailed, actualJailed); + } + } +} diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs new file mode 100644 index 0000000000..2e2111fd63 --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs @@ -0,0 +1,148 @@ +#nullable enable +namespace Lib9c.Tests.Action.ValidatorDelegation +{ + using System; + using Libplanet.Crypto; + using Nekoyume.Action; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.ValidatorDelegation; + using Xunit; + + public class UnjailValidatorTest : ValidatorDelegationTestBase + { + [Fact] + public void Serialization() + { + var action = new UnjailValidator(); + var plainValue = action.PlainValue; + + var deserialized = new UnjailValidator(); + deserialized.LoadPlainValue(plainValue); + } + + [Fact] + public void Execute() + { + // Given + var world = World; + var validatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, NCG * 10, blockHeight++); + world = EnsureValidatorToBeJailed(world, validatorPrivateKey, ref blockHeight); + + // When + var unjailValidator = new UnjailValidator(); + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight + SlashValidator.AbstainJailTime, + Signer = validatorPrivateKey.PublicKey.Address, + }; + world = unjailValidator.Execute(actionContext); + + // Then + var repository = new ValidatorRepository(world, actionContext); + var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); + Assert.False(delegatee.Jailed); + Assert.Equal(-1, delegatee.JailedUntil); + Assert.False(delegatee.Tombstoned); + } + + [Fact] + public void Unjail_NotExistedDelegatee_Throw() + { + // Given + var world = World; + var validatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + + // When + var unjailValidator = new UnjailValidator(); + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight + SlashValidator.AbstainJailTime, + Signer = validatorPrivateKey.Address, + }; + + // Then + Assert.Throws( + () => unjailValidator.Execute(actionContext)); + } + + [Fact] + public void Unjail_JaliedValidator_NotJailed_Throw() + { + // Given + var world = World; + var validatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, NCG * 10, blockHeight++); + + // When + var unjailValidator = new UnjailValidator(); + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight + SlashValidator.AbstainJailTime, + Signer = validatorPrivateKey.PublicKey.Address, + }; + + // Then + Assert.Throws( + () => unjailValidator.Execute(actionContext)); + } + + [Fact] + public void Unjail_JaliedValidator_Early_Throw() + { + // Given + var world = World; + var validatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, NCG * 10, blockHeight++); + world = EnsureValidatorToBeJailed(world, validatorPrivateKey, ref blockHeight); + + // When + var unjailValidator = new UnjailValidator(); + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight + SlashValidator.AbstainJailTime - 1, + Signer = validatorPrivateKey.PublicKey.Address, + }; + + // Then + Assert.Throws( + () => unjailValidator.Execute(actionContext)); + } + + [Fact] + public void Unjail_JaliedValidator_Tombstoned_Throw() + { + // Given + var world = World; + var validatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, NCG * 10, blockHeight++); + world = EnsureValidatorToBeTombstoned(world, validatorPrivateKey, blockHeight++); + + // When + var unjailValidator = new UnjailValidator(); + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight + SlashValidator.AbstainJailTime, + Signer = validatorPrivateKey.PublicKey.Address, + }; + + // Then + Assert.Throws( + () => unjailValidator.Execute(actionContext)); + } + } +} diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 008daea725..83529d8445 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -72,5 +72,234 @@ protected static IWorld EnsureValidatorToBePromoted( }; return promoteValidator.Execute(actionContext); } + + protected static IWorld ExecuteSlashValidator( + IWorld world, + PublicKey validatorPublicKey, + BlockCommit lastCommit, + long blockHeight) + { + var slashValidator = new SlashValidator(); + var actionContext = new ActionContext + { + PreviousState = world, + Signer = validatorPublicKey.Address, + BlockIndex = blockHeight, + LastCommit = lastCommit, + }; + return slashValidator.Execute(actionContext); + } + + protected static IWorld EnsureDelegatorToBeBond( + IWorld world, + PrivateKey delegatorPrivateKey, + PrivateKey validatorPrivateKey, + FungibleAssetValue amount, + long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } + + var delegatorAddress = delegatorPrivateKey.Address; + var validatorAddress = validatorPrivateKey.Address; + var actionContext = new ActionContext + { + PreviousState = MintAsset( + world, delegatorPrivateKey, amount, blockHeight), + BlockIndex = blockHeight, + Signer = delegatorAddress, + }; + var delegatorValidator = new DelegateValidator( + validatorAddress, amount); + return delegatorValidator.Execute(actionContext); + } + + protected static IWorld EnsureDelegatorsToBeBond( + IWorld world, + PrivateKey[] delegatorPrivateKeys, + PrivateKey validatorPrivateKey, + FungibleAssetValue[] amounts, + long blockHeight) + { + if (delegatorPrivateKeys.Length != amounts.Length) + { + throw new ArgumentException( + "The length of delegatorPrivateKeys and amounts must be the same."); + } + + for (var i = 0; i < delegatorPrivateKeys.Length; i++) + { + world = EnsureDelegatorToBeBond( + world, delegatorPrivateKeys[i], validatorPrivateKey, amounts[i], blockHeight); + } + + return world; + } + + protected static IWorld EnsureValidatorToBeJailed( + IWorld world, PrivateKey validatorPrivateKey, ref long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } + + var repository = new ValidatorRepository(world, new ActionContext()); + var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); + if (delegatee.Jailed) + { + throw new ArgumentException( + "The validator is already jailed.", nameof(validatorPrivateKey)); + } + + for (var i = 0L; i <= AbstainHistory.MaxAbstainAllowance; i++) + { + var vote = CreateNullVote(validatorPrivateKey, blockHeight - 1); + var lastCommit = new BlockCommit( + height: blockHeight - 1, + round: 0, + blockHash: vote.BlockHash, + ImmutableArray.Create(vote)); + world = ExecuteSlashValidator( + world, validatorPrivateKey.PublicKey, lastCommit, blockHeight); + blockHeight++; + repository = new ValidatorRepository(world, new ActionContext()); + delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); + if (delegatee.Jailed) + { + break; + } + } + + return world; + } + + protected static IWorld EnsureValidatorToBeTombstoned( + IWorld world, PrivateKey validatorPrivateKey, long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } + + var evidence = CreateDuplicateVoteEvidence(validatorPrivateKey, blockHeight - 1); + var lastCommit = new BlockCommit( + height: blockHeight - 1, + round: 0, + blockHash: EmptyBlockHash, + ImmutableArray.Create(evidence.VoteRef)); + + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight, + Evidence = new List { evidence }, + LastCommit = lastCommit, + }; + var slashValidator = new SlashValidator(); + + return slashValidator.Execute(actionContext); + } + + protected static Vote CreateNullVote( + PrivateKey privateKey, long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } + + var power = new BigInteger(100); + var validator = new Validator(privateKey.PublicKey, power); + var blockHash = EmptyBlockHash; + var timestamp = DateTimeOffset.UtcNow; + var voteMetadata = new VoteMetadata( + height: blockHeight, + round: 0, + blockHash: blockHash, + timestamp: timestamp, + validatorPublicKey: validator.PublicKey, + validatorPower: power, + flag: VoteFlag.Null); + return voteMetadata.Sign(null); + } + + protected static Vote CreateVote( + PrivateKey privateKey, long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } + + var power = new BigInteger(100); + var validator = new Validator(privateKey.PublicKey, power); + var blockHash = EmptyBlockHash; + var timestamp = DateTimeOffset.UtcNow; + var voteMetadata = new VoteMetadata( + height: blockHeight, + round: 0, + blockHash: blockHash, + timestamp: timestamp, + validatorPublicKey: validator.PublicKey, + validatorPower: power, + flag: VoteFlag.PreCommit); + return voteMetadata.Sign(privateKey); + } + + protected static BlockCommit CreateLastCommit( + PrivateKey privateKey, long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } + + var vote = CreateVote(privateKey, blockHeight); + return new BlockCommit( + height: blockHeight, + round: 0, + blockHash: vote.BlockHash, + ImmutableArray.Create(vote)); + } + + protected static DuplicateVoteEvidence CreateDuplicateVoteEvidence( + PrivateKey validatorPrivateKey, long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } + + var validatorSet = new ValidatorSet(new List + { + new (validatorPrivateKey.PublicKey, new BigInteger(1000)), + }); + var vote1 = new VoteMetadata( + height: blockHeight, + round: 0, + blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x01)), + timestamp: DateTimeOffset.UtcNow, + validatorPublicKey: validatorPrivateKey.PublicKey, + validatorPower: BigInteger.One, + flag: VoteFlag.PreCommit).Sign(validatorPrivateKey); + var vote2 = new VoteMetadata( + height: blockHeight, + round: 0, + blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x02)), + timestamp: DateTimeOffset.UtcNow, + validatorPublicKey: validatorPrivateKey.PublicKey, + validatorPower: BigInteger.One, + flag: VoteFlag.PreCommit).Sign(validatorPrivateKey); + var evidence = new DuplicateVoteEvidence( + vote1, + vote2, + validatorSet, + vote1.Timestamp); + + return evidence; + } } } From d34d90332aa5a19b279468323bef97f2a433f632 Mon Sep 17 00:00:00 2001 From: s2quake Date: Tue, 24 Sep 2024 11:04:31 +0900 Subject: [PATCH 048/206] test: Add delegation-related test code --- .../DelegateValidatorTest.cs | 26 ++- .../RedelegateValidatorTest.cs | 159 +++++++++++++++++- .../UndelegateValidatorTest.cs | 129 +++++++++++++- 3 files changed, 311 insertions(+), 3 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index 5ad8613c00..a7b71a632e 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -6,13 +6,14 @@ namespace Lib9c.Tests.Action.ValidatorDelegation using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; + using Nekoyume.Action; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.ValidatorDelegation; using Xunit; - public class DelegateValidatorTest + public class DelegateValidatorTest : ValidatorDelegationTestBase { [Fact] public void Serialization() @@ -138,5 +139,28 @@ public void CannotDelegateWithInsufficientBalance() Signer = publicKey.Address, })); } + + [Fact] + public void CannotDelegateToInvalidValidator() + { + // Given + var world = World; + var validatorPrivateKey = new PrivateKey(); + var delegatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + world = MintAsset(world, delegatorPrivateKey, NCG * 100, blockHeight++); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + Signer = delegatorPrivateKey.Address, + }; + var delegateValidator = new DelegateValidator(validatorPrivateKey.Address, NCG * 10); + + // Then + Assert.Throws( + () => delegateValidator.Execute(actionContext)); + } } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs index 1f5ed59d0e..ee4c8b73da 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs @@ -1,18 +1,20 @@ namespace Lib9c.Tests.Action.ValidatorDelegation { + using System; using System.Numerics; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; + using Nekoyume.Action; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.ValidatorDelegation; using Xunit; - public class RedelegateValidatorTest + public class RedelegateValidatorTest : ValidatorDelegationTestBase { [Fact] public void Serialization() @@ -86,5 +88,160 @@ public void Execute() Assert.Equal((gg * 20).RawValue, dstValidator.TotalShares); Assert.Equal((gg * 20).RawValue, dstValidator.Power); } + + [Fact] + public void Redelegate_ToInvalidValidator_Throw() + { + // Given + var world = World; + var validatorPrivateKey = new PrivateKey(); + var delegatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, NCG * 10, blockHeight++); + world = EnsureDelegatorToBeBond( + world, delegatorPrivateKey, validatorPrivateKey, NCG * 10, blockHeight++); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = delegatorPrivateKey.Address, + }; + var redelegateValidator = new RedelegateValidator( + validatorPrivateKey.PublicKey.Address, + new PrivateKey().PublicKey.Address, + 10); + + // Then + Assert.Throws( + () => redelegateValidator.Execute(actionContext)); + } + + [Theory] + [InlineData(0)] + [InlineData(-1)] + public void Redelegate_NotPositiveShare_Throw(long share) + { + // Given + var world = World; + var validatorPrivateKey1 = new PrivateKey(); + var validatorPrivateKey2 = new PrivateKey(); + var delegatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey1, NCG * 10, blockHeight++); + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey2, NCG * 10, blockHeight++); + world = EnsureDelegatorToBeBond( + world, delegatorPrivateKey, validatorPrivateKey1, NCG * 10, blockHeight++); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = delegatorPrivateKey.Address, + }; + var redelegateValidator = new RedelegateValidator( + validatorPrivateKey1.PublicKey.Address, + validatorPrivateKey2.PublicKey.Address, + share); + + // Then + Assert.Throws( + () => redelegateValidator.Execute(actionContext)); + } + + [Fact] + public void Redelegate_OverShare_Throw() + { + // Given + var world = World; + var validatorPrivateKey1 = new PrivateKey(); + var validatorPrivateKey2 = new PrivateKey(); + var delegatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey1, NCG * 10, blockHeight++); + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey2, NCG * 10, blockHeight++); + world = EnsureDelegatorToBeBond( + world, delegatorPrivateKey, validatorPrivateKey1, NCG * 10, blockHeight++); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = delegatorPrivateKey.Address, + }; + var repository = new ValidatorRepository(world, actionContext); + var delegatee = repository.GetDelegatee(validatorPrivateKey1.PublicKey.Address); + var bond = repository.GetBond(delegatee, delegatorPrivateKey.Address); + var redelegateValidator = new RedelegateValidator( + validatorPrivateKey1.PublicKey.Address, + validatorPrivateKey2.PublicKey.Address, + bond.Share + 1); + + // Then + Assert.Throws( + () => redelegateValidator.Execute(actionContext)); + } + + [Fact] + public void Redelegate_FromJailedValidator_Throw() + { + // Given + var world = World; + var delegatorPrivateKey = new PrivateKey(); + var validatorPrivateKey1 = new PrivateKey(); + var validatorPrivateKey2 = new PrivateKey(); + var blockHeight = 1L; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey1, NCG * 10, blockHeight++); + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey2, NCG * 10, blockHeight++); + world = EnsureDelegatorToBeBond( + world, delegatorPrivateKey, validatorPrivateKey1, NCG * 10, blockHeight++); + world = EnsureValidatorToBeJailed( + world, validatorPrivateKey1, ref blockHeight); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + Signer = delegatorPrivateKey.Address, + BlockIndex = blockHeight++, + }; + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee1 = expectedRepository.GetValidatorDelegatee( + validatorPrivateKey1.PublicKey.Address); + var expectedDelegatee2 = expectedRepository.GetValidatorDelegatee( + validatorPrivateKey2.PublicKey.Address); + var expectedBond1 = expectedRepository.GetBond( + expectedDelegatee1, delegatorPrivateKey.Address); + var expectedBond2 = expectedRepository.GetBond( + expectedDelegatee2, delegatorPrivateKey.Address); + + var redelegateValidator = new RedelegateValidator( + validatorPrivateKey1.Address, validatorPrivateKey2.Address, 10); + world = redelegateValidator.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee1 = actualRepository.GetValidatorDelegatee( + validatorPrivateKey1.PublicKey.Address); + var actualDelegatee2 = actualRepository.GetValidatorDelegatee( + validatorPrivateKey2.PublicKey.Address); + var actualBond1 = actualRepository.GetBond( + actualDelegatee1, delegatorPrivateKey.Address); + var actualBond2 = actualRepository.GetBond( + actualDelegatee2, delegatorPrivateKey.Address); + + Assert.Equal(expectedBond1.Share - 10, actualBond1.Share); + Assert.Equal(expectedBond2.Share + 10, actualBond2.Share); + } } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 3a9afdd9a9..2a78ba198e 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -1,18 +1,20 @@ namespace Lib9c.Tests.Action.ValidatorDelegation { + using System; using System.Numerics; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; + using Nekoyume.Action; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.ValidatorDelegation; using Xunit; - public class UndelegateValidatorTest + public class UndelegateValidatorTest : ValidatorDelegationTestBase { [Fact] public void Serialization() @@ -80,5 +82,130 @@ public void Execute() Assert.Equal(gg * 100, world.GetBalance(publicKey.Address, gg)); } + + [Fact] + public void Undelegate_FromInvalidValidtor_Throw() + { + // Given + var world = World; + var delegatorPrivateKey = new PrivateKey(); + var validatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, NCG * 10, blockHeight++); + world = EnsureDelegatorToBeBond( + world, delegatorPrivateKey, validatorPrivateKey, NCG * 10, blockHeight++); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + Signer = delegatorPrivateKey.Address, + BlockIndex = blockHeight++, + }; + var undelegateValidator = new UndelegateValidator( + new PrivateKey().Address, 10); + + // Then + Assert.Throws( + () => undelegateValidator.Execute(actionContext)); + } + + [Theory] + [InlineData(0)] + [InlineData(-1)] + public void Undelegate_NotPositiveShare_Throw(long share) + { + // Given + var world = World; + var delegatorPrivateKey = new PrivateKey(); + var validatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, NCG * 10, blockHeight++); + world = EnsureDelegatorToBeBond( + world, delegatorPrivateKey, validatorPrivateKey, NCG * 10, blockHeight++); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + Signer = delegatorPrivateKey.Address, + BlockIndex = blockHeight++, + }; + var undelegateValidator = new UndelegateValidator( + validatorPrivateKey.Address, share); + + // Then + Assert.Throws( + () => undelegateValidator.Execute(actionContext)); + } + + [Fact] + public void Undelegate_NotDelegated_Throw() + { + // Given + var world = World; + var delegatorPrivateKey = new PrivateKey(); + var validatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, NCG * 10, blockHeight++); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + Signer = delegatorPrivateKey.Address, + BlockIndex = blockHeight++, + }; + var undelegateValidator = new UndelegateValidator( + validatorPrivateKey.Address, 10); + + // Then + Assert.Throws( + () => undelegateValidator.Execute(actionContext)); + } + + [Fact] + public void Undelegate_FromJailedValidator() + { + // Given + var world = World; + var delegatorPrivateKey = new PrivateKey(); + var validatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, NCG * 10, blockHeight++); + world = EnsureDelegatorToBeBond( + world, delegatorPrivateKey, validatorPrivateKey, NCG * 10, blockHeight++); + world = EnsureValidatorToBeJailed( + world, validatorPrivateKey, ref blockHeight); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + Signer = delegatorPrivateKey.Address, + BlockIndex = blockHeight, + }; + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee( + validatorPrivateKey.PublicKey.Address); + var expectedBond = expectedRepository.GetBond( + expectedDelegatee, delegatorPrivateKey.Address); + + var undelegateValidator = new UndelegateValidator( + validatorPrivateKey.Address, 10); + world = undelegateValidator.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepository.GetValidatorDelegatee( + validatorPrivateKey.PublicKey.Address); + var actualBond = actualRepository.GetBond(actualDelegatee, delegatorPrivateKey.Address); + + Assert.Equal(expectedBond.Share - 10, actualBond.Share); + } } } From 3abe3300cebc40d859aa40ff638103205fb5f6de Mon Sep 17 00:00:00 2001 From: s2quake Date: Tue, 24 Sep 2024 11:05:36 +0900 Subject: [PATCH 049/206] test: Add RecordProposer, Update/Release Validator test code --- .../ValidatorDelegation/RecordProposerTest.cs | 50 ++++++++++++++ .../ReleaseValidatorUnbondingsTest.cs | 20 ++++++ .../UpdateValidatorsTest.cs | 69 +++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/RecordProposerTest.cs create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RecordProposerTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RecordProposerTest.cs new file mode 100644 index 0000000000..efda15a76c --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RecordProposerTest.cs @@ -0,0 +1,50 @@ +#nullable enable +namespace Lib9c.Tests.Action.ValidatorDelegation +{ + using System; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.Module.ValidatorDelegation; + using Nekoyume.ValidatorDelegation; + using Xunit; + + public class RecordProposerTest : ValidatorDelegationTestBase + { + [Fact] + public void Serialization() + { + var action = new RecordProposer(); + var plainValue = action.PlainValue; + + var deserialized = new RecordProposer(); + deserialized.LoadPlainValue(plainValue); + } + + [Fact] + public void Execute() + { + // Given + var world = World; + var minerPrivateKey = new PrivateKey(); + var blockIndex = (long)Random.Shared.Next(1, 100); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockIndex++, + Miner = minerPrivateKey.Address, + }; + var recordProposer = new RecordProposer(); + world = recordProposer.Execute(actionContext); + + // Then + var repository = new ValidatorRepository(world, actionContext); + var proposerInfo = repository.GetProposerInfo(); + + Assert.Equal(blockIndex - 1, proposerInfo.BlockIndex); + Assert.Equal(minerPrivateKey.Address, proposerInfo.Proposer); + } + } +} diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs new file mode 100644 index 0000000000..62b7569597 --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs @@ -0,0 +1,20 @@ +#nullable enable +namespace Lib9c.Tests.Action.ValidatorDelegation +{ + using System; + using Nekoyume.Action.ValidatorDelegation; + using Xunit; + + public class ReleaseValidatorUnbondingsTest + { + [Fact] + public void Serialization() + { + var action = new ReleaseValidatorUnbondings(); + var plainValue = action.PlainValue; + + var deserialized = new ReleaseValidatorUnbondings(); + deserialized.LoadPlainValue(plainValue); + } + } +} diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs new file mode 100644 index 0000000000..22a924dc08 --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs @@ -0,0 +1,69 @@ +#nullable enable +namespace Lib9c.Tests.Action.ValidatorDelegation +{ + using System; + using System.Linq; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.Model.State; + using Nekoyume.ValidatorDelegation; + using Xunit; + + public class UpdateValidatorsTest : ValidatorDelegationTestBase + { + [Fact] + public void Serialization() + { + var action = new UpdateValidators(); + var plainValue = action.PlainValue; + + var deserialized = new UpdateValidators(); + deserialized.LoadPlainValue(plainValue); + } + + [Fact] + public void Execute() + { + const int length = 10; + var world = World; + var privateKeys = GetRandomArray(length, _ => new PrivateKey()); + var favs = GetRandomArray(length, i => NCG * Random.Shared.Next(1, length + 1)); + + for (int i = 0; i < length; i++) + { + var signer = privateKeys[i]; + var fav = favs[i]; + var promoteValidator = new PromoteValidator(signer.PublicKey, fav); + var actionContext = new ActionContext + { + PreviousState = world.MintAsset(new ActionContext(), signer.Address, NCG * 1000), + Signer = signer.Address, + BlockIndex = 10L, + }; + world = promoteValidator.Execute(actionContext); + } + + var blockActionContext = new ActionContext + { + BlockIndex = 10L, + PreviousState = world, + Signer = AdminKey.Address, + }; + var expectedRepository = new ValidatorRepository(world, blockActionContext); + var expectedValidators = expectedRepository.GetValidatorList() + .GetBonded().OrderBy(item => item.OperatorAddress).ToList(); + + world = new UpdateValidators().Execute(blockActionContext); + + var actualValidators = world.GetValidatorSet().Validators; + Assert.Equal(expectedValidators.Count, actualValidators.Count); + for (var i = 0; i < expectedValidators.Count; i++) + { + var expectedValidator = expectedValidators[i]; + var actualValidator = actualValidators[i]; + Assert.Equal(expectedValidator, actualValidator); + } + } + } +} From d3656071237a45132ea5d891d1a007bcb36b1f63 Mon Sep 17 00:00:00 2001 From: s2quake Date: Tue, 24 Sep 2024 11:06:15 +0900 Subject: [PATCH 050/206] test: Add test for ClaimRewardValidator --- .../ClaimRewardValidatorTest.cs | 303 ++++++++++++++++++ .../ValidatorDelegationTestBase.cs | 59 ++++ 2 files changed, 362 insertions(+) create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs new file mode 100644 index 0000000000..98048b2236 --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -0,0 +1,303 @@ +#nullable enable +namespace Lib9c.Tests.Action.ValidatorDelegation +{ + using System; + using System.Linq; + using System.Numerics; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Types.Assets; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.ValidatorDelegation; + using Xunit; + + public class ClaimRewardValidatorTest : ValidatorDelegationTestBase + { + [Fact] + public void Serialization() + { + var action = new ClaimRewardValidator(); + var plainValue = action.PlainValue; + + var deserialized = new ClaimRewardValidator(); + deserialized.LoadPlainValue(plainValue); + } + + [Fact] + public void Execute() + { + // Given + var world = World; + var validatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + ActionContext actionContext; + var validatorGold = NCG * 10; + var allocatedReward = NCG * 100; + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, validatorGold, blockHeight++); + world = EnsureValidatorToBeAllocatedReward( + world, validatorPrivateKey, allocatedReward, ref blockHeight); + + // When + var expectedBalance = allocatedReward; + var lastCommit = CreateLastCommit(validatorPrivateKey, blockHeight - 1); + var claimRewardValidator = new ClaimRewardValidator(validatorPrivateKey.Address); + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = validatorPrivateKey.Address, + LastCommit = lastCommit, + }; + world = claimRewardValidator.Execute(actionContext); + + // Then + var actualBalance = world.GetBalance(validatorPrivateKey.Address, NCG); + + Assert.Equal(expectedBalance, actualBalance); + } + + [Theory] + [InlineData(33.33)] + [InlineData(11.11)] + [InlineData(10)] + [InlineData(1)] + public void Execute_OneDelegator(double reward) + { + // Given + var world = World; + var validatorPrivateKey = new PrivateKey(); + var delegatorPrivateKey = new PrivateKey(); + var blockHeight = 1L; + var actionContext = new ActionContext { }; + var promotedGold = NCG * 10; + var allocatedReward = FungibleAssetValue.Parse(NCG, $"{reward:R}"); + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, promotedGold, blockHeight++); + world = EnsureDelegatorToBeBond( + world, delegatorPrivateKey, validatorPrivateKey, NCG * 10, blockHeight++); + world = EnsureValidatorToBeAllocatedReward( + world, validatorPrivateKey, allocatedReward, ref blockHeight); + + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee( + validatorPrivateKey.Address); + var expectedCommission = GetCommission( + allocatedReward, expectedDelegatee.CommissionPercentage); + var expectedReward = allocatedReward - expectedCommission; + var expectedValidatorBalance = expectedCommission + expectedReward.DivRem(2).Quotient; + var expectedDelegatorBalance = expectedReward.DivRem(2).Quotient; + var expectedRemainReward = allocatedReward; + expectedRemainReward -= expectedValidatorBalance; + expectedRemainReward -= expectedDelegatorBalance; + + var lastCommit = CreateLastCommit(validatorPrivateKey, blockHeight - 1); + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = validatorPrivateKey.Address, + LastCommit = lastCommit, + }; + world = new ClaimRewardValidator(validatorPrivateKey.Address).Execute(actionContext); + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = delegatorPrivateKey.Address, + LastCommit = lastCommit, + }; + world = new ClaimRewardValidator(validatorPrivateKey.Address).Execute(actionContext); + + // Then + var repository = new ValidatorRepository(world, actionContext); + var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); + var actualRemainReward = world.GetBalance(delegatee.RewardDistributorAddress, NCG); + var actualValidatorBalance = world.GetBalance(validatorPrivateKey.Address, NCG); + var actualDelegatorBalance = world.GetBalance(delegatorPrivateKey.Address, NCG); + + Assert.Equal(expectedRemainReward, actualRemainReward); + Assert.Equal(expectedValidatorBalance, actualValidatorBalance); + Assert.Equal(expectedDelegatorBalance, actualDelegatorBalance); + } + + [Fact] + public void Execute_TwoDelegators() + { + // Given + var world = World; + var validatorPrivateKey = new PrivateKey(); + var delegatorPrivateKey1 = new PrivateKey(); + var delegatorPrivateKey2 = new PrivateKey(); + var blockHeight = 1L; + var actionContext = new ActionContext { }; + var promotedGold = NCG * 10; + var allocatedReward = FungibleAssetValue.Parse(NCG, $"{34.27:R}"); + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, promotedGold, blockHeight++); + world = EnsureDelegatorToBeBond( + world, delegatorPrivateKey1, validatorPrivateKey, NCG * 10, blockHeight++); + world = EnsureDelegatorToBeBond( + world, delegatorPrivateKey2, validatorPrivateKey, NCG * 10, blockHeight++); + world = EnsureValidatorToBeAllocatedReward( + world, validatorPrivateKey, allocatedReward, ref blockHeight); + + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee( + validatorPrivateKey.Address); + var expectedCommission = GetCommission( + allocatedReward, expectedDelegatee.CommissionPercentage); + var expectedReward = allocatedReward - expectedCommission; + var expectedValidatorBalance = expectedCommission + expectedReward.DivRem(3).Quotient; + var expectedDelegator1Balance = expectedReward.DivRem(3).Quotient; + var expectedDelegator2Balance = expectedReward.DivRem(3).Quotient; + var expectedRemainReward = allocatedReward; + expectedRemainReward -= expectedValidatorBalance; + expectedRemainReward -= expectedDelegator1Balance; + expectedRemainReward -= expectedDelegator2Balance; + + var lastCommit = CreateLastCommit(validatorPrivateKey, blockHeight - 1); + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = validatorPrivateKey.Address, + LastCommit = lastCommit, + }; + world = new ClaimRewardValidator(validatorPrivateKey.Address).Execute(actionContext); + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = delegatorPrivateKey1.Address, + LastCommit = lastCommit, + }; + world = new ClaimRewardValidator(validatorPrivateKey.Address).Execute(actionContext); + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = delegatorPrivateKey2.Address, + LastCommit = lastCommit, + }; + world = new ClaimRewardValidator(validatorPrivateKey.Address).Execute(actionContext); + + // Then + var repository = new ValidatorRepository(world, actionContext); + var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); + var actualRemainReward = world.GetBalance(delegatee.RewardDistributorAddress, NCG); + var actualValidatorBalance = world.GetBalance(validatorPrivateKey.Address, NCG); + var actualDelegator1Balance = world.GetBalance(delegatorPrivateKey1.Address, NCG); + var actualDelegator2Balance = world.GetBalance(delegatorPrivateKey2.Address, NCG); + + Assert.Equal(expectedRemainReward, actualRemainReward); + Assert.Equal(expectedValidatorBalance, actualValidatorBalance); + Assert.Equal(expectedDelegator1Balance, actualDelegator1Balance); + Assert.Equal(expectedDelegator2Balance, actualDelegator2Balance); + } + + [Fact] + public void Execute_MultipleDelegators() + { + // Given + var length = Random.Shared.Next(3, 100); + var world = World; + var validatorPrivateKey = new PrivateKey(); + var delegatorPrivateKeys = GetRandomArray(length, _ => new PrivateKey()); + var delegatorNCGs = GetRandomArray(length, _ => GetRandomNCG()); + var blockHeight = 1L; + var actionContext = new ActionContext(); + var promotedGold = GetRandomNCG(); + var allocatedReward = GetRandomNCG(); + world = EnsureValidatorToBePromoted( + world, validatorPrivateKey, promotedGold, blockHeight++); + world = EnsureDelegatorsToBeBond( + world, delegatorPrivateKeys, validatorPrivateKey, delegatorNCGs, blockHeight++); + world = EnsureValidatorToBeAllocatedReward( + world, validatorPrivateKey, allocatedReward, ref blockHeight); + + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee( + validatorPrivateKey.Address); + var expectedCommission = GetCommission( + allocatedReward, expectedDelegatee.CommissionPercentage); + var expectedReward = allocatedReward - expectedCommission; + var expectedValidatorBalance = expectedCommission + CalculateReward( + expectedRepository, validatorPrivateKey, validatorPrivateKey, expectedReward); + var expectedDelegatorBalances = CalculateRewards( + expectedRepository, validatorPrivateKey, delegatorPrivateKeys, expectedReward); + var expectedRemainReward = allocatedReward; + expectedRemainReward -= expectedValidatorBalance; + for (var i = 0; i < length; i++) + { + expectedRemainReward -= expectedDelegatorBalances[i]; + } + + var lastCommit = CreateLastCommit(validatorPrivateKey, blockHeight - 1); + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = validatorPrivateKey.Address, + LastCommit = lastCommit, + }; + world = new ClaimRewardValidator(validatorPrivateKey.Address).Execute(actionContext); + for (var i = 0; i < length; i++) + { + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = delegatorPrivateKeys[i].Address, + LastCommit = lastCommit, + }; + world = new ClaimRewardValidator(validatorPrivateKey.Address).Execute(actionContext); + } + + // Then + var repository = new ValidatorRepository(world, actionContext); + var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); + var actualRemainReward = world.GetBalance(delegatee.RewardDistributorAddress, NCG); + var actualValidatorBalance = world.GetBalance(validatorPrivateKey.Address, NCG); + Assert.Equal(expectedRemainReward, actualRemainReward); + Assert.Equal(expectedValidatorBalance, actualValidatorBalance); + + for (var i = 0; i < length; i++) + { + var actualDelegatorBalance = world.GetBalance(delegatorPrivateKeys[i].Address, NCG); + Assert.Equal(expectedDelegatorBalances[i], actualDelegatorBalance); + } + } + + private static FungibleAssetValue CalculateReward( + ValidatorRepository repository, + PrivateKey validatorPrivateKey, + PrivateKey delegatorPrivateKey, + FungibleAssetValue reward) + { + var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); + var bond = repository.GetBond(delegatee, delegatorPrivateKey.Address); + return CalculateReward(reward, bond.Share, delegatee.TotalShares); + } + + private static FungibleAssetValue[] CalculateRewards( + ValidatorRepository repository, + PrivateKey validatorPrivateKey, + PrivateKey[] delegatorPrivateKeys, + FungibleAssetValue reward) + { + return delegatorPrivateKeys + .Select(item => CalculateReward(repository, validatorPrivateKey, item, reward)) + .ToArray(); + } + + private static FungibleAssetValue CalculateReward( + FungibleAssetValue reward, BigInteger share, BigInteger totalShares) + { + return (reward * share).DivRem(totalShares).Quotient; + } + } +} diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 83529d8445..5f1df83ae9 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -203,6 +203,51 @@ protected static IWorld EnsureValidatorToBeTombstoned( return slashValidator.Execute(actionContext); } + protected static IWorld EnsureValidatorToBeAllocatedReward( + IWorld world, + PrivateKey validatorPrivateKey, + FungibleAssetValue reward, + ref long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } + + var actionContext1 = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = validatorPrivateKey.Address, + Miner = validatorPrivateKey.Address, + }; + world = new RecordProposer().Execute(actionContext1); + + var lastCommit2 = CreateLastCommit(validatorPrivateKey, blockHeight - 1); + var actionContext2 = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = validatorPrivateKey.Address, + LastCommit = lastCommit2, + }; + world = world.MintAsset(actionContext2, GoldCurrencyState.Address, reward); + world = world.TransferAsset( + actionContext2, GoldCurrencyState.Address, Addresses.RewardPool, reward); + + var lastCommit3 = CreateLastCommit(validatorPrivateKey, blockHeight - 1); + var actionContext3 = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = validatorPrivateKey.Address, + LastCommit = lastCommit3, + }; + world = new AllocateReward().Execute(actionContext3); + + return world; + } + protected static Vote CreateNullVote( PrivateKey privateKey, long blockHeight) { @@ -301,5 +346,19 @@ protected static DuplicateVoteEvidence CreateDuplicateVoteEvidence( return evidence; } + + protected static FungibleAssetValue GetCommission( + FungibleAssetValue fav, BigInteger percentage) + => (fav * percentage).DivRem(100).Quotient; + + protected static FungibleAssetValue GetWithoutCommission( + FungibleAssetValue fav, BigInteger percentage) + => fav - (fav * percentage).DivRem(100).Quotient; + + protected static FungibleAssetValue GetRandomNCG() + { + var value = Math.Round(Random.Shared.Next(1, 100000) / 100.0, 2); + return FungibleAssetValue.Parse(NCG, $"{value:R}"); + } } } From 9db4d2739f499d6439e58cf5813bd6ee94978873 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 25 Sep 2024 00:56:27 +0900 Subject: [PATCH 051/206] feat: Clear reward remainder --- .../ValidatorDelegation/AllocateRewardTest.cs | 4 +- .../ClaimRewardValidatorTest.cs | 28 ++++--- .Lib9c.Tests/Delegation/DelegateeTest.cs | 33 +++++++- .Lib9c.Tests/Delegation/DelegationFixture.cs | 19 +++++ .Lib9c.Tests/Delegation/DelegatorTest.cs | 66 ++++++++-------- .Lib9c.Tests/Delegation/DummyDelegatee.cs | 1 + .Lib9c.Tests/Delegation/TestDelegatee.cs | 1 + Lib9c/Delegation/Delegatee.cs | 79 ++++++++++++++++--- Lib9c/Delegation/DelegateeMetadata.cs | 35 ++++---- Lib9c/Delegation/DelegationAddress.cs | 23 ++---- Lib9c/Delegation/DelegationRepository.cs | 12 --- Lib9c/Delegation/IDelegatee.cs | 6 +- Lib9c/Delegation/IDelegateeMetadata.cs | 6 +- Lib9c/Delegation/IDelegationRepository.cs | 2 - Lib9c/Delegation/LumpSumRewardsRecord.cs | 31 ++++++-- Lib9c/Model/Guild/Guild.cs | 1 + .../ValidatorDelegation/ValidatorDelegatee.cs | 3 +- 17 files changed, 232 insertions(+), 118 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs index 0f875581c9..f5fb7ef470 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -121,7 +121,7 @@ FungibleAssetValue commission if (vote.Flag == VoteFlag.Null) { Assert.Equal(initialFAV, world.GetBalance(vote.ValidatorPublicKey.Address, ncg)); - Assert.Equal(ncg * 0, world.GetBalance(validator.RewardDistributorAddress, ncg)); + Assert.Equal(ncg * 0, world.GetBalance(validator.CurrentLumpSumRewardsRecordAddress(), ncg)); continue; } @@ -136,7 +136,7 @@ FungibleAssetValue commission Assert.Equal(commission + initialFAV, world.GetBalance(vote.ValidatorPublicKey.Address, ncg)); } - Assert.Equal(rewardAllocated - commission, world.GetBalance(validator.RewardDistributorAddress, ncg)); + Assert.Equal(rewardAllocated - commission, world.GetBalance(validator.CurrentLumpSumRewardsRecordAddress(), ncg)); } } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs index 98048b2236..17f5738248 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; + using Nekoyume; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.ValidatorDelegation; using Xunit; @@ -113,7 +114,7 @@ public void Execute_OneDelegator(double reward) // Then var repository = new ValidatorRepository(world, actionContext); var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); - var actualRemainReward = world.GetBalance(delegatee.RewardDistributorAddress, NCG); + var actualRemainReward = world.GetBalance(delegatee.CurrentLumpSumRewardsRecordAddress(), NCG); var actualValidatorBalance = world.GetBalance(validatorPrivateKey.Address, NCG); var actualDelegatorBalance = world.GetBalance(delegatorPrivateKey.Address, NCG); @@ -153,10 +154,11 @@ public void Execute_TwoDelegators() var expectedValidatorBalance = expectedCommission + expectedReward.DivRem(3).Quotient; var expectedDelegator1Balance = expectedReward.DivRem(3).Quotient; var expectedDelegator2Balance = expectedReward.DivRem(3).Quotient; - var expectedRemainReward = allocatedReward; - expectedRemainReward -= expectedValidatorBalance; - expectedRemainReward -= expectedDelegator1Balance; - expectedRemainReward -= expectedDelegator2Balance; + var expectedRemainReward = expectedDelegatee.RewardCurrency * 0; + var expectedCommunityBalance = allocatedReward; + expectedCommunityBalance -= expectedValidatorBalance; + expectedCommunityBalance -= expectedDelegator1Balance; + expectedCommunityBalance -= expectedDelegator2Balance; var lastCommit = CreateLastCommit(validatorPrivateKey, blockHeight - 1); actionContext = new ActionContext @@ -187,12 +189,14 @@ public void Execute_TwoDelegators() // Then var repository = new ValidatorRepository(world, actionContext); var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); - var actualRemainReward = world.GetBalance(delegatee.RewardDistributorAddress, NCG); + var actualRemainReward = world.GetBalance(delegatee.CurrentLumpSumRewardsRecordAddress(), NCG); var actualValidatorBalance = world.GetBalance(validatorPrivateKey.Address, NCG); var actualDelegator1Balance = world.GetBalance(delegatorPrivateKey1.Address, NCG); var actualDelegator2Balance = world.GetBalance(delegatorPrivateKey2.Address, NCG); + var actualCommunityBalance = world.GetBalance(Addresses.CommunityPool, NCG); Assert.Equal(expectedRemainReward, actualRemainReward); + Assert.Equal(expectedCommunityBalance, actualCommunityBalance); Assert.Equal(expectedValidatorBalance, actualValidatorBalance); Assert.Equal(expectedDelegator1Balance, actualDelegator1Balance); Assert.Equal(expectedDelegator2Balance, actualDelegator2Balance); @@ -229,13 +233,15 @@ public void Execute_MultipleDelegators() expectedRepository, validatorPrivateKey, validatorPrivateKey, expectedReward); var expectedDelegatorBalances = CalculateRewards( expectedRepository, validatorPrivateKey, delegatorPrivateKeys, expectedReward); - var expectedRemainReward = allocatedReward; - expectedRemainReward -= expectedValidatorBalance; + var expectedCommunityBalance = allocatedReward; + expectedCommunityBalance -= expectedValidatorBalance; for (var i = 0; i < length; i++) { - expectedRemainReward -= expectedDelegatorBalances[i]; + expectedCommunityBalance -= expectedDelegatorBalances[i]; } + var expectedRemainReward = expectedDelegatee.RewardCurrency * 0; + var lastCommit = CreateLastCommit(validatorPrivateKey, blockHeight - 1); actionContext = new ActionContext { @@ -260,10 +266,12 @@ public void Execute_MultipleDelegators() // Then var repository = new ValidatorRepository(world, actionContext); var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); - var actualRemainReward = world.GetBalance(delegatee.RewardDistributorAddress, NCG); + var actualRemainReward = world.GetBalance(delegatee.CurrentLumpSumRewardsRecordAddress(), NCG); var actualValidatorBalance = world.GetBalance(validatorPrivateKey.Address, NCG); + var actualCommunityBalance = world.GetBalance(Addresses.CommunityPool, NCG); Assert.Equal(expectedRemainReward, actualRemainReward); Assert.Equal(expectedValidatorBalance, actualValidatorBalance); + Assert.Equal(expectedCommunityBalance, actualCommunityBalance); for (var i = 0; i < length; i++) { diff --git a/.Lib9c.Tests/Delegation/DelegateeTest.cs b/.Lib9c.Tests/Delegation/DelegateeTest.cs index a8edff2fe9..180dee62d9 100644 --- a/.Lib9c.Tests/Delegation/DelegateeTest.cs +++ b/.Lib9c.Tests/Delegation/DelegateeTest.cs @@ -204,6 +204,32 @@ public void CannotUnbondInvalidDelegator() _fixture.DummyDelegator1, BigInteger.One, 10L)); } + [Fact] + public void ClearRemainderRewards() + { + var repo = _fixture.TestRepository; + var testDelegatee = _fixture.TestDelegatee1; + var testDelegator1 = _fixture.TestDelegator1; + var testDelegator2 = _fixture.TestDelegator2; + + var bonding1 = testDelegatee.DelegationCurrency * 3; + var bonding2 = testDelegatee.DelegationCurrency * 8; + + var bondedShare1 = testDelegatee.Bond(testDelegator1, bonding1, 10L); + var bondedShare2 = testDelegatee.Bond(testDelegator2, bonding2, 10L); + + repo.MintAsset(testDelegatee.RewardPoolAddress, testDelegatee.RewardCurrency * 10); + testDelegatee.CollectRewards(11L); + + testDelegatee.DistributeReward(testDelegator1, 11L); + var remainder = repo.GetBalance(DelegationFixture.FixedPoolAddress, testDelegatee.RewardCurrency); + Assert.Equal(testDelegatee.RewardCurrency * 0, remainder); + + testDelegatee.DistributeReward(testDelegator2, 11L); + remainder = repo.GetBalance(DelegationFixture.FixedPoolAddress, testDelegatee.RewardCurrency); + Assert.Equal(new FungibleAssetValue(testDelegatee.RewardCurrency, 0, 1), remainder); + } + [Fact] public void AddressConsistency() { @@ -245,8 +271,11 @@ public void AddressConsistency() Assert.Equal(testDelegatee1.Address, dummyDelegatee1.Address); Assert.NotEqual( - testDelegatee1.RewardDistributorAddress, - dummyDelegatee1.RewardDistributorAddress); + testDelegatee1.CurrentLumpSumRewardsRecordAddress(), + dummyDelegatee1.CurrentLumpSumRewardsRecordAddress()); + Assert.NotEqual( + testDelegatee1.LumpSumRewardsRecordAddress(1L), + dummyDelegatee1.LumpSumRewardsRecordAddress(1L)); Assert.NotEqual( testDelegatee1.BondAddress(testDelegator1.Address), dummyDelegatee1.BondAddress(testDelegator1.Address)); diff --git a/.Lib9c.Tests/Delegation/DelegationFixture.cs b/.Lib9c.Tests/Delegation/DelegationFixture.cs index 7f9b712514..4b6a94241f 100644 --- a/.Lib9c.Tests/Delegation/DelegationFixture.cs +++ b/.Lib9c.Tests/Delegation/DelegationFixture.cs @@ -60,5 +60,24 @@ public DelegationFixture() public DummyDelegatee DummyDelegatee1 { get; } public DummyDelegator DummyDelegator1 { get; } + + public static FungibleAssetValue TotalRewardsOfRecords(IDelegatee delegatee, IDelegationRepository repo) + { + var reward = delegatee.RewardCurrency * 0; + var record = repo.GetCurrentLumpSumRewardsRecord(delegatee); + while (true) + { + reward += repo.World.GetBalance(record.Address, delegatee.RewardCurrency); + + if (record.LastStartHeight is null) + { + break; + } + + record = repo.GetLumpSumRewardsRecord(delegatee, record.LastStartHeight.Value); + } + + return reward; + } } } diff --git a/.Lib9c.Tests/Delegation/DelegatorTest.cs b/.Lib9c.Tests/Delegation/DelegatorTest.cs index a0427a5c02..1f8cab0c87 100644 --- a/.Lib9c.Tests/Delegation/DelegatorTest.cs +++ b/.Lib9c.Tests/Delegation/DelegatorTest.cs @@ -298,9 +298,9 @@ public void RewardOnDelegate() repo.MintAsset(delegator2.Address, delegatorInitialBalance); var reward = delegatee.DelegationCurrency * 100; - repo.MintAsset(delegatee.RewardDistributorAddress, reward); + repo.MintAsset(delegatee.RewardPoolAddress, reward); // EndBlock after delegatee's reward - repo.AddLumpSumRewards(delegatee, 10L, reward); + delegatee.CollectRewards(10L); var delegatingFAV1 = delegatee.DelegationCurrency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 10L); @@ -318,28 +318,28 @@ public void RewardOnDelegate() var totalShares = delegatee.TotalShares; - repo.MintAsset(delegatee.RewardDistributorAddress, reward); + repo.MintAsset(delegatee.RewardPoolAddress, reward); // EndBlock after delegatee's reward - repo.AddLumpSumRewards(delegatee, 10L, reward); + delegatee.CollectRewards(10L); delegatingFAV1 = delegatee.DelegationCurrency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 11L); delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); - var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); + var collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var reward1 = (reward * share1).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 * 2 + reward1, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); - Assert.Equal(reward * 2 - reward1, rewardPoolBalance); + Assert.Equal(reward * 2 - reward1, collectedRewards); delegator2.Delegate(delegatee, delegatingFAV2, 11L); delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); - rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); + collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var reward2 = (reward * share2).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 * 2 + reward1, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2 * 2 + reward2, delegator2Balance); - Assert.Equal(reward * 2 - reward1 - reward2, rewardPoolBalance); + Assert.Equal(delegatee.RewardCurrency * 0, collectedRewards); } [Fact] @@ -353,8 +353,8 @@ public void RewardOnUndelegate() repo.MintAsset(delegator1.Address, delegatorInitialBalance); repo.MintAsset(delegator2.Address, delegatorInitialBalance); - var reward = delegatee.DelegationCurrency * 100; - repo.MintAsset(delegatee.RewardCollectorAddress, reward); + var reward = delegatee.RewardCurrency * 100; + repo.MintAsset(delegatee.RewardPoolAddress, reward); // EndBlock after delegatee's reward delegatee.CollectRewards(10L); @@ -374,29 +374,29 @@ public void RewardOnUndelegate() var totalShares = delegatee.TotalShares; - repo.MintAsset(delegatee.RewardDistributorAddress, reward); + repo.MintAsset(delegatee.RewardPoolAddress, reward); // EndBlock after delegatee's reward - repo.AddLumpSumRewards(delegatee, 10L, reward); + delegatee.CollectRewards(10L); var shareToUndelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; delegator1.Undelegate(delegatee, shareToUndelegate, 11L); delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); - var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); + var collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var reward1 = (reward * share1).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); - Assert.Equal(reward * 2 - reward1, rewardPoolBalance); + Assert.Equal(reward * 2 - reward1, collectedRewards); shareToUndelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.Undelegate(delegatee, shareToUndelegate, 11L); delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); - rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); + collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var reward2 = (reward * share2).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2 + reward2, delegator2Balance); - Assert.Equal(reward * 2 - reward1 - reward2, rewardPoolBalance); + Assert.Equal(delegatee.RewardCurrency * 0, collectedRewards); } [Fact] @@ -411,10 +411,10 @@ public void RewardOnRedelegate() repo.MintAsset(delegator1.Address, delegatorInitialBalance); repo.MintAsset(delegator2.Address, delegatorInitialBalance); - var reward = delegatee.DelegationCurrency * 100; - repo.MintAsset(delegatee.RewardDistributorAddress, reward); + var reward = delegatee.RewardCurrency * 100; + repo.MintAsset(delegatee.RewardPoolAddress, reward); // EndBlock after delegatee's reward - repo.AddLumpSumRewards(delegatee, 10L, reward); + delegatee.CollectRewards(10L); var delegatingFAV1 = delegatee.DelegationCurrency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 10L); @@ -432,29 +432,29 @@ public void RewardOnRedelegate() var totalShares = delegatee.TotalShares; - repo.MintAsset(delegatee.RewardDistributorAddress, reward); + repo.MintAsset(delegatee.RewardPoolAddress, reward); // EndBlock after delegatee's reward - repo.AddLumpSumRewards(delegatee, 10L, reward); + delegatee.CollectRewards(10L); var shareToRedelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; delegator1.Redelegate(delegatee, dstDelegatee, shareToRedelegate, 11L); delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); - var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); + var collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var reward1 = (reward * share1).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); - Assert.Equal(reward * 2 - reward1, rewardPoolBalance); + Assert.Equal(reward * 2 - reward1, collectedRewards); shareToRedelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.Redelegate(delegatee, dstDelegatee, shareToRedelegate, 11L); delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); - rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); + collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var reward2 = (reward * share2).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2 + reward2, delegator2Balance); - Assert.Equal(reward * 2 - reward1 - reward2, rewardPoolBalance); + Assert.Equal(delegatee.RewardCurrency * 0, collectedRewards); } [Fact] @@ -470,9 +470,9 @@ public void RewardOnClaim() repo.MintAsset(delegator2.Address, delegatorInitialBalance); var reward = delegatee.DelegationCurrency * 100; - repo.MintAsset(delegatee.RewardDistributorAddress, reward); + repo.MintAsset(delegatee.RewardPoolAddress, reward); // EndBlock after delegatee's reward - repo.AddLumpSumRewards(delegatee, 10L, reward); + delegatee.CollectRewards(10L); var delegatingFAV1 = delegatee.DelegationCurrency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 10L); @@ -490,29 +490,29 @@ public void RewardOnClaim() var totalShares = delegatee.TotalShares; - repo.MintAsset(delegatee.RewardDistributorAddress, reward); + repo.MintAsset(delegatee.RewardPoolAddress, reward); // EndBlock after delegatee's reward - repo.AddLumpSumRewards(delegatee, 10L, reward); + delegatee.CollectRewards(10L); var shareToRedelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; delegator1.ClaimReward(delegatee, 11L); delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); - var rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); + var collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var reward1 = (reward * share1).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); - Assert.Equal(reward * 2 - reward1, rewardPoolBalance); + Assert.Equal(reward * 2 - reward1, collectedRewards); shareToRedelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.ClaimReward(delegatee, 11L); delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); - rewardPoolBalance = repo.World.GetBalance(delegatee.RewardDistributorAddress, delegatee.DelegationCurrency); + collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); var reward2 = (reward * share2).DivRem(totalShares, out _); Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2 + reward2, delegator2Balance); - Assert.Equal(reward * 2 - reward1 - reward2, rewardPoolBalance); + Assert.Equal(delegatee.RewardCurrency * 0, collectedRewards); } } } diff --git a/.Lib9c.Tests/Delegation/DummyDelegatee.cs b/.Lib9c.Tests/Delegation/DummyDelegatee.cs index 03c00d4feb..1092b4bd86 100644 --- a/.Lib9c.Tests/Delegation/DummyDelegatee.cs +++ b/.Lib9c.Tests/Delegation/DummyDelegatee.cs @@ -16,6 +16,7 @@ public DummyDelegatee(Address address, Address accountAddress, DummyRepository r DelegationFixture.TestCurrency, DelegationFixture.TestCurrency, address, + DelegationFixture.FixedPoolAddress, 3, 5, 5, diff --git a/.Lib9c.Tests/Delegation/TestDelegatee.cs b/.Lib9c.Tests/Delegation/TestDelegatee.cs index 078d1267d9..ef1b18695e 100644 --- a/.Lib9c.Tests/Delegation/TestDelegatee.cs +++ b/.Lib9c.Tests/Delegation/TestDelegatee.cs @@ -17,6 +17,7 @@ public TestDelegatee(Address address, Address accountAddress, TestRepository rep DelegationFixture.TestCurrency, DelegationFixture.TestCurrency, address, + DelegationFixture.FixedPoolAddress, 3, 5, 5, diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index fb394156fd..efd8d056a3 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -19,6 +19,7 @@ public Delegatee( Currency delegationCurrency, Currency rewardCurrency, Address delegationPoolAddress, + Address rewardRemainderPoolAddress, long unbondingPeriod, int maxUnbondLockInEntries, int maxRebondGraceEntries, @@ -30,6 +31,7 @@ public Delegatee( delegationCurrency, rewardCurrency, delegationPoolAddress, + rewardRemainderPoolAddress, unbondingPeriod, maxUnbondLockInEntries, maxRebondGraceEntries), @@ -72,15 +74,15 @@ private Delegatee(DelegateeMetadata metadata, IDelegationRepository repository) public Address DelegationPoolAddress => Metadata.DelegationPoolAddress; + public Address RewardRemainderPoolAddress => Metadata.RewardRemainderPoolAddress; + public long UnbondingPeriod => Metadata.UnbondingPeriod; public int MaxUnbondLockInEntries => Metadata.MaxUnbondLockInEntries; public int MaxRebondGraceEntries => Metadata.MaxRebondGraceEntries; - public Address RewardCollectorAddress => Metadata.RewardCollectorAddress; - - public Address RewardDistributorAddress => Metadata.RewardDistributorAddress; + public Address RewardPoolAddress => Metadata.RewardPoolAddress; public ImmutableSortedSet
Delegators => Metadata.Delegators; @@ -213,10 +215,47 @@ public void DistributeReward(T delegator, long height) { IEnumerable lumpSumRewardsRecords = GetLumpSumRewardsRecords(bond.LastDistributeHeight); - FungibleAssetValue reward = CalculateReward(share, lumpSumRewardsRecords); - if (reward.Sign > 0) + + long? linkedStartHeight = null; + foreach (LumpSumRewardsRecord record in lumpSumRewardsRecords) { - Repository.TransferAsset(RewardDistributorAddress, delegator.Address, reward); + if (!(record.StartHeight is long startHeight)) + { + throw new ArgumentException("lump sum reward record wasn't started."); + } + + if (linkedStartHeight is long startHeightFromHigher + && startHeightFromHigher != startHeight) + { + throw new ArgumentException("lump sum reward record was started."); + } + + FungibleAssetValue reward = record.RewardsDuringPeriod(share); + if (reward.Sign > 0) + { + Repository.TransferAsset(record.Address, delegator.Address, reward); + } + + LumpSumRewardsRecord newRecord = record.RemoveDelegator(delegator.Address); + + if (newRecord.Delegators.IsEmpty) + { + FungibleAssetValue remainder = Repository.GetBalance(newRecord.Address, RewardCurrency); + + if (remainder.Sign > 0) + { + Repository.TransferAsset(newRecord.Address, RewardRemainderPoolAddress, remainder); + } + } + + Repository.SetLumpSumRewardsRecord(newRecord); + + linkedStartHeight = newRecord.LastStartHeight; + + if (linkedStartHeight == -1) + { + break; + } } } @@ -229,9 +268,17 @@ void IDelegatee.DistributeReward(IDelegator delegator, long height) public void CollectRewards(long height) { - FungibleAssetValue rewards = Repository.GetBalance(RewardCollectorAddress, RewardCurrency); - Repository.AddLumpSumRewards(this, height, rewards); - Repository.TransferAsset(RewardCollectorAddress, RewardDistributorAddress, rewards); + FungibleAssetValue rewards = Repository.GetBalance(RewardPoolAddress, RewardCurrency); + LumpSumRewardsRecord record = Repository.GetCurrentLumpSumRewardsRecord(this) + ?? new LumpSumRewardsRecord( + CurrentLumpSumRewardsRecordAddress(), + height, + TotalShares, + Delegators, + RewardCurrency); + record = record.AddLumpSumRewards(rewards); + Repository.TransferAsset(RewardPoolAddress, record.Address, rewards); + Repository.SetLumpSumRewardsRecord(record); } public void Slash(BigInteger slashFactor, long infractionHeight, long height) @@ -299,6 +346,7 @@ private void StartNewRewardPeriod(long height) currentRecord.Address, currentRecord.StartHeight, TotalShares, + Delegators, RewardCurrency, currentRecord.LastStartHeight); @@ -306,15 +354,22 @@ private void StartNewRewardPeriod(long height) return; } - Repository.SetLumpSumRewardsRecord( - lastRecord.MoveAddress( - LumpSumRewardsRecordAddress(lastRecord.StartHeight))); + Address archiveAddress = LumpSumRewardsRecordAddress(lastRecord.StartHeight); + FungibleAssetValue reward = Repository.GetBalance(lastRecord.Address, RewardCurrency); + if (reward.Sign > 0) + { + Repository.TransferAsset(lastRecord.Address, archiveAddress, reward); + } + + lastRecord = lastRecord.MoveAddress(archiveAddress); + Repository.SetLumpSumRewardsRecord(lastRecord); } LumpSumRewardsRecord newRecord = new( CurrentLumpSumRewardsRecordAddress(), height, TotalShares, + Delegators, RewardCurrency, lastStartHeight); diff --git a/Lib9c/Delegation/DelegateeMetadata.cs b/Lib9c/Delegation/DelegateeMetadata.cs index a7438c3e32..79702c3744 100644 --- a/Lib9c/Delegation/DelegateeMetadata.cs +++ b/Lib9c/Delegation/DelegateeMetadata.cs @@ -21,6 +21,7 @@ public DelegateeMetadata( Currency delegationCurrency, Currency rewardCurrency, Address delegationPoolAddress, + Address rewardRemainderPoolAddress, long unbondingPeriod, int maxUnbondLockInEntries, int maxRebondGraceEntries) @@ -30,6 +31,7 @@ public DelegateeMetadata( delegationCurrency, rewardCurrency, delegationPoolAddress, + rewardRemainderPoolAddress, unbondingPeriod, maxUnbondLockInEntries, maxRebondGraceEntries, @@ -61,16 +63,17 @@ public DelegateeMetadata( new Currency(bencoded[0]), new Currency(bencoded[1]), new Address(bencoded[2]), - (Integer)bencoded[3], + new Address(bencoded[3]), (Integer)bencoded[4], (Integer)bencoded[5], - ((List)bencoded[6]).Select(item => new Address(item)), - new FungibleAssetValue(bencoded[7]), - (Integer)bencoded[8], - (Bencodex.Types.Boolean)bencoded[9], - (Integer)bencoded[10], - (Bencodex.Types.Boolean)bencoded[11], - ((List)bencoded[12]).Select(item => new UnbondingRef(item))) + (Integer)bencoded[6], + ((List)bencoded[7]).Select(item => new Address(item)), + new FungibleAssetValue(bencoded[8]), + (Integer)bencoded[9], + (Bencodex.Types.Boolean)bencoded[10], + (Integer)bencoded[11], + (Bencodex.Types.Boolean)bencoded[12], + ((List)bencoded[13]).Select(item => new UnbondingRef(item))) { } @@ -80,6 +83,7 @@ private DelegateeMetadata( Currency delegationCurrency, Currency rewardCurrency, Address delegationPoolAddress, + Address rewardRemainderPoolAddress, long unbondingPeriod, int maxUnbondLockInEntries, int maxRebondGraceEntries, @@ -117,6 +121,7 @@ private DelegateeMetadata( DelegationCurrency = delegationCurrency; RewardCurrency = rewardCurrency; DelegationPoolAddress = delegationPoolAddress; + RewardRemainderPoolAddress = rewardRemainderPoolAddress; UnbondingPeriod = unbondingPeriod; MaxUnbondLockInEntries = maxUnbondLockInEntries; MaxRebondGraceEntries = maxRebondGraceEntries; @@ -144,17 +149,16 @@ public Address Address public Address DelegationPoolAddress { get; } + public Address RewardRemainderPoolAddress { get; } + public long UnbondingPeriod { get; } public int MaxUnbondLockInEntries { get; } public int MaxRebondGraceEntries { get; } - public Address RewardCollectorAddress - => DelegationAddress.RewardCollectorAddress(Address); - - public Address RewardDistributorAddress - => DelegationAddress.RewardDistributorAddress(Address); + public Address RewardPoolAddress + => DelegationAddress.RewardPoolAddress(Address); public ImmutableSortedSet
Delegators { get; private set; } @@ -174,6 +178,7 @@ public Address RewardDistributorAddress .Add(DelegationCurrency.Serialize()) .Add(RewardCurrency.Serialize()) .Add(DelegationPoolAddress.Bencoded) + .Add(RewardRemainderPoolAddress.Bencoded) .Add(UnbondingPeriod) .Add(MaxUnbondLockInEntries) .Add(MaxRebondGraceEntries) @@ -297,9 +302,9 @@ public virtual bool Equals(IDelegateeMetadata? other) && DelegationCurrency.Equals(delegatee.DelegationCurrency) && RewardCurrency.Equals(delegatee.RewardCurrency) && DelegationPoolAddress.Equals(delegatee.DelegationPoolAddress) + && RewardRemainderPoolAddress.Equals(delegatee.RewardRemainderPoolAddress) && UnbondingPeriod == delegatee.UnbondingPeriod - && RewardCollectorAddress.Equals(delegatee.RewardCollectorAddress) - && RewardDistributorAddress.Equals(delegatee.RewardDistributorAddress) + && RewardPoolAddress.Equals(delegatee.RewardPoolAddress) && Delegators.SequenceEqual(delegatee.Delegators) && TotalDelegatedFAV.Equals(delegatee.TotalDelegatedFAV) && TotalShares.Equals(delegatee.TotalShares) diff --git a/Lib9c/Delegation/DelegationAddress.cs b/Lib9c/Delegation/DelegationAddress.cs index d57f91b332..5ea154cd6b 100644 --- a/Lib9c/Delegation/DelegationAddress.cs +++ b/Lib9c/Delegation/DelegationAddress.cs @@ -91,28 +91,16 @@ public static Address LumpSumRewardsRecordAddress( delegateeMetadataAddress, BitConverter.GetBytes(height)); - public static Address RewardCollectorAddress( + public static Address RewardPoolAddress( Address delegateeAddress, Address delegateeAccountAddress) => DeriveAddress( - DelegationElementType.RewardCollector, + DelegationElementType.RewardPool, DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress)); - public static Address RewardCollectorAddress( + public static Address RewardPoolAddress( Address delegateeMetadataAddress) => DeriveAddress( - DelegationElementType.RewardCollector, - delegateeMetadataAddress); - - public static Address RewardDistributorAddress( - Address delegateeAddress, Address delegateeAccountAddress) - => DeriveAddress( - DelegationElementType.RewardDistributor, - DelegateeMetadataAddress(delegateeAddress, delegateeAccountAddress)); - - public static Address RewardDistributorAddress( - Address delegateeMetadataAddress) - => DeriveAddress( - DelegationElementType.RewardDistributor, + DelegationElementType.RewardPool, delegateeMetadataAddress); private static Address DeriveAddress( @@ -139,8 +127,7 @@ private enum DelegationElementType UnbondLockIn, RebondGrace, LumpSumRewardsRecord, - RewardCollector, - RewardDistributor, + RewardPool, DelegationPool, } } diff --git a/Lib9c/Delegation/DelegationRepository.cs b/Lib9c/Delegation/DelegationRepository.cs index 8a909878d4..374d531d2c 100644 --- a/Lib9c/Delegation/DelegationRepository.cs +++ b/Lib9c/Delegation/DelegationRepository.cs @@ -230,18 +230,6 @@ public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) lumpSumRewardsRecord.Address, lumpSumRewardsRecord.Bencoded); } - public void AddLumpSumRewards(IDelegatee delegatee, long height, FungibleAssetValue rewards) - { - LumpSumRewardsRecord record = GetCurrentLumpSumRewardsRecord(delegatee) - ?? new LumpSumRewardsRecord( - delegatee.CurrentLumpSumRewardsRecordAddress(), - height, - delegatee.TotalShares, - delegatee.RewardCurrency); - record = record.AddLumpSumRewards(rewards); - SetLumpSumRewardsRecord(record); - } - public void TransferAsset(Address sender, Address recipient, FungibleAssetValue value) => previousWorld = previousWorld.TransferAsset(actionContext, sender, recipient, value); diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index fc0d71b45c..958790b314 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -20,15 +20,15 @@ public interface IDelegatee Address DelegationPoolAddress { get; } + Address RewardRemainderPoolAddress { get; } + long UnbondingPeriod { get; } int MaxUnbondLockInEntries { get; } int MaxRebondGraceEntries { get; } - Address RewardCollectorAddress { get; } - - Address RewardDistributorAddress { get; } + Address RewardPoolAddress { get; } ImmutableSortedSet
Delegators { get; } diff --git a/Lib9c/Delegation/IDelegateeMetadata.cs b/Lib9c/Delegation/IDelegateeMetadata.cs index 98ead5073d..678f7cc6f3 100644 --- a/Lib9c/Delegation/IDelegateeMetadata.cs +++ b/Lib9c/Delegation/IDelegateeMetadata.cs @@ -22,15 +22,15 @@ public interface IDelegateeMetadata : IBencodable Address DelegationPoolAddress { get; } + Address RewardRemainderPoolAddress { get; } + long UnbondingPeriod { get; } int MaxUnbondLockInEntries { get; } int MaxRebondGraceEntries { get; } - Address RewardCollectorAddress { get; } - - Address RewardDistributorAddress { get; } + Address RewardPoolAddress { get; } ImmutableSortedSet
Delegators { get; } diff --git a/Lib9c/Delegation/IDelegationRepository.cs b/Lib9c/Delegation/IDelegationRepository.cs index 340f9a7c4d..7b86472252 100644 --- a/Lib9c/Delegation/IDelegationRepository.cs +++ b/Lib9c/Delegation/IDelegationRepository.cs @@ -57,8 +57,6 @@ public interface IDelegationRepository void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord); - void AddLumpSumRewards(IDelegatee delegatee, long height, FungibleAssetValue rewards); - void TransferAsset(Address sender, Address recipient, FungibleAssetValue value); } } diff --git a/Lib9c/Delegation/LumpSumRewardsRecord.cs b/Lib9c/Delegation/LumpSumRewardsRecord.cs index ef32497f8b..fabde59293 100644 --- a/Lib9c/Delegation/LumpSumRewardsRecord.cs +++ b/Lib9c/Delegation/LumpSumRewardsRecord.cs @@ -6,6 +6,7 @@ using Bencodex; using Libplanet.Crypto; using Libplanet.Types.Assets; +using System.Collections.Immutable; namespace Nekoyume.Delegation { @@ -15,8 +16,9 @@ public LumpSumRewardsRecord( Address address, long startHeight, BigInteger totalShares, + ImmutableSortedSet
delegators, Currency currency) - : this(address, startHeight, totalShares, currency, null) + : this(address, startHeight, totalShares, delegators, currency, null) { } @@ -24,9 +26,10 @@ public LumpSumRewardsRecord( Address address, long startHeight, BigInteger totalShares, + ImmutableSortedSet
delegators, Currency currency, long? lastStartHeight) - : this(address, startHeight, totalShares, currency * 0, lastStartHeight) + : this(address, startHeight, totalShares, delegators, currency * 0, lastStartHeight) { } @@ -34,12 +37,14 @@ public LumpSumRewardsRecord( Address address, long startHeight, BigInteger totalShares, + ImmutableSortedSet
delegators, FungibleAssetValue lumpSumRewards, long? lastStartHeight) { Address = address; StartHeight = startHeight; TotalShares = totalShares; + Delegators = delegators; LumpSumRewards = lumpSumRewards; LastStartHeight = lastStartHeight; } @@ -54,8 +59,9 @@ public LumpSumRewardsRecord(Address address, List bencoded) address, (Integer)bencoded[0], (Integer)bencoded[1], - new FungibleAssetValue(bencoded[2]), - (Integer?)bencoded.ElementAtOrDefault(3)) + ((List)bencoded[2]).Select(a => new Address(a)).ToImmutableSortedSet(), + new FungibleAssetValue(bencoded[3]), + (Integer?)bencoded.ElementAtOrDefault(4)) { } @@ -67,6 +73,8 @@ public LumpSumRewardsRecord(Address address, List bencoded) public FungibleAssetValue LumpSumRewards { get; } + public ImmutableSortedSet
Delegators { get; } + public long? LastStartHeight { get; } public List Bencoded @@ -76,6 +84,7 @@ public List Bencoded var bencoded = List.Empty .Add(StartHeight) .Add(TotalShares) + .Add(new List(Delegators.Select(a => a.Bencoded))) .Add(LumpSumRewards.Serialize()); return LastStartHeight is long lastStartHeight @@ -91,6 +100,7 @@ public LumpSumRewardsRecord MoveAddress(Address address) address, StartHeight, TotalShares, + Delegators, LumpSumRewards, LastStartHeight); @@ -99,9 +109,19 @@ public LumpSumRewardsRecord AddLumpSumRewards(FungibleAssetValue rewards) Address, StartHeight, TotalShares, + Delegators, LumpSumRewards + rewards, LastStartHeight); + public LumpSumRewardsRecord RemoveDelegator(Address delegator) + => new LumpSumRewardsRecord( + Address, + StartHeight, + TotalShares, + Delegators.Remove(delegator), + LumpSumRewards, + LastStartHeight); + public FungibleAssetValue RewardsDuringPeriod(BigInteger share) => (LumpSumRewards * share).DivRem(TotalShares).Quotient; @@ -115,7 +135,8 @@ public bool Equals(LumpSumRewardsRecord? other) && StartHeight == lumpSumRewardRecord.StartHeight && TotalShares == lumpSumRewardRecord.TotalShares && LumpSumRewards.Equals(lumpSumRewardRecord.LumpSumRewards) - && LastStartHeight == lumpSumRewardRecord.LastStartHeight); + && LastStartHeight == lumpSumRewardRecord.LastStartHeight + && Delegators.SequenceEqual(lumpSumRewardRecord.Delegators)); public override int GetHashCode() => Address.GetHashCode(); diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index 64a77e4451..bfbc29f432 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -28,6 +28,7 @@ public Guild( delegationCurrency: Currencies.GuildGold, rewardCurrency: rewardCurrency, delegationPoolAddress: address, + rewardRemainderPoolAddress: Addresses.CommunityPool, unbondingPeriod: 75600L, maxUnbondLockInEntries: 10, maxRebondGraceEntries: 10, diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index 22a8c7eb30..a9467c923f 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -27,6 +27,7 @@ public ValidatorDelegatee( delegationCurrency: rewardCurrency, rewardCurrency: rewardCurrency, delegationPoolAddress: UnbondedPoolAddress, + rewardRemainderPoolAddress: Addresses.CommunityPool, unbondingPeriod: ValidatorUnbondingPeriod, maxUnbondLockInEntries: ValidatorMaxUnbondLockInEntries, maxRebondGraceEntries: ValidatorMaxRebondGraceEntries, @@ -123,7 +124,7 @@ FungibleAssetValue commission FungibleAssetValue delegationRewards = rewardAllocated - commission; repository.TransferAsset(RewardSource, Address, commission); - repository.TransferAsset(RewardSource, RewardCollectorAddress, delegationRewards); + repository.TransferAsset(RewardSource, RewardPoolAddress, delegationRewards); CollectRewards(height); } From f8bd4dd33c0fdabbd085e8d2f7313f9f4fdcc965 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 25 Sep 2024 18:12:16 +0900 Subject: [PATCH 052/206] fix: Fix to handle zero partial rewards --- Lib9c/Action/ValidatorDelegation/AllocateReward.cs | 11 +++++++---- Lib9c/ValidatorDelegation/ValidatorDelegatee.cs | 12 ++++++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs index 9c3fa300b4..f31cc85bc9 100644 --- a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs +++ b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs @@ -109,10 +109,13 @@ var bonusProposerReward .DivRem(votePowerDenominator * 100).Quotient; FungibleAssetValue proposerReward = baseProposerReward + bonusProposerReward; - repository.TransferAsset( - Addresses.RewardPool, - proposerInfo.Proposer, - proposerReward); + if (proposerReward.Sign > 0) + { + repository.TransferAsset( + Addresses.RewardPool, + proposerInfo.Proposer, + proposerReward); + } } internal static void DistributeValidatorReward( diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index a9467c923f..420dcb39ec 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -123,8 +123,16 @@ FungibleAssetValue commission = (rewardAllocated * CommissionPercentage).DivRem(100).Quotient; FungibleAssetValue delegationRewards = rewardAllocated - commission; - repository.TransferAsset(RewardSource, Address, commission); - repository.TransferAsset(RewardSource, RewardPoolAddress, delegationRewards); + if (commission.Sign > 0) + { + repository.TransferAsset(RewardSource, Address, commission); + } + + if (delegationRewards.Sign > 0) + { + repository.TransferAsset(RewardSource, RewardPoolAddress, delegationRewards); + } + CollectRewards(height); } From a50bceb85a6d4271067a2c1391ebd7195e8acfbb Mon Sep 17 00:00:00 2001 From: s2quake Date: Wed, 25 Sep 2024 13:18:30 +0900 Subject: [PATCH 053/206] test: Improve code readability for PoS test --- .../ValidatorDelegation/AllocateRewardTest.cs | 237 ++++--- .../ClaimRewardValidatorTest.cs | 528 ++++++++------- .../DelegateValidatorTest.cs | 288 ++++---- .../PromoteValidatorTest.cs | 300 ++++----- .../ValidatorDelegation/RecordProposerTest.cs | 79 ++- .../RedelegateValidatorTest.cs | 428 ++++++------ .../ReleaseValidatorUnbondingsTest.cs | 1 - .../ValidatorDelegation/SlashValidatorTest.cs | 292 ++++----- .../UndelegateValidatorTest.cs | 369 +++++------ .../UnjailValidatorTest.cs | 267 ++++---- .../UpdateValidatorsTest.cs | 109 ++-- .../ValidatorDelegationTestBase.cs | 617 +++++++++--------- 12 files changed, 1686 insertions(+), 1829 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs index f5fb7ef470..55dd5f1c4d 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -1,143 +1,136 @@ #nullable enable -namespace Lib9c.Tests.Action.ValidatorDelegation +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Numerics; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Types.Assets; +using Libplanet.Types.Blocks; +using Libplanet.Types.Consensus; +using Nekoyume; +using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.Model.State; +using Nekoyume.Module.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; +using Xunit; + +public class AllocateRewardTest : ValidatorDelegationTestBase { - using System; - using System.Collections.Immutable; - using System.Linq; - using System.Numerics; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Mocks; - using Libplanet.Types.Assets; - using Libplanet.Types.Blocks; - using Libplanet.Types.Consensus; - using Nekoyume; - using Nekoyume.Action.ValidatorDelegation; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.Module.ValidatorDelegation; - using Nekoyume.ValidatorDelegation; - using Xunit; - - public class AllocateRewardTest + [Fact] + public void Serialization() { - [Fact] - public void Serialization() - { - var action = new AllocateReward(); - var plainValue = action.PlainValue; + var action = new AllocateReward(); + var plainValue = action.PlainValue; - var deserialized = new AllocateReward(); - deserialized.LoadPlainValue(plainValue); - } + var deserialized = new AllocateReward(); + deserialized.LoadPlainValue(plainValue); + } - [Fact] - public void Execute() + [Fact] + public void Execute() + { + IWorld world = World; + var context = new ActionContext { }; + var privateKeys = Enumerable.Range(0, 200).Select(_ => new PrivateKey()).ToArray(); + var favs = Enumerable.Range(0, 200).Select(i => NCG * (i + 1)).ToArray(); + + for (int i = 0; i < 200; i++) { - IWorld world = new World(MockUtil.MockModernWorldState); - var context = new ActionContext { }; - var ncg = Currency.Uncapped("NCG", 2, null); - // TODO: Use Currencies.GuildGold when it's available. - // var gg = Currencies.GuildGold; - var gg = ncg; - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - var privateKeys = Enumerable.Range(0, 200).Select(_ => new PrivateKey()).ToArray(); - var favs = Enumerable.Range(0, 200).Select(i => gg * (i + 1)).ToArray(); - - for (int i = 0; i < 200; i++) + var signer = privateKeys[i]; + world = world.MintAsset(context, signer.Address, NCG * 1000); + world = new PromoteValidator(signer.PublicKey, favs[i]).Execute(new ActionContext { - var signer = privateKeys[i]; - world = world.MintAsset(context, signer.Address, gg * 1000); - world = new PromoteValidator(signer.PublicKey, favs[i]).Execute(new ActionContext - { - PreviousState = world, - Signer = signer.Address, - BlockIndex = 10L, - }); - } - - var blockHash = new BlockHash(Enumerable.Repeat((byte)0x01, BlockHash.Size).ToArray()); - var timestamp = DateTimeOffset.UtcNow; - var voteFlags = Enumerable.Range(0, 100).Select(i => i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null).ToArray(); - var repository = new ValidatorRepository(world, context); - var bondedSet = repository.GetValidatorList().GetBonded(); - - var proposer = bondedSet.First(); - repository.SetProposerInfo(new ProposerInfo(9L, proposer.OperatorAddress)); - var votes = bondedSet.Select( - (v, i) => new VoteMetadata( - 9L, 0, blockHash, timestamp, v.PublicKey, v.Power, i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null) - .Sign(i % 2 == 0 ? privateKeys.First(k => k.PublicKey.Equals(v.PublicKey)) : null)).ToImmutableArray(); - - var totalReward = ncg * 1000; - world = repository.World.MintAsset(context, Addresses.RewardPool, totalReward); + PreviousState = world, + Signer = signer.Address, + BlockIndex = 10L, + }); + } - // TODO: Remove this after delegation currency has been changed into GuildGold. - var initialFAVs = votes.Select(vote => world.GetBalance(vote.ValidatorPublicKey.Address, ncg)).ToArray(); + var blockHash = new BlockHash(Enumerable.Repeat((byte)0x01, BlockHash.Size).ToArray()); + var timestamp = DateTimeOffset.UtcNow; + var voteFlags = Enumerable.Range(0, 100).Select(i => i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null).ToArray(); + var repository = new ValidatorRepository(world, context); + var bondedSet = repository.GetValidatorList().GetBonded(); - context = new ActionContext - { - BlockIndex = 10L, - PreviousState = world, - Signer = privateKeys[199].Address, - LastCommit = new BlockCommit(9L, 0, blockHash, votes), - }; + var proposer = bondedSet.First(); + repository.SetProposerInfo(new ProposerInfo(9L, proposer.OperatorAddress)); + var votes = bondedSet.Select( + (v, i) => new VoteMetadata( + 9L, 0, blockHash, timestamp, v.PublicKey, v.Power, i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null) + .Sign(i % 2 == 0 ? privateKeys.First(k => k.PublicKey.Equals(v.PublicKey)) : null)).ToImmutableArray(); - var action = new AllocateReward(); - world = action.Execute(context); + var totalReward = NCG * 1000; + world = repository.World.MintAsset(context, Addresses.RewardPool, totalReward); - BigInteger totalPower = votes.Aggregate( - BigInteger.Zero, - (accum, next) => accum + (BigInteger)next.ValidatorPower!); + // TODO: Remove this after delegation currency has been changed into GuildGold. + var initialFAVs = votes.Select(vote => world.GetBalance(vote.ValidatorPublicKey.Address, NCG)).ToArray(); - BigInteger preCommitPower = votes.Aggregate( - BigInteger.Zero, - (accum, next) => accum + (BigInteger)next.ValidatorPower! * (next.Flag == VoteFlag.PreCommit ? 1 : 0)); + context = new ActionContext + { + BlockIndex = 10L, + PreviousState = world, + Signer = privateKeys[199].Address, + LastCommit = new BlockCommit(9L, 0, blockHash, votes), + }; + + var action = new AllocateReward(); + world = action.Execute(context); + + BigInteger totalPower = votes.Aggregate( + BigInteger.Zero, + (accum, next) => accum + (BigInteger)next.ValidatorPower!); + + BigInteger preCommitPower = votes.Aggregate( + BigInteger.Zero, + (accum, next) => accum + (BigInteger)next.ValidatorPower! * (next.Flag == VoteFlag.PreCommit ? 1 : 0)); + + var baseProposerReward + = (totalReward * ValidatorDelegatee.BaseProposerRewardPercentage) + .DivRem(100).Quotient; + var bonusProposerReward + = (totalReward * preCommitPower * ValidatorDelegatee.BonusProposerRewardPercentage) + .DivRem(totalPower * 100).Quotient; + + var proposerReward = baseProposerReward + bonusProposerReward; + var remains = totalReward - proposerReward; + repository.UpdateWorld(world); + + foreach (var (vote, index) in votes.Select((v, i) => (v, i))) + { + var initialFAV = initialFAVs[index]; + var validator = repository.GetValidatorDelegatee(vote.ValidatorPublicKey.Address); - var baseProposerReward - = (totalReward * ValidatorDelegatee.BaseProposerRewardPercentage) + FungibleAssetValue rewardAllocated + = (remains * vote.ValidatorPower!.Value).DivRem(totalPower).Quotient; + FungibleAssetValue commission + = (rewardAllocated * validator.CommissionPercentage) .DivRem(100).Quotient; - var bonusProposerReward - = (totalReward * preCommitPower * ValidatorDelegatee.BonusProposerRewardPercentage) - .DivRem(totalPower * 100).Quotient; - var proposerReward = baseProposerReward + bonusProposerReward; - var remains = totalReward - proposerReward; - repository.UpdateWorld(world); + if (vote.Flag == VoteFlag.Null) + { + Assert.Equal(initialFAV, world.GetBalance(vote.ValidatorPublicKey.Address, NCG)); + Assert.Equal( + NCG * 0, world.GetBalance(validator.CurrentLumpSumRewardsRecordAddress(), NCG)); + continue; + } - foreach (var (vote, index) in votes.Select((v, i) => (v, i))) + if (vote.ValidatorPublicKey.Equals(proposer.PublicKey)) { - var initialFAV = initialFAVs[index]; - var validator = repository.GetValidatorDelegatee(vote.ValidatorPublicKey.Address); - - FungibleAssetValue rewardAllocated - = (remains * vote.ValidatorPower!.Value).DivRem(totalPower).Quotient; - FungibleAssetValue commission - = (rewardAllocated * validator.CommissionPercentage) - .DivRem(100).Quotient; - - if (vote.Flag == VoteFlag.Null) - { - Assert.Equal(initialFAV, world.GetBalance(vote.ValidatorPublicKey.Address, ncg)); - Assert.Equal(ncg * 0, world.GetBalance(validator.CurrentLumpSumRewardsRecordAddress(), ncg)); - continue; - } - - if (vote.ValidatorPublicKey.Equals(proposer.PublicKey)) - { - Assert.Equal( - proposerReward + commission + initialFAV, - world.GetBalance(vote.ValidatorPublicKey.Address, ncg)); - } - else - { - Assert.Equal(commission + initialFAV, world.GetBalance(vote.ValidatorPublicKey.Address, ncg)); - } - - Assert.Equal(rewardAllocated - commission, world.GetBalance(validator.CurrentLumpSumRewardsRecordAddress(), ncg)); + Assert.Equal( + proposerReward + commission + initialFAV, + world.GetBalance(vote.ValidatorPublicKey.Address, NCG)); } + else + { + Assert.Equal(commission + initialFAV, world.GetBalance(vote.ValidatorPublicKey.Address, NCG)); + } + + Assert.Equal( + rewardAllocated - commission, + world.GetBalance(validator.CurrentLumpSumRewardsRecordAddress(), NCG)); } } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs index 17f5738248..0148fb5374 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -1,311 +1,295 @@ #nullable enable -namespace Lib9c.Tests.Action.ValidatorDelegation -{ - using System; - using System.Linq; - using System.Numerics; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using System; +using System.Linq; +using System.Numerics; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Types.Assets; +using Nekoyume; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.ValidatorDelegation; - using Xunit; +using Xunit; - public class ClaimRewardValidatorTest : ValidatorDelegationTestBase +public class ClaimRewardValidatorTest : ValidatorDelegationTestBase +{ + [Fact] + public void Serialization() { - [Fact] - public void Serialization() - { - var action = new ClaimRewardValidator(); - var plainValue = action.PlainValue; + var action = new ClaimRewardValidator(); + var plainValue = action.PlainValue; - var deserialized = new ClaimRewardValidator(); - deserialized.LoadPlainValue(plainValue); - } + var deserialized = new ClaimRewardValidator(); + deserialized.LoadPlainValue(plainValue); + } + + [Fact] + public void Execute() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var height = 1L; + var validatorGold = NCG * 10; + var allocatedReward = NCG * 100; + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++, mint: true); + world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); - [Fact] - public void Execute() + // When + var expectedBalance = allocatedReward; + var lastCommit = CreateLastCommit(validatorKey, height - 1); + var claimRewardValidator = new ClaimRewardValidator(validatorKey.Address); + var actionContext = new ActionContext { - // Given - var world = World; - var validatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - ActionContext actionContext; - var validatorGold = NCG * 10; - var allocatedReward = NCG * 100; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, validatorGold, blockHeight++); - world = EnsureValidatorToBeAllocatedReward( - world, validatorPrivateKey, allocatedReward, ref blockHeight); + PreviousState = world, + BlockIndex = height++, + Signer = validatorKey.Address, + LastCommit = lastCommit, + }; + world = claimRewardValidator.Execute(actionContext); - // When - var expectedBalance = allocatedReward; - var lastCommit = CreateLastCommit(validatorPrivateKey, blockHeight - 1); - var claimRewardValidator = new ClaimRewardValidator(validatorPrivateKey.Address); - actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight++, - Signer = validatorPrivateKey.Address, - LastCommit = lastCommit, - }; - world = claimRewardValidator.Execute(actionContext); + // Then + var actualBalance = world.GetBalance(validatorKey.Address, NCG); - // Then - var actualBalance = world.GetBalance(validatorPrivateKey.Address, NCG); + Assert.Equal(expectedBalance, actualBalance); + } - Assert.Equal(expectedBalance, actualBalance); - } + [Theory] + [InlineData(33.33)] + [InlineData(11.11)] + [InlineData(10)] + [InlineData(1)] + public void Execute_OneDelegator(double reward) + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var delegatorKey = new PrivateKey(); + var height = 1L; + var actionContext = new ActionContext { }; + var promotedGold = NCG * 10; + var allocatedReward = FungibleAssetValue.Parse(NCG, $"{reward:R}"); + world = EnsurePromotedValidator(world, validatorKey, promotedGold, height++, mint: true); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); + world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); + + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee( + validatorKey.Address); + var expectedCommission = GetCommission( + allocatedReward, expectedDelegatee.CommissionPercentage); + var expectedReward = allocatedReward - expectedCommission; + var expectedValidatorBalance = expectedCommission + expectedReward.DivRem(2).Quotient; + var expectedDelegatorBalance = expectedReward.DivRem(2).Quotient; + var expectedRemainReward = allocatedReward; + expectedRemainReward -= expectedValidatorBalance; + expectedRemainReward -= expectedDelegatorBalance; - [Theory] - [InlineData(33.33)] - [InlineData(11.11)] - [InlineData(10)] - [InlineData(1)] - public void Execute_OneDelegator(double reward) + var lastCommit = CreateLastCommit(validatorKey, height - 1); + actionContext = new ActionContext { - // Given - var world = World; - var validatorPrivateKey = new PrivateKey(); - var delegatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - var actionContext = new ActionContext { }; - var promotedGold = NCG * 10; - var allocatedReward = FungibleAssetValue.Parse(NCG, $"{reward:R}"); - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, promotedGold, blockHeight++); - world = EnsureDelegatorToBeBond( - world, delegatorPrivateKey, validatorPrivateKey, NCG * 10, blockHeight++); - world = EnsureValidatorToBeAllocatedReward( - world, validatorPrivateKey, allocatedReward, ref blockHeight); + PreviousState = world, + BlockIndex = height++, + Signer = validatorKey.Address, + LastCommit = lastCommit, + }; + world = new ClaimRewardValidator(validatorKey.Address).Execute(actionContext); + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = height++, + Signer = delegatorKey.Address, + LastCommit = lastCommit, + }; + world = new ClaimRewardValidator(validatorKey.Address).Execute(actionContext); - // When - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedDelegatee = expectedRepository.GetValidatorDelegatee( - validatorPrivateKey.Address); - var expectedCommission = GetCommission( - allocatedReward, expectedDelegatee.CommissionPercentage); - var expectedReward = allocatedReward - expectedCommission; - var expectedValidatorBalance = expectedCommission + expectedReward.DivRem(2).Quotient; - var expectedDelegatorBalance = expectedReward.DivRem(2).Quotient; - var expectedRemainReward = allocatedReward; - expectedRemainReward -= expectedValidatorBalance; - expectedRemainReward -= expectedDelegatorBalance; + // Then + var repository = new ValidatorRepository(world, actionContext); + var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); + var actualRemainReward = world.GetBalance(delegatee.CurrentLumpSumRewardsRecordAddress(), NCG); + var actualValidatorBalance = world.GetBalance(validatorKey.Address, NCG); + var actualDelegatorBalance = world.GetBalance(delegatorKey.Address, NCG); - var lastCommit = CreateLastCommit(validatorPrivateKey, blockHeight - 1); - actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight++, - Signer = validatorPrivateKey.Address, - LastCommit = lastCommit, - }; - world = new ClaimRewardValidator(validatorPrivateKey.Address).Execute(actionContext); - actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight++, - Signer = delegatorPrivateKey.Address, - LastCommit = lastCommit, - }; - world = new ClaimRewardValidator(validatorPrivateKey.Address).Execute(actionContext); + Assert.Equal(expectedRemainReward, actualRemainReward); + Assert.Equal(expectedValidatorBalance, actualValidatorBalance); + Assert.Equal(expectedDelegatorBalance, actualDelegatorBalance); + } - // Then - var repository = new ValidatorRepository(world, actionContext); - var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); - var actualRemainReward = world.GetBalance(delegatee.CurrentLumpSumRewardsRecordAddress(), NCG); - var actualValidatorBalance = world.GetBalance(validatorPrivateKey.Address, NCG); - var actualDelegatorBalance = world.GetBalance(delegatorPrivateKey.Address, NCG); + [Fact] + public void Execute_TwoDelegators() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var delegatorKey1 = new PrivateKey(); + var delegatorKey2 = new PrivateKey(); + var height = 1L; + var actionContext = new ActionContext { }; + var promotedGold = NCG * 10; + var allocatedReward = FungibleAssetValue.Parse(NCG, $"{34.27:R}"); + world = EnsurePromotedValidator(world, validatorKey, promotedGold, height++, mint: true); + world = EnsureBondedDelegator(world, delegatorKey1, validatorKey, NCG * 10, height++); + world = EnsureBondedDelegator(world, delegatorKey2, validatorKey, NCG * 10, height++); + world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); - Assert.Equal(expectedRemainReward, actualRemainReward); - Assert.Equal(expectedValidatorBalance, actualValidatorBalance); - Assert.Equal(expectedDelegatorBalance, actualDelegatorBalance); - } + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedCommission = GetCommission( + allocatedReward, expectedDelegatee.CommissionPercentage); + var expectedReward = allocatedReward - expectedCommission; + var expectedValidatorBalance = expectedCommission + expectedReward.DivRem(3).Quotient; + var expectedDelegator1Balance = expectedReward.DivRem(3).Quotient; + var expectedDelegator2Balance = expectedReward.DivRem(3).Quotient; + var expectedRemainReward = expectedDelegatee.RewardCurrency * 0; + var expectedCommunityBalance = allocatedReward; + expectedCommunityBalance -= expectedValidatorBalance; + expectedCommunityBalance -= expectedDelegator1Balance; + expectedCommunityBalance -= expectedDelegator2Balance; - [Fact] - public void Execute_TwoDelegators() + var lastCommit = CreateLastCommit(validatorKey, height - 1); + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = height++, + Signer = validatorKey.Address, + LastCommit = lastCommit, + }; + world = new ClaimRewardValidator(validatorKey.Address).Execute(actionContext); + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = height++, + Signer = delegatorKey1.Address, + LastCommit = lastCommit, + }; + world = new ClaimRewardValidator(validatorKey.Address).Execute(actionContext); + actionContext = new ActionContext { - // Given - var world = World; - var validatorPrivateKey = new PrivateKey(); - var delegatorPrivateKey1 = new PrivateKey(); - var delegatorPrivateKey2 = new PrivateKey(); - var blockHeight = 1L; - var actionContext = new ActionContext { }; - var promotedGold = NCG * 10; - var allocatedReward = FungibleAssetValue.Parse(NCG, $"{34.27:R}"); - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, promotedGold, blockHeight++); - world = EnsureDelegatorToBeBond( - world, delegatorPrivateKey1, validatorPrivateKey, NCG * 10, blockHeight++); - world = EnsureDelegatorToBeBond( - world, delegatorPrivateKey2, validatorPrivateKey, NCG * 10, blockHeight++); - world = EnsureValidatorToBeAllocatedReward( - world, validatorPrivateKey, allocatedReward, ref blockHeight); + PreviousState = world, + BlockIndex = height++, + Signer = delegatorKey2.Address, + LastCommit = lastCommit, + }; + world = new ClaimRewardValidator(validatorKey.Address).Execute(actionContext); - // When - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedDelegatee = expectedRepository.GetValidatorDelegatee( - validatorPrivateKey.Address); - var expectedCommission = GetCommission( - allocatedReward, expectedDelegatee.CommissionPercentage); - var expectedReward = allocatedReward - expectedCommission; - var expectedValidatorBalance = expectedCommission + expectedReward.DivRem(3).Quotient; - var expectedDelegator1Balance = expectedReward.DivRem(3).Quotient; - var expectedDelegator2Balance = expectedReward.DivRem(3).Quotient; - var expectedRemainReward = expectedDelegatee.RewardCurrency * 0; - var expectedCommunityBalance = allocatedReward; - expectedCommunityBalance -= expectedValidatorBalance; - expectedCommunityBalance -= expectedDelegator1Balance; - expectedCommunityBalance -= expectedDelegator2Balance; + // Then + var repository = new ValidatorRepository(world, actionContext); + var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); + var actualRemainReward = world.GetBalance( + delegatee.CurrentLumpSumRewardsRecordAddress(), NCG); + var actualValidatorBalance = world.GetBalance(validatorKey.Address, NCG); + var actualDelegator1Balance = world.GetBalance(delegatorKey1.Address, NCG); + var actualDelegator2Balance = world.GetBalance(delegatorKey2.Address, NCG); - var lastCommit = CreateLastCommit(validatorPrivateKey, blockHeight - 1); - actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight++, - Signer = validatorPrivateKey.Address, - LastCommit = lastCommit, - }; - world = new ClaimRewardValidator(validatorPrivateKey.Address).Execute(actionContext); - actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight++, - Signer = delegatorPrivateKey1.Address, - LastCommit = lastCommit, - }; - world = new ClaimRewardValidator(validatorPrivateKey.Address).Execute(actionContext); - actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight++, - Signer = delegatorPrivateKey2.Address, - LastCommit = lastCommit, - }; - world = new ClaimRewardValidator(validatorPrivateKey.Address).Execute(actionContext); - - // Then - var repository = new ValidatorRepository(world, actionContext); - var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); - var actualRemainReward = world.GetBalance(delegatee.CurrentLumpSumRewardsRecordAddress(), NCG); - var actualValidatorBalance = world.GetBalance(validatorPrivateKey.Address, NCG); - var actualDelegator1Balance = world.GetBalance(delegatorPrivateKey1.Address, NCG); - var actualDelegator2Balance = world.GetBalance(delegatorPrivateKey2.Address, NCG); - var actualCommunityBalance = world.GetBalance(Addresses.CommunityPool, NCG); + Assert.Equal(expectedRemainReward, actualRemainReward); + Assert.Equal(expectedValidatorBalance, actualValidatorBalance); + Assert.Equal(expectedDelegator1Balance, actualDelegator1Balance); + Assert.Equal(expectedDelegator2Balance, actualDelegator2Balance); + } - Assert.Equal(expectedRemainReward, actualRemainReward); - Assert.Equal(expectedCommunityBalance, actualCommunityBalance); - Assert.Equal(expectedValidatorBalance, actualValidatorBalance); - Assert.Equal(expectedDelegator1Balance, actualDelegator1Balance); - Assert.Equal(expectedDelegator2Balance, actualDelegator2Balance); - } + [Fact] + public void Execute_MultipleDelegators() + { + // Given + var length = Random.Shared.Next(3, 100); + var world = World; + var validatorKey = new PrivateKey(); + var delegatorKeys = GetRandomArray(length, _ => new PrivateKey()); + var delegatorNCGs = GetRandomArray(length, _ => GetRandomNCG()); + var height = 1L; + var actionContext = new ActionContext(); + var promotedGold = GetRandomNCG(); + var allocatedReward = GetRandomNCG(); + world = EnsurePromotedValidator(world, validatorKey, promotedGold, height++, mint: true); + world = EnsureBondedDelegators(world, delegatorKeys, validatorKey, delegatorNCGs, height++); + world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); - [Fact] - public void Execute_MultipleDelegators() + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedCommission = GetCommission( + allocatedReward, expectedDelegatee.CommissionPercentage); + var expectedReward = allocatedReward - expectedCommission; + var expectedValidatorBalance = expectedCommission + CalculateReward( + expectedRepository, validatorKey, validatorKey, expectedReward); + var expectedDelegatorBalances = CalculateRewards( + expectedRepository, validatorKey, delegatorKeys, expectedReward); + var expectedCommunityBalance = allocatedReward; + expectedCommunityBalance -= expectedValidatorBalance; + for (var i = 0; i < length; i++) { - // Given - var length = Random.Shared.Next(3, 100); - var world = World; - var validatorPrivateKey = new PrivateKey(); - var delegatorPrivateKeys = GetRandomArray(length, _ => new PrivateKey()); - var delegatorNCGs = GetRandomArray(length, _ => GetRandomNCG()); - var blockHeight = 1L; - var actionContext = new ActionContext(); - var promotedGold = GetRandomNCG(); - var allocatedReward = GetRandomNCG(); - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, promotedGold, blockHeight++); - world = EnsureDelegatorsToBeBond( - world, delegatorPrivateKeys, validatorPrivateKey, delegatorNCGs, blockHeight++); - world = EnsureValidatorToBeAllocatedReward( - world, validatorPrivateKey, allocatedReward, ref blockHeight); - - // When - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedDelegatee = expectedRepository.GetValidatorDelegatee( - validatorPrivateKey.Address); - var expectedCommission = GetCommission( - allocatedReward, expectedDelegatee.CommissionPercentage); - var expectedReward = allocatedReward - expectedCommission; - var expectedValidatorBalance = expectedCommission + CalculateReward( - expectedRepository, validatorPrivateKey, validatorPrivateKey, expectedReward); - var expectedDelegatorBalances = CalculateRewards( - expectedRepository, validatorPrivateKey, delegatorPrivateKeys, expectedReward); - var expectedCommunityBalance = allocatedReward; - expectedCommunityBalance -= expectedValidatorBalance; - for (var i = 0; i < length; i++) - { - expectedCommunityBalance -= expectedDelegatorBalances[i]; - } + expectedCommunityBalance -= expectedDelegatorBalances[i]; + } - var expectedRemainReward = expectedDelegatee.RewardCurrency * 0; + var expectedRemainReward = expectedDelegatee.RewardCurrency * 0; - var lastCommit = CreateLastCommit(validatorPrivateKey, blockHeight - 1); + var lastCommit = CreateLastCommit(validatorKey, height - 1); + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = height++, + Signer = validatorKey.Address, + LastCommit = lastCommit, + }; + world = new ClaimRewardValidator(validatorKey.Address).Execute(actionContext); + for (var i = 0; i < length; i++) + { actionContext = new ActionContext { PreviousState = world, - BlockIndex = blockHeight++, - Signer = validatorPrivateKey.Address, + BlockIndex = height++, + Signer = delegatorKeys[i].Address, LastCommit = lastCommit, }; - world = new ClaimRewardValidator(validatorPrivateKey.Address).Execute(actionContext); - for (var i = 0; i < length; i++) - { - actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight++, - Signer = delegatorPrivateKeys[i].Address, - LastCommit = lastCommit, - }; - world = new ClaimRewardValidator(validatorPrivateKey.Address).Execute(actionContext); - } - - // Then - var repository = new ValidatorRepository(world, actionContext); - var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); - var actualRemainReward = world.GetBalance(delegatee.CurrentLumpSumRewardsRecordAddress(), NCG); - var actualValidatorBalance = world.GetBalance(validatorPrivateKey.Address, NCG); - var actualCommunityBalance = world.GetBalance(Addresses.CommunityPool, NCG); - Assert.Equal(expectedRemainReward, actualRemainReward); - Assert.Equal(expectedValidatorBalance, actualValidatorBalance); - Assert.Equal(expectedCommunityBalance, actualCommunityBalance); - - for (var i = 0; i < length; i++) - { - var actualDelegatorBalance = world.GetBalance(delegatorPrivateKeys[i].Address, NCG); - Assert.Equal(expectedDelegatorBalances[i], actualDelegatorBalance); - } + world = new ClaimRewardValidator(validatorKey.Address).Execute(actionContext); } - private static FungibleAssetValue CalculateReward( - ValidatorRepository repository, - PrivateKey validatorPrivateKey, - PrivateKey delegatorPrivateKey, - FungibleAssetValue reward) - { - var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); - var bond = repository.GetBond(delegatee, delegatorPrivateKey.Address); - return CalculateReward(reward, bond.Share, delegatee.TotalShares); - } + // Then + var repository = new ValidatorRepository(world, actionContext); + var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); + var actualRemainReward = world.GetBalance( + delegatee.CurrentLumpSumRewardsRecordAddress(), NCG); + var actualValidatorBalance = world.GetBalance(validatorKey.Address, NCG); + var actualCommunityBalance = world.GetBalance(Addresses.CommunityPool, NCG); + Assert.Equal(expectedRemainReward, actualRemainReward); + Assert.Equal(expectedValidatorBalance, actualValidatorBalance); + Assert.Equal(expectedCommunityBalance, actualCommunityBalance); - private static FungibleAssetValue[] CalculateRewards( - ValidatorRepository repository, - PrivateKey validatorPrivateKey, - PrivateKey[] delegatorPrivateKeys, - FungibleAssetValue reward) + for (var i = 0; i < length; i++) { - return delegatorPrivateKeys - .Select(item => CalculateReward(repository, validatorPrivateKey, item, reward)) - .ToArray(); + var actualDelegatorBalance = world.GetBalance(delegatorKeys[i].Address, NCG); + Assert.Equal(expectedDelegatorBalances[i], actualDelegatorBalance); } + } - private static FungibleAssetValue CalculateReward( - FungibleAssetValue reward, BigInteger share, BigInteger totalShares) - { - return (reward * share).DivRem(totalShares).Quotient; - } + private static FungibleAssetValue CalculateReward( + ValidatorRepository repository, + PrivateKey validatorKey, + PrivateKey delegatorKey, + FungibleAssetValue reward) + { + var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); + var bond = repository.GetBond(delegatee, delegatorKey.Address); + return CalculateReward(reward, bond.Share, delegatee.TotalShares); + } + + private static FungibleAssetValue[] CalculateRewards( + ValidatorRepository repository, + PrivateKey validatorKey, + PrivateKey[] delegatorKeys, + FungibleAssetValue reward) + { + return delegatorKeys + .Select(item => CalculateReward(repository, validatorKey, item, reward)) + .ToArray(); + } + + private static FungibleAssetValue CalculateReward( + FungibleAssetValue reward, BigInteger share, BigInteger totalShares) + { + return (reward * share).DivRem(totalShares).Quotient; } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index a7b71a632e..174b19c0af 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -1,166 +1,138 @@ -namespace Lib9c.Tests.Action.ValidatorDelegation +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using System; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Action; +using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; +using Xunit; + +public class DelegateValidatorTest : ValidatorDelegationTestBase { - using System; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Mocks; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Action.ValidatorDelegation; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.ValidatorDelegation; - using Xunit; - - public class DelegateValidatorTest : ValidatorDelegationTestBase + [Fact] + public void Serialization() { - [Fact] - public void Serialization() - { - var address = new PrivateKey().Address; - var gg = Currencies.GuildGold; - var fav = gg * 10; - var action = new DelegateValidator(address, fav); - var plainValue = action.PlainValue; - - var deserialized = new DelegateValidator(); - deserialized.LoadPlainValue(plainValue); - Assert.Equal(address, deserialized.ValidatorDelegatee); - Assert.Equal(fav, deserialized.FAV); - } - - [Fact] - public void Execute() + var address = new PrivateKey().Address; + var gold = NCG * 10; + var action = new DelegateValidator(address, gold); + var plainValue = action.PlainValue; + + var deserialized = new DelegateValidator(); + deserialized.LoadPlainValue(plainValue); + Assert.Equal(address, deserialized.ValidatorDelegatee); + Assert.Equal(gold, deserialized.FAV); + } + + [Fact] + public void Execute() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var delegatorKey = new PrivateKey(); + var height = 1L; + var validatorGold = NCG * 10; + var delegatorGold = NCG * 20; + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++, mint: true); + world = MintAsset(world, delegatorKey, NCG * 100, height++); + + // When + var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorGold); + var actionContext = new ActionContext { - IWorld world = new World(MockUtil.MockModernWorldState); - var context = new ActionContext { }; - var ncg = Currency.Uncapped("NCG", 2, null); - // TODO: Use Currencies.GuildGold when it's available. - // var gg = Currencies.GuildGold; - var gg = ncg; - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - var validatorPublicKey = new PrivateKey().PublicKey; - world = world.MintAsset(context, validatorPublicKey.Address, gg * 100); - var promoteFAV = gg * 10; - world = new PromoteValidator(validatorPublicKey, promoteFAV).Execute(new ActionContext - { - PreviousState = world, - Signer = validatorPublicKey.Address, - }); - - var publicKey = new PrivateKey().PublicKey; - world = world.MintAsset(context, publicKey.Address, gg * 100); - var delegateFAV = gg * 20; - var action = new DelegateValidator(validatorPublicKey.Address, delegateFAV); - world = action.Execute(new ActionContext - { - PreviousState = world, - Signer = publicKey.Address, - }); - - var repository = new ValidatorRepository(world, context); - var validator = repository.GetValidatorDelegatee(validatorPublicKey.Address); - var bond = repository.GetBond(validator, publicKey.Address); - var validatorList = repository.GetValidatorList(); - - Assert.Contains(publicKey.Address, validator.Delegators); - Assert.Equal(delegateFAV.RawValue, bond.Share); - Assert.Equal(promoteFAV.RawValue + delegateFAV.RawValue, validator.Validator.Power); - Assert.Equal(validator.Validator, Assert.Single(validatorList.Validators)); - Assert.Equal(gg * 80, world.GetBalance(publicKey.Address, gg)); - } - - [Fact] - public void CannotDelegateWithInvalidCurrency() + PreviousState = world, + Signer = delegatorKey.Address, + }; + world = delegateValidator.Execute(actionContext); + + // Then + var repository = new ValidatorRepository(world, actionContext); + var validator = repository.GetValidatorDelegatee(validatorKey.Address); + var bond = repository.GetBond(validator, delegatorKey.Address); + var validatorList = repository.GetValidatorList(); + + Assert.Contains(delegatorKey.Address, validator.Delegators); + Assert.Equal(delegatorGold.RawValue, bond.Share); + Assert.Equal(validatorGold.RawValue + delegatorGold.RawValue, validator.Validator.Power); + Assert.Equal(validator.Validator, Assert.Single(validatorList.Validators)); + Assert.Equal(NCG * 80, world.GetBalance(delegatorKey.Address, NCG)); + } + + [Fact] + public void Execute_WithInvalidCurrency_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var delegatorKey = new PrivateKey(); + var height = 1L; + var validatorGold = NCG * 10; + var delegatorGold = Dollar * 20; + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++, mint: true); + world = MintAsset(world, delegatorKey, delegatorGold, height++); + + // When + var actionContext = new ActionContext { - IWorld world = new World(MockUtil.MockModernWorldState); - var context = new ActionContext { }; - var ncg = Currency.Uncapped("NCG", 2, null); - // TODO: Use Currencies.GuildGold when it's available. - // var gg = Currencies.GuildGold; - var gg = ncg; - var invalid = Currency.Uncapped("invalid", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - var validatorPublicKey = new PrivateKey().PublicKey; - world = world.MintAsset(context, validatorPublicKey.Address, gg * 100); - var promoteFAV = gg * 10; - world = new PromoteValidator(validatorPublicKey, promoteFAV).Execute(new ActionContext - { - PreviousState = world, - Signer = validatorPublicKey.Address, - }); - - var publicKey = new PrivateKey().PublicKey; - world = world.MintAsset(context, publicKey.Address, invalid * 100); - var delegateFAV = invalid * 20; - var action = new DelegateValidator(validatorPublicKey.Address, delegateFAV); - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = world, - Signer = publicKey.Address, - })); - } - - [Fact] - public void CannotDelegateWithInsufficientBalance() + PreviousState = world, + Signer = delegatorKey.Address, + BlockIndex = height++, + }; + var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorGold); + + // Then + Assert.Throws( + () => delegateValidator.Execute(actionContext)); + } + + [Fact] + public void Execute_WithInsufficientBalance_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var delegatorKey = new PrivateKey(); + var validatorGold = NCG * 10; + var delegatorGold = NCG * 10; + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++, mint: true); + world = MintAsset(world, delegatorKey, delegatorGold, height++); + + // When + var actionContext = new ActionContext { - IWorld world = new World(MockUtil.MockModernWorldState); - var context = new ActionContext { }; - var ncg = Currency.Uncapped("NCG", 2, null); - // TODO: Use Currencies.GuildGold when it's available. - // var gg = Currencies.GuildGold; - var gg = ncg; - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - var validatorPublicKey = new PrivateKey().PublicKey; - world = world.MintAsset(context, validatorPublicKey.Address, gg * 100); - var promoteFAV = gg * 10; - world = new PromoteValidator(validatorPublicKey, promoteFAV).Execute(new ActionContext - { - PreviousState = world, - Signer = validatorPublicKey.Address, - }); - - var publicKey = new PrivateKey().PublicKey; - var delegateFAV = gg * 20; - var action = new DelegateValidator(validatorPublicKey.Address, delegateFAV); - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = world, - Signer = publicKey.Address, - })); - } - - [Fact] - public void CannotDelegateToInvalidValidator() + PreviousState = world, + Signer = delegatorKey.Address, + BlockIndex = height++, + }; + var delegateValidator = new DelegateValidator(validatorKey.Address, NCG * 11); + + // Then + Assert.Throws( + () => delegateValidator.Execute(actionContext)); + } + + [Fact] + public void Execute_ToInvalidValidator_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var delegatorKey = new PrivateKey(); + var height = 1L; + world = MintAsset(world, delegatorKey, NCG * 100, height++); + + // When + var actionContext = new ActionContext { - // Given - var world = World; - var validatorPrivateKey = new PrivateKey(); - var delegatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - world = MintAsset(world, delegatorPrivateKey, NCG * 100, blockHeight++); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - Signer = delegatorPrivateKey.Address, - }; - var delegateValidator = new DelegateValidator(validatorPrivateKey.Address, NCG * 10); - - // Then - Assert.Throws( - () => delegateValidator.Execute(actionContext)); - } + PreviousState = world, + Signer = delegatorKey.Address, + }; + var delegateValidator = new DelegateValidator(validatorKey.Address, NCG * 10); + + // Then + Assert.Throws( + () => delegateValidator.Execute(actionContext)); } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs index 100161608d..7269b66bcb 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs @@ -1,164 +1,154 @@ -namespace Lib9c.Tests.Action.ValidatorDelegation +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using System; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; +using Xunit; + +public class PromoteValidatorTest : ValidatorDelegationTestBase { - using System; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Mocks; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action.ValidatorDelegation; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.ValidatorDelegation; - using Xunit; - - public class PromoteValidatorTest : ValidatorDelegationTestBase + [Fact] + public void Serialization() { - [Fact] - public void Serialization() - { - var publicKey = new PrivateKey().PublicKey; - var gg = Currencies.GuildGold; - var fav = gg * 10; - var action = new PromoteValidator(publicKey, fav); - var plainValue = action.PlainValue; - - var deserialized = new PromoteValidator(); - deserialized.LoadPlainValue(plainValue); - Assert.Equal(publicKey, deserialized.PublicKey); - Assert.Equal(fav, deserialized.FAV); - } - - [Fact] - public void Execute() + var publicKey = new PrivateKey().PublicKey; + var gold = NCG * 10; + var action = new PromoteValidator(publicKey, gold); + var plainValue = action.PlainValue; + + var deserialized = new PromoteValidator(); + deserialized.LoadPlainValue(plainValue); + Assert.Equal(publicKey, deserialized.PublicKey); + Assert.Equal(gold, deserialized.FAV); + } + + [Fact] + public void Execute() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var height = 1L; + var gold = NCG * 10; + world = MintAsset(world, validatorKey, NCG * 100, height++); + + // When + var actionContext = new ActionContext { - IWorld world = new World(MockUtil.MockModernWorldState); - var context = new ActionContext { }; - var ncg = Currency.Uncapped("NCG", 2, null); - // TODO: Use Currencies.GuildGold when it's available. - // var gg = Currencies.GuildGold; - var gg = ncg; - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - var publicKey = new PrivateKey().PublicKey; - world = world.MintAsset(context, publicKey.Address, gg * 100); - var fav = gg * 10; - var action = new PromoteValidator(publicKey, fav); - - world = action.Execute(new ActionContext - { - PreviousState = world, - Signer = publicKey.Address, - }); - - var repository = new ValidatorRepository(world, context); - var validator = repository.GetValidatorDelegatee(publicKey.Address); - var bond = repository.GetBond(validator, publicKey.Address); - var validatorList = repository.GetValidatorList(); - - Assert.Equal(publicKey.Address, Assert.Single(validator.Delegators)); - Assert.Equal(fav.RawValue, bond.Share); - Assert.Equal(validator.Validator, Assert.Single(validatorList.Validators)); - Assert.Equal(validator.Validator, Assert.Single(validatorList.GetBonded())); - Assert.Equal(gg * 90, world.GetBalance(publicKey.Address, gg)); - Assert.Empty(validatorList.GetUnbonded()); - } - - [Fact] - public void CannotPromoteWithInvalidPublicKey() + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height++, + }; + var promoteValidator = new PromoteValidator(validatorKey.PublicKey, gold); + world = promoteValidator.Execute(actionContext); + + // Then + var repository = new ValidatorRepository(world, actionContext); + var validator = repository.GetValidatorDelegatee(validatorKey.Address); + var bond = repository.GetBond(validator, validatorKey.Address); + var validatorList = repository.GetValidatorList(); + + Assert.Equal(validatorKey.Address, Assert.Single(validator.Delegators)); + Assert.Equal(gold.RawValue, bond.Share); + Assert.Equal(validator.Validator, Assert.Single(validatorList.Validators)); + Assert.Equal(validator.Validator, Assert.Single(validatorList.GetBonded())); + Assert.Equal(NCG * 90, world.GetBalance(validatorKey.Address, NCG)); + Assert.Empty(validatorList.GetUnbonded()); + } + + [Fact] + public void Execute_ToInvalidValidator_Throw() + { + // Given + var world = World; + var context = new ActionContext { }; + var validatorKey = new PrivateKey(); + var height = 1L; + var gold = NCG * 10; + world = MintAsset(world, validatorKey, NCG * 100, height++); + + // When + var actionContext = new ActionContext { - IWorld world = new World(MockUtil.MockModernWorldState); - var context = new ActionContext { }; - var ncg = Currency.Uncapped("NCG", 2, null); - var gg = Currencies.GuildGold; - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - var publicKey = new PrivateKey().PublicKey; - world = world.MintAsset(context, publicKey.Address, gg * 100); - var fav = gg * 10; - var action = new PromoteValidator(new PrivateKey().PublicKey, fav); - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = world, - Signer = publicKey.Address, - })); - } - - [Fact] - public void CannotPromoteWithInvalidCurrency() + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height++, + }; + var promoteValidator = new PromoteValidator(new PrivateKey().PublicKey, gold); + + // Then + Assert.Throws( + () => promoteValidator.Execute(actionContext)); + } + + [Fact] + public void Execute_WithInvalidCurrency_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var height = 1L; + var gold = Dollar * 10; + world = MintAsset(world, validatorKey, Dollar * 100, height++); + + // When + var actionContext = new ActionContext { - IWorld world = new World(MockUtil.MockModernWorldState); - var context = new ActionContext { }; - var ncg = Currency.Uncapped("NCG", 2, null); - var gg = Currency.Uncapped("invalid", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - var publicKey = new PrivateKey().PublicKey; - world = world.MintAsset(context, publicKey.Address, gg * 100); - var fav = gg * 10; - var action = new PromoteValidator(publicKey, fav); - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = world, - Signer = publicKey.Address, - })); - } - - [Fact] - public void CannotPromoteWithInsufficientBalance() + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height++, + }; + var promoteValidator = new PromoteValidator(validatorKey.PublicKey, gold); + + // Then + Assert.Throws( + () => promoteValidator.Execute(actionContext)); + } + + [Fact] + public void Execute_With_InsufficientBalance_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey().PublicKey; + var height = 1L; + var gold = NCG * 10; + + // When + var actionContext = new ActionContext { - IWorld world = new World(MockUtil.MockModernWorldState); - var context = new ActionContext { }; - var ncg = Currency.Uncapped("NCG", 2, null); - // TODO: Use Currencies.GuildGold when it's available. - // var gg = Currencies.GuildGold; - var gg = ncg; - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - var publicKey = new PrivateKey().PublicKey; - var fav = gg * 10; - var action = new PromoteValidator(publicKey, fav); - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = world, - Signer = publicKey.Address, - })); - } - - [Fact] - public void Promote_PromotedValidator_Throw() + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height++, + }; + var promoteValidator = new PromoteValidator(validatorKey, gold); + + // Then + Assert.Throws( + () => promoteValidator.Execute(actionContext)); + } + + [Fact] + public void Execute_PromotedValidator_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + + // When + var promoteValidator = new PromoteValidator(validatorKey.PublicKey, NCG * 10); + var actionContext = new ActionContext { - // Given - var world = World; - var validatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, NCG * 10, blockHeight++); - - // When - var promoteValidator = new PromoteValidator( - validatorPrivateKey.PublicKey, NCG * 10); - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight++, - Signer = validatorPrivateKey.Address, - }; - - // Then - Assert.Throws( - () => promoteValidator.Execute(actionContext)); - } + PreviousState = world, + BlockIndex = height++, + Signer = validatorKey.Address, + }; + + // Then + Assert.Throws( + () => promoteValidator.Execute(actionContext)); } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RecordProposerTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RecordProposerTest.cs index efda15a76c..1c83b5296f 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RecordProposerTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RecordProposerTest.cs @@ -1,50 +1,49 @@ #nullable enable -namespace Lib9c.Tests.Action.ValidatorDelegation -{ - using System; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume.Action.ValidatorDelegation; - using Nekoyume.Module.ValidatorDelegation; - using Nekoyume.ValidatorDelegation; - using Xunit; +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using System; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.Module.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; +using Xunit; - public class RecordProposerTest : ValidatorDelegationTestBase +public class RecordProposerTest : ValidatorDelegationTestBase +{ + [Fact] + public void Serialization() { - [Fact] - public void Serialization() - { - var action = new RecordProposer(); - var plainValue = action.PlainValue; + var action = new RecordProposer(); + var plainValue = action.PlainValue; - var deserialized = new RecordProposer(); - deserialized.LoadPlainValue(plainValue); - } + var deserialized = new RecordProposer(); + deserialized.LoadPlainValue(plainValue); + } - [Fact] - public void Execute() - { - // Given - var world = World; - var minerPrivateKey = new PrivateKey(); - var blockIndex = (long)Random.Shared.Next(1, 100); + [Fact] + public void Execute() + { + // Given + var world = World; + var minerKey = new PrivateKey(); + var blockIndex = (long)Random.Shared.Next(1, 100); - // When - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockIndex++, - Miner = minerPrivateKey.Address, - }; - var recordProposer = new RecordProposer(); - world = recordProposer.Execute(actionContext); + // When + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockIndex++, + Miner = minerKey.Address, + }; + var recordProposer = new RecordProposer(); + world = recordProposer.Execute(actionContext); - // Then - var repository = new ValidatorRepository(world, actionContext); - var proposerInfo = repository.GetProposerInfo(); + // Then + var repository = new ValidatorRepository(world, actionContext); + var proposerInfo = repository.GetProposerInfo(); - Assert.Equal(blockIndex - 1, proposerInfo.BlockIndex); - Assert.Equal(minerPrivateKey.Address, proposerInfo.Proposer); - } + Assert.Equal(blockIndex - 1, proposerInfo.BlockIndex); + Assert.Equal(minerKey.Address, proposerInfo.Proposer); } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs index ee4c8b73da..cd25698e42 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs @@ -1,247 +1,199 @@ -namespace Lib9c.Tests.Action.ValidatorDelegation +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using System; +using System.Numerics; +using Libplanet.Crypto; +using Nekoyume.Action; +using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; +using Xunit; + +public class RedelegateValidatorTest : ValidatorDelegationTestBase { - using System; - using System.Numerics; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Mocks; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Action.ValidatorDelegation; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.ValidatorDelegation; - using Xunit; - - public class RedelegateValidatorTest : ValidatorDelegationTestBase + [Fact] + public void Serialization() { - [Fact] - public void Serialization() - { - var srcAddress = new PrivateKey().Address; - var dstAddress = new PrivateKey().Address; - var share = BigInteger.One; - var action = new RedelegateValidator(srcAddress, dstAddress, share); - var plainValue = action.PlainValue; - - var deserialized = new RedelegateValidator(); - deserialized.LoadPlainValue(plainValue); - Assert.Equal(srcAddress, deserialized.SrcValidatorDelegatee); - Assert.Equal(dstAddress, deserialized.DstValidatorDelegatee); - Assert.Equal(share, deserialized.Share); - } - - [Fact] - public void Execute() + var srcAddress = new PrivateKey().Address; + var dstAddress = new PrivateKey().Address; + var share = BigInteger.One; + var action = new RedelegateValidator(srcAddress, dstAddress, share); + var plainValue = action.PlainValue; + + var deserialized = new RedelegateValidator(); + deserialized.LoadPlainValue(plainValue); + Assert.Equal(srcAddress, deserialized.SrcValidatorDelegatee); + Assert.Equal(dstAddress, deserialized.DstValidatorDelegatee); + Assert.Equal(share, deserialized.Share); + } + + [Fact] + public void Execute() + { + // Given + var world = World; + var actionContext = new ActionContext { }; + var srcPrivateKey = new PrivateKey(); + var dstPrivateKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, srcPrivateKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, dstPrivateKey, NCG * 10, height++, mint: true); + + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedSrcValidator = expectedRepository.GetValidatorDelegatee(srcPrivateKey.Address); + var expectedBond = expectedRepository.GetBond(expectedSrcValidator, srcPrivateKey.Address); + var redelegateValidator = new RedelegateValidator( + srcPrivateKey.Address, dstPrivateKey.Address, expectedBond.Share); + actionContext = new ActionContext { - IWorld world = new World(MockUtil.MockModernWorldState); - var context = new ActionContext { }; - var ncg = Currency.Uncapped("NCG", 2, null); - // TODO: Use Currencies.GuildGold when it's available. - // var gg = Currencies.GuildGold; - var gg = ncg; - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - var srcPublicKey = new PrivateKey().PublicKey; - world = world.MintAsset(context, srcPublicKey.Address, gg * 100); - var promoteFAV = gg * 10; - world = new PromoteValidator(srcPublicKey, promoteFAV).Execute(new ActionContext - { - PreviousState = world, - Signer = srcPublicKey.Address, - }); - - var dstPublicKey = new PrivateKey().PublicKey; - world = world.MintAsset(context, dstPublicKey.Address, gg * 100); - world = new PromoteValidator(dstPublicKey, promoteFAV).Execute(new ActionContext - { - PreviousState = world, - Signer = dstPublicKey.Address, - }); - - var repository = new ValidatorRepository(world, context); - var srcValidator = repository.GetValidatorDelegatee(srcPublicKey.Address); - var bond = repository.GetBond(srcValidator, srcPublicKey.Address); - var action = new RedelegateValidator(srcPublicKey.Address, dstPublicKey.Address, bond.Share); - context = new ActionContext - { - PreviousState = world, - Signer = srcPublicKey.Address, - BlockIndex = 1, - }; - - world = action.Execute(context); - - repository.UpdateWorld(world); - srcValidator = repository.GetValidatorDelegatee(srcPublicKey.Address); - var dstValidator = repository.GetValidatorDelegatee(dstPublicKey.Address); - var validatorList = repository.GetValidatorList(); - var dstBond = repository.GetBond(dstValidator, srcPublicKey.Address); - - Assert.Contains(srcPublicKey.Address, dstValidator.Delegators); - Assert.Equal(dstValidator.Validator, Assert.Single(validatorList.Validators)); - Assert.Equal((gg * 10).RawValue, dstBond.Share); - Assert.Equal(gg * 20, dstValidator.TotalDelegated); - Assert.Equal((gg * 20).RawValue, dstValidator.TotalShares); - Assert.Equal((gg * 20).RawValue, dstValidator.Power); - } - - [Fact] - public void Redelegate_ToInvalidValidator_Throw() + PreviousState = world, + Signer = srcPrivateKey.Address, + BlockIndex = height++, + }; + + world = redelegateValidator.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDstValidator = actualRepository.GetValidatorDelegatee(dstPrivateKey.Address); + var actualValidatorList = actualRepository.GetValidatorList(); + var actualDstBond = actualRepository.GetBond(actualDstValidator, srcPrivateKey.Address); + + Assert.Contains(srcPrivateKey.Address, actualDstValidator.Delegators); + Assert.Single(actualValidatorList.Validators); + Assert.Equal(actualDstValidator.Validator, actualValidatorList.Validators[0]); + Assert.Equal((NCG * 10).RawValue, actualDstBond.Share); + Assert.Equal(NCG * 20, actualDstValidator.TotalDelegated); + Assert.Equal((NCG * 20).RawValue, actualDstValidator.TotalShares); + Assert.Equal((NCG * 20).RawValue, actualDstValidator.Power); + } + + [Fact] + public void Execute_ToInvalidValidator_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var delegatorKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); + + // When + var actionContext = new ActionContext { - // Given - var world = World; - var validatorPrivateKey = new PrivateKey(); - var delegatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, NCG * 10, blockHeight++); - world = EnsureDelegatorToBeBond( - world, delegatorPrivateKey, validatorPrivateKey, NCG * 10, blockHeight++); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight++, - Signer = delegatorPrivateKey.Address, - }; - var redelegateValidator = new RedelegateValidator( - validatorPrivateKey.PublicKey.Address, - new PrivateKey().PublicKey.Address, - 10); - - // Then - Assert.Throws( - () => redelegateValidator.Execute(actionContext)); - } - - [Theory] - [InlineData(0)] - [InlineData(-1)] - public void Redelegate_NotPositiveShare_Throw(long share) + PreviousState = world, + BlockIndex = height++, + Signer = delegatorKey.Address, + }; + var invalidAddress = new PrivateKey().Address; + var redelegateValidator = new RedelegateValidator(validatorKey.Address, invalidAddress, 10); + + // Then + Assert.Throws( + () => redelegateValidator.Execute(actionContext)); + } + + [Theory] + [InlineData(0)] + [InlineData(-1)] + public void Execute_NotPositiveShare_Throw(long share) + { + // Given + var world = World; + var validatorKey1 = new PrivateKey(); + var validatorKey2 = new PrivateKey(); + var delegatorKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, height++, mint: true); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, NCG * 10, height++); + + // When + var actionContext = new ActionContext { - // Given - var world = World; - var validatorPrivateKey1 = new PrivateKey(); - var validatorPrivateKey2 = new PrivateKey(); - var delegatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey1, NCG * 10, blockHeight++); - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey2, NCG * 10, blockHeight++); - world = EnsureDelegatorToBeBond( - world, delegatorPrivateKey, validatorPrivateKey1, NCG * 10, blockHeight++); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight++, - Signer = delegatorPrivateKey.Address, - }; - var redelegateValidator = new RedelegateValidator( - validatorPrivateKey1.PublicKey.Address, - validatorPrivateKey2.PublicKey.Address, - share); - - // Then - Assert.Throws( - () => redelegateValidator.Execute(actionContext)); - } - - [Fact] - public void Redelegate_OverShare_Throw() + PreviousState = world, + BlockIndex = height++, + Signer = delegatorKey.Address, + }; + var redelegateValidator = new RedelegateValidator( + validatorKey1.Address, validatorKey2.Address, share); + + // Then + Assert.Throws( + () => redelegateValidator.Execute(actionContext)); + } + + [Fact] + public void Execute_OverShare_Throw() + { + // Given + var world = World; + var validatorKey1 = new PrivateKey(); + var validatorKey2 = new PrivateKey(); + var delegatorKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, height++, mint: true); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, NCG * 10, height++); + + // When + var actionContext = new ActionContext { - // Given - var world = World; - var validatorPrivateKey1 = new PrivateKey(); - var validatorPrivateKey2 = new PrivateKey(); - var delegatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey1, NCG * 10, blockHeight++); - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey2, NCG * 10, blockHeight++); - world = EnsureDelegatorToBeBond( - world, delegatorPrivateKey, validatorPrivateKey1, NCG * 10, blockHeight++); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight++, - Signer = delegatorPrivateKey.Address, - }; - var repository = new ValidatorRepository(world, actionContext); - var delegatee = repository.GetDelegatee(validatorPrivateKey1.PublicKey.Address); - var bond = repository.GetBond(delegatee, delegatorPrivateKey.Address); - var redelegateValidator = new RedelegateValidator( - validatorPrivateKey1.PublicKey.Address, - validatorPrivateKey2.PublicKey.Address, - bond.Share + 1); - - // Then - Assert.Throws( - () => redelegateValidator.Execute(actionContext)); - } - - [Fact] - public void Redelegate_FromJailedValidator_Throw() + PreviousState = world, + BlockIndex = height++, + Signer = delegatorKey.Address, + }; + var repository = new ValidatorRepository(world, actionContext); + var delegatee = repository.GetDelegatee(validatorKey1.Address); + var bond = repository.GetBond(delegatee, delegatorKey.Address); + var redelegateValidator = new RedelegateValidator( + validatorKey1.Address, validatorKey2.Address, bond.Share + 1); + + // Then + Assert.Throws( + () => redelegateValidator.Execute(actionContext)); + } + + [Fact] + public void Execute_FromJailedValidator_Throw() + { + // Given + var world = World; + var delegatorKey = new PrivateKey(); + var validatorKey1 = new PrivateKey(); + var validatorKey2 = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, height++, mint: true); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, NCG * 10, height++); + world = EnsureJailedValidator(world, validatorKey1, ref height); + + // When + var actionContext = new ActionContext { - // Given - var world = World; - var delegatorPrivateKey = new PrivateKey(); - var validatorPrivateKey1 = new PrivateKey(); - var validatorPrivateKey2 = new PrivateKey(); - var blockHeight = 1L; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey1, NCG * 10, blockHeight++); - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey2, NCG * 10, blockHeight++); - world = EnsureDelegatorToBeBond( - world, delegatorPrivateKey, validatorPrivateKey1, NCG * 10, blockHeight++); - world = EnsureValidatorToBeJailed( - world, validatorPrivateKey1, ref blockHeight); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - Signer = delegatorPrivateKey.Address, - BlockIndex = blockHeight++, - }; - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedDelegatee1 = expectedRepository.GetValidatorDelegatee( - validatorPrivateKey1.PublicKey.Address); - var expectedDelegatee2 = expectedRepository.GetValidatorDelegatee( - validatorPrivateKey2.PublicKey.Address); - var expectedBond1 = expectedRepository.GetBond( - expectedDelegatee1, delegatorPrivateKey.Address); - var expectedBond2 = expectedRepository.GetBond( - expectedDelegatee2, delegatorPrivateKey.Address); - - var redelegateValidator = new RedelegateValidator( - validatorPrivateKey1.Address, validatorPrivateKey2.Address, 10); - world = redelegateValidator.Execute(actionContext); - - // Then - var actualRepository = new ValidatorRepository(world, actionContext); - var actualDelegatee1 = actualRepository.GetValidatorDelegatee( - validatorPrivateKey1.PublicKey.Address); - var actualDelegatee2 = actualRepository.GetValidatorDelegatee( - validatorPrivateKey2.PublicKey.Address); - var actualBond1 = actualRepository.GetBond( - actualDelegatee1, delegatorPrivateKey.Address); - var actualBond2 = actualRepository.GetBond( - actualDelegatee2, delegatorPrivateKey.Address); - - Assert.Equal(expectedBond1.Share - 10, actualBond1.Share); - Assert.Equal(expectedBond2.Share + 10, actualBond2.Share); - } + PreviousState = world, + Signer = delegatorKey.Address, + BlockIndex = height++, + }; + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee1 = expectedRepository.GetValidatorDelegatee(validatorKey1.Address); + var expectedDelegatee2 = expectedRepository.GetValidatorDelegatee(validatorKey2.Address); + var expectedBond1 = expectedRepository.GetBond(expectedDelegatee1, delegatorKey.Address); + var expectedBond2 = expectedRepository.GetBond(expectedDelegatee2, delegatorKey.Address); + + var redelegateValidator = new RedelegateValidator( + validatorKey1.Address, validatorKey2.Address, 10); + world = redelegateValidator.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee1 = actualRepository.GetValidatorDelegatee(validatorKey1.Address); + var actualDelegatee2 = actualRepository.GetValidatorDelegatee(validatorKey2.Address); + var actualBond1 = actualRepository.GetBond(actualDelegatee1, delegatorKey.Address); + var actualBond2 = actualRepository.GetBond(actualDelegatee2, delegatorKey.Address); + + Assert.Equal(expectedBond1.Share - 10, actualBond1.Share); + Assert.Equal(expectedBond2.Share + 10, actualBond2.Share); } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs index 62b7569597..a87f907423 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs @@ -1,7 +1,6 @@ #nullable enable namespace Lib9c.Tests.Action.ValidatorDelegation { - using System; using Nekoyume.Action.ValidatorDelegation; using Xunit; diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs index 5c9b77f849..f853c3ec81 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs @@ -1,172 +1,172 @@ #nullable enable -namespace Lib9c.Tests.Action.ValidatorDelegation +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Numerics; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Types.Blocks; +using Libplanet.Types.Consensus; +using Libplanet.Types.Evidence; +using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; +using Xunit; + +public class SlashValidatorTest : ValidatorDelegationTestBase { - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Numerics; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Blocks; - using Libplanet.Types.Consensus; - using Libplanet.Types.Evidence; - using Nekoyume.Action.ValidatorDelegation; - using Nekoyume.ValidatorDelegation; - using Xunit; + [Fact] + public void Serialization() + { + var action = new SlashValidator(); + var plainValue = action.PlainValue; + + var deserialized = new SlashValidator(); + deserialized.LoadPlainValue(plainValue); + } - public class SlashValidatorTest : ValidatorDelegationTestBase + [Fact] + public void Execute() { - [Fact] - public void Serialization() + // Given + const int length = 10; + var world = World; + var validatorKey = new PrivateKey(); + var validatorGold = NCG * 10; + var deletatorKeys = GetRandomArray(length, _ => new PrivateKey()); + var delegatorGolds = GetRandomArray(length, i => NCG * Random.Shared.Next(10, 100)); + var height = 1L; + var actionContext = new ActionContext { }; + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsureBondedDelegators( + world, deletatorKeys, validatorKey, delegatorGolds, height++); + + // When + var validatorSet = new ValidatorSet(new List + { + new (validatorKey.PublicKey, new BigInteger(1000)), + }); + var vote1 = new VoteMetadata( + height: height - 1, + round: 0, + blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x01)), + timestamp: DateTimeOffset.UtcNow, + validatorPublicKey: validatorKey.PublicKey, + validatorPower: BigInteger.One, + flag: VoteFlag.PreCommit).Sign(validatorKey); + var vote2 = new VoteMetadata( + height: height - 1, + round: 0, + blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x02)), + timestamp: DateTimeOffset.UtcNow, + validatorPublicKey: validatorKey.PublicKey, + validatorPower: BigInteger.One, + flag: VoteFlag.PreCommit).Sign(validatorKey); + var evidence = new DuplicateVoteEvidence( + vote1, + vote2, + validatorSet, + vote1.Timestamp); + var lastCommit = new BlockCommit( + height: height - 1, + round: 0, + blockHash: EmptyBlockHash, + ImmutableArray.Create(vote1)); + var slashValidator = new SlashValidator(); + actionContext = new ActionContext { - var action = new SlashValidator(); - var plainValue = action.PlainValue; + PreviousState = world, + BlockIndex = height++, + Evidence = new List { evidence }, + LastCommit = lastCommit, + }; + world = slashValidator.Execute(actionContext); - var deserialized = new SlashValidator(); - deserialized.LoadPlainValue(plainValue); - } + // Then + var balance = world.GetBalance(validatorKey.Address, NCG); + var repository = new ValidatorRepository(world, actionContext); + var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); - [Fact] - public void Execute() - { - // Given - const int length = 10; - var world = World; - var validatorPrivateKey = new PrivateKey(); - var validatorGold = NCG * 10; - var deletatorPrivateKeys = GetRandomArray(length, _ => new PrivateKey()); - var delegatorNCGs = GetRandomArray( - length, i => NCG * Random.Shared.Next(10, 100)); - var blockHeight = 1L; - var actionContext = new ActionContext { }; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, NCG * 10, blockHeight++); - world = EnsureDelegatorsToBeBond( - world, deletatorPrivateKeys, validatorPrivateKey, delegatorNCGs, blockHeight++); + Assert.True(delegatee.Jailed); + Assert.Equal(long.MaxValue, delegatee.JailedUntil); + Assert.True(delegatee.Tombstoned); + } - // When - var validatorSet = new ValidatorSet(new List - { - new (validatorPrivateKey.PublicKey, new BigInteger(1000)), - }); - var vote1 = new VoteMetadata( - height: blockHeight - 1, - round: 0, - blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x01)), - timestamp: DateTimeOffset.UtcNow, - validatorPublicKey: validatorPrivateKey.PublicKey, - validatorPower: BigInteger.One, - flag: VoteFlag.PreCommit).Sign(validatorPrivateKey); - var vote2 = new VoteMetadata( - height: blockHeight - 1, - round: 0, - blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x02)), - timestamp: DateTimeOffset.UtcNow, - validatorPublicKey: validatorPrivateKey.PublicKey, - validatorPower: BigInteger.One, - flag: VoteFlag.PreCommit).Sign(validatorPrivateKey); - var evidence = new DuplicateVoteEvidence( - vote1, - vote2, - validatorSet, - vote1.Timestamp); + [Fact] + public void Execute_By_Abstain() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var actionContext = new ActionContext { }; + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + + // When + for (var i = 0L; i <= AbstainHistory.MaxAbstainAllowance; i++) + { + var vote = CreateNullVote(validatorKey, height - 1); var lastCommit = new BlockCommit( - height: blockHeight - 1, + height: height - 1, round: 0, - blockHash: EmptyBlockHash, - ImmutableArray.Create(vote1)); + blockHash: vote.BlockHash, + ImmutableArray.Create(vote)); var slashValidator = new SlashValidator(); actionContext = new ActionContext { PreviousState = world, - BlockIndex = blockHeight++, - Evidence = new List { evidence }, + Signer = validatorKey.Address, + BlockIndex = height++, LastCommit = lastCommit, }; world = slashValidator.Execute(actionContext); - - // Then - var balance = world.GetBalance(validatorPrivateKey.Address, NCG); - var repository = new ValidatorRepository(world, actionContext); - var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); - - Assert.True(delegatee.Jailed); - Assert.Equal(long.MaxValue, delegatee.JailedUntil); - Assert.True(delegatee.Tombstoned); } - [Fact] - public void Jail_By_Abstain() - { - // Given - var world = World; - var validatorPrivateKey = new PrivateKey(); - var actionContext = new ActionContext { }; - var blockHeight = 1L; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, NCG * 10, blockHeight++); - - // When - for (var i = 0L; i <= AbstainHistory.MaxAbstainAllowance; i++) - { - var vote = CreateNullVote(validatorPrivateKey, blockHeight - 1); - var lastCommit = new BlockCommit( - height: blockHeight - 1, - round: 0, - blockHash: vote.BlockHash, - ImmutableArray.Create(vote)); - world = ExecuteSlashValidator( - world, validatorPrivateKey.PublicKey, lastCommit, blockHeight++); - } + // Then + var repository = new ValidatorRepository(world, actionContext); + var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); + Assert.True(delegatee.Jailed); + Assert.False(delegatee.Tombstoned); + } - // Then - var repository = new ValidatorRepository(world, actionContext); - var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); - Assert.True(delegatee.Jailed); - Assert.False(delegatee.Tombstoned); - } + [Fact] + public void Execute_JailedDelegatee_Nothing_Happens() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var height = 1L; + var actionContext = new ActionContext(); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsureTombstonedValidator(world, validatorKey, height++); - [Fact] - public void Jail_JailedDelegatee_Nothing_Happens_Test() + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedJailed = expectedDelegatee.Jailed; + var evidence = CreateDuplicateVoteEvidence(validatorKey, height - 1); + var lastCommit = new BlockCommit( + height: height - 1, + round: 0, + blockHash: EmptyBlockHash, + ImmutableArray.Create(evidence.VoteRef)); + var slashValidator = new SlashValidator(); + actionContext = new ActionContext { - // Given - var world = World; - var validatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - var actionContext = new ActionContext(); - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, NCG * 10, blockHeight++); - world = EnsureValidatorToBeTombstoned(world, validatorPrivateKey, blockHeight++); + PreviousState = world, + BlockIndex = height + SlashValidator.AbstainJailTime, + Signer = validatorKey.Address, + Evidence = new List { evidence }, + LastCommit = lastCommit, + }; + world = slashValidator.Execute(actionContext); - // When - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedDelegatee = expectedRepository.GetValidatorDelegatee( - validatorPrivateKey.Address); - var expectedJailed = expectedDelegatee.Jailed; - var evidence = CreateDuplicateVoteEvidence(validatorPrivateKey, blockHeight - 1); - var lastCommit = new BlockCommit( - height: blockHeight - 1, - round: 0, - blockHash: EmptyBlockHash, - ImmutableArray.Create(evidence.VoteRef)); - var slashValidator = new SlashValidator(); - actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight + SlashValidator.AbstainJailTime, - Signer = validatorPrivateKey.Address, - Evidence = new List { evidence }, - LastCommit = lastCommit, - }; - world = slashValidator.Execute(actionContext); + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); + var actualJailed = actualDelegatee.Jailed; - // Then - var actualRepository = new ValidatorRepository(world, actionContext); - var actualDelegatee = actualRepository.GetValidatorDelegatee( - validatorPrivateKey.Address); - var actualJailed = actualDelegatee.Jailed; - - Assert.Equal(expectedJailed, actualJailed); - } + Assert.Equal(expectedJailed, actualJailed); } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 2a78ba198e..7b0d135053 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -1,211 +1,176 @@ -namespace Lib9c.Tests.Action.ValidatorDelegation +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using System; +using System.Numerics; +using Libplanet.Crypto; +using Nekoyume.Action; +using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; +using Xunit; + +public class UndelegateValidatorTest : ValidatorDelegationTestBase { - using System; - using System.Numerics; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Mocks; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Action.ValidatorDelegation; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.ValidatorDelegation; - using Xunit; - - public class UndelegateValidatorTest : ValidatorDelegationTestBase + [Fact] + public void Serialization() { - [Fact] - public void Serialization() - { - var address = new PrivateKey().Address; - var share = BigInteger.One; - var action = new UndelegateValidator(address, share); - var plainValue = action.PlainValue; - - var deserialized = new UndelegateValidator(); - deserialized.LoadPlainValue(plainValue); - Assert.Equal(address, deserialized.ValidatorDelegatee); - Assert.Equal(share, deserialized.Share); - } - - [Fact] - public void Execute() + var address = new PrivateKey().Address; + var share = BigInteger.One; + var action = new UndelegateValidator(address, share); + var plainValue = action.PlainValue; + + var deserialized = new UndelegateValidator(); + deserialized.LoadPlainValue(plainValue); + Assert.Equal(address, deserialized.ValidatorDelegatee); + Assert.Equal(share, deserialized.Share); + } + + [Fact] + public void Execute() + { + // Given + var world = World; + var actionContext = new ActionContext { }; + var validatorKey = new PrivateKey(); + var height = 1L; + var validatorGold = NCG * 10; + world = MintAsset(world, validatorKey, NCG * 100, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedBond = expectedRepository.GetBond(expectedDelegatee, validatorKey.Address); + var undelegateValidator = new UndelegateValidator(validatorKey.Address, expectedBond.Share); + actionContext = new ActionContext { - IWorld world = new World(MockUtil.MockModernWorldState); - var context = new ActionContext { }; - var ncg = Currency.Uncapped("NCG", 2, null); - // TODO: Use Currencies.GuildGold when it's available. - // var gg = Currencies.GuildGold; - var gg = ncg; - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - var publicKey = new PrivateKey().PublicKey; - world = world.MintAsset(context, publicKey.Address, gg * 100); - var promoteFAV = gg * 10; - world = new PromoteValidator(publicKey, promoteFAV).Execute(new ActionContext - { - PreviousState = world, - Signer = publicKey.Address, - }); - - var repository = new ValidatorRepository(world, context); - var validator = repository.GetValidatorDelegatee(publicKey.Address); - var bond = repository.GetBond(validator, publicKey.Address); - var action = new UndelegateValidator(publicKey.Address, bond.Share); - context = new ActionContext - { - PreviousState = world, - Signer = publicKey.Address, - BlockIndex = 1, - }; - - world = action.Execute(context); - - repository.UpdateWorld(world); - validator = repository.GetValidatorDelegatee(publicKey.Address); - var validatorList = repository.GetValidatorList(); - - Assert.Empty(validator.Delegators); - Assert.Equal(BigInteger.Zero, validator.Validator.Power); - Assert.Empty(validatorList.Validators); - - world = new ReleaseValidatorUnbondings().Execute(new ActionContext - { - PreviousState = world, - Signer = publicKey.Address, - BlockIndex = 1, - }); - - Assert.Equal(gg * 100, world.GetBalance(publicKey.Address, gg)); - } - - [Fact] - public void Undelegate_FromInvalidValidtor_Throw() + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height++, + }; + + world = undelegateValidator.Execute(actionContext); + + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); + var actualValidatorList = actualRepository.GetValidatorList(); + var actualBond = actualRepository.GetBond(actualDelegatee, validatorKey.Address); + + Assert.NotEqual(expectedDelegatee.Delegators, actualDelegatee.Delegators); + Assert.NotEqual(expectedDelegatee.Validator.Power, actualDelegatee.Validator.Power); + Assert.Equal(BigInteger.Zero, actualDelegatee.Validator.Power); + Assert.Empty(actualValidatorList.Validators); + Assert.NotEqual(expectedBond.Share, actualBond.Share); + Assert.Equal(BigInteger.Zero, actualBond.Share); + } + + [Fact] + public void Execute_FromInvalidValidtor_Throw() + { + // Given + var world = World; + var delegatorKey = new PrivateKey(); + var validatorKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); + + // When + var actionContext = new ActionContext { - // Given - var world = World; - var delegatorPrivateKey = new PrivateKey(); - var validatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, NCG * 10, blockHeight++); - world = EnsureDelegatorToBeBond( - world, delegatorPrivateKey, validatorPrivateKey, NCG * 10, blockHeight++); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - Signer = delegatorPrivateKey.Address, - BlockIndex = blockHeight++, - }; - var undelegateValidator = new UndelegateValidator( - new PrivateKey().Address, 10); - - // Then - Assert.Throws( - () => undelegateValidator.Execute(actionContext)); - } - - [Theory] - [InlineData(0)] - [InlineData(-1)] - public void Undelegate_NotPositiveShare_Throw(long share) + PreviousState = world, + Signer = delegatorKey.Address, + BlockIndex = height++, + }; + var undelegateValidator = new UndelegateValidator(new PrivateKey().Address, 10); + + // Then + Assert.Throws( + () => undelegateValidator.Execute(actionContext)); + } + + [Theory] + [InlineData(0)] + [InlineData(-1)] + public void Execute_NotPositiveShare_Throw(long share) + { + // Given + var world = World; + var delegatorKey = new PrivateKey(); + var validatorKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); + + // When + var actionContext = new ActionContext { - // Given - var world = World; - var delegatorPrivateKey = new PrivateKey(); - var validatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, NCG * 10, blockHeight++); - world = EnsureDelegatorToBeBond( - world, delegatorPrivateKey, validatorPrivateKey, NCG * 10, blockHeight++); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - Signer = delegatorPrivateKey.Address, - BlockIndex = blockHeight++, - }; - var undelegateValidator = new UndelegateValidator( - validatorPrivateKey.Address, share); - - // Then - Assert.Throws( - () => undelegateValidator.Execute(actionContext)); - } - - [Fact] - public void Undelegate_NotDelegated_Throw() + PreviousState = world, + Signer = delegatorKey.Address, + BlockIndex = height++, + }; + var undelegateValidator = new UndelegateValidator(validatorKey.Address, share); + + // Then + Assert.Throws( + () => undelegateValidator.Execute(actionContext)); + } + + [Fact] + public void Execute_NotDelegated_Throw() + { + // Given + var world = World; + var delegatorKey = new PrivateKey(); + var validatorKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + + // When + var actionContext = new ActionContext { - // Given - var world = World; - var delegatorPrivateKey = new PrivateKey(); - var validatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, NCG * 10, blockHeight++); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - Signer = delegatorPrivateKey.Address, - BlockIndex = blockHeight++, - }; - var undelegateValidator = new UndelegateValidator( - validatorPrivateKey.Address, 10); - - // Then - Assert.Throws( - () => undelegateValidator.Execute(actionContext)); - } - - [Fact] - public void Undelegate_FromJailedValidator() + PreviousState = world, + Signer = delegatorKey.Address, + BlockIndex = height++, + }; + var undelegateValidator = new UndelegateValidator( + validatorKey.Address, 10); + + // Then + Assert.Throws( + () => undelegateValidator.Execute(actionContext)); + } + + [Fact] + public void Execute_FromJailedValidator() + { + // Given + var world = World; + var delegatorKey = new PrivateKey(); + var validatorKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); + world = EnsureJailedValidator(world, validatorKey, ref height); + + // When + var actionContext = new ActionContext { - // Given - var world = World; - var delegatorPrivateKey = new PrivateKey(); - var validatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, NCG * 10, blockHeight++); - world = EnsureDelegatorToBeBond( - world, delegatorPrivateKey, validatorPrivateKey, NCG * 10, blockHeight++); - world = EnsureValidatorToBeJailed( - world, validatorPrivateKey, ref blockHeight); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - Signer = delegatorPrivateKey.Address, - BlockIndex = blockHeight, - }; - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedDelegatee = expectedRepository.GetValidatorDelegatee( - validatorPrivateKey.PublicKey.Address); - var expectedBond = expectedRepository.GetBond( - expectedDelegatee, delegatorPrivateKey.Address); - - var undelegateValidator = new UndelegateValidator( - validatorPrivateKey.Address, 10); - world = undelegateValidator.Execute(actionContext); - - // Then - var actualRepository = new ValidatorRepository(world, actionContext); - var actualDelegatee = actualRepository.GetValidatorDelegatee( - validatorPrivateKey.PublicKey.Address); - var actualBond = actualRepository.GetBond(actualDelegatee, delegatorPrivateKey.Address); - - Assert.Equal(expectedBond.Share - 10, actualBond.Share); - } + PreviousState = world, + Signer = delegatorKey.Address, + BlockIndex = height, + }; + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedBond = expectedRepository.GetBond(expectedDelegatee, delegatorKey.Address); + + var undelegateValidator = new UndelegateValidator(validatorKey.Address, 10); + world = undelegateValidator.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); + var actualBond = actualRepository.GetBond(actualDelegatee, delegatorKey.Address); + + Assert.Equal(expectedBond.Share - 10, actualBond.Share); } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs index 2e2111fd63..2900fd5baf 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs @@ -1,148 +1,143 @@ #nullable enable -namespace Lib9c.Tests.Action.ValidatorDelegation +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using System; +using Libplanet.Crypto; +using Nekoyume.Action; +using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; +using Xunit; + +public class UnjailValidatorTest : ValidatorDelegationTestBase { - using System; - using Libplanet.Crypto; - using Nekoyume.Action; - using Nekoyume.Action.ValidatorDelegation; - using Nekoyume.ValidatorDelegation; - using Xunit; - - public class UnjailValidatorTest : ValidatorDelegationTestBase + [Fact] + public void Serialization() { - [Fact] - public void Serialization() - { - var action = new UnjailValidator(); - var plainValue = action.PlainValue; + var action = new UnjailValidator(); + var plainValue = action.PlainValue; - var deserialized = new UnjailValidator(); - deserialized.LoadPlainValue(plainValue); - } + var deserialized = new UnjailValidator(); + deserialized.LoadPlainValue(plainValue); + } - [Fact] - public void Execute() + [Fact] + public void Execute() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsureJailedValidator(world, validatorKey, ref height); + + // When + var unjailValidator = new UnjailValidator(); + var actionContext = new ActionContext { - // Given - var world = World; - var validatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, NCG * 10, blockHeight++); - world = EnsureValidatorToBeJailed(world, validatorPrivateKey, ref blockHeight); - - // When - var unjailValidator = new UnjailValidator(); - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight + SlashValidator.AbstainJailTime, - Signer = validatorPrivateKey.PublicKey.Address, - }; - world = unjailValidator.Execute(actionContext); - - // Then - var repository = new ValidatorRepository(world, actionContext); - var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); - Assert.False(delegatee.Jailed); - Assert.Equal(-1, delegatee.JailedUntil); - Assert.False(delegatee.Tombstoned); - } - - [Fact] - public void Unjail_NotExistedDelegatee_Throw() + PreviousState = world, + BlockIndex = height + SlashValidator.AbstainJailTime, + Signer = validatorKey.Address, + }; + world = unjailValidator.Execute(actionContext); + + // Then + var repository = new ValidatorRepository(world, actionContext); + var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); + Assert.False(delegatee.Jailed); + Assert.Equal(-1, delegatee.JailedUntil); + Assert.False(delegatee.Tombstoned); + } + + [Fact] + public void Execute_NotExistedDelegatee_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var height = 1L; + + // When + var unjailValidator = new UnjailValidator(); + var actionContext = new ActionContext { - // Given - var world = World; - var validatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - - // When - var unjailValidator = new UnjailValidator(); - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight + SlashValidator.AbstainJailTime, - Signer = validatorPrivateKey.Address, - }; - - // Then - Assert.Throws( - () => unjailValidator.Execute(actionContext)); - } - - [Fact] - public void Unjail_JaliedValidator_NotJailed_Throw() + PreviousState = world, + BlockIndex = height + SlashValidator.AbstainJailTime, + Signer = validatorKey.Address, + }; + + // Then + Assert.Throws( + () => unjailValidator.Execute(actionContext)); + } + + [Fact] + public void Execute_JaliedValidator_NotJailed_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + + // When + var unjailValidator = new UnjailValidator(); + var actionContext = new ActionContext { - // Given - var world = World; - var validatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, NCG * 10, blockHeight++); - - // When - var unjailValidator = new UnjailValidator(); - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight + SlashValidator.AbstainJailTime, - Signer = validatorPrivateKey.PublicKey.Address, - }; - - // Then - Assert.Throws( - () => unjailValidator.Execute(actionContext)); - } - - [Fact] - public void Unjail_JaliedValidator_Early_Throw() + PreviousState = world, + BlockIndex = height + SlashValidator.AbstainJailTime, + Signer = validatorKey.Address, + }; + + // Then + Assert.Throws( + () => unjailValidator.Execute(actionContext)); + } + + [Fact] + public void Execute_JaliedValidator_Early_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsureJailedValidator(world, validatorKey, ref height); + + // When + var unjailValidator = new UnjailValidator(); + var actionContext = new ActionContext { - // Given - var world = World; - var validatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, NCG * 10, blockHeight++); - world = EnsureValidatorToBeJailed(world, validatorPrivateKey, ref blockHeight); - - // When - var unjailValidator = new UnjailValidator(); - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight + SlashValidator.AbstainJailTime - 1, - Signer = validatorPrivateKey.PublicKey.Address, - }; - - // Then - Assert.Throws( - () => unjailValidator.Execute(actionContext)); - } - - [Fact] - public void Unjail_JaliedValidator_Tombstoned_Throw() + PreviousState = world, + BlockIndex = height + SlashValidator.AbstainJailTime - 1, + Signer = validatorKey.Address, + }; + + // Then + Assert.Throws( + () => unjailValidator.Execute(actionContext)); + } + + [Fact] + public void Execute_JaliedValidator_Tombstoned_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsureTombstonedValidator(world, validatorKey, height++); + + // When + var unjailValidator = new UnjailValidator(); + var actionContext = new ActionContext { - // Given - var world = World; - var validatorPrivateKey = new PrivateKey(); - var blockHeight = 1L; - world = EnsureValidatorToBePromoted( - world, validatorPrivateKey, NCG * 10, blockHeight++); - world = EnsureValidatorToBeTombstoned(world, validatorPrivateKey, blockHeight++); - - // When - var unjailValidator = new UnjailValidator(); - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight + SlashValidator.AbstainJailTime, - Signer = validatorPrivateKey.PublicKey.Address, - }; - - // Then - Assert.Throws( - () => unjailValidator.Execute(actionContext)); - } + PreviousState = world, + BlockIndex = height + SlashValidator.AbstainJailTime, + Signer = validatorKey.Address, + }; + + // Then + Assert.Throws( + () => unjailValidator.Execute(actionContext)); } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs index 22a924dc08..c953e4754f 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs @@ -1,69 +1,68 @@ #nullable enable -namespace Lib9c.Tests.Action.ValidatorDelegation -{ - using System; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume.Action.ValidatorDelegation; - using Nekoyume.Model.State; - using Nekoyume.ValidatorDelegation; - using Xunit; +namespace Lib9c.Tests.Action.ValidatorDelegation; - public class UpdateValidatorsTest : ValidatorDelegationTestBase - { - [Fact] - public void Serialization() - { - var action = new UpdateValidators(); - var plainValue = action.PlainValue; +using System; +using System.Linq; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.Model.State; +using Nekoyume.ValidatorDelegation; +using Xunit; - var deserialized = new UpdateValidators(); - deserialized.LoadPlainValue(plainValue); - } +public class UpdateValidatorsTest : ValidatorDelegationTestBase +{ + [Fact] + public void Serialization() + { + var action = new UpdateValidators(); + var plainValue = action.PlainValue; - [Fact] - public void Execute() - { - const int length = 10; - var world = World; - var privateKeys = GetRandomArray(length, _ => new PrivateKey()); - var favs = GetRandomArray(length, i => NCG * Random.Shared.Next(1, length + 1)); + var deserialized = new UpdateValidators(); + deserialized.LoadPlainValue(plainValue); + } - for (int i = 0; i < length; i++) - { - var signer = privateKeys[i]; - var fav = favs[i]; - var promoteValidator = new PromoteValidator(signer.PublicKey, fav); - var actionContext = new ActionContext - { - PreviousState = world.MintAsset(new ActionContext(), signer.Address, NCG * 1000), - Signer = signer.Address, - BlockIndex = 10L, - }; - world = promoteValidator.Execute(actionContext); - } + [Fact] + public void Execute() + { + const int length = 10; + var world = World; + var privateKeys = GetRandomArray(length, _ => new PrivateKey()); + var golds = GetRandomArray(length, i => NCG * Random.Shared.Next(1, length + 1)); - var blockActionContext = new ActionContext + for (int i = 0; i < length; i++) + { + var signer = privateKeys[i]; + var gold = golds[i]; + var promoteValidator = new PromoteValidator(signer.PublicKey, gold); + var actionContext = new ActionContext { + PreviousState = world.MintAsset(new ActionContext(), signer.Address, NCG * 1000), + Signer = signer.Address, BlockIndex = 10L, - PreviousState = world, - Signer = AdminKey.Address, }; - var expectedRepository = new ValidatorRepository(world, blockActionContext); - var expectedValidators = expectedRepository.GetValidatorList() - .GetBonded().OrderBy(item => item.OperatorAddress).ToList(); + world = promoteValidator.Execute(actionContext); + } - world = new UpdateValidators().Execute(blockActionContext); + var blockActionContext = new ActionContext + { + BlockIndex = 10L, + PreviousState = world, + Signer = AdminKey.Address, + }; + var expectedRepository = new ValidatorRepository(world, blockActionContext); + var expectedValidators = expectedRepository.GetValidatorList() + .GetBonded().OrderBy(item => item.OperatorAddress).ToList(); - var actualValidators = world.GetValidatorSet().Validators; - Assert.Equal(expectedValidators.Count, actualValidators.Count); - for (var i = 0; i < expectedValidators.Count; i++) - { - var expectedValidator = expectedValidators[i]; - var actualValidator = actualValidators[i]; - Assert.Equal(expectedValidator, actualValidator); - } + world = new UpdateValidators().Execute(blockActionContext); + + var actualValidators = world.GetValidatorSet().Validators; + Assert.Equal(expectedValidators.Count, actualValidators.Count); + for (var i = 0; i < expectedValidators.Count; i++) + { + var expectedValidator = expectedValidators[i]; + var actualValidator = actualValidators[i]; + Assert.Equal(expectedValidator, actualValidator); } } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 5f1df83ae9..182c15e3ac 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -1,364 +1,373 @@ #nullable enable -namespace Lib9c.Tests.Action.ValidatorDelegation +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Numerics; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Mocks; +using Libplanet.Types.Assets; +using Libplanet.Types.Blocks; +using Libplanet.Types.Consensus; +using Libplanet.Types.Evidence; +using Nekoyume; +using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.Model.State; +using Nekoyume.Module; +using Nekoyume.ValidatorDelegation; + +public class ValidatorDelegationTestBase { - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using System.Numerics; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Mocks; - using Libplanet.Types.Assets; - using Libplanet.Types.Blocks; - using Libplanet.Types.Consensus; - using Libplanet.Types.Evidence; - using Nekoyume; - using Nekoyume.Action.ValidatorDelegation; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.ValidatorDelegation; - - public class ValidatorDelegationTestBase - { - protected static readonly Currency NCG = Currency.Uncapped("NCG", 2, null); + protected static readonly Currency NCG = Currency.Uncapped("NCG", 2, null); + protected static readonly Currency Dollar = Currency.Uncapped("dollar", 2, null); - public ValidatorDelegationTestBase() - { - var world = new World(MockUtil.MockModernWorldState); - var goldCurrencyState = new GoldCurrencyState(NCG); - World = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - } + public ValidatorDelegationTestBase() + { + var world = new World(MockUtil.MockModernWorldState); + var goldCurrencyState = new GoldCurrencyState(NCG); + World = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + } - protected static BlockHash EmptyBlockHash { get; } - = new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x01)); + protected static BlockHash EmptyBlockHash { get; } + = new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x01)); - protected PrivateKey AdminKey { get; } = new PrivateKey(); + protected PrivateKey AdminKey { get; } = new PrivateKey(); - protected IWorld World { get; } + protected IWorld World { get; } - protected static T[] GetRandomArray(int length, Func creator) - => Enumerable.Range(0, length).Select(creator).ToArray(); + protected static T[] GetRandomArray(int length, Func creator) + => Enumerable.Range(0, length).Select(creator).ToArray(); - protected static IWorld MintAsset( - IWorld world, - PrivateKey delegatorPrivateKey, - FungibleAssetValue amount, - long blockHeight) + protected static IWorld MintAsset( + IWorld world, PrivateKey privateKey, FungibleAssetValue amount, long blockHeight) + { + var actionContext = new ActionContext { - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight, - }; - return world.MintAsset(actionContext, delegatorPrivateKey.Address, amount); - } + PreviousState = world, + BlockIndex = blockHeight, + }; + return world.MintAsset(actionContext, privateKey.Address, amount); + } + + protected static IWorld EnsurePromotedValidator( + IWorld world, PrivateKey validatorKey, FungibleAssetValue amount, long blockHeight) + => EnsurePromotedValidator(world, validatorKey, amount, blockHeight, mint: false); + + protected static IWorld EnsurePromotedValidator( + IWorld world, + PrivateKey validatorKey, + FungibleAssetValue amount, + long blockHeight, + bool mint) + => EnsurePromotedValidator( + world, validatorKey, amount, blockHeight, mint ? amount : amount * 0); + + protected static IWorld EnsurePromotedValidator( + IWorld world, + PrivateKey validatorKey, + FungibleAssetValue amount, + long blockHeight, + FungibleAssetValue mintAmount) + { + var validatorPublicKey = validatorKey.PublicKey; + var promoteValidator = new PromoteValidator(validatorPublicKey, amount); - protected static IWorld EnsureValidatorToBePromoted( - IWorld world, - PrivateKey validatorPrivateKey, - FungibleAssetValue amount, - long blockHeight) + if (mintAmount.RawValue > 0) { - var validatorPublicKey = validatorPrivateKey.PublicKey; - var promoteValidator = new PromoteValidator(validatorPublicKey, amount); - var actionContext = new ActionContext - { - PreviousState = MintAsset( - world, validatorPrivateKey, amount, blockHeight), - Signer = validatorPublicKey.Address, - BlockIndex = blockHeight, - }; - return promoteValidator.Execute(actionContext); + world = MintAsset(world, validatorKey, mintAmount, blockHeight); } - protected static IWorld ExecuteSlashValidator( - IWorld world, - PublicKey validatorPublicKey, - BlockCommit lastCommit, - long blockHeight) + var actionContext = new ActionContext { - var slashValidator = new SlashValidator(); - var actionContext = new ActionContext - { - PreviousState = world, - Signer = validatorPublicKey.Address, - BlockIndex = blockHeight, - LastCommit = lastCommit, - }; - return slashValidator.Execute(actionContext); - } + PreviousState = world, + Signer = validatorPublicKey.Address, + BlockIndex = blockHeight, + }; + return promoteValidator.Execute(actionContext); + } - protected static IWorld EnsureDelegatorToBeBond( - IWorld world, - PrivateKey delegatorPrivateKey, - PrivateKey validatorPrivateKey, - FungibleAssetValue amount, - long blockHeight) + protected static IWorld ExecuteSlashValidator( + IWorld world, PublicKey validatorKey, BlockCommit lastCommit, long blockHeight) + { + var slashValidator = new SlashValidator(); + var actionContext = new ActionContext { - if (blockHeight < 0) - { - throw new ArgumentOutOfRangeException(nameof(blockHeight)); - } + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = blockHeight, + LastCommit = lastCommit, + }; + return slashValidator.Execute(actionContext); + } - var delegatorAddress = delegatorPrivateKey.Address; - var validatorAddress = validatorPrivateKey.Address; - var actionContext = new ActionContext - { - PreviousState = MintAsset( - world, delegatorPrivateKey, amount, blockHeight), - BlockIndex = blockHeight, - Signer = delegatorAddress, - }; - var delegatorValidator = new DelegateValidator( - validatorAddress, amount); - return delegatorValidator.Execute(actionContext); + protected static IWorld EnsureBondedDelegator( + IWorld world, + PrivateKey delegatorKey, + PrivateKey validatorKey, + FungibleAssetValue amount, + long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); } - protected static IWorld EnsureDelegatorsToBeBond( - IWorld world, - PrivateKey[] delegatorPrivateKeys, - PrivateKey validatorPrivateKey, - FungibleAssetValue[] amounts, - long blockHeight) + var delegatorAddress = delegatorKey.Address; + var validatorAddress = validatorKey.Address; + var actionContext = new ActionContext { - if (delegatorPrivateKeys.Length != amounts.Length) - { - throw new ArgumentException( - "The length of delegatorPrivateKeys and amounts must be the same."); - } - - for (var i = 0; i < delegatorPrivateKeys.Length; i++) - { - world = EnsureDelegatorToBeBond( - world, delegatorPrivateKeys[i], validatorPrivateKey, amounts[i], blockHeight); - } + PreviousState = MintAsset(world, delegatorKey, amount, blockHeight), + BlockIndex = blockHeight, + Signer = delegatorAddress, + }; + var delegatorValidator = new DelegateValidator( + validatorAddress, amount); + return delegatorValidator.Execute(actionContext); + } - return world; + protected static IWorld EnsureBondedDelegators( + IWorld world, + PrivateKey[] delegatorKeys, + PrivateKey validatorKey, + FungibleAssetValue[] amounts, + long blockHeight) + { + if (delegatorKeys.Length != amounts.Length) + { + throw new ArgumentException( + "The length of delegatorPrivateKeys and amounts must be the same."); } - protected static IWorld EnsureValidatorToBeJailed( - IWorld world, PrivateKey validatorPrivateKey, ref long blockHeight) + for (var i = 0; i < delegatorKeys.Length; i++) { - if (blockHeight < 0) - { - throw new ArgumentOutOfRangeException(nameof(blockHeight)); - } - - var repository = new ValidatorRepository(world, new ActionContext()); - var delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); - if (delegatee.Jailed) - { - throw new ArgumentException( - "The validator is already jailed.", nameof(validatorPrivateKey)); - } + world = EnsureBondedDelegator( + world, delegatorKeys[i], validatorKey, amounts[i], blockHeight); + } - for (var i = 0L; i <= AbstainHistory.MaxAbstainAllowance; i++) - { - var vote = CreateNullVote(validatorPrivateKey, blockHeight - 1); - var lastCommit = new BlockCommit( - height: blockHeight - 1, - round: 0, - blockHash: vote.BlockHash, - ImmutableArray.Create(vote)); - world = ExecuteSlashValidator( - world, validatorPrivateKey.PublicKey, lastCommit, blockHeight); - blockHeight++; - repository = new ValidatorRepository(world, new ActionContext()); - delegatee = repository.GetValidatorDelegatee(validatorPrivateKey.Address); - if (delegatee.Jailed) - { - break; - } - } + return world; + } - return world; + protected static IWorld EnsureJailedValidator( + IWorld world, PrivateKey validatorKey, ref long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); } - protected static IWorld EnsureValidatorToBeTombstoned( - IWorld world, PrivateKey validatorPrivateKey, long blockHeight) + var repository = new ValidatorRepository(world, new ActionContext()); + var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); + if (delegatee.Jailed) { - if (blockHeight < 0) - { - throw new ArgumentOutOfRangeException(nameof(blockHeight)); - } + throw new ArgumentException( + "The validator is already jailed.", nameof(validatorKey)); + } - var evidence = CreateDuplicateVoteEvidence(validatorPrivateKey, blockHeight - 1); + for (var i = 0L; i <= AbstainHistory.MaxAbstainAllowance; i++) + { + var vote = CreateNullVote(validatorKey, blockHeight - 1); var lastCommit = new BlockCommit( height: blockHeight - 1, round: 0, - blockHash: EmptyBlockHash, - ImmutableArray.Create(evidence.VoteRef)); - - var actionContext = new ActionContext + blockHash: vote.BlockHash, + ImmutableArray.Create(vote)); + world = ExecuteSlashValidator( + world, validatorKey.PublicKey, lastCommit, blockHeight); + blockHeight++; + repository = new ValidatorRepository(world, new ActionContext()); + delegatee = repository.GetValidatorDelegatee(validatorKey.Address); + if (delegatee.Jailed) { - PreviousState = world, - BlockIndex = blockHeight, - Evidence = new List { evidence }, - LastCommit = lastCommit, - }; - var slashValidator = new SlashValidator(); - - return slashValidator.Execute(actionContext); + break; + } } - protected static IWorld EnsureValidatorToBeAllocatedReward( - IWorld world, - PrivateKey validatorPrivateKey, - FungibleAssetValue reward, - ref long blockHeight) - { - if (blockHeight < 0) - { - throw new ArgumentOutOfRangeException(nameof(blockHeight)); - } + return world; + } - var actionContext1 = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight++, - Signer = validatorPrivateKey.Address, - Miner = validatorPrivateKey.Address, - }; - world = new RecordProposer().Execute(actionContext1); - - var lastCommit2 = CreateLastCommit(validatorPrivateKey, blockHeight - 1); - var actionContext2 = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight++, - Signer = validatorPrivateKey.Address, - LastCommit = lastCommit2, - }; - world = world.MintAsset(actionContext2, GoldCurrencyState.Address, reward); - world = world.TransferAsset( - actionContext2, GoldCurrencyState.Address, Addresses.RewardPool, reward); - - var lastCommit3 = CreateLastCommit(validatorPrivateKey, blockHeight - 1); - var actionContext3 = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight++, - Signer = validatorPrivateKey.Address, - LastCommit = lastCommit3, - }; - world = new AllocateReward().Execute(actionContext3); - - return world; + protected static IWorld EnsureTombstonedValidator( + IWorld world, PrivateKey validatorKey, long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); } - protected static Vote CreateNullVote( - PrivateKey privateKey, long blockHeight) + var evidence = CreateDuplicateVoteEvidence(validatorKey, blockHeight - 1); + var lastCommit = new BlockCommit( + height: blockHeight - 1, + round: 0, + blockHash: EmptyBlockHash, + ImmutableArray.Create(evidence.VoteRef)); + + var actionContext = new ActionContext { - if (blockHeight < 0) - { - throw new ArgumentOutOfRangeException(nameof(blockHeight)); - } + PreviousState = world, + BlockIndex = blockHeight, + Evidence = new List { evidence }, + LastCommit = lastCommit, + }; + var slashValidator = new SlashValidator(); + + return slashValidator.Execute(actionContext); + } - var power = new BigInteger(100); - var validator = new Validator(privateKey.PublicKey, power); - var blockHash = EmptyBlockHash; - var timestamp = DateTimeOffset.UtcNow; - var voteMetadata = new VoteMetadata( - height: blockHeight, - round: 0, - blockHash: blockHash, - timestamp: timestamp, - validatorPublicKey: validator.PublicKey, - validatorPower: power, - flag: VoteFlag.Null); - return voteMetadata.Sign(null); + protected static IWorld EnsureRewardAllocatedValidator( + IWorld world, PrivateKey validatorKey, FungibleAssetValue reward, ref long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); } - protected static Vote CreateVote( - PrivateKey privateKey, long blockHeight) + var actionContext1 = new ActionContext { - if (blockHeight < 0) - { - throw new ArgumentOutOfRangeException(nameof(blockHeight)); - } + PreviousState = world, + BlockIndex = blockHeight++, + Signer = validatorKey.Address, + Miner = validatorKey.Address, + }; + world = new RecordProposer().Execute(actionContext1); + + var lastCommit2 = CreateLastCommit(validatorKey, blockHeight - 1); + var actionContext2 = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = validatorKey.Address, + LastCommit = lastCommit2, + }; + world = world.MintAsset(actionContext2, GoldCurrencyState.Address, reward); + world = world.TransferAsset( + actionContext2, GoldCurrencyState.Address, Addresses.RewardPool, reward); + + var lastCommit3 = CreateLastCommit(validatorKey, blockHeight - 1); + var actionContext3 = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight++, + Signer = validatorKey.Address, + LastCommit = lastCommit3, + }; + world = new AllocateReward().Execute(actionContext3); + + return world; + } - var power = new BigInteger(100); - var validator = new Validator(privateKey.PublicKey, power); - var blockHash = EmptyBlockHash; - var timestamp = DateTimeOffset.UtcNow; - var voteMetadata = new VoteMetadata( - height: blockHeight, - round: 0, - blockHash: blockHash, - timestamp: timestamp, - validatorPublicKey: validator.PublicKey, - validatorPower: power, - flag: VoteFlag.PreCommit); - return voteMetadata.Sign(privateKey); + protected static Vote CreateNullVote( + PrivateKey privateKey, long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); } - protected static BlockCommit CreateLastCommit( - PrivateKey privateKey, long blockHeight) + var power = new BigInteger(100); + var validator = new Validator(privateKey.PublicKey, power); + var blockHash = EmptyBlockHash; + var timestamp = DateTimeOffset.UtcNow; + var voteMetadata = new VoteMetadata( + height: blockHeight, + round: 0, + blockHash: blockHash, + timestamp: timestamp, + validatorPublicKey: validator.PublicKey, + validatorPower: power, + flag: VoteFlag.Null); + return voteMetadata.Sign(null); + } + + protected static Vote CreateVote( + PrivateKey privateKey, long blockHeight) + { + if (blockHeight < 0) { - if (blockHeight < 0) - { - throw new ArgumentOutOfRangeException(nameof(blockHeight)); - } + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } - var vote = CreateVote(privateKey, blockHeight); - return new BlockCommit( - height: blockHeight, - round: 0, - blockHash: vote.BlockHash, - ImmutableArray.Create(vote)); + var power = new BigInteger(100); + var validator = new Validator(privateKey.PublicKey, power); + var blockHash = EmptyBlockHash; + var timestamp = DateTimeOffset.UtcNow; + var voteMetadata = new VoteMetadata( + height: blockHeight, + round: 0, + blockHash: blockHash, + timestamp: timestamp, + validatorPublicKey: validator.PublicKey, + validatorPower: power, + flag: VoteFlag.PreCommit); + return voteMetadata.Sign(privateKey); + } + + protected static BlockCommit CreateLastCommit( + PrivateKey privateKey, long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); } - protected static DuplicateVoteEvidence CreateDuplicateVoteEvidence( - PrivateKey validatorPrivateKey, long blockHeight) + var vote = CreateVote(privateKey, blockHeight); + return new BlockCommit( + height: blockHeight, + round: 0, + blockHash: vote.BlockHash, + ImmutableArray.Create(vote)); + } + + protected static DuplicateVoteEvidence CreateDuplicateVoteEvidence( + PrivateKey validatorKey, long blockHeight) + { + if (blockHeight < 0) { - if (blockHeight < 0) - { - throw new ArgumentOutOfRangeException(nameof(blockHeight)); - } + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } - var validatorSet = new ValidatorSet(new List + var validatorSet = new ValidatorSet(new List { - new (validatorPrivateKey.PublicKey, new BigInteger(1000)), + new (validatorKey.PublicKey, new BigInteger(1000)), }); - var vote1 = new VoteMetadata( - height: blockHeight, - round: 0, - blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x01)), - timestamp: DateTimeOffset.UtcNow, - validatorPublicKey: validatorPrivateKey.PublicKey, - validatorPower: BigInteger.One, - flag: VoteFlag.PreCommit).Sign(validatorPrivateKey); - var vote2 = new VoteMetadata( - height: blockHeight, - round: 0, - blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x02)), - timestamp: DateTimeOffset.UtcNow, - validatorPublicKey: validatorPrivateKey.PublicKey, - validatorPower: BigInteger.One, - flag: VoteFlag.PreCommit).Sign(validatorPrivateKey); - var evidence = new DuplicateVoteEvidence( - vote1, - vote2, - validatorSet, - vote1.Timestamp); - - return evidence; - } + var vote1 = new VoteMetadata( + height: blockHeight, + round: 0, + blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x01)), + timestamp: DateTimeOffset.UtcNow, + validatorPublicKey: validatorKey.PublicKey, + validatorPower: BigInteger.One, + flag: VoteFlag.PreCommit).Sign(validatorKey); + var vote2 = new VoteMetadata( + height: blockHeight, + round: 0, + blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x02)), + timestamp: DateTimeOffset.UtcNow, + validatorPublicKey: validatorKey.PublicKey, + validatorPower: BigInteger.One, + flag: VoteFlag.PreCommit).Sign(validatorKey); + var evidence = new DuplicateVoteEvidence( + vote1, + vote2, + validatorSet, + vote1.Timestamp); + + return evidence; + } - protected static FungibleAssetValue GetCommission( - FungibleAssetValue fav, BigInteger percentage) - => (fav * percentage).DivRem(100).Quotient; + protected static FungibleAssetValue GetCommission( + FungibleAssetValue gold, BigInteger percentage) + => (gold * percentage).DivRem(100).Quotient; - protected static FungibleAssetValue GetWithoutCommission( - FungibleAssetValue fav, BigInteger percentage) - => fav - (fav * percentage).DivRem(100).Quotient; + protected static FungibleAssetValue GetWithoutCommission( + FungibleAssetValue gold, BigInteger percentage) + => gold - (gold * percentage).DivRem(100).Quotient; - protected static FungibleAssetValue GetRandomNCG() - { - var value = Math.Round(Random.Shared.Next(1, 100000) / 100.0, 2); - return FungibleAssetValue.Parse(NCG, $"{value:R}"); - } + protected static FungibleAssetValue GetRandomNCG() + { + var value = Math.Round(Random.Shared.Next(1, 100000) / 100.0, 2); + return FungibleAssetValue.Parse(NCG, $"{value:R}"); } } From a028091fba3cc014d9a975fe5c3759051bda21e6 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 4 Oct 2024 16:34:57 +0900 Subject: [PATCH 054/206] fix: Fix bugs on delegation --- .Lib9c.Tests/Delegation/DummyDelegatee.cs | 1 + .Lib9c.Tests/Delegation/TestDelegatee.cs | 1 + .../ReleaseValidatorUnbondings.cs | 23 ++++++- .../ValidatorDelegation/SlashValidator.cs | 2 +- Lib9c/Delegation/Delegatee.cs | 69 ++++++++++++++----- Lib9c/Delegation/DelegateeMetadata.cs | 64 ++++++----------- Lib9c/Delegation/IDelegatee.cs | 2 +- Lib9c/Delegation/UnbondingEntry.cs | 7 +- Lib9c/Model/Guild/Guild.cs | 1 + .../ValidatorDelegation/ValidatorDelegatee.cs | 20 ++++-- 10 files changed, 119 insertions(+), 71 deletions(-) diff --git a/.Lib9c.Tests/Delegation/DummyDelegatee.cs b/.Lib9c.Tests/Delegation/DummyDelegatee.cs index 1092b4bd86..ef5a942cc7 100644 --- a/.Lib9c.Tests/Delegation/DummyDelegatee.cs +++ b/.Lib9c.Tests/Delegation/DummyDelegatee.cs @@ -17,6 +17,7 @@ public DummyDelegatee(Address address, Address accountAddress, DummyRepository r DelegationFixture.TestCurrency, address, DelegationFixture.FixedPoolAddress, + DelegationFixture.FixedPoolAddress, 3, 5, 5, diff --git a/.Lib9c.Tests/Delegation/TestDelegatee.cs b/.Lib9c.Tests/Delegation/TestDelegatee.cs index ef1b18695e..d754f787db 100644 --- a/.Lib9c.Tests/Delegation/TestDelegatee.cs +++ b/.Lib9c.Tests/Delegation/TestDelegatee.cs @@ -18,6 +18,7 @@ public TestDelegatee(Address address, Address accountAddress, TestRepository rep DelegationFixture.TestCurrency, address, DelegationFixture.FixedPoolAddress, + DelegationFixture.FixedPoolAddress, 3, 5, 5, diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs index 5d1250c3e6..1ac8e7f721 100644 --- a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -3,6 +3,10 @@ using Libplanet.Action; using Libplanet.Crypto; using Nekoyume.ValidatorDelegation; +using Nekoyume.Delegation; +using System; +using System.Linq; +using System.Collections.Immutable; namespace Nekoyume.Action.ValidatorDelegation { @@ -26,13 +30,28 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; var repository = new ValidatorRepository(world, context); - var unbondings = repository.GetUnbondingSet().UnbondingsToRelease(context.BlockIndex); + var unbondingSet = repository.GetUnbondingSet(); + var unbondings = unbondingSet.UnbondingsToRelease(context.BlockIndex); + + unbondings = unbondings.Select(unbonding => unbonding.Release(context.BlockIndex)).ToImmutableArray(); foreach (var unbonding in unbondings) { - unbonding.Release(context.BlockIndex); + switch (unbonding) + { + case UnbondLockIn unbondLockIn: + repository.SetUnbondLockIn(unbondLockIn); + break; + case RebondGrace rebondGrace: + repository.SetRebondGrace(rebondGrace); + break; + default: + throw new InvalidOperationException("Invalid unbonding type."); + } } + repository.SetUnbondingSet(unbondingSet.SetUnbondings(unbondings)); + return repository.World; } } diff --git a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs index 4807765364..74ab18bd9c 100644 --- a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs @@ -47,7 +47,7 @@ public override IWorld Execute(IActionContext context) { var validatorDelegatee = repository.GetValidatorDelegatee(abstain.Address); validatorDelegatee.Slash(LivenessSlashFactor, context.BlockIndex, context.BlockIndex); - validatorDelegatee.Jail(context.BlockIndex + AbstainJailTime, context.BlockIndex); + validatorDelegatee.Jail(context.BlockIndex + AbstainJailTime); } foreach (var evidence in context.Evidence) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index efd8d056a3..4cdc6a0e44 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -20,6 +20,7 @@ public Delegatee( Currency rewardCurrency, Address delegationPoolAddress, Address rewardRemainderPoolAddress, + Address slashedPoolAddress, long unbondingPeriod, int maxUnbondLockInEntries, int maxRebondGraceEntries, @@ -32,6 +33,7 @@ public Delegatee( rewardCurrency, delegationPoolAddress, rewardRemainderPoolAddress, + slashedPoolAddress, unbondingPeriod, maxUnbondLockInEntries, maxRebondGraceEntries), @@ -54,9 +56,9 @@ private Delegatee(DelegateeMetadata metadata, IDelegationRepository repository) public event EventHandler? DelegationChanged; - public event EventHandler? Enjailed; + public event EventHandler? Enjailed; - public event EventHandler? Unjailed; + public event EventHandler? Unjailed; public DelegateeMetadata Metadata { get; } @@ -76,6 +78,8 @@ private Delegatee(DelegateeMetadata metadata, IDelegationRepository repository) public Address RewardRemainderPoolAddress => Metadata.RewardRemainderPoolAddress; + public Address SlashedPoolAddress => Metadata.SlashedPoolAddress; + public long UnbondingPeriod => Metadata.UnbondingPeriod; public int MaxUnbondLockInEntries => Metadata.MaxUnbondLockInEntries; @@ -113,23 +117,41 @@ public FungibleAssetValue Unbond(IDelegator delegator, BigInteger share, long he public void DistributeReward(IDelegator delegator, long height) => DistributeReward((T)delegator, height); - public void Jail(long releaseHeight, long height) + public void Jail(long releaseHeight) { - Metadata.JailUntil(releaseHeight); + Metadata.JailedUntil = releaseHeight; + Metadata.Jailed = true; Repository.SetDelegateeMetadata(Metadata); - Enjailed?.Invoke(this, height); + Enjailed?.Invoke(this, EventArgs.Empty); } public void Unjail(long height) { - Metadata.Unjail(height); + if (!Jailed) + { + throw new InvalidOperationException("Cannot unjail non-jailed delegatee."); + } + + if (Tombstoned) + { + throw new InvalidOperationException("Cannot unjail tombstoned delegatee."); + } + + if (JailedUntil >= height) + { + throw new InvalidOperationException("Cannot unjail before jailed until."); + } + + Metadata.JailedUntil = -1L; + Metadata.Jailed = false; Repository.SetDelegateeMetadata(Metadata); - Unjailed?.Invoke(this, height); + Unjailed?.Invoke(this, EventArgs.Empty); } public void Tombstone() { - Metadata.Tombstone(); + Jail(long.MaxValue); + Metadata.Tombstoned = true; Repository.SetDelegateeMetadata(Metadata); } @@ -277,13 +299,30 @@ public void CollectRewards(long height) Delegators, RewardCurrency); record = record.AddLumpSumRewards(rewards); - Repository.TransferAsset(RewardPoolAddress, record.Address, rewards); + + if (rewards.Sign > 0) + { + Repository.TransferAsset(RewardPoolAddress, record.Address, rewards); + } + Repository.SetLumpSumRewardsRecord(record); } public void Slash(BigInteger slashFactor, long infractionHeight, long height) { - FungibleAssetValue? fav = null; + FungibleAssetValue slashed = TotalDelegated.DivRem(slashFactor, out var rem); + if (rem.Sign > 0) + { + slashed += FungibleAssetValue.FromRawValue(rem.Currency, 1); + } + + if (slashed > Metadata.TotalDelegatedFAV) + { + slashed = Metadata.TotalDelegatedFAV; + } + + Metadata.RemoveDelegatedFAV(slashed); + foreach (var item in Metadata.UnbondingRefs) { var unbonding = UnbondingFactory.GetUnbondingFromRef(item, Repository); @@ -292,9 +331,7 @@ public void Slash(BigInteger slashFactor, long infractionHeight, long height) if (slashedFAV.HasValue) { - fav = fav.HasValue - ? fav.Value + slashedFAV.Value - : slashedFAV.Value; + slashed += slashedFAV.Value; } if (unbonding.IsEmpty) @@ -315,11 +352,7 @@ public void Slash(BigInteger slashFactor, long infractionHeight, long height) } } - if (fav.HasValue) - { - Metadata.RemoveDelegatedFAV(fav.Value); - } - + Repository.TransferAsset(DelegationPoolAddress, SlashedPoolAddress, slashed); Repository.SetDelegateeMetadata(Metadata); DelegationChanged?.Invoke(this, height); } diff --git a/Lib9c/Delegation/DelegateeMetadata.cs b/Lib9c/Delegation/DelegateeMetadata.cs index 79702c3744..b0fcc9703d 100644 --- a/Lib9c/Delegation/DelegateeMetadata.cs +++ b/Lib9c/Delegation/DelegateeMetadata.cs @@ -22,6 +22,7 @@ public DelegateeMetadata( Currency rewardCurrency, Address delegationPoolAddress, Address rewardRemainderPoolAddress, + Address slashedPoolAddress, long unbondingPeriod, int maxUnbondLockInEntries, int maxRebondGraceEntries) @@ -32,6 +33,7 @@ public DelegateeMetadata( rewardCurrency, delegationPoolAddress, rewardRemainderPoolAddress, + slashedPoolAddress, unbondingPeriod, maxUnbondLockInEntries, maxRebondGraceEntries, @@ -64,16 +66,17 @@ public DelegateeMetadata( new Currency(bencoded[1]), new Address(bencoded[2]), new Address(bencoded[3]), - (Integer)bencoded[4], + new Address(bencoded[4]), (Integer)bencoded[5], (Integer)bencoded[6], - ((List)bencoded[7]).Select(item => new Address(item)), - new FungibleAssetValue(bencoded[8]), - (Integer)bencoded[9], - (Bencodex.Types.Boolean)bencoded[10], - (Integer)bencoded[11], - (Bencodex.Types.Boolean)bencoded[12], - ((List)bencoded[13]).Select(item => new UnbondingRef(item))) + (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))) { } @@ -84,6 +87,7 @@ private DelegateeMetadata( Currency rewardCurrency, Address delegationPoolAddress, Address rewardRemainderPoolAddress, + Address slashedPoolAddress, long unbondingPeriod, int maxUnbondLockInEntries, int maxRebondGraceEntries, @@ -122,6 +126,7 @@ private DelegateeMetadata( RewardCurrency = rewardCurrency; DelegationPoolAddress = delegationPoolAddress; RewardRemainderPoolAddress = rewardRemainderPoolAddress; + SlashedPoolAddress = slashedPoolAddress; UnbondingPeriod = unbondingPeriod; MaxUnbondLockInEntries = maxUnbondLockInEntries; MaxRebondGraceEntries = maxRebondGraceEntries; @@ -151,6 +156,8 @@ public Address Address public Address RewardRemainderPoolAddress { get; } + public Address SlashedPoolAddress { get; } + public long UnbondingPeriod { get; } public int MaxUnbondLockInEntries { get; } @@ -166,11 +173,11 @@ public Address RewardPoolAddress public BigInteger TotalShares { get; private set; } - public bool Jailed { get; private set; } + public bool Jailed { get; internal set; } - public long JailedUntil { get; private set; } + public long JailedUntil { get; internal set; } - public bool Tombstoned { get; private set; } + public bool Tombstoned { get; internal set; } public ImmutableSortedSet UnbondingRefs { get; private set; } @@ -179,6 +186,7 @@ public Address RewardPoolAddress .Add(RewardCurrency.Serialize()) .Add(DelegationPoolAddress.Bencoded) .Add(RewardRemainderPoolAddress.Bencoded) + .Add(SlashedPoolAddress.Bencoded) .Add(UnbondingPeriod) .Add(MaxUnbondLockInEntries) .Add(MaxRebondGraceEntries) @@ -232,39 +240,6 @@ public void RemoveShare(BigInteger share) TotalShares -= share; } - public void JailUntil(long height) - { - JailedUntil = height; - Jailed = true; - } - - public void Unjail(long height) - { - if (!Jailed) - { - throw new InvalidOperationException("Cannot unjail non-jailed delegatee."); - } - - if (Tombstoned) - { - throw new InvalidOperationException("Cannot unjail tombstoned delegatee."); - } - - if (JailedUntil >= height) - { - throw new InvalidOperationException("Cannot unjail before jailed until."); - } - - JailedUntil = -1L; - Jailed = false; - } - - public void Tombstone() - { - JailUntil(long.MaxValue); - Tombstoned = true; - } - public void AddUnbondingRef(UnbondingRef unbondingRef) { UnbondingRefs = UnbondingRefs.Add(unbondingRef); @@ -303,6 +278,7 @@ public virtual bool Equals(IDelegateeMetadata? other) && RewardCurrency.Equals(delegatee.RewardCurrency) && DelegationPoolAddress.Equals(delegatee.DelegationPoolAddress) && RewardRemainderPoolAddress.Equals(delegatee.RewardRemainderPoolAddress) + && SlashedPoolAddress.Equals(delegatee.SlashedPoolAddress) && UnbondingPeriod == delegatee.UnbondingPeriod && RewardPoolAddress.Equals(delegatee.RewardPoolAddress) && Delegators.SequenceEqual(delegatee.Delegators) diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index 958790b314..ce820e8c12 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -56,7 +56,7 @@ public interface IDelegatee void Slash(BigInteger slashFactor, long infractionHeight, long height); - void Jail(long releaseHeight, long height); + void Jail(long releaseHeight); void Unjail(long height); diff --git a/Lib9c/Delegation/UnbondingEntry.cs b/Lib9c/Delegation/UnbondingEntry.cs index e1f786146e..66484a382b 100644 --- a/Lib9c/Delegation/UnbondingEntry.cs +++ b/Lib9c/Delegation/UnbondingEntry.cs @@ -152,7 +152,12 @@ public UnbondingEntry Slash( "The infraction height must be between in creation height and expire height of entry."); } - var favToSlash = InitialUnbondingFAV.DivRem(slashFactor).Quotient; + var favToSlash = InitialUnbondingFAV.DivRem(slashFactor, out var rem); + if (rem.Sign > 0) + { + favToSlash += FungibleAssetValue.FromRawValue(rem.Currency, 1); + } + slashedFAV = favToSlash < UnbondingFAV ? favToSlash : UnbondingFAV; diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index bfbc29f432..04c071d3af 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -29,6 +29,7 @@ public Guild( rewardCurrency: rewardCurrency, delegationPoolAddress: address, rewardRemainderPoolAddress: Addresses.CommunityPool, + slashedPoolAddress: Addresses.CommunityPool, unbondingPeriod: 75600L, maxUnbondLockInEntries: 10, maxRebondGraceEntries: 10, diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index 420dcb39ec..9db45b71cb 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -28,6 +28,7 @@ public ValidatorDelegatee( rewardCurrency: rewardCurrency, delegationPoolAddress: UnbondedPoolAddress, rewardRemainderPoolAddress: Addresses.CommunityPool, + slashedPoolAddress: Addresses.CommunityPool, unbondingPeriod: ValidatorUnbondingPeriod, maxUnbondLockInEntries: ValidatorMaxUnbondLockInEntries, maxRebondGraceEntries: ValidatorMaxRebondGraceEntries, @@ -154,6 +155,17 @@ public void SetCommissionPercentage(BigInteger percentage) CommissionPercentage = percentage; } + public new void Unjail(long height) + { + ValidatorRepository repository = (ValidatorRepository)Repository; + var selfDelegation = FAVFromShare(repository.GetBond(this, Address).Share); + if (MinSelfDelegation > selfDelegation) + { + throw new InvalidOperationException("The self-delegation is still below the minimum."); + } + + base.Unjail(height); + } public void OnDelegationChanged(object? sender, long height) { @@ -174,19 +186,19 @@ public void OnDelegationChanged(object? sender, long height) } var selfDelegation = FAVFromShare(repository.GetBond(this, Address).Share); - if (MinSelfDelegation > selfDelegation) + if (MinSelfDelegation > selfDelegation && !Jailed) { - Jail(height, height); + Jail(height); } } - public void OnEnjailed(object? sender, long height) + public void OnEnjailed(object? sender, EventArgs e) { ValidatorRepository repository = (ValidatorRepository)Repository; repository.SetValidatorList(repository.GetValidatorList().RemoveValidator(Validator.PublicKey)); } - public void OnUnjailed(object? sender, long height) + public void OnUnjailed(object? sender, EventArgs e) { ValidatorRepository repository = (ValidatorRepository)Repository; repository.SetValidatorList(repository.GetValidatorList().SetValidator(Validator)); From a76947242f57cc5764e1af10ae8756f9e0f570e0 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 7 Oct 2024 14:51:31 +0900 Subject: [PATCH 055/206] feat: Add commission rate change cooldown --- Lib9c/Action/InitializeStates.cs | 7 +++- .../ValidatorDelegation/PromoteValidator.cs | 15 ++++++- .../SetValidatorCommission.cs | 2 +- .../ValidatorDelegateeModule.cs | 5 ++- .../ValidatorDelegation/ValidatorDelegatee.cs | 40 ++++++++++++++++--- .../ValidatorRepository.cs | 4 +- 6 files changed, 59 insertions(+), 14 deletions(-) diff --git a/Lib9c/Action/InitializeStates.cs b/Lib9c/Action/InitializeStates.cs index eec637f7d6..394ad46114 100644 --- a/Lib9c/Action/InitializeStates.cs +++ b/Lib9c/Action/InitializeStates.cs @@ -203,7 +203,12 @@ public override IWorld Execute(IActionContext context) foreach (var validator in validatorSet.Validators) { var validatorDelegatee = new ValidatorDelegatee( - validator.OperatorAddress, validator.PublicKey, repository.World.GetGoldCurrency(), repository); + validator.OperatorAddress, + validator.PublicKey, + repository.World.GetGoldCurrency(), + ValidatorDelegatee.DefaultCommissionPercentage, + context.BlockIndex, + repository); var delegationFAV = FungibleAssetValue.FromRawValue( validatorDelegatee.DelegationCurrency, validator.Power); var validatorDelegator = repository.GetValidatorDelegator(validator.OperatorAddress); diff --git a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs index 6a4c08ef66..9ab13b7f46 100644 --- a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Action; @@ -17,20 +18,29 @@ public sealed class PromoteValidator : ActionBase public PromoteValidator() { } public PromoteValidator(PublicKey publicKey, FungibleAssetValue fav) + : this(publicKey, fav, ValidatorDelegatee.DefaultCommissionPercentage) + { + } + + public PromoteValidator(PublicKey publicKey, FungibleAssetValue fav, BigInteger commissionPercentage) { PublicKey = publicKey; FAV = fav; + CommissionPercentage = commissionPercentage; } public PublicKey PublicKey { get; private set; } public FungibleAssetValue FAV { get; private set; } + public BigInteger CommissionPercentage { get; private set; } + public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) .Add("values", List.Empty .Add(PublicKey.Format(true)) - .Add(FAV.Serialize())); + .Add(FAV.Serialize()) + .Add(CommissionPercentage)); public override void LoadPlainValue(IValue plainValue) { @@ -44,6 +54,7 @@ public override void LoadPlainValue(IValue plainValue) PublicKey = new PublicKey(((Binary)values[0]).ByteArray); FAV = new FungibleAssetValue(values[1]); + CommissionPercentage = (Integer)values[2]; } public override IWorld Execute(IActionContext context) @@ -53,7 +64,7 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; var repository = new ValidatorRepository(world, context); - repository.CreateValidatorDelegatee(context, PublicKey); + repository.CreateValidatorDelegatee(context, PublicKey, CommissionPercentage); repository.DelegateValidator(context, context.Signer, FAV); return repository.World; diff --git a/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs b/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs index de623d60df..ebf99fbb4b 100644 --- a/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs +++ b/Lib9c/Action/ValidatorDelegation/SetValidatorCommission.cs @@ -51,7 +51,7 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; var repository = new ValidatorRepository(world, context); - repository.SetCommissionPercentage(ValidatorDelegatee, CommissionPercentage); + repository.SetCommissionPercentage(ValidatorDelegatee, CommissionPercentage, context.BlockIndex); return repository.World; } diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs index afcd8aa1e9..474780690b 100644 --- a/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs @@ -1,6 +1,7 @@ #nullable enable using System; using System.Diagnostics.CodeAnalysis; +using System.Numerics; using Libplanet.Action; using Libplanet.Crypto; using Nekoyume.ValidatorDelegation; @@ -27,7 +28,7 @@ public static bool TryGetValidatorDelegatee( } public static ValidatorRepository CreateValidatorDelegatee( - this ValidatorRepository repository, IActionContext context, PublicKey publicKey) + this ValidatorRepository repository, IActionContext context, PublicKey publicKey, BigInteger commissionPercentage) { var signer = context.Signer; @@ -42,7 +43,7 @@ public static ValidatorRepository CreateValidatorDelegatee( } var validatorDelegatee = new ValidatorDelegatee( - signer, publicKey, repository.World.GetGoldCurrency(), repository); + signer, publicKey, repository.World.GetGoldCurrency(), commissionPercentage, context.BlockIndex, repository); repository.SetValidatorDelegatee(validatorDelegatee); diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index 9db45b71cb..fe0f9a2646 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -20,6 +20,8 @@ public ValidatorDelegatee( Address address, PublicKey publicKey, Currency rewardCurrency, + BigInteger commissionPercentage, + long creationHeight, ValidatorRepository repository) : base( address: address, @@ -39,9 +41,17 @@ public ValidatorDelegatee( throw new ArgumentException("The address and the public key do not match."); } + if (commissionPercentage < MinCommissionPercentage || commissionPercentage > MaxCommissionPercentage) + { + throw new ArgumentOutOfRangeException( + nameof(commissionPercentage), + $"The commission percentage must be between {MinCommissionPercentage} and {MaxCommissionPercentage}."); + } + PublicKey = publicKey; IsBonded = false; - CommissionPercentage = DefaultCommissionPercentage; + CommissionPercentage = commissionPercentage; + CommissionPercentageLastUpdateHeight = creationHeight; DelegationChanged += OnDelegationChanged; Enjailed += OnEnjailed; Unjailed += OnUnjailed; @@ -69,7 +79,10 @@ public ValidatorDelegatee( PublicKey = new PublicKey(((Binary)bencoded[0]).ByteArray); IsBonded = (Bencodex.Types.Boolean)bencoded[1]; CommissionPercentage = (Integer)bencoded[2]; + CommissionPercentageLastUpdateHeight = (Integer)bencoded[3]; DelegationChanged += OnDelegationChanged; + Enjailed += OnEnjailed; + Unjailed += OnUnjailed; } public static Currency ValidatorDelegationCurrency => Currencies.GuildGold; @@ -86,16 +99,23 @@ public ValidatorDelegatee( public static BigInteger DefaultCommissionPercentage => 10; + public static BigInteger MinCommissionPercentage => 0; + public static BigInteger MaxCommissionPercentage => 20; + public static long CommissionPercentageUpdateCooldown => 100; + public static BigInteger CommissionPercentageMaxChange => 1; public BigInteger CommissionPercentage { get; private set; } + public long CommissionPercentageLastUpdateHeight { get; private set; } + public List Bencoded => List.Empty .Add(PublicKey.Format(true)) .Add(IsBonded) - .Add(CommissionPercentage); + .Add(CommissionPercentage) + .Add(CommissionPercentageLastUpdateHeight); IValue IBencodable.Bencoded => Bencoded; @@ -137,13 +157,19 @@ FungibleAssetValue commission CollectRewards(height); } - public void SetCommissionPercentage(BigInteger percentage) + public void SetCommissionPercentage(BigInteger percentage, long height) { - if (percentage < 0 || percentage > MaxCommissionPercentage) + if (height - CommissionPercentageLastUpdateHeight < CommissionPercentageUpdateCooldown) + { + throw new InvalidOperationException( + $"The commission percentage can be updated only once in {CommissionPercentageUpdateCooldown} blocks."); + } + + if (percentage < MinCommissionPercentage || percentage > MaxCommissionPercentage) { throw new ArgumentOutOfRangeException( nameof(percentage), - $"The commission percentage must be between 0 and {MaxCommissionPercentage}."); + $"The commission percentage must be between {MinCommissionPercentage} and {MaxCommissionPercentage}."); } if (BigInteger.Abs(CommissionPercentage - percentage) > CommissionPercentageMaxChange) @@ -154,6 +180,7 @@ public void SetCommissionPercentage(BigInteger percentage) } CommissionPercentage = percentage; + CommissionPercentageLastUpdateHeight = height; } public new void Unjail(long height) { @@ -209,7 +236,8 @@ public bool Equals(ValidatorDelegatee? other) && Metadata.Equals(validatorDelegatee.Metadata) && PublicKey.Equals(validatorDelegatee.PublicKey) && IsBonded == validatorDelegatee.IsBonded - && CommissionPercentage == validatorDelegatee.CommissionPercentage; + && CommissionPercentage == validatorDelegatee.CommissionPercentage + && CommissionPercentageLastUpdateHeight == validatorDelegatee.CommissionPercentageLastUpdateHeight; public bool Equals(IDelegatee? other) => Equals(other as ValidatorDelegatee); diff --git a/Lib9c/ValidatorDelegation/ValidatorRepository.cs b/Lib9c/ValidatorDelegation/ValidatorRepository.cs index 1f69355e5e..e8dc5353ff 100644 --- a/Lib9c/ValidatorDelegation/ValidatorRepository.cs +++ b/Lib9c/ValidatorDelegation/ValidatorRepository.cs @@ -94,10 +94,10 @@ public void SetValidatorList(ValidatorList validatorList) ValidatorList.Address, validatorList.Bencoded); } - public void SetCommissionPercentage(Address address, BigInteger commissionPercentage) + public void SetCommissionPercentage(Address address, BigInteger commissionPercentage, long height) { ValidatorDelegatee validatorDelegatee = GetValidatorDelegatee(address); - validatorDelegatee.SetCommissionPercentage(commissionPercentage); + validatorDelegatee.SetCommissionPercentage(commissionPercentage, height); SetValidatorDelegatee(validatorDelegatee); } From 5581d20ccd2e7831b8b936e38057d8554b1dfa67 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 7 Oct 2024 18:05:10 +0900 Subject: [PATCH 056/206] fix: Fix RemoveUnbonding --- Lib9c/Delegation/UnbondingSet.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Lib9c/Delegation/UnbondingSet.cs b/Lib9c/Delegation/UnbondingSet.cs index b9394caa44..98a4b53fd5 100644 --- a/Lib9c/Delegation/UnbondingSet.cs +++ b/Lib9c/Delegation/UnbondingSet.cs @@ -149,8 +149,18 @@ private UnbondingSet RemoveUnbonding(IUnbonding unbonding) if (_lowestExpireHeights.TryGetValue(unbondigRef, out var expireHeight) && UnbondingRefs.TryGetValue(expireHeight, out var refs)) { + refs = refs.Remove(unbondigRef); + + if (refs.IsEmpty) + { + return new UnbondingSet( + UnbondingRefs.Remove(expireHeight), + _lowestExpireHeights.Remove(unbondigRef), + _repository); + } + return new UnbondingSet( - UnbondingRefs.SetItem(expireHeight, refs.Remove(unbondigRef)), + UnbondingRefs.SetItem(expireHeight, refs), _lowestExpireHeights.Remove(unbondigRef), _repository); } From e134679b6555298fbf0734c07a07e918b23cffb5 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 7 Oct 2024 20:20:05 +0900 Subject: [PATCH 057/206] test: Fix BlockPolicyTest to handle vote power --- .Lib9c.Tests/Policy/BlockPolicyTest.cs | 118 ++++++++++++++++++++----- 1 file changed, 97 insertions(+), 21 deletions(-) diff --git a/.Lib9c.Tests/Policy/BlockPolicyTest.cs b/.Lib9c.Tests/Policy/BlockPolicyTest.cs index 2ac7b9e174..afa9c7377d 100644 --- a/.Lib9c.Tests/Policy/BlockPolicyTest.cs +++ b/.Lib9c.Tests/Policy/BlockPolicyTest.cs @@ -93,7 +93,12 @@ public void ValidateNextBlockTx_Mead() ); Block block = blockChain.ProposeBlock(adminPrivateKey); - blockChain.Append(block, GenerateBlockCommit(block, adminPrivateKey)); + blockChain.Append( + block, + GenerateBlockCommit( + block, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey })); Assert.Equal( 1 * Currencies.Mead, @@ -182,8 +187,26 @@ public void BlockCommitFromNonValidator() renderers: new[] { new BlockRenderer() } ); Block block1 = blockChain.ProposeBlock(adminPrivateKey); + var invalidBlockCommit = new BlockCommit( + block1.Index, + 0, + block1.Hash, + new[] + { + new VoteMetadata( + block1.Index, + 0, + block1.Hash, + DateTimeOffset.UtcNow, + nonValidator.PublicKey, + 1000, + VoteFlag.PreCommit + ).Sign(nonValidator), + }.ToImmutableArray()); Assert.Throws( - () => blockChain.Append(block1, GenerateBlockCommit(block1, nonValidator))); + () => blockChain.Append( + block1, + invalidBlockCommit)); } [Fact] @@ -293,7 +316,10 @@ public void EarnMiningGoldWhenSuccessMining() Block block = blockChain.ProposeBlock(adminPrivateKey); BigInteger power = blockChain.GetNextWorldState().GetValidatorSet().GetValidator(adminPrivateKey.PublicKey).Power; - BlockCommit commit = GenerateBlockCommit(block, adminPrivateKey, power); + BlockCommit commit = GenerateBlockCommit( + block, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey }); // Since it's a block right after the Genesis, the reward is 0. blockChain.Append(block, commit); @@ -312,7 +338,10 @@ public void EarnMiningGoldWhenSuccessMining() ); block = blockChain.ProposeBlock(adminPrivateKey, commit); power = blockChain.GetNextWorldState().GetValidatorSet().GetValidator(adminPrivateKey.PublicKey).Power; - commit = GenerateBlockCommit(block, adminPrivateKey, power); + commit = GenerateBlockCommit( + block, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey }); // First Reward : Proposer base reward 10 * 0.01, proposer bonus reward 10 * 0.04, Commission 9.5 * 0.1 // Total 0.5 + 0.95 = 1.45 blockChain.Append(block, commit); @@ -330,7 +359,10 @@ public void EarnMiningGoldWhenSuccessMining() block = blockChain.ProposeBlock(adminPrivateKey, commit); power = blockChain.GetNextWorldState().GetValidatorSet().GetValidator(adminPrivateKey.PublicKey).Power; - commit = GenerateBlockCommit(block, adminPrivateKey, power); + commit = GenerateBlockCommit( + block, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey }); // First + Second Reward : Total reward of two blocks : 10 * 2 = 20 blockChain.Append(block, commit); @@ -412,10 +444,17 @@ List GenerateTransactions(int count) evidence: evs).Propose(); var stateRootHash = blockChain.DetermineNextBlockStateRootHash(blockChain.Tip, out _); Block block1 = EvaluateAndSign(stateRootHash, preEvalBlock1, adminPrivateKey); - blockChain.Append(block1, GenerateBlockCommit(block1, adminPrivateKey)); + blockChain.Append(block1, GenerateBlockCommit( + block1, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey })); Assert.Equal(2, blockChain.Count); Assert.True(blockChain.ContainsBlock(block1.Hash)); txs = GenerateTransactions(10).OrderBy(tx => tx.Id).ToList(); + var blockCommit = GenerateBlockCommit( + blockChain.Tip, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey }); PreEvaluationBlock preEvalBlock2 = new BlockContent( new BlockMetadata( index: 2, @@ -423,16 +462,25 @@ List GenerateTransactions(int count) publicKey: adminPublicKey, previousHash: blockChain.Tip.Hash, txHash: BlockContent.DeriveTxHash(txs), - lastCommit: GenerateBlockCommit(blockChain.Tip, adminPrivateKey), + lastCommit: blockCommit, evidenceHash: BlockContent.DeriveEvidenceHash(evs)), transactions: txs, evidence: evs).Propose(); stateRootHash = blockChain.DetermineNextBlockStateRootHash(blockChain.Tip, out _); Block block2 = EvaluateAndSign(stateRootHash, preEvalBlock2, adminPrivateKey); - blockChain.Append(block2, GenerateBlockCommit(block2, adminPrivateKey)); + blockChain.Append( + block2, + GenerateBlockCommit( + block2, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey })); Assert.Equal(3, blockChain.Count); Assert.True(blockChain.ContainsBlock(block2.Hash)); txs = GenerateTransactions(11).OrderBy(tx => tx.Id).ToList(); + blockCommit = GenerateBlockCommit( + blockChain.Tip, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey }); PreEvaluationBlock preEvalBlock3 = new BlockContent( new BlockMetadata( index: 3, @@ -440,14 +488,19 @@ List GenerateTransactions(int count) publicKey: adminPublicKey, previousHash: blockChain.Tip.Hash, txHash: BlockContent.DeriveTxHash(txs), - lastCommit: GenerateBlockCommit(blockChain.Tip, adminPrivateKey), + lastCommit: blockCommit, evidenceHash: BlockContent.DeriveEvidenceHash(evs)), transactions: txs, evidence: evs).Propose(); stateRootHash = blockChain.DetermineNextBlockStateRootHash(blockChain.Tip, out _); Block block3 = EvaluateAndSign(stateRootHash, preEvalBlock3, adminPrivateKey); Assert.Throws( - () => blockChain.Append(block3, GenerateBlockCommit(block3, adminPrivateKey))); + () => blockChain.Append( + block3, + GenerateBlockCommit( + block3, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey }))); Assert.Equal(3, blockChain.Count); Assert.False(blockChain.ContainsBlock(block3.Hash)); } @@ -528,11 +581,20 @@ List GenerateTransactions(int count) Block block1 = EvaluateAndSign(stateRootHash, preEvalBlock1, adminPrivateKey); // Should be fine since policy hasn't kicked in yet. - blockChain.Append(block1, GenerateBlockCommit(block1, adminPrivateKey)); + blockChain.Append( + block1, + GenerateBlockCommit( + block1, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey })); Assert.Equal(2, blockChain.Count); Assert.True(blockChain.ContainsBlock(block1.Hash)); txs = GenerateTransactions(10).OrderBy(tx => tx.Id).ToList(); + var blockCommit = GenerateBlockCommit( + blockChain.Tip, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey }); PreEvaluationBlock preEvalBlock2 = new BlockContent( new BlockMetadata( index: 2, @@ -540,7 +602,7 @@ List GenerateTransactions(int count) publicKey: adminPublicKey, previousHash: blockChain.Tip.Hash, txHash: BlockContent.DeriveTxHash(txs), - lastCommit: GenerateBlockCommit(blockChain.Tip, adminPrivateKey), + lastCommit: blockCommit, evidenceHash: BlockContent.DeriveEvidenceHash(evs)), transactions: txs, evidence: evs).Propose(); @@ -549,7 +611,12 @@ List GenerateTransactions(int count) // Subpolicy kicks in. Assert.Throws( - () => blockChain.Append(block2, GenerateBlockCommit(block2, adminPrivateKey))); + () => blockChain.Append( + block2, + GenerateBlockCommit( + block2, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey }))); Assert.Equal(2, blockChain.Count); Assert.False(blockChain.ContainsBlock(block2.Hash)); // Since failed, roll back nonce. @@ -557,6 +624,10 @@ List GenerateTransactions(int count) // Limit should also pass. txs = GenerateTransactions(5).OrderBy(tx => tx.Id).ToList(); + blockCommit = GenerateBlockCommit( + blockChain.Tip, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey }); PreEvaluationBlock preEvalBlock3 = new BlockContent( new BlockMetadata( index: 2, @@ -564,32 +635,37 @@ List GenerateTransactions(int count) publicKey: adminPublicKey, previousHash: blockChain.Tip.Hash, txHash: BlockContent.DeriveTxHash(txs), - lastCommit: GenerateBlockCommit(blockChain.Tip, adminPrivateKey), + lastCommit: blockCommit, evidenceHash: BlockContent.DeriveEvidenceHash(evs)), transactions: txs, evidence: evs).Propose(); Block block3 = EvaluateAndSign(stateRootHash, preEvalBlock3, adminPrivateKey); - blockChain.Append(block3, GenerateBlockCommit(block3, adminPrivateKey)); + blockChain.Append( + block3, + GenerateBlockCommit( + block3, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey })); Assert.Equal(3, blockChain.Count); Assert.True(blockChain.ContainsBlock(block3.Hash)); } - private BlockCommit GenerateBlockCommit(Block block, PrivateKey key, BigInteger? power = null) + private BlockCommit GenerateBlockCommit( + Block block, ValidatorSet validatorSet, IEnumerable validatorPrivateKeys) { - PrivateKey privateKey = key; return block.Index != 0 ? new BlockCommit( block.Index, 0, block.Hash, - ImmutableArray.Empty.Add(new VoteMetadata( + validatorPrivateKeys.Select(k => new VoteMetadata( block.Index, 0, block.Hash, DateTimeOffset.UtcNow, - privateKey.PublicKey, - power, - VoteFlag.PreCommit).Sign(privateKey))) + k.PublicKey, + validatorSet.GetValidator(k.PublicKey).Power, + VoteFlag.PreCommit).Sign(k)).ToImmutableArray()) : null; } From 03ae7db4479d87328cbf1915073d773a6de79f6c Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 26 Sep 2024 14:47:14 +0900 Subject: [PATCH 058/206] fix: Fix issues with delegation and undelegation on tombstoned validator --- Lib9c/Action/ValidatorDelegation/AllocateReward.cs | 4 +++- Lib9c/Delegation/Delegatee.cs | 6 ++++++ Lib9c/Delegation/Delegator.cs | 10 ++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs index f31cc85bc9..829ed66d8b 100644 --- a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs +++ b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs @@ -47,7 +47,7 @@ public override IWorld Execute(IActionContext context) rewardCurrency, lastCommit.Votes); } - + var communityFund = repository.GetBalance(Addresses.RewardPool, rewardCurrency); if (communityFund.Sign > 0) @@ -109,6 +109,7 @@ var bonusProposerReward .DivRem(votePowerDenominator * 100).Quotient; FungibleAssetValue proposerReward = baseProposerReward + bonusProposerReward; + var b1 = repository.GetBalance(proposerInfo.Proposer, proposerReward.Currency); if (proposerReward.Sign > 0) { repository.TransferAsset( @@ -116,6 +117,7 @@ var bonusProposerReward proposerInfo.Proposer, proposerReward); } + var b2 = repository.GetBalance(proposerInfo.Proposer, proposerReward.Currency); } internal static void DistributeValidatorReward( diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 4cdc6a0e44..51d64bed10 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -180,6 +180,12 @@ public virtual BigInteger Bond(T delegator, FungibleAssetValue fav, long height) "Cannot bond with invalid currency."); } + if (Tombstoned) + { + throw new InvalidOperationException( + "Cannot bond to tombstoned delegatee."); + } + Bond bond = Repository.GetBond(this, delegator.Address); BigInteger share = ShareFromFAV(fav); bond = bond.AddShare(share); diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index 2b233fb404..33ffc6699d 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -64,6 +64,11 @@ public virtual void Delegate( nameof(fav), fav, "Fungible asset value must be positive."); } + if (delegatee.Tombstoned) + { + throw new InvalidOperationException("Delegatee is tombstoned."); + } + delegatee.Bond(this, fav, height); Metadata.AddDelegatee(delegatee.Address); Repository.TransferAsset(DelegationPoolAddress, delegatee.DelegationPoolAddress, fav); @@ -133,6 +138,11 @@ public 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( From c9a8405503b019522a134e09513174881bf564a7 Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 26 Sep 2024 14:52:17 +0900 Subject: [PATCH 059/206] style: Change code style for readability --- .../ClaimRewardValidatorTest.cs | 12 +-- .../DelegateValidatorTest.cs | 47 +++++++++--- .../PromoteValidatorTest.cs | 4 +- .../RedelegateValidatorTest.cs | 55 +++++++++++--- .../ValidatorDelegation/SlashValidatorTest.cs | 63 +++++++++++---- .../UndelegateValidatorTest.cs | 46 +++++++++-- .../UnjailValidatorTest.cs | 22 +++--- .../UpdateValidatorsTest.cs | 4 +- .../ValidatorDelegationTestBase.cs | 76 ++++++++++++++++--- .../ValidatorDelegation/AllocateReward.cs | 2 - Lib9c/Delegation/Delegatee.cs | 2 +- 11 files changed, 252 insertions(+), 81 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs index 0148fb5374..1898b3dd14 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -33,7 +33,7 @@ public void Execute() var height = 1L; var validatorGold = NCG * 10; var allocatedReward = NCG * 100; - world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, mint: true, height++); world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); // When @@ -70,7 +70,7 @@ public void Execute_OneDelegator(double reward) var actionContext = new ActionContext { }; var promotedGold = NCG * 10; var allocatedReward = FungibleAssetValue.Parse(NCG, $"{reward:R}"); - world = EnsurePromotedValidator(world, validatorKey, promotedGold, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, promotedGold, mint: true, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); @@ -129,7 +129,7 @@ public void Execute_TwoDelegators() var actionContext = new ActionContext { }; var promotedGold = NCG * 10; var allocatedReward = FungibleAssetValue.Parse(NCG, $"{34.27:R}"); - world = EnsurePromotedValidator(world, validatorKey, promotedGold, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, promotedGold, mint: true, height++); world = EnsureBondedDelegator(world, delegatorKey1, validatorKey, NCG * 10, height++); world = EnsureBondedDelegator(world, delegatorKey2, validatorKey, NCG * 10, height++); world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); @@ -197,13 +197,13 @@ public void Execute_MultipleDelegators() var length = Random.Shared.Next(3, 100); var world = World; var validatorKey = new PrivateKey(); - var delegatorKeys = GetRandomArray(length, _ => new PrivateKey()); - var delegatorNCGs = GetRandomArray(length, _ => GetRandomNCG()); + var delegatorKeys = CreateArray(length, _ => new PrivateKey()); + var delegatorNCGs = CreateArray(length, _ => GetRandomNCG()); var height = 1L; var actionContext = new ActionContext(); var promotedGold = GetRandomNCG(); var allocatedReward = GetRandomNCG(); - world = EnsurePromotedValidator(world, validatorKey, promotedGold, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, promotedGold, mint: true, height++); world = EnsureBondedDelegators(world, delegatorKeys, validatorKey, delegatorNCGs, height++); world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index 174b19c0af..5714bb65ac 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -34,7 +34,7 @@ public void Execute() var height = 1L; var validatorGold = NCG * 10; var delegatorGold = NCG * 20; - world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, mint: true, height++); world = MintAsset(world, delegatorKey, NCG * 100, height++); // When @@ -68,9 +68,9 @@ public void Execute_WithInvalidCurrency_Throw() var delegatorKey = new PrivateKey(); var height = 1L; var validatorGold = NCG * 10; - var delegatorGold = Dollar * 20; - world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++, mint: true); - world = MintAsset(world, delegatorKey, delegatorGold, height++); + var delegatorDollar = Dollar * 20; + world = EnsurePromotedValidator(world, validatorKey, validatorGold, mint: true, height++); + world = MintAsset(world, delegatorKey, delegatorDollar, height++); // When var actionContext = new ActionContext @@ -79,11 +79,10 @@ public void Execute_WithInvalidCurrency_Throw() Signer = delegatorKey.Address, BlockIndex = height++, }; - var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorGold); + var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorDollar); // Then - Assert.Throws( - () => delegateValidator.Execute(actionContext)); + Assert.Throws(() => delegateValidator.Execute(actionContext)); } [Fact] @@ -96,7 +95,7 @@ public void Execute_WithInsufficientBalance_Throw() var validatorGold = NCG * 10; var delegatorGold = NCG * 10; var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, mint: true, height++); world = MintAsset(world, delegatorKey, delegatorGold, height++); // When @@ -109,8 +108,7 @@ public void Execute_WithInsufficientBalance_Throw() var delegateValidator = new DelegateValidator(validatorKey.Address, NCG * 11); // Then - Assert.Throws( - () => delegateValidator.Execute(actionContext)); + Assert.Throws(() => delegateValidator.Execute(actionContext)); } [Fact] @@ -132,7 +130,32 @@ public void Execute_ToInvalidValidator_Throw() var delegateValidator = new DelegateValidator(validatorKey.Address, NCG * 10); // Then - Assert.Throws( - () => delegateValidator.Execute(actionContext)); + Assert.Throws(() => delegateValidator.Execute(actionContext)); + } + + [Fact] + public void Execute_ToTombstonedValidator_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var delegatorKey = new PrivateKey(); + var height = 1L; + var validatorGold = NCG * 10; + var delegatorGold = NCG * 10; + world = EnsurePromotedValidator(world, validatorKey, validatorGold, mint: true, height++); + world = EnsureTombstonedValidator(world, validatorKey, height++); + world = MintAsset(world, delegatorKey, delegatorGold, height++); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + Signer = delegatorKey.Address, + }; + var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorGold); + + // Then + Assert.Throws(() => delegateValidator.Execute(actionContext)); } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs index 7269b66bcb..52cc5b867a 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs @@ -107,7 +107,7 @@ public void Execute_WithInvalidCurrency_Throw() } [Fact] - public void Execute_With_InsufficientBalance_Throw() + public void Execute_WithInsufficientBalance_Throw() { // Given var world = World; @@ -136,7 +136,7 @@ public void Execute_PromotedValidator_Throw() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); // When var promoteValidator = new PromoteValidator(validatorKey.PublicKey, NCG * 10); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs index cd25698e42..cacfd617c2 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs @@ -35,8 +35,8 @@ public void Execute() var srcPrivateKey = new PrivateKey(); var dstPrivateKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, srcPrivateKey, NCG * 10, height++, mint: true); - world = EnsurePromotedValidator(world, dstPrivateKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, srcPrivateKey, NCG * 10, mint: true, height++); + world = EnsurePromotedValidator(world, dstPrivateKey, NCG * 10, mint: true, height++); // When var expectedRepository = new ValidatorRepository(world, actionContext); @@ -76,7 +76,7 @@ public void Execute_ToInvalidValidator_Throw() var validatorKey = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); // When @@ -97,7 +97,7 @@ public void Execute_ToInvalidValidator_Throw() [Theory] [InlineData(0)] [InlineData(-1)] - public void Execute_NotPositiveShare_Throw(long share) + public void Execute_WithNotPositiveShare_Throw(long share) { // Given var world = World; @@ -105,8 +105,8 @@ public void Execute_NotPositiveShare_Throw(long share) var validatorKey2 = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, height++, mint: true); - world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, mint: true, height++); + world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, mint: true, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, NCG * 10, height++); // When @@ -125,7 +125,7 @@ public void Execute_NotPositiveShare_Throw(long share) } [Fact] - public void Execute_OverShare_Throw() + public void Execute_WithOverShare_Throw() { // Given var world = World; @@ -133,8 +133,8 @@ public void Execute_OverShare_Throw() var validatorKey2 = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, height++, mint: true); - world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, mint: true, height++); + world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, mint: true, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, NCG * 10, height++); // When @@ -156,7 +156,7 @@ public void Execute_OverShare_Throw() } [Fact] - public void Execute_FromJailedValidator_Throw() + public void Execute_ToJailedValidator() { // Given var world = World; @@ -164,8 +164,8 @@ public void Execute_FromJailedValidator_Throw() var validatorKey1 = new PrivateKey(); var validatorKey2 = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, height++, mint: true); - world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, mint: true, height++); + world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, mint: true, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, NCG * 10, height++); world = EnsureJailedValidator(world, validatorKey1, ref height); @@ -196,4 +196,35 @@ public void Execute_FromJailedValidator_Throw() Assert.Equal(expectedBond1.Share - 10, actualBond1.Share); Assert.Equal(expectedBond2.Share + 10, actualBond2.Share); } + + [Fact] + public void Execute_ToTombstonedValidator_Throw() + { + // Given + var world = World; + var actionContext = new ActionContext { }; + var srcPrivateKey = new PrivateKey(); + var dstPrivateKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, srcPrivateKey, NCG * 10, mint: true, height++); + world = EnsurePromotedValidator(world, dstPrivateKey, NCG * 10, mint: true, height++); + world = EnsureTombstonedValidator(world, dstPrivateKey, height++); + + // When + var repository = new ValidatorRepository(world, actionContext); + var srcValidator = repository.GetValidatorDelegatee(srcPrivateKey.Address); + var bone = repository.GetBond(srcValidator, srcPrivateKey.Address); + var redelegateValidator = new RedelegateValidator( + srcPrivateKey.Address, dstPrivateKey.Address, bone.Share); + actionContext = new ActionContext + { + PreviousState = world, + Signer = srcPrivateKey.Address, + BlockIndex = height++, + }; + + // Then + Assert.Throws( + () => redelegateValidator.Execute(actionContext)); + } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs index f853c3ec81..a3cfbf6798 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs @@ -10,6 +10,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using Libplanet.Types.Blocks; using Libplanet.Types.Consensus; using Libplanet.Types.Evidence; +using Nekoyume.Action; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.ValidatorDelegation; using Xunit; @@ -34,15 +35,20 @@ public void Execute() var world = World; var validatorKey = new PrivateKey(); var validatorGold = NCG * 10; - var deletatorKeys = GetRandomArray(length, _ => new PrivateKey()); - var delegatorGolds = GetRandomArray(length, i => NCG * Random.Shared.Next(10, 100)); + var deletatorKeys = CreateArray(length, _ => new PrivateKey()); + var delegatorGolds = CreateArray(length, i => NCG * Random.Shared.Next(10, 100)); var height = 1L; var actionContext = new ActionContext { }; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); world = EnsureBondedDelegators( world, deletatorKeys, validatorKey, delegatorGolds, height++); // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedValidatorShare = expectedRepository.GetBond( + expectedDelegatee, validatorKey.Address).Share; + var validatorSet = new ValidatorSet(new List { new (validatorKey.PublicKey, new BigInteger(1000)), @@ -50,7 +56,7 @@ public void Execute() var vote1 = new VoteMetadata( height: height - 1, round: 0, - blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x01)), + blockHash: new BlockHash(CreateArray(BlockHash.Size, _ => (byte)0x01)), timestamp: DateTimeOffset.UtcNow, validatorPublicKey: validatorKey.PublicKey, validatorPower: BigInteger.One, @@ -58,7 +64,7 @@ public void Execute() var vote2 = new VoteMetadata( height: height - 1, round: 0, - blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x02)), + blockHash: new BlockHash(CreateArray(BlockHash.Size, _ => (byte)0x02)), timestamp: DateTimeOffset.UtcNow, validatorPublicKey: validatorKey.PublicKey, validatorPower: BigInteger.One, @@ -85,23 +91,52 @@ public void Execute() // Then var balance = world.GetBalance(validatorKey.Address, NCG); - var repository = new ValidatorRepository(world, actionContext); - var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); + var actualValidatorShare = actualRepository.GetBond(actualDelegatee, validatorKey.Address).Share; - Assert.True(delegatee.Jailed); - Assert.Equal(long.MaxValue, delegatee.JailedUntil); - Assert.True(delegatee.Tombstoned); + Assert.True(actualDelegatee.Jailed); + Assert.Equal(long.MaxValue, actualDelegatee.JailedUntil); + Assert.True(actualDelegatee.Tombstoned); + } + + [Fact] + public void Execute_ToNotPromotedValidator_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var height = 1L; + + // When + var evidence = CreateDuplicateVoteEvidence(validatorKey, height - 1); + var lastCommit = new BlockCommit( + height: height - 1, + round: 0, + blockHash: EmptyBlockHash, + ImmutableArray.Create(evidence.VoteRef)); + var slashValidator = new SlashValidator(); + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = height++, + Evidence = new List { evidence }, + LastCommit = lastCommit, + }; + + // Then + Assert.Throws(() => slashValidator.Execute(actionContext)); } [Fact] - public void Execute_By_Abstain() + public void Execute_ByAbstain() { // Given var world = World; var validatorKey = new PrivateKey(); var actionContext = new ActionContext { }; var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); // When for (var i = 0L; i <= AbstainHistory.MaxAbstainAllowance; i++) @@ -131,14 +166,14 @@ public void Execute_By_Abstain() } [Fact] - public void Execute_JailedDelegatee_Nothing_Happens() + public void Execute_ToJailedValidator_ThenNothingHappens() { // Given var world = World; var validatorKey = new PrivateKey(); var height = 1L; var actionContext = new ActionContext(); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); world = EnsureTombstonedValidator(world, validatorKey, height++); // When diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 7b0d135053..24e9d425c7 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -71,7 +71,7 @@ public void Execute_FromInvalidValidtor_Throw() var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); // When @@ -91,14 +91,14 @@ public void Execute_FromInvalidValidtor_Throw() [Theory] [InlineData(0)] [InlineData(-1)] - public void Execute_NotPositiveShare_Throw(long share) + public void Execute_WithNotPositiveShare_Throw(long share) { // Given var world = World; var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); // When @@ -116,14 +116,14 @@ public void Execute_NotPositiveShare_Throw(long share) } [Fact] - public void Execute_NotDelegated_Throw() + public void Execute_WithoutDelegating_Throw() { // Given var world = World; var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); // When var actionContext = new ActionContext @@ -148,7 +148,7 @@ public void Execute_FromJailedValidator() var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); world = EnsureJailedValidator(world, validatorKey, ref height); @@ -173,4 +173,38 @@ public void Execute_FromJailedValidator() Assert.Equal(expectedBond.Share - 10, actualBond.Share); } + + [Fact] + public void Execute_FromTombstonedValidator() + { + // Given + var world = World; + var delegatorKey = new PrivateKey(); + var validatorKey = new PrivateKey(); + var height = 1L; + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); + world = EnsureTombstonedValidator(world, validatorKey, height++); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + Signer = delegatorKey.Address, + BlockIndex = height, + }; + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedBond = expectedRepository.GetBond(expectedDelegatee, delegatorKey.Address); + + var undelegateValidator = new UndelegateValidator(validatorKey.Address, 10); + world = undelegateValidator.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); + var actualBond = actualRepository.GetBond(actualDelegatee, delegatorKey.Address); + + Assert.Equal(expectedBond.Share - 10, actualBond.Share); + } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs index 2900fd5baf..b656a0f563 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs @@ -27,7 +27,7 @@ public void Execute() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); world = EnsureJailedValidator(world, validatorKey, ref height); // When @@ -49,7 +49,7 @@ public void Execute() } [Fact] - public void Execute_NotExistedDelegatee_Throw() + public void Execute_OnNotPromotedValidator_Throw() { // Given var world = World; @@ -66,18 +66,17 @@ public void Execute_NotExistedDelegatee_Throw() }; // Then - Assert.Throws( - () => unjailValidator.Execute(actionContext)); + Assert.Throws(() => unjailValidator.Execute(actionContext)); } [Fact] - public void Execute_JaliedValidator_NotJailed_Throw() + public void Execute_OnNotJailedValidator_Throw() { // Given var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); // When var unjailValidator = new UnjailValidator(); @@ -89,18 +88,17 @@ public void Execute_JaliedValidator_NotJailed_Throw() }; // Then - Assert.Throws( - () => unjailValidator.Execute(actionContext)); + Assert.Throws(() => unjailValidator.Execute(actionContext)); } [Fact] - public void Execute_JaliedValidator_Early_Throw() + public void Execute_TooEarly_Throw() { // Given var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); world = EnsureJailedValidator(world, validatorKey, ref height); // When @@ -118,13 +116,13 @@ public void Execute_JaliedValidator_Early_Throw() } [Fact] - public void Execute_JaliedValidator_Tombstoned_Throw() + public void Execute_OnTombstonedValidator_Throw() { // Given var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++, mint: true); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); world = EnsureTombstonedValidator(world, validatorKey, height++); // When diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs index c953e4754f..813731c693 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs @@ -27,8 +27,8 @@ public void Execute() { const int length = 10; var world = World; - var privateKeys = GetRandomArray(length, _ => new PrivateKey()); - var golds = GetRandomArray(length, i => NCG * Random.Shared.Next(1, length + 1)); + var privateKeys = CreateArray(length, _ => new PrivateKey()); + var golds = CreateArray(length, i => NCG * Random.Shared.Next(1, length + 1)); for (int i = 0; i < length; i++) { diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 182c15e3ac..32684039af 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -33,13 +33,13 @@ public ValidatorDelegationTestBase() } protected static BlockHash EmptyBlockHash { get; } - = new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x01)); + = new BlockHash(CreateArray(BlockHash.Size, _ => (byte)0x01)); protected PrivateKey AdminKey { get; } = new PrivateKey(); protected IWorld World { get; } - protected static T[] GetRandomArray(int length, Func creator) + protected static T[] CreateArray(int length, Func creator) => Enumerable.Range(0, length).Select(creator).ToArray(); protected static IWorld MintAsset( @@ -53,25 +53,66 @@ protected static IWorld MintAsset( return world.MintAsset(actionContext, privateKey.Address, amount); } + protected static IWorld EnsureProposer( + IWorld world, PrivateKey validatorKey, long blockHeight) + { + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight, + Signer = validatorKey.Address, + Miner = validatorKey.Address, + }; + return new RecordProposer().Execute(actionContext); + } + + protected static IWorld EnsurePromotedValidators( + IWorld world, + PrivateKey[] validatorKeys, + FungibleAssetValue[] amounts, + FungibleAssetValue[] mintAmounts, + long blockHeight) + { + if (validatorKeys.Length != amounts.Length) + { + throw new ArgumentException( + "The length of validatorPrivateKeys and amounts must be the same."); + } + + if (validatorKeys.Length != mintAmounts.Length) + { + throw new ArgumentException( + "The length of validatorPrivateKeys and mintAmounts must be the same."); + } + + for (var i = 0; i < validatorKeys.Length; i++) + { + world = EnsurePromotedValidator( + world, validatorKeys[i], amounts[i], mintAmounts[i], blockHeight); + } + + return world; + } + protected static IWorld EnsurePromotedValidator( IWorld world, PrivateKey validatorKey, FungibleAssetValue amount, long blockHeight) - => EnsurePromotedValidator(world, validatorKey, amount, blockHeight, mint: false); + => EnsurePromotedValidator(world, validatorKey, amount, mint: false, blockHeight); protected static IWorld EnsurePromotedValidator( IWorld world, PrivateKey validatorKey, FungibleAssetValue amount, - long blockHeight, - bool mint) + bool mint, + long blockHeight) => EnsurePromotedValidator( - world, validatorKey, amount, blockHeight, mint ? amount : amount * 0); + world, validatorKey, amount, mint ? amount : amount * 0, blockHeight); protected static IWorld EnsurePromotedValidator( IWorld world, PrivateKey validatorKey, FungibleAssetValue amount, - long blockHeight, - FungibleAssetValue mintAmount) + FungibleAssetValue mintAmount, + long blockHeight) { var validatorPublicKey = validatorKey.PublicKey; var promoteValidator = new PromoteValidator(validatorPublicKey, amount); @@ -335,7 +376,7 @@ protected static DuplicateVoteEvidence CreateDuplicateVoteEvidence( var vote1 = new VoteMetadata( height: blockHeight, round: 0, - blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x01)), + blockHash: new BlockHash(CreateArray(BlockHash.Size, _ => (byte)0x01)), timestamp: DateTimeOffset.UtcNow, validatorPublicKey: validatorKey.PublicKey, validatorPower: BigInteger.One, @@ -343,7 +384,7 @@ protected static DuplicateVoteEvidence CreateDuplicateVoteEvidence( var vote2 = new VoteMetadata( height: blockHeight, round: 0, - blockHash: new BlockHash(GetRandomArray(BlockHash.Size, _ => (byte)0x02)), + blockHash: new BlockHash(CreateArray(BlockHash.Size, _ => (byte)0x02)), timestamp: DateTimeOffset.UtcNow, validatorPublicKey: validatorKey.PublicKey, validatorPower: BigInteger.One, @@ -357,6 +398,10 @@ protected static DuplicateVoteEvidence CreateDuplicateVoteEvidence( return evidence; } + protected static FungibleAssetValue CalculateCommission( + FungibleAssetValue gold, ValidatorDelegatee delegatee) + => GetCommission(gold, delegatee.CommissionPercentage); + protected static FungibleAssetValue GetCommission( FungibleAssetValue gold, BigInteger percentage) => (gold * percentage).DivRem(100).Quotient; @@ -365,9 +410,16 @@ protected static FungibleAssetValue GetWithoutCommission( FungibleAssetValue gold, BigInteger percentage) => gold - (gold * percentage).DivRem(100).Quotient; - protected static FungibleAssetValue GetRandomNCG() + protected static FungibleAssetValue GetRandomNCG() => GetRandomNCG(Random.Shared, 1, 100000); + + protected static FungibleAssetValue GetRandomNCG(Random random) + => GetRandomNCG(random, 0.01m, 1000.0m); + + protected static FungibleAssetValue GetRandomNCG(Random random, decimal min, decimal max) { - var value = Math.Round(Random.Shared.Next(1, 100000) / 100.0, 2); + var minLong = (int)(min * 100); + var maxLong = (int)(max * 100); + var value = Math.Round(random.Next(minLong, maxLong) / 100.0, 2); return FungibleAssetValue.Parse(NCG, $"{value:R}"); } } diff --git a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs index 829ed66d8b..50715b1421 100644 --- a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs +++ b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs @@ -109,7 +109,6 @@ var bonusProposerReward .DivRem(votePowerDenominator * 100).Quotient; FungibleAssetValue proposerReward = baseProposerReward + bonusProposerReward; - var b1 = repository.GetBalance(proposerInfo.Proposer, proposerReward.Currency); if (proposerReward.Sign > 0) { repository.TransferAsset( @@ -117,7 +116,6 @@ var bonusProposerReward proposerInfo.Proposer, proposerReward); } - var b2 = repository.GetBalance(proposerInfo.Proposer, proposerReward.Currency); } internal static void DistributeValidatorReward( diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 51d64bed10..8adbb3563a 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -311,7 +311,7 @@ record = record.AddLumpSumRewards(rewards); Repository.TransferAsset(RewardPoolAddress, record.Address, rewards); } - Repository.SetLumpSumRewardsRecord(record); + Repository.SetLumpSumRewardsRecord(record); } public void Slash(BigInteger slashFactor, long infractionHeight, long height) From abb9668b737145974b06dfff83fc3edc4612a7a2 Mon Sep 17 00:00:00 2001 From: s2quake Date: Fri, 27 Sep 2024 17:41:18 +0900 Subject: [PATCH 060/206] style: Improve test code readability --- .../DelegateValidatorTest.cs | 22 ++-- .../PromoteValidatorTest.cs | 12 +- .../RedelegateValidatorTest.cs | 37 ++++-- .../ValidatorDelegation/SlashValidatorTest.cs | 10 +- .../UndelegateValidatorTest.cs | 21 +++- .../UnjailValidatorTest.cs | 12 +- .../ValidatorDelegationTestBase.cs | 118 ++++++++++++------ 7 files changed, 158 insertions(+), 74 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index 5714bb65ac..058ea0a295 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -34,8 +34,9 @@ public void Execute() var height = 1L; var validatorGold = NCG * 10; var delegatorGold = NCG * 20; - world = EnsurePromotedValidator(world, validatorKey, validatorGold, mint: true, height++); - world = MintAsset(world, delegatorKey, NCG * 100, height++); + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + world = EnsureToMintAsset(world, delegatorKey, NCG * 100, height++); // When var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorGold); @@ -69,8 +70,9 @@ public void Execute_WithInvalidCurrency_Throw() var height = 1L; var validatorGold = NCG * 10; var delegatorDollar = Dollar * 20; - world = EnsurePromotedValidator(world, validatorKey, validatorGold, mint: true, height++); - world = MintAsset(world, delegatorKey, delegatorDollar, height++); + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + world = EnsureToMintAsset(world, delegatorKey, delegatorDollar, height++); // When var actionContext = new ActionContext @@ -95,8 +97,9 @@ public void Execute_WithInsufficientBalance_Throw() var validatorGold = NCG * 10; var delegatorGold = NCG * 10; var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, validatorGold, mint: true, height++); - world = MintAsset(world, delegatorKey, delegatorGold, height++); + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + world = EnsureToMintAsset(world, delegatorKey, delegatorGold, height++); // When var actionContext = new ActionContext @@ -119,7 +122,7 @@ public void Execute_ToInvalidValidator_Throw() var validatorKey = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - world = MintAsset(world, delegatorKey, NCG * 100, height++); + world = EnsureToMintAsset(world, delegatorKey, NCG * 100, height++); // When var actionContext = new ActionContext @@ -143,9 +146,10 @@ public void Execute_ToTombstonedValidator_Throw() var height = 1L; var validatorGold = NCG * 10; var delegatorGold = NCG * 10; - world = EnsurePromotedValidator(world, validatorKey, validatorGold, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); world = EnsureTombstonedValidator(world, validatorKey, height++); - world = MintAsset(world, delegatorKey, delegatorGold, height++); + world = EnsureToMintAsset(world, delegatorKey, delegatorGold, height++); // When var actionContext = new ActionContext diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs index 52cc5b867a..eb1c0c33ee 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs @@ -31,7 +31,7 @@ public void Execute() var validatorKey = new PrivateKey(); var height = 1L; var gold = NCG * 10; - world = MintAsset(world, validatorKey, NCG * 100, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 100, height++); // When var actionContext = new ActionContext @@ -66,7 +66,7 @@ public void Execute_ToInvalidValidator_Throw() var validatorKey = new PrivateKey(); var height = 1L; var gold = NCG * 10; - world = MintAsset(world, validatorKey, NCG * 100, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 100, height++); // When var actionContext = new ActionContext @@ -90,7 +90,7 @@ public void Execute_WithInvalidCurrency_Throw() var validatorKey = new PrivateKey(); var height = 1L; var gold = Dollar * 10; - world = MintAsset(world, validatorKey, Dollar * 100, height++); + world = EnsureToMintAsset(world, validatorKey, Dollar * 100, height++); // When var actionContext = new ActionContext @@ -136,10 +136,12 @@ public void Execute_PromotedValidator_Throw() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + var validatorGold = NCG * 10; + world = EnsureToMintAsset(world, validatorKey, validatorGold * 2, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); // When - var promoteValidator = new PromoteValidator(validatorKey.PublicKey, NCG * 10); + var promoteValidator = new PromoteValidator(validatorKey.PublicKey, validatorGold); var actionContext = new ActionContext { PreviousState = world, diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs index cacfd617c2..56b00aa42b 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs @@ -35,8 +35,10 @@ public void Execute() var srcPrivateKey = new PrivateKey(); var dstPrivateKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, srcPrivateKey, NCG * 10, mint: true, height++); - world = EnsurePromotedValidator(world, dstPrivateKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, srcPrivateKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, srcPrivateKey, NCG * 10, height++); + world = EnsureToMintAsset(world, dstPrivateKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, dstPrivateKey, NCG * 10, height++); // When var expectedRepository = new ValidatorRepository(world, actionContext); @@ -76,7 +78,9 @@ public void Execute_ToInvalidValidator_Throw() var validatorKey = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); // When @@ -105,8 +109,11 @@ public void Execute_WithNotPositiveShare_Throw(long share) var validatorKey2 = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, mint: true, height++); - world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey1, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey2, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, NCG * 10, height++); // When @@ -133,8 +140,11 @@ public void Execute_WithOverShare_Throw() var validatorKey2 = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, mint: true, height++); - world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey1, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey2, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, NCG * 10, height++); // When @@ -164,8 +174,11 @@ public void Execute_ToJailedValidator() var validatorKey1 = new PrivateKey(); var validatorKey2 = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, mint: true, height++); - world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey1, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey2, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, NCG * 10, height++); world = EnsureJailedValidator(world, validatorKey1, ref height); @@ -206,8 +219,10 @@ public void Execute_ToTombstonedValidator_Throw() var srcPrivateKey = new PrivateKey(); var dstPrivateKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, srcPrivateKey, NCG * 10, mint: true, height++); - world = EnsurePromotedValidator(world, dstPrivateKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, srcPrivateKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, srcPrivateKey, NCG * 10, height++); + world = EnsureToMintAsset(world, dstPrivateKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, dstPrivateKey, NCG * 10, height++); world = EnsureTombstonedValidator(world, dstPrivateKey, height++); // When diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs index a3cfbf6798..b1b85326ff 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs @@ -39,7 +39,9 @@ public void Execute() var delegatorGolds = CreateArray(length, i => NCG * Random.Shared.Next(10, 100)); var height = 1L; var actionContext = new ActionContext { }; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAssets(world, deletatorKeys, delegatorGolds, height++); world = EnsureBondedDelegators( world, deletatorKeys, validatorKey, delegatorGolds, height++); @@ -136,7 +138,8 @@ public void Execute_ByAbstain() var validatorKey = new PrivateKey(); var actionContext = new ActionContext { }; var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); // When for (var i = 0L; i <= AbstainHistory.MaxAbstainAllowance; i++) @@ -173,7 +176,8 @@ public void Execute_ToJailedValidator_ThenNothingHappens() var validatorKey = new PrivateKey(); var height = 1L; var actionContext = new ActionContext(); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); world = EnsureTombstonedValidator(world, validatorKey, height++); // When diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 24e9d425c7..bea14d81bc 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -33,7 +33,7 @@ public void Execute() var validatorKey = new PrivateKey(); var height = 1L; var validatorGold = NCG * 10; - world = MintAsset(world, validatorKey, NCG * 100, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 100, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); // When @@ -71,7 +71,9 @@ public void Execute_FromInvalidValidtor_Throw() var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); // When @@ -98,7 +100,9 @@ public void Execute_WithNotPositiveShare_Throw(long share) var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); // When @@ -123,7 +127,8 @@ public void Execute_WithoutDelegating_Throw() var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); // When var actionContext = new ActionContext @@ -148,7 +153,9 @@ public void Execute_FromJailedValidator() var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); world = EnsureJailedValidator(world, validatorKey, ref height); @@ -182,7 +189,9 @@ public void Execute_FromTombstonedValidator() var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); world = EnsureTombstonedValidator(world, validatorKey, height++); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs index b656a0f563..ca7f3a0a51 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs @@ -27,7 +27,8 @@ public void Execute() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); world = EnsureJailedValidator(world, validatorKey, ref height); // When @@ -76,7 +77,8 @@ public void Execute_OnNotJailedValidator_Throw() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); // When var unjailValidator = new UnjailValidator(); @@ -98,7 +100,8 @@ public void Execute_TooEarly_Throw() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); world = EnsureJailedValidator(world, validatorKey, ref height); // When @@ -122,7 +125,8 @@ public void Execute_OnTombstonedValidator_Throw() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); world = EnsureTombstonedValidator(world, validatorKey, height++); // When diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 32684039af..3d925d8752 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -42,7 +42,7 @@ public ValidatorDelegationTestBase() protected static T[] CreateArray(int length, Func creator) => Enumerable.Range(0, length).Select(creator).ToArray(); - protected static IWorld MintAsset( + protected static IWorld EnsureToMintAsset( IWorld world, PrivateKey privateKey, FungibleAssetValue amount, long blockHeight) { var actionContext = new ActionContext @@ -53,6 +53,23 @@ protected static IWorld MintAsset( return world.MintAsset(actionContext, privateKey.Address, amount); } + protected static IWorld EnsureToMintAssets( + IWorld world, PrivateKey[] privateKeys, FungibleAssetValue[] amounts, long blockHeight) + { + if (privateKeys.Length != amounts.Length) + { + throw new ArgumentException( + "The length of privateKeys and amounts must be the same."); + } + + for (var i = 0; i < privateKeys.Length; i++) + { + world = EnsureToMintAsset(world, privateKeys[i], amounts[i], blockHeight); + } + + return world; + } + protected static IWorld EnsureProposer( IWorld world, PrivateKey validatorKey, long blockHeight) { @@ -70,7 +87,6 @@ protected static IWorld EnsurePromotedValidators( IWorld world, PrivateKey[] validatorKeys, FungibleAssetValue[] amounts, - FungibleAssetValue[] mintAmounts, long blockHeight) { if (validatorKeys.Length != amounts.Length) @@ -79,49 +95,24 @@ protected static IWorld EnsurePromotedValidators( "The length of validatorPrivateKeys and amounts must be the same."); } - if (validatorKeys.Length != mintAmounts.Length) - { - throw new ArgumentException( - "The length of validatorPrivateKeys and mintAmounts must be the same."); - } - for (var i = 0; i < validatorKeys.Length; i++) { world = EnsurePromotedValidator( - world, validatorKeys[i], amounts[i], mintAmounts[i], blockHeight); + world, validatorKeys[i], amounts[i], blockHeight); } return world; } - protected static IWorld EnsurePromotedValidator( - IWorld world, PrivateKey validatorKey, FungibleAssetValue amount, long blockHeight) - => EnsurePromotedValidator(world, validatorKey, amount, mint: false, blockHeight); - protected static IWorld EnsurePromotedValidator( IWorld world, PrivateKey validatorKey, FungibleAssetValue amount, - bool mint, - long blockHeight) - => EnsurePromotedValidator( - world, validatorKey, amount, mint ? amount : amount * 0, blockHeight); - - protected static IWorld EnsurePromotedValidator( - IWorld world, - PrivateKey validatorKey, - FungibleAssetValue amount, - FungibleAssetValue mintAmount, long blockHeight) { var validatorPublicKey = validatorKey.PublicKey; var promoteValidator = new PromoteValidator(validatorPublicKey, amount); - if (mintAmount.RawValue > 0) - { - world = MintAsset(world, validatorKey, mintAmount, blockHeight); - } - var actionContext = new ActionContext { PreviousState = world, @@ -161,7 +152,7 @@ protected static IWorld EnsureBondedDelegator( var validatorAddress = validatorKey.Address; var actionContext = new ActionContext { - PreviousState = MintAsset(world, delegatorKey, amount, blockHeight), + PreviousState = world, BlockIndex = blockHeight, Signer = delegatorAddress, }; @@ -278,7 +269,7 @@ protected static IWorld EnsureRewardAllocatedValidator( var actionContext2 = new ActionContext { PreviousState = world, - BlockIndex = blockHeight++, + BlockIndex = blockHeight, Signer = validatorKey.Address, LastCommit = lastCommit2, }; @@ -290,7 +281,7 @@ protected static IWorld EnsureRewardAllocatedValidator( var actionContext3 = new ActionContext { PreviousState = world, - BlockIndex = blockHeight++, + BlockIndex = blockHeight, Signer = validatorKey.Address, LastCommit = lastCommit3, }; @@ -400,15 +391,58 @@ protected static DuplicateVoteEvidence CreateDuplicateVoteEvidence( protected static FungibleAssetValue CalculateCommission( FungibleAssetValue gold, ValidatorDelegatee delegatee) - => GetCommission(gold, delegatee.CommissionPercentage); + => CalculateCommission(gold, delegatee.CommissionPercentage); - protected static FungibleAssetValue GetCommission( + protected static FungibleAssetValue CalculateCommission( FungibleAssetValue gold, BigInteger percentage) => (gold * percentage).DivRem(100).Quotient; - protected static FungibleAssetValue GetWithoutCommission( - FungibleAssetValue gold, BigInteger percentage) - => gold - (gold * percentage).DivRem(100).Quotient; + protected static FungibleAssetValue CalculatePropserReward(FungibleAssetValue reward) + => (reward * ValidatorDelegatee.BaseProposerRewardPercentage).DivRem(100).Quotient; + + protected static FungibleAssetValue CalculateBonusPropserReward( + BigInteger preCommitPower, BigInteger totalPower, FungibleAssetValue reward) + => (reward * preCommitPower * ValidatorDelegatee.BonusProposerRewardPercentage) + .DivRem(totalPower * 100).Quotient; + + protected static FungibleAssetValue CalculateBonusPropserReward( + ImmutableArray votes, FungibleAssetValue reward) + { + var totalPower = votes.Select(item => item.ValidatorPower) + .OfType() + .Aggregate(BigInteger.Zero, (accum, next) => accum + next); + + var preCommitPower = votes.Where(item => item.Flag == VoteFlag.PreCommit) + .Select(item => item.ValidatorPower) + .OfType() + .Aggregate(BigInteger.Zero, (accum, next) => accum + next); + + return CalculateBonusPropserReward(preCommitPower, totalPower, reward); + } + + protected static FungibleAssetValue CalculateClaim(BigInteger share, BigInteger totalShare, FungibleAssetValue totalClaim) + => (totalClaim * share).DivRem(totalShare).Quotient; + + protected static FungibleAssetValue CalculateCommunityFund(ImmutableArray votes, FungibleAssetValue reward) + { + var totalPower = votes.Select(item => item.ValidatorPower) + .OfType() + .Aggregate(BigInteger.Zero, (accum, next) => accum + next); + + var powers = votes.Where(item => item.Flag == VoteFlag.PreCommit) + .Select(item => item.ValidatorPower) + .OfType(); + + var communityFund = reward; + foreach (var power in powers) + { + var distribution = (reward * power).DivRem(totalPower).Quotient; + System.Diagnostics.Trace.WriteLine($"expected validator distribution: {reward} * {power} / {totalPower} = {distribution}"); + communityFund -= distribution; + } + + return communityFund; + } protected static FungibleAssetValue GetRandomNCG() => GetRandomNCG(Random.Shared, 1, 100000); @@ -422,4 +456,16 @@ protected static FungibleAssetValue GetRandomNCG(Random random, decimal min, dec var value = Math.Round(random.Next(minLong, maxLong) / 100.0, 2); return FungibleAssetValue.Parse(NCG, $"{value:R}"); } + + protected static FungibleAssetValue GetRandomCash(Random random, FungibleAssetValue fav) + { + var num = random.Next(1, 10000); + var cash = (fav * num).DivRem(10000).Quotient; + if (cash.Sign < 0 || cash > fav) + { + throw new InvalidOperationException("Invalid cash value."); + } + + return cash; + } } From 568432d0ccf36c23626ebf34282dfcb9d65f579c Mon Sep 17 00:00:00 2001 From: s2quake Date: Fri, 27 Sep 2024 17:41:36 +0900 Subject: [PATCH 061/206] test: Add test code for AllocateReward --- .../ValidatorDelegation/AllocateRewardTest.cs | 557 +++++++++++++++--- 1 file changed, 489 insertions(+), 68 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs index 55dd5f1c4d..23c1d73857 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -2,6 +2,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Numerics; @@ -13,12 +14,46 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using Nekoyume; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.State; -using Nekoyume.Module.ValidatorDelegation; using Nekoyume.ValidatorDelegation; using Xunit; public class AllocateRewardTest : ValidatorDelegationTestBase { + private interface IAllocateRewardFixture + { + FungibleAssetValue TotalReward { get; } + + ValidatorInfo[] ValidatorsInfos { get; } + + DelegatorInfo[] Delegatorinfos { get; } + + PrivateKey[] ValidatorKeys => ValidatorsInfos.Select(info => info.Key).ToArray(); + + FungibleAssetValue[] ValidatorCashes => ValidatorsInfos.Select(info => info.Cash).ToArray(); + + FungibleAssetValue[] ValidatorBalances + => ValidatorsInfos.Select(info => info.Balance).ToArray(); + + PrivateKey GetProposerKey() + { + return ValidatorsInfos + .Where(item => item.VoteFlag == VoteFlag.PreCommit) + .Take(ValidatorList.MaxBondedSetSize) + .First() + .Key; + } + } + + public static IEnumerable RandomSeeds => new List + { + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + }; + [Fact] public void Serialization() { @@ -32,105 +67,491 @@ public void Serialization() [Fact] public void Execute() { - IWorld world = World; - var context = new ActionContext { }; - var privateKeys = Enumerable.Range(0, 200).Select(_ => new PrivateKey()).ToArray(); - var favs = Enumerable.Range(0, 200).Select(i => NCG * (i + 1)).ToArray(); + var fixture = new StaticFixture + { + TotalReward = NCG * 1000, + ValidatorsInfos = CreateArray(4, i => new ValidatorInfo + { + Key = new PrivateKey(), + Cash = NCG * 10, + Balance = NCG * 100, + VoteFlag = i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null, + }), + Delegatorinfos = Array.Empty(), + }; + ExecuteWithFixture(fixture); + } - for (int i = 0; i < 200; i++) + [Theory] + [InlineData(1, 1000)] + [InlineData(33, 33)] + [InlineData(33, 33.33)] + [InlineData(17, 71.57)] + [InlineData(1, 3)] + [InlineData(10, 2.79)] + [InlineData(1, 0.01)] + [InlineData(10, 0.01)] + public void Execute_Theory(int validatorCount, double totalReward) + { + var fixture = new StaticFixture { - var signer = privateKeys[i]; - world = world.MintAsset(context, signer.Address, NCG * 1000); - world = new PromoteValidator(signer.PublicKey, favs[i]).Execute(new ActionContext + TotalReward = FungibleAssetValue.Parse(NCG, $"{totalReward:R}"), + ValidatorsInfos = CreateArray(validatorCount, i => new ValidatorInfo { - PreviousState = world, - Signer = signer.Address, - BlockIndex = 10L, - }); + Key = new PrivateKey(), + Cash = NCG * 10, + Balance = NCG * 100, + VoteFlag = i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null, + }), + Delegatorinfos = Array.Empty(), + }; + ExecuteWithFixture(fixture); + } + + [Fact] + public void Execute_WithoutReward_Throw() + { + var fixture = new StaticFixture + { + TotalReward = NCG * 0, + ValidatorsInfos = CreateArray(4, i => new ValidatorInfo + { + Key = new PrivateKey(), + Cash = NCG * 10, + Balance = NCG * 100, + VoteFlag = i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null, + }), + Delegatorinfos = Array.Empty(), + }; + Assert.Throws(() => ExecuteWithFixture(fixture)); + } + + [Theory] + [InlineData(0)] + [InlineData(1181126949)] + [InlineData(793705868)] + public void Execute_Theory_WithStaticSeed(int randomSeed) + { + var fixture = new RandomFixture(randomSeed); + ExecuteWithFixture(fixture); + } + + [Theory] + [MemberData(nameof(RandomSeeds))] + public void Execute_Theory_WithRandomSeed(int randomSeed) + { + var fixture = new RandomFixture(randomSeed); + ExecuteWithFixture(fixture); + } + + private static ImmutableArray CreateVotes( + ValidatorInfo[] validatorInfos, IReadOnlyList validatorList, long blockHeight) + { + var infoByPublicKey = validatorInfos.ToDictionary(k => k.Key.PublicKey, k => k); + var voteList = new List(validatorList.Count); + for (var i = 0; i < validatorList.Count; i++) + { + var validator = validatorList[i]; + var validatorInfo = infoByPublicKey[validator.PublicKey]; + var voteFlags = validatorInfo.VoteFlag; + var privateKey = voteFlags == VoteFlag.PreCommit ? validatorInfo.Key : null; + var voteMetadata = new VoteMetadata( + height: blockHeight, + round: 0, + blockHash: EmptyBlockHash, + timestamp: DateTimeOffset.UtcNow, + validatorPublicKey: validator.PublicKey, + validatorPower: validator.Power, + flag: voteFlags); + var vote = voteMetadata.Sign(privateKey); + voteList.Add(vote); + } + + return voteList.ToImmutableArray(); + } + + private void ExecuteWithStaticVariable(int validatorCount, FungibleAssetValue totalReward) + { + if (validatorCount <= 0) + { + throw new ArgumentOutOfRangeException(nameof(validatorCount)); } - var blockHash = new BlockHash(Enumerable.Repeat((byte)0x01, BlockHash.Size).ToArray()); - var timestamp = DateTimeOffset.UtcNow; - var voteFlags = Enumerable.Range(0, 100).Select(i => i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null).ToArray(); - var repository = new ValidatorRepository(world, context); - var bondedSet = repository.GetValidatorList().GetBonded(); + if (totalReward.Sign <= 0) + { + throw new ArgumentOutOfRangeException(nameof(totalReward)); + } - var proposer = bondedSet.First(); - repository.SetProposerInfo(new ProposerInfo(9L, proposer.OperatorAddress)); - var votes = bondedSet.Select( - (v, i) => new VoteMetadata( - 9L, 0, blockHash, timestamp, v.PublicKey, v.Power, i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null) - .Sign(i % 2 == 0 ? privateKeys.First(k => k.PublicKey.Equals(v.PublicKey)) : null)).ToImmutableArray(); + // Given + var length = validatorCount; + var world = World; + var actionContext = new ActionContext { }; + var validatorInfos = CreateArray(length, i => new ValidatorInfo + { + Key = new PrivateKey(), + Cash = NCG * (i + 1), + Balance = NCG * 1000, + VoteFlag = i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null, + }); + var validatorKeys = validatorInfos.Select(info => info.Key).ToArray(); + var validatorPromotes = validatorInfos.Select(info => info.Cash).ToArray(); + var validatorBalances = validatorInfos.Select(info => info.Balance).ToArray(); + var height = 1L; + var proposerKey = validatorInfos + .Where(item => item.VoteFlag == VoteFlag.PreCommit) + .Take(ValidatorList.MaxBondedSetSize) + .First() + .Key; - var totalReward = NCG * 1000; - world = repository.World.MintAsset(context, Addresses.RewardPool, totalReward); + world = EnsureToMintAssets(world, validatorKeys, validatorBalances, height++); + world = EnsurePromotedValidators(world, validatorKeys, validatorPromotes, height++); + world = EnsureProposer(world, proposerKey, height++); + world = world.MintAsset(actionContext, Addresses.RewardPool, totalReward); - // TODO: Remove this after delegation currency has been changed into GuildGold. - var initialFAVs = votes.Select(vote => world.GetBalance(vote.ValidatorPublicKey.Address, NCG)).ToArray(); + // Calculate expected values for comparison with actual values. + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedBondedSet = expectedRepository.GetValidatorList().GetBonded(); + var expectedProposer = proposerKey; + var votes = CreateVotes(validatorInfos, expectedBondedSet, height - 1); + var balances = votes + .Select(vote => world.GetBalance(vote.ValidatorPublicKey.Address, NCG)).ToArray(); + var expectedProposerReward + = CalculatePropserReward(totalReward) + CalculateBonusPropserReward(votes, totalReward); + var expectedValidatorsReward = totalReward - expectedProposerReward; + var expectedCommunityFund = CalculateCommunityFund(votes, expectedValidatorsReward); + var expectedAllocatedReward = expectedValidatorsReward - expectedCommunityFund; - context = new ActionContext + // When + var lastCommit = new BlockCommit(height - 1, round: 0, votes[0].BlockHash, votes); + var allocateReward = new AllocateReward(); + actionContext = new ActionContext { - BlockIndex = 10L, PreviousState = world, - Signer = privateKeys[199].Address, - LastCommit = new BlockCommit(9L, 0, blockHash, votes), + Signer = expectedProposer.PublicKey.Address, + LastCommit = lastCommit, + BlockIndex = height++, }; + world = allocateReward.Execute(actionContext); - var action = new AllocateReward(); - world = action.Execute(context); + // Then + var totalPower = votes.Select(item => item.ValidatorPower) + .OfType() + .Aggregate(BigInteger.Zero, (accum, next) => accum + next); + var actualRepository = new ValidatorRepository(world, actionContext); + var actualAllocatedReward = NCG * 0; + var actualCommunityFund = world.GetBalance(Addresses.CommunityPool, NCG); + foreach (var (vote, index) in votes.Select((v, i) => (v, i))) + { + if (vote.ValidatorPower is not { } validatorPower) + { + throw new InvalidOperationException("ValidatorPower cannot be null."); + } + + var validatorAddress = vote.ValidatorPublicKey.Address; + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorAddress); + var validatorRewardAddress = actualDelegatee.CurrentLumpSumRewardsRecordAddress(); + var balance = balances[index]; + var actualBalance = world.GetBalance(validatorAddress, NCG); + var actualReward = world.GetBalance(validatorRewardAddress, NCG); + var isProposer = vote.ValidatorPublicKey.Equals(expectedProposer.PublicKey); + + if (vote.Flag == VoteFlag.Null) + { + Assert.Equal(balance, actualBalance); + Assert.Equal(NCG * 0, actualReward); + Assert.False(isProposer); + continue; + } + + var reward = (expectedValidatorsReward * validatorPower).DivRem(totalPower).Quotient; + var expectedCommission = CalculateCommission(reward, actualDelegatee); + var expectedBalance = isProposer + ? expectedCommission + balance + expectedProposerReward + : expectedCommission + balance; + var expectedReward = reward - expectedCommission; + + Assert.Equal(expectedBalance, actualBalance); + Assert.Equal(expectedReward, actualReward); + + actualAllocatedReward += expectedCommission + expectedReward; + } + + Assert.Equal(expectedAllocatedReward, actualAllocatedReward); + Assert.Equal(expectedCommunityFund, actualCommunityFund); + } - BigInteger totalPower = votes.Aggregate( - BigInteger.Zero, - (accum, next) => accum + (BigInteger)next.ValidatorPower!); + private void ExecuteWithRandomVariable(int randomSeed) + { + // Given + var random = new Random(randomSeed); + var length = random.Next(1, 200); + var totalReward = GetRandomNCG(random); + var world = World; + var actionContext = new ActionContext { }; + var voteCount = 0; + var validatorInfos = CreateArray(length, i => + { + var promote = GetRandomNCG(random); + var flag = voteCount < 1 || random.Next() % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null; + voteCount += flag == VoteFlag.PreCommit ? 1 : 0; + return new ValidatorInfo + { + Key = new PrivateKey(), + Cash = promote, + Balance = promote + GetRandomNCG(random), + VoteFlag = flag, + }; + }); + var validatorKeys = validatorInfos.Select(info => info.Key).ToArray(); + var validatorPromotes = validatorInfos.Select(info => info.Cash).ToArray(); + var validatorBalances = validatorInfos.Select(info => info.Balance).ToArray(); + var height = 1L; + var proposerKey = validatorInfos + .Where(item => item.VoteFlag == VoteFlag.PreCommit) + .Take(ValidatorList.MaxBondedSetSize) + .First() + .Key; - BigInteger preCommitPower = votes.Aggregate( - BigInteger.Zero, - (accum, next) => accum + (BigInteger)next.ValidatorPower! * (next.Flag == VoteFlag.PreCommit ? 1 : 0)); + world = EnsureToMintAssets(world, validatorKeys, validatorBalances, height++); + world = EnsurePromotedValidators(world, validatorKeys, validatorPromotes, height++); + world = EnsureProposer(world, proposerKey, height++); + world = world.MintAsset(actionContext, Addresses.RewardPool, totalReward); - var baseProposerReward - = (totalReward * ValidatorDelegatee.BaseProposerRewardPercentage) - .DivRem(100).Quotient; - var bonusProposerReward - = (totalReward * preCommitPower * ValidatorDelegatee.BonusProposerRewardPercentage) - .DivRem(totalPower * 100).Quotient; + // Calculate expected values for comparison with actual values. + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedBondedSet = expectedRepository.GetValidatorList().GetBonded(); + var expectedProposer = proposerKey; + var votes = CreateVotes(validatorInfos, expectedBondedSet, height - 1); + var balances = votes + .Select(vote => world.GetBalance(vote.ValidatorPublicKey.Address, NCG)).ToArray(); + var expectedProposerReward + = CalculatePropserReward(totalReward) + CalculateBonusPropserReward(votes, totalReward); + var expectedValidatorsReward = totalReward - expectedProposerReward; + var expectedCommunityFund = CalculateCommunityFund(votes, expectedValidatorsReward); + var expectedAllocatedReward = expectedValidatorsReward - expectedCommunityFund; - var proposerReward = baseProposerReward + bonusProposerReward; - var remains = totalReward - proposerReward; - repository.UpdateWorld(world); + // When + var lastCommit = new BlockCommit(height - 1, round: 0, votes[0].BlockHash, votes); + var allocateReward = new AllocateReward(); + actionContext = new ActionContext + { + PreviousState = world, + Signer = expectedProposer.PublicKey.Address, + LastCommit = lastCommit, + BlockIndex = height++, + }; + world = allocateReward.Execute(actionContext); + // Then + var totalPower = votes.Select(item => item.ValidatorPower) + .OfType() + .Aggregate(BigInteger.Zero, (accum, next) => accum + next); + var actualRepository = new ValidatorRepository(world, actionContext); + var actualAllocatedReward = NCG * 0; + var actualCommunityFund = world.GetBalance(Addresses.CommunityPool, NCG); foreach (var (vote, index) in votes.Select((v, i) => (v, i))) { - var initialFAV = initialFAVs[index]; - var validator = repository.GetValidatorDelegatee(vote.ValidatorPublicKey.Address); + if (vote.ValidatorPower is not { } validatorPower) + { + throw new InvalidOperationException("ValidatorPower cannot be null."); + } - FungibleAssetValue rewardAllocated - = (remains * vote.ValidatorPower!.Value).DivRem(totalPower).Quotient; - FungibleAssetValue commission - = (rewardAllocated * validator.CommissionPercentage) - .DivRem(100).Quotient; + var validatorAddress = vote.ValidatorPublicKey.Address; + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorAddress); + var validatorRewardAddress = actualDelegatee.CurrentLumpSumRewardsRecordAddress(); + var balance = balances[index]; + var actualBalance = world.GetBalance(validatorAddress, NCG); + var actualReward = world.GetBalance(validatorRewardAddress, NCG); + var isProposer = vote.ValidatorPublicKey.Equals(expectedProposer.PublicKey); if (vote.Flag == VoteFlag.Null) { - Assert.Equal(initialFAV, world.GetBalance(vote.ValidatorPublicKey.Address, NCG)); - Assert.Equal( - NCG * 0, world.GetBalance(validator.CurrentLumpSumRewardsRecordAddress(), NCG)); + Assert.Equal(balance, actualBalance); + Assert.Equal(NCG * 0, actualReward); + Assert.False(isProposer); continue; } - if (vote.ValidatorPublicKey.Equals(proposer.PublicKey)) + var reward = (expectedValidatorsReward * validatorPower).DivRem(totalPower).Quotient; + var expectedCommission = CalculateCommission(reward, actualDelegatee); + var expectedBalance = isProposer + ? expectedCommission + balance + expectedProposerReward + : expectedCommission + balance; + var expectedReward = reward - expectedCommission; + + Assert.Equal(expectedBalance, actualBalance); + Assert.Equal(expectedReward, actualReward); + + Assert.Equal(expectedBalance, actualBalance); + Assert.Equal(expectedReward, actualReward); + + actualAllocatedReward += expectedCommission + expectedReward; + } + + Assert.Equal(expectedAllocatedReward, actualAllocatedReward); + Assert.Equal(expectedCommunityFund, actualCommunityFund); + } + + private void ExecuteWithFixture(IAllocateRewardFixture fixture) + { + // Given + // var length = fixture.ValidatorLength; + var totalReward = fixture.TotalReward; + var world = World; + var actionContext = new ActionContext { }; + var voteCount = fixture.ValidatorsInfos.Where( + item => item.VoteFlag == VoteFlag.PreCommit).Count(); + var validatorInfos = fixture.ValidatorsInfos; + var validatorKeys = fixture.ValidatorKeys; + var validatorCashes = fixture.ValidatorCashes; + var validatorBalances = fixture.ValidatorBalances; + var proposerKey = fixture.GetProposerKey(); + var height = 1L; + world = EnsureToMintAssets(world, validatorKeys, validatorBalances, height++); + world = EnsurePromotedValidators(world, validatorKeys, validatorCashes, height++); + world = EnsureProposer(world, proposerKey, height++); + world = world.MintAsset(actionContext, Addresses.RewardPool, totalReward); + + // Calculate expected values for comparison with actual values. + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedBondedSet = expectedRepository.GetValidatorList().GetBonded(); + var expectedProposer = proposerKey; + var votes = CreateVotes(validatorInfos, expectedBondedSet, height - 1); + var balances = votes + .Select(vote => world.GetBalance(vote.ValidatorPublicKey.Address, NCG)).ToArray(); + var expectedProposerReward + = CalculatePropserReward(totalReward) + CalculateBonusPropserReward(votes, totalReward); + var expectedValidatorsReward = totalReward - expectedProposerReward; + var expectedCommunityFund = CalculateCommunityFund(votes, expectedValidatorsReward); + var expectedAllocatedReward = expectedValidatorsReward - expectedCommunityFund; + + // When + var lastCommit = new BlockCommit(height - 1, round: 0, votes[0].BlockHash, votes); + var allocateReward = new AllocateReward(); + actionContext = new ActionContext + { + PreviousState = world, + Signer = expectedProposer.PublicKey.Address, + LastCommit = lastCommit, + BlockIndex = height++, + }; + world = allocateReward.Execute(actionContext); + + // Then + var totalPower = votes.Select(item => item.ValidatorPower) + .OfType() + .Aggregate(BigInteger.Zero, (accum, next) => accum + next); + var actualRepository = new ValidatorRepository(world, actionContext); + var actualAllocatedReward = NCG * 0; + var actualCommunityFund = world.GetBalance(Addresses.CommunityPool, NCG); + foreach (var (vote, index) in votes.Select((v, i) => (v, i))) + { + if (vote.ValidatorPower is not { } validatorPower) { - Assert.Equal( - proposerReward + commission + initialFAV, - world.GetBalance(vote.ValidatorPublicKey.Address, NCG)); + throw new InvalidOperationException("ValidatorPower cannot be null."); } - else + + var validatorAddress = vote.ValidatorPublicKey.Address; + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorAddress); + var validatorRewardAddress = actualDelegatee.CurrentLumpSumRewardsRecordAddress(); + var balance = balances[index]; + var actualBalance = world.GetBalance(validatorAddress, NCG); + var actualReward = world.GetBalance(validatorRewardAddress, NCG); + var isProposer = vote.ValidatorPublicKey.Equals(expectedProposer.PublicKey); + + if (vote.Flag == VoteFlag.Null) { - Assert.Equal(commission + initialFAV, world.GetBalance(vote.ValidatorPublicKey.Address, NCG)); + Assert.Equal(balance, actualBalance); + Assert.Equal(NCG * 0, actualReward); + Assert.False(isProposer); + continue; } - Assert.Equal( - rewardAllocated - commission, - world.GetBalance(validator.CurrentLumpSumRewardsRecordAddress(), NCG)); + var reward = (expectedValidatorsReward * validatorPower).DivRem(totalPower).Quotient; + var expectedCommission = CalculateCommission(reward, actualDelegatee); + var expectedBalance = isProposer + ? expectedCommission + balance + expectedProposerReward + : expectedCommission + balance; + var expectedReward = reward - expectedCommission; + + Assert.Equal(expectedBalance, actualBalance); + Assert.Equal(expectedReward, actualReward); + + Assert.Equal(expectedBalance, actualBalance); + Assert.Equal(expectedReward, actualReward); + + actualAllocatedReward += expectedCommission + expectedReward; } + + Assert.Equal(expectedAllocatedReward, actualAllocatedReward); + Assert.Equal(expectedCommunityFund, actualCommunityFund); + } + + private struct ValidatorInfo + { + public PrivateKey Key { get; set; } + + public FungibleAssetValue Cash { get; set; } + + public FungibleAssetValue Balance { get; set; } + + public VoteFlag VoteFlag { get; set; } + } + + private struct DelegatorInfo + { + public PrivateKey Key { get; set; } + + public FungibleAssetValue Cash { get; set; } + + public FungibleAssetValue Balance { get; set; } + } + + private struct StaticFixture : IAllocateRewardFixture + { + public FungibleAssetValue TotalReward { get; set; } + + public ValidatorInfo[] ValidatorsInfos { get; set; } + + public DelegatorInfo[] Delegatorinfos { get; set; } + } + + private class RandomFixture : IAllocateRewardFixture + { + private readonly Random _random; + + public RandomFixture(int randomSeed) + { + _random = new Random(randomSeed); + TotalReward = GetRandomNCG(_random); + ValidatorsInfos = CreateArray(_random.Next(1, 200), i => + { + var balance = GetRandomNCG(_random); + var flag = _random.Next() % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null; + return new ValidatorInfo + { + Key = new PrivateKey(), + Balance = balance, + Cash = GetRandomCash(_random, balance), + VoteFlag = flag, + }; + }); + Delegatorinfos = CreateArray(_random.Next(1, 200), i => + { + var balance = GetRandomNCG(_random); + return new DelegatorInfo + { + Key = new PrivateKey(), + Balance = balance, + Cash = GetRandomCash(_random, balance), + }; + }); + } + + public FungibleAssetValue TotalReward { get; } + + public ValidatorInfo[] ValidatorsInfos { get; } + + public DelegatorInfo[] Delegatorinfos { get; } } } From 783d27739f121ea20db00af0ee301130710bd3a8 Mon Sep 17 00:00:00 2001 From: s2quake Date: Fri, 27 Sep 2024 17:41:47 +0900 Subject: [PATCH 062/206] test: Add test code for ClaimRewardValidator --- .../ClaimRewardValidatorTest.cs | 384 ++++++++++-------- 1 file changed, 208 insertions(+), 176 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs index 1898b3dd14..3b0a4f4fc7 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -2,18 +2,46 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System; +using System.Collections.Generic; using System.Linq; -using System.Numerics; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; -using Nekoyume; - using Nekoyume.Action.ValidatorDelegation; - using Nekoyume.ValidatorDelegation; +using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; using Xunit; public class ClaimRewardValidatorTest : ValidatorDelegationTestBase { + private interface IClaimRewardFixture + { + FungibleAssetValue TotalReward { get; } + + PrivateKey ValidatorKey { get; } + + FungibleAssetValue ValidatorBalance { get; } + + FungibleAssetValue ValidatorCash { get; } + + DelegatorInfo[] Delegatorinfos { get; } + + PrivateKey[] DelegatorKeys => Delegatorinfos.Select(i => i.Key).ToArray(); + + FungibleAssetValue[] DelegatorBalances => Delegatorinfos.Select(i => i.Balance).ToArray(); + + FungibleAssetValue[] DelegatorCashes => Delegatorinfos.Select(i => i.Cash).ToArray(); + } + + public static IEnumerable RandomSeeds => new List + { + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + }; + [Fact] public void Serialization() { @@ -33,7 +61,8 @@ public void Execute() var height = 1L; var validatorGold = NCG * 10; var allocatedReward = NCG * 100; - world = EnsurePromotedValidator(world, validatorKey, validatorGold, mint: true, height++); + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); // When @@ -60,172 +89,147 @@ public void Execute() [InlineData(11.11)] [InlineData(10)] [InlineData(1)] - public void Execute_OneDelegator(double reward) + public void Execute_Theory_OneDelegator(decimal totalReward) { - // Given - var world = World; - var validatorKey = new PrivateKey(); - var delegatorKey = new PrivateKey(); - var height = 1L; - var actionContext = new ActionContext { }; - var promotedGold = NCG * 10; - var allocatedReward = FungibleAssetValue.Parse(NCG, $"{reward:R}"); - world = EnsurePromotedValidator(world, validatorKey, promotedGold, mint: true, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); - world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); - - // When - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedDelegatee = expectedRepository.GetValidatorDelegatee( - validatorKey.Address); - var expectedCommission = GetCommission( - allocatedReward, expectedDelegatee.CommissionPercentage); - var expectedReward = allocatedReward - expectedCommission; - var expectedValidatorBalance = expectedCommission + expectedReward.DivRem(2).Quotient; - var expectedDelegatorBalance = expectedReward.DivRem(2).Quotient; - var expectedRemainReward = allocatedReward; - expectedRemainReward -= expectedValidatorBalance; - expectedRemainReward -= expectedDelegatorBalance; - - var lastCommit = CreateLastCommit(validatorKey, height - 1); - actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = height++, - Signer = validatorKey.Address, - LastCommit = lastCommit, - }; - world = new ClaimRewardValidator(validatorKey.Address).Execute(actionContext); - actionContext = new ActionContext + var fixture = new StaticFixture { - PreviousState = world, - BlockIndex = height++, - Signer = delegatorKey.Address, - LastCommit = lastCommit, + DelegatorLength = 1, + TotalReward = FungibleAssetValue.Parse(NCG, $"{totalReward}"), + ValidatorKey = new PrivateKey(), + ValidatorBalance = NCG * 100, + ValidatorCash = NCG * 10, + Delegatorinfos = new[] + { + new DelegatorInfo + { + Key = new PrivateKey(), + Balance = NCG * 100, + Cash = NCG * 10, + }, + }, }; - world = new ClaimRewardValidator(validatorKey.Address).Execute(actionContext); - - // Then - var repository = new ValidatorRepository(world, actionContext); - var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); - var actualRemainReward = world.GetBalance(delegatee.CurrentLumpSumRewardsRecordAddress(), NCG); - var actualValidatorBalance = world.GetBalance(validatorKey.Address, NCG); - var actualDelegatorBalance = world.GetBalance(delegatorKey.Address, NCG); - - Assert.Equal(expectedRemainReward, actualRemainReward); - Assert.Equal(expectedValidatorBalance, actualValidatorBalance); - Assert.Equal(expectedDelegatorBalance, actualDelegatorBalance); + ExecuteWithFixture(fixture); } - [Fact] - public void Execute_TwoDelegators() + [Theory] + [InlineData(0.1)] + [InlineData(1)] + [InlineData(3)] + [InlineData(5)] + [InlineData(7)] + [InlineData(9)] + [InlineData(11.11)] + [InlineData(11.12)] + [InlineData(33.33)] + [InlineData(33.34)] + [InlineData(34.27)] + [InlineData(34.28)] + [InlineData(34.29)] + public void Execute_Theory_TwoDelegators(decimal totalReward) { - // Given - var world = World; - var validatorKey = new PrivateKey(); - var delegatorKey1 = new PrivateKey(); - var delegatorKey2 = new PrivateKey(); - var height = 1L; - var actionContext = new ActionContext { }; - var promotedGold = NCG * 10; - var allocatedReward = FungibleAssetValue.Parse(NCG, $"{34.27:R}"); - world = EnsurePromotedValidator(world, validatorKey, promotedGold, mint: true, height++); - world = EnsureBondedDelegator(world, delegatorKey1, validatorKey, NCG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey2, validatorKey, NCG * 10, height++); - world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); - - // When - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); - var expectedCommission = GetCommission( - allocatedReward, expectedDelegatee.CommissionPercentage); - var expectedReward = allocatedReward - expectedCommission; - var expectedValidatorBalance = expectedCommission + expectedReward.DivRem(3).Quotient; - var expectedDelegator1Balance = expectedReward.DivRem(3).Quotient; - var expectedDelegator2Balance = expectedReward.DivRem(3).Quotient; - var expectedRemainReward = expectedDelegatee.RewardCurrency * 0; - var expectedCommunityBalance = allocatedReward; - expectedCommunityBalance -= expectedValidatorBalance; - expectedCommunityBalance -= expectedDelegator1Balance; - expectedCommunityBalance -= expectedDelegator2Balance; - - var lastCommit = CreateLastCommit(validatorKey, height - 1); - actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = height++, - Signer = validatorKey.Address, - LastCommit = lastCommit, - }; - world = new ClaimRewardValidator(validatorKey.Address).Execute(actionContext); - actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = height++, - Signer = delegatorKey1.Address, - LastCommit = lastCommit, - }; - world = new ClaimRewardValidator(validatorKey.Address).Execute(actionContext); - actionContext = new ActionContext + var fixture = new StaticFixture { - PreviousState = world, - BlockIndex = height++, - Signer = delegatorKey2.Address, - LastCommit = lastCommit, + DelegatorLength = 2, + TotalReward = FungibleAssetValue.Parse(NCG, $"{totalReward}"), + ValidatorKey = new PrivateKey(), + ValidatorBalance = NCG * 100, + ValidatorCash = NCG * 10, + Delegatorinfos = new[] + { + new DelegatorInfo + { + Key = new PrivateKey(), + Balance = NCG * 100, + Cash = NCG * 10, + }, + new DelegatorInfo + { + Key = new PrivateKey(), + Balance = NCG * 100, + Cash = NCG * 10, + }, + }, }; - world = new ClaimRewardValidator(validatorKey.Address).Execute(actionContext); + ExecuteWithFixture(fixture); + } - // Then - var repository = new ValidatorRepository(world, actionContext); - var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); - var actualRemainReward = world.GetBalance( - delegatee.CurrentLumpSumRewardsRecordAddress(), NCG); - var actualValidatorBalance = world.GetBalance(validatorKey.Address, NCG); - var actualDelegator1Balance = world.GetBalance(delegatorKey1.Address, NCG); - var actualDelegator2Balance = world.GetBalance(delegatorKey2.Address, NCG); + [Theory] + [InlineData(0)] + [InlineData(123)] + [InlineData(34352535)] + public void Execute_Theory_WithStaticSeed(int randomSeed) + { + var fixture = new RandomFixture(randomSeed); + ExecuteWithFixture(fixture); + } - Assert.Equal(expectedRemainReward, actualRemainReward); - Assert.Equal(expectedValidatorBalance, actualValidatorBalance); - Assert.Equal(expectedDelegator1Balance, actualDelegator1Balance); - Assert.Equal(expectedDelegator2Balance, actualDelegator2Balance); + [Theory] + [MemberData(nameof(RandomSeeds))] + public void Execute_Theory_WithRandomSeed(int randomSeed) + { + var fixture = new RandomFixture(randomSeed); + ExecuteWithFixture(fixture); } - [Fact] - public void Execute_MultipleDelegators() + private void ExecuteWithFixture(IClaimRewardFixture fixture) { // Given - var length = Random.Shared.Next(3, 100); + var length = fixture.Delegatorinfos.Length; var world = World; - var validatorKey = new PrivateKey(); - var delegatorKeys = CreateArray(length, _ => new PrivateKey()); - var delegatorNCGs = CreateArray(length, _ => GetRandomNCG()); + var validatorKey = fixture.ValidatorKey; + var delegatorKeys = fixture.DelegatorKeys; + var delegatorBalances = fixture.DelegatorBalances; + var delegatorCashes = fixture.DelegatorCashes; var height = 1L; var actionContext = new ActionContext(); - var promotedGold = GetRandomNCG(); - var allocatedReward = GetRandomNCG(); - world = EnsurePromotedValidator(world, validatorKey, promotedGold, mint: true, height++); - world = EnsureBondedDelegators(world, delegatorKeys, validatorKey, delegatorNCGs, height++); - world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); + var validatorBalance = fixture.ValidatorBalance; + var validatorCash = fixture.ValidatorCash; + var totalReward = fixture.TotalReward; + world = EnsureToMintAsset(world, validatorKey, validatorBalance, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorCash, height++); + world = EnsureToMintAssets(world, delegatorKeys, delegatorBalances, height++); + world = EnsureBondedDelegators( + world, delegatorKeys, validatorKey, delegatorCashes, height++); + world = EnsureRewardAllocatedValidator(world, validatorKey, totalReward, ref height); - // When + // Calculate expected values for comparison with actual values. var expectedRepository = new ValidatorRepository(world, actionContext); var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); - var expectedCommission = GetCommission( - allocatedReward, expectedDelegatee.CommissionPercentage); - var expectedReward = allocatedReward - expectedCommission; - var expectedValidatorBalance = expectedCommission + CalculateReward( - expectedRepository, validatorKey, validatorKey, expectedReward); - var expectedDelegatorBalances = CalculateRewards( - expectedRepository, validatorKey, delegatorKeys, expectedReward); - var expectedCommunityBalance = allocatedReward; - expectedCommunityBalance -= expectedValidatorBalance; + var expectedTotalShares = expectedDelegatee.TotalShares; + var expectedValidatorShare + = expectedRepository.GetBond(expectedDelegatee, validatorKey.Address).Share; + var expectedDelegatorShares = delegatorKeys + .Select(item => expectedRepository.GetBond(expectedDelegatee, item.Address).Share) + .ToArray(); + var expectedProposerReward + = CalculatePropserReward(totalReward) + CalculateBonusPropserReward(1, 1, totalReward); + var expectedReward = totalReward - expectedProposerReward; + var expectedCommission = CalculateCommission( + expectedReward, expectedDelegatee.CommissionPercentage); + var expectedClaim = expectedReward - expectedCommission; + var expectedValidatorClaim = CalculateClaim( + expectedValidatorShare, expectedTotalShares, expectedClaim); + var expectedDelegatorClaims = CreateArray( + length, + i => CalculateClaim(expectedDelegatorShares[i], expectedTotalShares, expectedClaim)); + var expectedValidatorBalance = validatorBalance; + expectedValidatorBalance -= validatorCash; + expectedValidatorBalance += expectedProposerReward; + expectedValidatorBalance += expectedCommission; + expectedValidatorBalance += expectedValidatorClaim; + var expectedDelegatorBalances = CreateArray( + length, + i => delegatorBalances[i] - delegatorCashes[i] + expectedDelegatorClaims[i]); + var expectedRemainReward = totalReward; + expectedRemainReward -= expectedProposerReward; + expectedRemainReward -= expectedCommission; + expectedRemainReward -= expectedValidatorClaim; for (var i = 0; i < length; i++) { - expectedCommunityBalance -= expectedDelegatorBalances[i]; + expectedRemainReward -= expectedDelegatorClaims[i]; } - var expectedRemainReward = expectedDelegatee.RewardCurrency * 0; - + // When var lastCommit = CreateLastCommit(validatorKey, height - 1); actionContext = new ActionContext { @@ -250,46 +254,74 @@ public void Execute_MultipleDelegators() // Then var repository = new ValidatorRepository(world, actionContext); var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); - var actualRemainReward = world.GetBalance( - delegatee.CurrentLumpSumRewardsRecordAddress(), NCG); + var actualRemainReward = world.GetBalance(delegatee.RewardRemainderPoolAddress, NCG); var actualValidatorBalance = world.GetBalance(validatorKey.Address, NCG); - var actualCommunityBalance = world.GetBalance(Addresses.CommunityPool, NCG); + var actualDelegatorBalances = delegatorKeys + .Select(item => world.GetBalance(item.Address, NCG)) + .ToArray(); Assert.Equal(expectedRemainReward, actualRemainReward); Assert.Equal(expectedValidatorBalance, actualValidatorBalance); - Assert.Equal(expectedCommunityBalance, actualCommunityBalance); - - for (var i = 0; i < length; i++) - { - var actualDelegatorBalance = world.GetBalance(delegatorKeys[i].Address, NCG); - Assert.Equal(expectedDelegatorBalances[i], actualDelegatorBalance); - } + Assert.Equal(expectedDelegatorBalances, actualDelegatorBalances); } - private static FungibleAssetValue CalculateReward( - ValidatorRepository repository, - PrivateKey validatorKey, - PrivateKey delegatorKey, - FungibleAssetValue reward) + private struct DelegatorInfo { - var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); - var bond = repository.GetBond(delegatee, delegatorKey.Address); - return CalculateReward(reward, bond.Share, delegatee.TotalShares); + public PrivateKey Key { get; set; } + + public FungibleAssetValue Cash { get; set; } + + public FungibleAssetValue Balance { get; set; } } - private static FungibleAssetValue[] CalculateRewards( - ValidatorRepository repository, - PrivateKey validatorKey, - PrivateKey[] delegatorKeys, - FungibleAssetValue reward) + private struct StaticFixture : IClaimRewardFixture { - return delegatorKeys - .Select(item => CalculateReward(repository, validatorKey, item, reward)) - .ToArray(); + public int DelegatorLength { get; set; } + + public FungibleAssetValue TotalReward { get; set; } + + public PrivateKey ValidatorKey { get; set; } + + public FungibleAssetValue ValidatorBalance { get; set; } + + public FungibleAssetValue ValidatorCash { get; set; } + + public DelegatorInfo[] Delegatorinfos { get; set; } } - private static FungibleAssetValue CalculateReward( - FungibleAssetValue reward, BigInteger share, BigInteger totalShares) + private class RandomFixture : IClaimRewardFixture { - return (reward * share).DivRem(totalShares).Quotient; + private readonly Random _random; + + public RandomFixture(int randomSeed) + { + _random = new Random(randomSeed); + DelegatorLength = _random.Next(3, 100); + ValidatorKey = new PrivateKey(); + TotalReward = GetRandomNCG(_random); + ValidatorBalance = GetRandomNCG(_random); + ValidatorCash = GetRandomCash(_random, ValidatorBalance); + Delegatorinfos = CreateArray(DelegatorLength, _ => + { + var balance = GetRandomNCG(_random); + return new DelegatorInfo + { + Key = new PrivateKey(), + Balance = balance, + Cash = GetRandomCash(_random, balance), + }; + }); + } + + public int DelegatorLength { get; } + + public FungibleAssetValue TotalReward { get; } + + public PrivateKey ValidatorKey { get; } + + public FungibleAssetValue ValidatorBalance { get; } + + public FungibleAssetValue ValidatorCash { get; } + + public DelegatorInfo[] Delegatorinfos { get; } } } From 96e997d469f976a9aad268f9557e90a28cd0f6a4 Mon Sep 17 00:00:00 2001 From: s2quake Date: Wed, 2 Oct 2024 14:01:47 +0900 Subject: [PATCH 063/206] test: Add test for static properties --- .../Action/ValidatorDelegation/ConstantTest.cs | 14 ++++++++++++++ Lib9c/ValidatorDelegation/ValidatorDelegatee.cs | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/ConstantTest.cs diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ConstantTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ConstantTest.cs new file mode 100644 index 0000000000..b0269dec6c --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ConstantTest.cs @@ -0,0 +1,14 @@ +#nullable enable +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using Nekoyume.ValidatorDelegation; +using Xunit; + +public class ConstantTest +{ + [Fact] + public void StaticPropertyTest() + { + Assert.True(ValidatorDelegatee.ValidatorUnbondingPeriod > 0); + } +} diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index fe0f9a2646..4994bfd7b4 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -87,7 +87,7 @@ public ValidatorDelegatee( public static Currency ValidatorDelegationCurrency => Currencies.GuildGold; - public static long ValidatorUnbondingPeriod => 0L; + public static long ValidatorUnbondingPeriod => 10L; public static int ValidatorMaxUnbondLockInEntries => 10; From 8e1fbf1128a1e2fe7b8ea9a570278909ab5799f8 Mon Sep 17 00:00:00 2001 From: s2quake Date: Wed, 2 Oct 2024 14:01:52 +0900 Subject: [PATCH 064/206] fix: Fix an issue where RedelegateValidator does not throw an exception when the SrcValidatorDelegatee and the DstValidatorDelegatee are the same --- Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs index 21f5423464..fdd52c8db6 100644 --- a/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs @@ -56,6 +56,11 @@ public override IWorld Execute(IActionContext context) { GasTracer.UseGas(1); + if (SrcValidatorDelegatee.Equals(DstValidatorDelegatee)) + { + throw new InvalidOperationException("Src and Dst cannot be the same."); + } + var world = context.PreviousState; var repository = new ValidatorRepository(world, context); repository.RedelegateValidator(context, SrcValidatorDelegatee, DstValidatorDelegatee, Share); From 152145cf1e9233789b111bbd9a541de7c7273f9b Mon Sep 17 00:00:00 2001 From: s2quake Date: Wed, 2 Oct 2024 15:10:00 +0900 Subject: [PATCH 065/206] test: Add test for RedelegateValidator --- .../RedelegateValidatorTest.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs index 56b00aa42b..79243c579f 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs @@ -242,4 +242,32 @@ public void Execute_ToTombstonedValidator_Throw() Assert.Throws( () => redelegateValidator.Execute(actionContext)); } + + [Fact] + public void Execute_SrcAndDstAddressAreSame_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var delegatorKey = new PrivateKey(); + var height = 1L; + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = height++, + Signer = delegatorKey.Address, + }; + var redelegateValidator = new RedelegateValidator( + validatorKey.Address, validatorKey.Address, 10); + + // Then + Assert.Throws( + () => redelegateValidator.Execute(actionContext)); + } } From 38234b1571eda4aceaa55d1a961d5cef05ad1a92 Mon Sep 17 00:00:00 2001 From: s2quake Date: Wed, 2 Oct 2024 14:06:26 +0900 Subject: [PATCH 066/206] test: Add test code to verify that a validator cannot be jailed due to a delegator's delegation --- .../DelegateValidatorTest.cs | 43 ++++++++++++++ .../RedelegateValidatorTest.cs | 50 ++++++++++++++++ .../UndelegateValidatorTest.cs | 42 +++++++++++++ .../ValidatorDelegationTestBase.cs | 59 +++++++++++++++++++ 4 files changed, 194 insertions(+) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index 058ea0a295..5e139ddfa7 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -162,4 +162,47 @@ public void Execute_ToTombstonedValidator_Throw() // Then Assert.Throws(() => delegateValidator.Execute(actionContext)); } + + [Fact] + public void Execute_CannotBeJailedDueToDelegatorDelegating() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var delegatorKey = new PrivateKey(); + var validatorGold = NCG * 10; + var delegatorGold = NCG * 10; + var delegatorBalance = NCG * 100; + var actionContext = new ActionContext { }; + + var height = 1L; + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + world = EnsureToMintAsset(world, delegatorKey, delegatorBalance, height++); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, delegatorGold, height++); + world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, 10, height++); + world = EnsureUnjailedValidator(world, validatorKey, ref height); + + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedJailed = expectedDelegatee.Jailed; + + var delegateValidator = new DelegateValidator(validatorKey.Address, 1 * NCG); + actionContext = new ActionContext + { + PreviousState = world, + Signer = delegatorKey.Address, + BlockIndex = height, + }; + world = delegateValidator.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); + var actualJailed = actualDelegatee.Jailed; + + Assert.False(actualJailed); + Assert.Equal(expectedJailed, actualJailed); + } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs index 79243c579f..8bed3e840d 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs @@ -270,4 +270,54 @@ public void Execute_SrcAndDstAddressAreSame_Throw() Assert.Throws( () => redelegateValidator.Execute(actionContext)); } + + [Fact] + public void Execute_CannotBeJailedDueToDelegatorRedelegating() + { + // Given + var world = World; + var validatorKey1 = new PrivateKey(); + var validatorKey2 = new PrivateKey(); + var delegatorKey = new PrivateKey(); + var validatorGold = NCG * 10; + var delegatorGold = NCG * 10; + var delegatorBalance = NCG * 100; + var actionContext = new ActionContext { }; + + var height = 1L; + world = EnsureToMintAsset(world, validatorKey1, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey1, validatorGold, height++); + world = EnsureToMintAsset(world, validatorKey2, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey2, validatorGold, height++); + world = EnsureToMintAsset(world, delegatorKey, delegatorBalance, height++); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, delegatorGold, height++); + world = EnsureUnbondingDelegator(world, validatorKey1, validatorKey1, 10, height++); + world = EnsureUnjailedValidator(world, validatorKey1, ref height); + height++; + + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey1.Address); + var expectedJailed = expectedDelegatee.Jailed; + + var redelegateValidator = new RedelegateValidator( + srcValidatorDelegatee: validatorKey1.Address, + dstValidatorDelegatee: validatorKey2.Address, + share: 10); + actionContext = new ActionContext + { + PreviousState = world, + Signer = delegatorKey.Address, + BlockIndex = height, + }; + world = redelegateValidator.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey1.Address); + var actualJailed = actualDelegatee.Jailed; + + Assert.False(actualJailed); + Assert.Equal(expectedJailed, actualJailed); + } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index bea14d81bc..190b600eab 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -216,4 +216,46 @@ public void Execute_FromTombstonedValidator() Assert.Equal(expectedBond.Share - 10, actualBond.Share); } + + [Fact] + public void Execute_CannotBeJailedDueToDelegatorUndelegating() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var delegatorKey = new PrivateKey(); + var validatorGold = NCG * 10; + var delegatorGold = NCG * 10; + var actionContext = new ActionContext { }; + + var height = 1L; + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + world = EnsureToMintAsset(world, delegatorKey, delegatorGold, height++); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, delegatorGold, height++); + world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, 10, height++); + world = EnsureUnjailedValidator(world, validatorKey, ref height); + + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedJailed = expectedDelegatee.Jailed; + + var undelegateValidator = new UndelegateValidator(validatorKey.Address, 10); + actionContext = new ActionContext + { + PreviousState = world, + Signer = delegatorKey.Address, + BlockIndex = height, + }; + world = undelegateValidator.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); + var actualJailed = actualDelegatee.Jailed; + + Assert.False(actualJailed); + Assert.Equal(expectedJailed, actualJailed); + } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 3d925d8752..a0b305822d 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -183,6 +183,31 @@ protected static IWorld EnsureBondedDelegators( return world; } + protected static IWorld EnsureUnbondingDelegator( + IWorld world, + PrivateKey delegatorKey, + PrivateKey validatorKey, + BigInteger share, + long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } + + var delegatorAddress = delegatorKey.Address; + var validatorAddress = validatorKey.Address; + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight, + Signer = delegatorAddress, + }; + var undelegateValidator = new UndelegateValidator( + validatorAddress, share); + return undelegateValidator.Execute(actionContext); + } + protected static IWorld EnsureJailedValidator( IWorld world, PrivateKey validatorKey, ref long blockHeight) { @@ -248,6 +273,40 @@ protected static IWorld EnsureTombstonedValidator( return slashValidator.Execute(actionContext); } + protected static IWorld EnsureUnjailedValidator( + IWorld world, PrivateKey validatorKey, ref long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } + + var repository = new ValidatorRepository(world, new ActionContext()); + var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); + if (!delegatee.Jailed) + { + throw new ArgumentException( + "The validator is not jailed.", nameof(validatorKey)); + } + + if (delegatee.Tombstoned) + { + throw new ArgumentException( + "The validator is tombstoned.", nameof(validatorKey)); + } + + blockHeight = Math.Max(blockHeight, delegatee.JailedUntil + 1); + + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight, + Signer = validatorKey.Address, + }; + var unjailedValidator = new UnjailValidator(); + return unjailedValidator.Execute(actionContext); + } + protected static IWorld EnsureRewardAllocatedValidator( IWorld world, PrivateKey validatorKey, FungibleAssetValue reward, ref long blockHeight) { From 56ae7633c52280a09dc4283c9b9a065cd51d88ca Mon Sep 17 00:00:00 2001 From: s2quake Date: Wed, 2 Oct 2024 15:04:54 +0900 Subject: [PATCH 067/206] test: Add test for ReleaseValidatorUnbondings --- .../ReleaseValidatorUnbondingsTest.cs | 108 ++++++++++++++++-- 1 file changed, 97 insertions(+), 11 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs index a87f907423..ef695b9881 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs @@ -1,19 +1,105 @@ #nullable enable -namespace Lib9c.Tests.Action.ValidatorDelegation +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using System.Numerics; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; +using Xunit; + +public class ReleaseValidatorUnbondingsTest : ValidatorDelegationTestBase { - using Nekoyume.Action.ValidatorDelegation; - using Xunit; + [Fact] + public void Serialization() + { + var action = new ReleaseValidatorUnbondings(); + var plainValue = action.PlainValue; + + var deserialized = new ReleaseValidatorUnbondings(); + deserialized.LoadPlainValue(plainValue); + } + + [Fact] + public void Execute() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var validatorGold = NCG * 50; + var validatorBalance = NCG * 100; + var share = new BigInteger(10); + var height = 1L; + var actionContext = new ActionContext { }; + + world = EnsureToMintAsset(world, validatorKey, validatorBalance, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, share, height); + + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedUnbondingSet = expectedRepository.GetUnbondingSet(); + var expectedReleaseCount = expectedUnbondingSet.UnbondingRefs.Count; + var expectedDepositGold = expectedDelegatee.FAVFromShare(share); + var expectedBalance = world.GetBalance(validatorKey.Address, NCG) + expectedDepositGold; - public class ReleaseValidatorUnbondingsTest + var releaseValidatorUnbondings = new ReleaseValidatorUnbondings(validatorKey.Address); + actionContext = new ActionContext + { + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height + ValidatorDelegatee.ValidatorUnbondingPeriod, + }; + world = releaseValidatorUnbondings.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualBalance = world.GetBalance(validatorKey.Address, NCG); + var actualUnbondingSet = actualRepository.GetUnbondingSet(); + var actualReleaseCount = actualUnbondingSet.UnbondingRefs.Count; + + Assert.Equal(expectedBalance, actualBalance); + Assert.NotEqual(expectedUnbondingSet.IsEmpty, actualUnbondingSet.IsEmpty); + Assert.True(actualUnbondingSet.IsEmpty); + Assert.Equal(expectedReleaseCount - 1, actualReleaseCount); + } + + [Fact] + public void Execute_ThereIsNoUnbonding_AtEarlyHeight() { - [Fact] - public void Serialization() + // Given + var world = World; + var validatorKey = new PrivateKey(); + var height = 1L; + var actionContext = new ActionContext { }; + var share = new BigInteger(10); + + world = EnsureToMintAsset(world, validatorKey, NCG * 100, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 50, height++); + world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, share, height); + + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedUnbondingSet = expectedRepository.GetUnbondingSet(); + var expectedReleaseCount = expectedUnbondingSet.UnbondingRefs.Count; + + var releaseValidatorUnbondings = new ReleaseValidatorUnbondings(validatorKey.Address); + actionContext = new ActionContext { - var action = new ReleaseValidatorUnbondings(); - var plainValue = action.PlainValue; + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height, + }; + world = releaseValidatorUnbondings.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualUnbondingSet = actualRepository.GetUnbondingSet(); + var actualReleaseCount = actualUnbondingSet.UnbondingRefs.Count; - var deserialized = new ReleaseValidatorUnbondings(); - deserialized.LoadPlainValue(plainValue); - } + Assert.Equal(expectedUnbondingSet.IsEmpty, actualUnbondingSet.IsEmpty); + Assert.False(actualUnbondingSet.IsEmpty); + Assert.Equal(expectedReleaseCount, actualReleaseCount); } } From 83f994b2c8f7ef601213b0198570c6448aa1f5a9 Mon Sep 17 00:00:00 2001 From: s2quake Date: Wed, 2 Oct 2024 17:08:42 +0900 Subject: [PATCH 068/206] test: Add complex test cod for delegating and undelegating --- .../ClaimRewardValidatorTest.cs | 20 +- .../DelegateValidatorTest.cs | 187 ++++++++++++++++ .../UndelegateValidatorTest.cs | 203 ++++++++++++++++++ .../ValidatorDelegationTestBase.cs | 2 + 4 files changed, 402 insertions(+), 10 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs index 3b0a4f4fc7..9fd1183c83 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -23,13 +23,13 @@ private interface IClaimRewardFixture FungibleAssetValue ValidatorCash { get; } - DelegatorInfo[] Delegatorinfos { get; } + DelegatorInfo[] DelegatorInfos { get; } - PrivateKey[] DelegatorKeys => Delegatorinfos.Select(i => i.Key).ToArray(); + PrivateKey[] DelegatorKeys => DelegatorInfos.Select(i => i.Key).ToArray(); - FungibleAssetValue[] DelegatorBalances => Delegatorinfos.Select(i => i.Balance).ToArray(); + FungibleAssetValue[] DelegatorBalances => DelegatorInfos.Select(i => i.Balance).ToArray(); - FungibleAssetValue[] DelegatorCashes => Delegatorinfos.Select(i => i.Cash).ToArray(); + FungibleAssetValue[] DelegatorCashes => DelegatorInfos.Select(i => i.Cash).ToArray(); } public static IEnumerable RandomSeeds => new List @@ -98,7 +98,7 @@ public void Execute_Theory_OneDelegator(decimal totalReward) ValidatorKey = new PrivateKey(), ValidatorBalance = NCG * 100, ValidatorCash = NCG * 10, - Delegatorinfos = new[] + DelegatorInfos = new[] { new DelegatorInfo { @@ -134,7 +134,7 @@ public void Execute_Theory_TwoDelegators(decimal totalReward) ValidatorKey = new PrivateKey(), ValidatorBalance = NCG * 100, ValidatorCash = NCG * 10, - Delegatorinfos = new[] + DelegatorInfos = new[] { new DelegatorInfo { @@ -174,7 +174,7 @@ public void Execute_Theory_WithRandomSeed(int randomSeed) private void ExecuteWithFixture(IClaimRewardFixture fixture) { // Given - var length = fixture.Delegatorinfos.Length; + var length = fixture.DelegatorInfos.Length; var world = World; var validatorKey = fixture.ValidatorKey; var delegatorKeys = fixture.DelegatorKeys; @@ -285,7 +285,7 @@ private struct StaticFixture : IClaimRewardFixture public FungibleAssetValue ValidatorCash { get; set; } - public DelegatorInfo[] Delegatorinfos { get; set; } + public DelegatorInfo[] DelegatorInfos { get; set; } } private class RandomFixture : IClaimRewardFixture @@ -300,7 +300,7 @@ public RandomFixture(int randomSeed) TotalReward = GetRandomNCG(_random); ValidatorBalance = GetRandomNCG(_random); ValidatorCash = GetRandomCash(_random, ValidatorBalance); - Delegatorinfos = CreateArray(DelegatorLength, _ => + DelegatorInfos = CreateArray(DelegatorLength, _ => { var balance = GetRandomNCG(_random); return new DelegatorInfo @@ -322,6 +322,6 @@ public RandomFixture(int randomSeed) public FungibleAssetValue ValidatorCash { get; } - public DelegatorInfo[] Delegatorinfos { get; } + public DelegatorInfo[] DelegatorInfos { get; } } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index 5e139ddfa7..42124f2fde 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -1,8 +1,11 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System; +using System.Collections.Generic; +using System.Linq; using Libplanet.Action.State; using Libplanet.Crypto; +using Libplanet.Types.Assets; using Nekoyume.Action; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.ValidatorDelegation; @@ -10,6 +13,23 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; public class DelegateValidatorTest : ValidatorDelegationTestBase { + private interface IDelegateValidatorFixture + { + ValidatorInfo ValidatorInfo { get; } + + DelegatorInfo[] DelegatorInfos { get; } + } + + public static IEnumerable RandomSeeds => new List + { + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + }; + [Fact] public void Serialization() { @@ -60,6 +80,50 @@ public void Execute() Assert.Equal(NCG * 80, world.GetBalance(delegatorKey.Address, NCG)); } + [Theory] + [InlineData(2)] + [InlineData(3)] + [InlineData(9)] + public void Execute_Theory(int delegatorCount) + { + var fixture = new StaticFixture + { + ValidatorInfo = new ValidatorInfo + { + Key = new PrivateKey(), + Cash = NCG * 10, + Balance = NCG * 100, + }, + DelegatorInfos = Enumerable.Range(0, delegatorCount) + .Select(_ => new DelegatorInfo + { + Key = new PrivateKey(), + Cash = NCG * 20, + Balance = NCG * 100, + }) + .ToArray(), + }; + ExecuteWithFixture(fixture); + } + + [Theory] + [InlineData(0)] + [InlineData(1181126949)] + [InlineData(793705868)] + public void Execute_Theory_WithStaticSeed(int randomSeed) + { + var fixture = new RandomFixture(randomSeed); + ExecuteWithFixture(fixture); + } + + [Theory] + [MemberData(nameof(RandomSeeds))] + public void Execute_Theory_WithRandomSeed(int randomSeed) + { + var fixture = new RandomFixture(randomSeed); + ExecuteWithFixture(fixture); + } + [Fact] public void Execute_WithInvalidCurrency_Throw() { @@ -205,4 +269,127 @@ public void Execute_CannotBeJailedDueToDelegatorDelegating() Assert.False(actualJailed); Assert.Equal(expectedJailed, actualJailed); } + + private void ExecuteWithFixture(IDelegateValidatorFixture fixture) + { + // Given + var world = World; + var validatorKey = fixture.ValidatorInfo.Key; + var height = 1L; + var validatorCash = fixture.ValidatorInfo.Cash; + var validatorBalance = fixture.ValidatorInfo.Balance; + var delegatorKeys = fixture.DelegatorInfos.Select(i => i.Key).ToArray(); + var delegatorCashes = fixture.DelegatorInfos.Select(i => i.Cash).ToArray(); + var delegatorBalances = fixture.DelegatorInfos.Select(i => i.Balance).ToArray(); + var actionContext = new ActionContext { }; + world = EnsureToMintAsset(world, validatorKey, validatorBalance, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorCash, height++); + world = EnsureToMintAssets(world, delegatorKeys, delegatorBalances, height++); + + // When + var expectedRepository = new ValidatorRepository(world, new ActionContext()); + var expectedValidator = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedValidatorBalance = validatorBalance - validatorCash; + var expectedDelegatorBalances = delegatorBalances + .Select((b, i) => b - delegatorCashes[i]).ToArray(); + var expectedPower = delegatorCashes.Aggregate( + validatorCash.RawValue, (a, b) => a + b.RawValue); + + for (var i = 0; i < delegatorKeys.Length; i++) + { + var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorCashes[i]); + actionContext = new ActionContext + { + PreviousState = world, + Signer = delegatorKeys[i].Address, + BlockIndex = height++, + }; + world = delegateValidator.Execute(actionContext); + } + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualValidator = actualRepository.GetValidatorDelegatee(validatorKey.Address); + var actualValidatorBalance = world.GetBalance(validatorKey.Address, NCG); + var actualDelegatorBalances = delegatorKeys + .Select(k => world.GetBalance(k.Address, NCG)).ToArray(); + var actualPower = actualValidator.Power; + + for (var i = 0; i < delegatorKeys.Length; i++) + { + var actualBond = actualRepository.GetBond(actualValidator, delegatorKeys[i].Address); + Assert.Contains(delegatorKeys[i].Address, actualValidator.Delegators); + Assert.Equal(delegatorCashes[i].RawValue, actualBond.Share); + Assert.Equal(expectedDelegatorBalances[i], actualDelegatorBalances[i]); + } + + Assert.Equal(expectedValidatorBalance, actualValidatorBalance); + Assert.Equal(expectedPower, actualPower); + Assert.Equal(expectedDelegatorBalances, actualDelegatorBalances); + } + + private struct ValidatorInfo + { + public ValidatorInfo() + { + } + + public ValidatorInfo(Random random) + { + Balance = GetRandomNCG(random); + Cash = GetRandomCash(random, Balance); + } + + public PrivateKey Key { get; set; } = new PrivateKey(); + + public FungibleAssetValue Cash { get; set; } = NCG * 10; + + public FungibleAssetValue Balance { get; set; } = NCG * 100; + } + + private struct DelegatorInfo + { + public DelegatorInfo() + { + } + + public DelegatorInfo(Random random) + { + Balance = GetRandomNCG(random); + Cash = GetRandomCash(random, Balance); + } + + public PrivateKey Key { get; set; } = new PrivateKey(); + + public FungibleAssetValue Cash { get; set; } = NCG * 10; + + public FungibleAssetValue Balance { get; set; } = NCG * 100; + } + + private struct StaticFixture : IDelegateValidatorFixture + { + public DelegateValidator DelegateValidator { get; set; } + + public ValidatorInfo ValidatorInfo { get; set; } + + public DelegatorInfo[] DelegatorInfos { get; set; } + } + + private class RandomFixture : IDelegateValidatorFixture + { + private readonly Random _random; + + public RandomFixture(int randomSeed) + { + _random = new Random(randomSeed); + ValidatorInfo = new ValidatorInfo(_random); + DelegatorInfos = Enumerable.Range(0, _random.Next(1, 10)) + .Select(_ => new DelegatorInfo(_random)) + .ToArray(); + } + + public ValidatorInfo ValidatorInfo { get; } + + public DelegatorInfo[] DelegatorInfos { get; } + } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 190b600eab..0958a18c5c 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -1,8 +1,12 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System; +using System.Collections.Generic; +using System.Linq; using System.Numerics; +using Libplanet.Action.State; using Libplanet.Crypto; +using Libplanet.Types.Assets; using Nekoyume.Action; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.ValidatorDelegation; @@ -10,6 +14,23 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; public class UndelegateValidatorTest : ValidatorDelegationTestBase { + private interface IUndelegateValidatorFixture + { + ValidatorInfo ValidatorInfo { get; } + + DelegatorInfo[] DelegatorInfos { get; } + } + + public static IEnumerable RandomSeeds => new List + { + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + }; + [Fact] public void Serialization() { @@ -63,6 +84,52 @@ public void Execute() Assert.Equal(BigInteger.Zero, actualBond.Share); } + [Theory] + [InlineData(2)] + [InlineData(3)] + [InlineData(9)] + public void Execute_Theory(int delegatorCount) + { + var fixture = new StaticFixture + { + ValidatorInfo = new ValidatorInfo + { + Key = new PrivateKey(), + Cash = NCG * 10, + Balance = NCG * 100, + SubtractShare = 10, + }, + DelegatorInfos = Enumerable.Range(0, delegatorCount) + .Select(_ => new DelegatorInfo + { + Key = new PrivateKey(), + Cash = NCG * 20, + Balance = NCG * 100, + SubtractShare = 20, + }) + .ToArray(), + }; + ExecuteWithFixture(fixture); + } + + [Theory] + [InlineData(0)] + [InlineData(1181126949)] + [InlineData(793705868)] + public void Execute_Theory_WithStaticSeed(int randomSeed) + { + var fixture = new RandomFixture(randomSeed); + ExecuteWithFixture(fixture); + } + + [Theory] + [MemberData(nameof(RandomSeeds))] + public void Execute_Theory_WithRandomSeed(int randomSeed) + { + var fixture = new RandomFixture(randomSeed); + ExecuteWithFixture(fixture); + } + [Fact] public void Execute_FromInvalidValidtor_Throw() { @@ -258,4 +325,140 @@ public void Execute_CannotBeJailedDueToDelegatorUndelegating() Assert.False(actualJailed); Assert.Equal(expectedJailed, actualJailed); } + + private void ExecuteWithFixture(IUndelegateValidatorFixture fixture) + { + // Given + var world = World; + var validatorKey = fixture.ValidatorInfo.Key; + var height = 1L; + var validatorCash = fixture.ValidatorInfo.Cash; + var validatorBalance = fixture.ValidatorInfo.Balance; + var delegatorKeys = fixture.DelegatorInfos.Select(i => i.Key).ToArray(); + var delegatorCashes = fixture.DelegatorInfos.Select(i => i.Cash).ToArray(); + var delegatorBalances = fixture.DelegatorInfos.Select(i => i.Balance).ToArray(); + var delegatorSubtractShares = fixture.DelegatorInfos.Select(i => i.SubtractShare).ToArray(); + var actionContext = new ActionContext { }; + world = EnsureToMintAsset(world, validatorKey, validatorBalance, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorCash, height++); + world = EnsureToMintAssets(world, delegatorKeys, delegatorBalances, height++); + world = EnsureBondedDelegators( + world, delegatorKeys, validatorKey, delegatorCashes, height++); + + // When + var expectedRepository = new ValidatorRepository(world, new ActionContext()); + var expectedValidator = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedValidatorBalance = validatorBalance - validatorCash; + var expectedDelegatorBalances = delegatorKeys + .Select(k => world.GetBalance(k.Address, NCG)).ToArray(); + var expectedShares = delegatorCashes + .Select((c, i) => c.RawValue - delegatorSubtractShares[i]).ToArray(); + var expectedPower = expectedValidator.Power - delegatorSubtractShares.Aggregate( + BigInteger.Zero, (a, b) => a + b); + + for (var i = 0; i < delegatorKeys.Length; i++) + { + var subtractShare = fixture.DelegatorInfos[i].SubtractShare; + var undelegateValidator = new UndelegateValidator( + validatorKey.Address, subtractShare); + actionContext = new ActionContext + { + PreviousState = world, + Signer = delegatorKeys[i].Address, + BlockIndex = height++, + }; + world = undelegateValidator.Execute(actionContext); + } + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualValidator = actualRepository.GetValidatorDelegatee(validatorKey.Address); + var actualValidatorBalance = world.GetBalance(validatorKey.Address, NCG); + var actualDelegatorBalances = delegatorKeys + .Select(k => world.GetBalance(k.Address, NCG)).ToArray(); + var actualPower = actualValidator.Power; + + for (var i = 0; i < delegatorKeys.Length; i++) + { + var actualBond = actualRepository.GetBond(actualValidator, delegatorKeys[i].Address); + Assert.Contains(delegatorKeys[i].Address, actualValidator.Delegators); + Assert.Equal(expectedShares[i], actualBond.Share); + Assert.Equal(expectedDelegatorBalances[i], actualDelegatorBalances[i]); + } + + Assert.Equal(expectedValidatorBalance, actualValidatorBalance); + Assert.Equal(expectedPower, actualPower); + Assert.Equal(expectedDelegatorBalances, actualDelegatorBalances); + } + + private struct ValidatorInfo + { + public ValidatorInfo() + { + } + + public ValidatorInfo(Random random) + { + Balance = GetRandomNCG(random); + Cash = GetRandomCash(random, Balance); + SubtractShare = GetRandomCash(random, Cash).RawValue; + } + + public PrivateKey Key { get; set; } = new PrivateKey(); + + public FungibleAssetValue Cash { get; set; } = NCG * 10; + + public FungibleAssetValue Balance { get; set; } = NCG * 100; + + public BigInteger SubtractShare { get; set; } = 100; + } + + private struct DelegatorInfo + { + public DelegatorInfo() + { + } + + public DelegatorInfo(Random random) + { + Balance = GetRandomNCG(random); + Cash = GetRandomCash(random, Balance); + SubtractShare = GetRandomCash(random, Cash).RawValue; + } + + public PrivateKey Key { get; set; } = new PrivateKey(); + + public FungibleAssetValue Cash { get; set; } = NCG * 10; + + public FungibleAssetValue Balance { get; set; } = NCG * 100; + + public BigInteger SubtractShare { get; set; } = 100; + } + + private struct StaticFixture : IUndelegateValidatorFixture + { + public DelegateValidator DelegateValidator { get; set; } + + public ValidatorInfo ValidatorInfo { get; set; } + + public DelegatorInfo[] DelegatorInfos { get; set; } + } + + private class RandomFixture : IUndelegateValidatorFixture + { + private readonly Random _random; + + public RandomFixture(int randomSeed) + { + _random = new Random(randomSeed); + ValidatorInfo = new ValidatorInfo(_random); + DelegatorInfos = Enumerable.Range(0, _random.Next(1, 10)) + .Select(_ => new DelegatorInfo(_random)) + .ToArray(); + } + + public ValidatorInfo ValidatorInfo { get; } + + public DelegatorInfo[] DelegatorInfos { get; } + } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index a0b305822d..021d64b8b6 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -39,6 +39,8 @@ public ValidatorDelegationTestBase() protected IWorld World { get; } + protected FungibleAssetValue MinimumDelegation { get; } = NCG * 10; + protected static T[] CreateArray(int length, Func creator) => Enumerable.Range(0, length).Select(creator).ToArray(); From 147059f5d918534faaa47b9a25802a7ed2035cc4 Mon Sep 17 00:00:00 2001 From: s2quake Date: Wed, 2 Oct 2024 17:50:44 +0900 Subject: [PATCH 069/206] test: Add test for redelegation with random value --- .../RedelegateValidatorTest.cs | 228 ++++++++++++++++++ .../UndelegateValidatorTest.cs | 2 - 2 files changed, 228 insertions(+), 2 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs index 8bed3e840d..84b8a197e4 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs @@ -1,8 +1,12 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System; +using System.Collections.Generic; +using System.Linq; using System.Numerics; +using Libplanet.Action.State; using Libplanet.Crypto; +using Libplanet.Types.Assets; using Nekoyume.Action; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.ValidatorDelegation; @@ -10,6 +14,25 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; public class RedelegateValidatorTest : ValidatorDelegationTestBase { + private interface IRedelegateValidatorFixture + { + ValidatorInfo ValidatorInfo1 { get; } + + ValidatorInfo ValidatorInfo2 { get; } + + DelegatorInfo[] DelegatorInfos { get; } + } + + public static IEnumerable RandomSeeds => new List + { + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + new object[] { Random.Shared.Next() }, + }; + [Fact] public void Serialization() { @@ -70,6 +93,57 @@ public void Execute() Assert.Equal((NCG * 20).RawValue, actualDstValidator.Power); } + [Theory] + [InlineData(2)] + [InlineData(3)] + [InlineData(9)] + public void Execute_Theory(int delegatorCount) + { + var fixture = new StaticFixture + { + ValidatorInfo1 = new ValidatorInfo + { + Key = new PrivateKey(), + Cash = NCG * 10, + Balance = NCG * 100, + }, + ValidatorInfo2 = new ValidatorInfo + { + Key = new PrivateKey(), + Cash = NCG * 10, + Balance = NCG * 100, + }, + DelegatorInfos = Enumerable.Range(0, delegatorCount) + .Select(_ => new DelegatorInfo + { + Key = new PrivateKey(), + Cash = NCG * 20, + Balance = NCG * 100, + Redelegating = 20, + }) + .ToArray(), + }; + ExecuteWithFixture(fixture); + } + + [Theory] + [InlineData(0)] + [InlineData(1181126949)] + [InlineData(793705868)] + public void Execute_Theory_WithStaticSeed(int randomSeed) + { + var fixture = new RandomFixture(randomSeed); + ExecuteWithFixture(fixture); + } + + [Theory] + [MemberData(nameof(RandomSeeds))] + public void Execute_Theory_WithRandomSeed(int randomSeed) + { + var fixture = new RandomFixture(randomSeed); + ExecuteWithFixture(fixture); + } + [Fact] public void Execute_ToInvalidValidator_Throw() { @@ -320,4 +394,158 @@ public void Execute_CannotBeJailedDueToDelegatorRedelegating() Assert.False(actualJailed); Assert.Equal(expectedJailed, actualJailed); } + + private void ExecuteWithFixture(IRedelegateValidatorFixture fixture) + { + // Given + var world = World; + var validatorKey1 = fixture.ValidatorInfo1.Key; + var validatorKey2 = fixture.ValidatorInfo2.Key; + var height = 1L; + var validatorCash1 = fixture.ValidatorInfo1.Cash; + var validatorBalance1 = fixture.ValidatorInfo1.Balance; + var validatorCash2 = fixture.ValidatorInfo2.Cash; + var validatorBalance2 = fixture.ValidatorInfo2.Balance; + var delegatorKeys = fixture.DelegatorInfos.Select(i => i.Key).ToArray(); + var delegatorCashes = fixture.DelegatorInfos.Select(i => i.Cash).ToArray(); + var delegatorBalances = fixture.DelegatorInfos.Select(i => i.Balance).ToArray(); + var delegatorRedelegatings = fixture.DelegatorInfos.Select(i => i.Redelegating).ToArray(); + var actionContext = new ActionContext { }; + world = EnsureToMintAsset(world, validatorKey1, validatorBalance1, height++); + world = EnsurePromotedValidator(world, validatorKey1, validatorCash1, height++); + world = EnsureToMintAsset(world, validatorKey2, validatorBalance2, height++); + world = EnsurePromotedValidator(world, validatorKey2, validatorCash2, height++); + world = EnsureToMintAssets(world, delegatorKeys, delegatorBalances, height++); + world = EnsureBondedDelegators( + world, delegatorKeys, validatorKey1, delegatorCashes, height++); + + // When + var expectedRepository = new ValidatorRepository(world, new ActionContext()); + var expectedValidator1 = expectedRepository.GetValidatorDelegatee(validatorKey1.Address); + var expectedValidator2 = expectedRepository.GetValidatorDelegatee(validatorKey2.Address); + var expectedValidatorBalance1 = validatorBalance1 - validatorCash1; + var expectedValidatorBalance2 = validatorBalance2 - validatorCash2; + var expectedDelegatorBalances = delegatorKeys + .Select(k => world.GetBalance(k.Address, NCG)).ToArray(); + var expectedShares1 = delegatorCashes + .Select((c, i) => c.RawValue - delegatorRedelegatings[i]).ToArray(); + var expectedShares2 = delegatorRedelegatings; + var expectedPower1 = expectedValidator1.Power - delegatorRedelegatings.Aggregate( + BigInteger.Zero, (a, b) => a + b); + var expectedPower2 = expectedValidator2.Power + delegatorRedelegatings.Aggregate( + BigInteger.Zero, (a, b) => a + b); + + for (var i = 0; i < delegatorKeys.Length; i++) + { + var redelegating = fixture.DelegatorInfos[i].Redelegating; + var redelegateValidator = new RedelegateValidator( + validatorKey1.Address, validatorKey2.Address, redelegating); + actionContext = new ActionContext + { + PreviousState = world, + Signer = delegatorKeys[i].Address, + BlockIndex = height++, + }; + world = redelegateValidator.Execute(actionContext); + } + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualValidator1 = actualRepository.GetValidatorDelegatee(validatorKey1.Address); + var actualValidator2 = actualRepository.GetValidatorDelegatee(validatorKey2.Address); + var actualValidatorBalance1 = world.GetBalance(validatorKey1.Address, NCG); + var actualValidatorBalance2 = world.GetBalance(validatorKey2.Address, NCG); + var actualDelegatorBalances = delegatorKeys + .Select(k => world.GetBalance(k.Address, NCG)).ToArray(); + var actualPower1 = actualValidator1.Power; + var actualPower2 = actualValidator2.Power; + + for (var i = 0; i < delegatorKeys.Length; i++) + { + var actualBond1 = actualRepository.GetBond(actualValidator1, delegatorKeys[i].Address); + var actualBond2 = actualRepository.GetBond(actualValidator2, delegatorKeys[i].Address); + Assert.Contains(delegatorKeys[i].Address, actualValidator1.Delegators); + Assert.Contains(delegatorKeys[i].Address, actualValidator2.Delegators); + Assert.Equal(expectedShares1[i], actualBond1.Share); + Assert.Equal(expectedShares2[i], actualBond2.Share); + Assert.Equal(expectedDelegatorBalances[i], actualDelegatorBalances[i]); + } + + Assert.Equal(expectedValidatorBalance1, actualValidatorBalance1); + Assert.Equal(expectedValidatorBalance2, actualValidatorBalance2); + Assert.Equal(expectedPower1, actualPower1); + Assert.Equal(expectedPower2, actualPower2); + Assert.Equal(expectedDelegatorBalances, actualDelegatorBalances); + } + + private struct ValidatorInfo + { + public ValidatorInfo() + { + } + + public ValidatorInfo(Random random) + { + Balance = GetRandomNCG(random); + Cash = GetRandomCash(random, Balance); + } + + public PrivateKey Key { get; set; } = new PrivateKey(); + + public FungibleAssetValue Cash { get; set; } = NCG * 10; + + public FungibleAssetValue Balance { get; set; } = NCG * 100; + } + + private struct DelegatorInfo + { + public DelegatorInfo() + { + } + + public DelegatorInfo(Random random) + { + Balance = GetRandomNCG(random); + Cash = GetRandomCash(random, Balance); + Redelegating = GetRandomCash(random, Cash).RawValue; + } + + public PrivateKey Key { get; set; } = new PrivateKey(); + + public FungibleAssetValue Cash { get; set; } = NCG * 10; + + public FungibleAssetValue Balance { get; set; } = NCG * 100; + + public BigInteger Redelegating { get; set; } = 100; + } + + private struct StaticFixture : IRedelegateValidatorFixture + { + public ValidatorInfo ValidatorInfo1 { get; set; } + + public ValidatorInfo ValidatorInfo2 { get; set; } + + public DelegatorInfo[] DelegatorInfos { get; set; } + } + + private class RandomFixture : IRedelegateValidatorFixture + { + private readonly Random _random; + + public RandomFixture(int randomSeed) + { + _random = new Random(randomSeed); + ValidatorInfo1 = new ValidatorInfo(_random); + ValidatorInfo2 = new ValidatorInfo(_random); + DelegatorInfos = Enumerable.Range(0, _random.Next(1, 10)) + .Select(_ => new DelegatorInfo(_random)) + .ToArray(); + } + + public ValidatorInfo ValidatorInfo1 { get; } + + public ValidatorInfo ValidatorInfo2 { get; } + + public DelegatorInfo[] DelegatorInfos { get; } + } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 0958a18c5c..3359c31601 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -437,8 +437,6 @@ public DelegatorInfo(Random random) private struct StaticFixture : IUndelegateValidatorFixture { - public DelegateValidator DelegateValidator { get; set; } - public ValidatorInfo ValidatorInfo { get; set; } public DelegatorInfo[] DelegatorInfos { get; set; } From 272bdada2419cead64dbe1726063127a15a55c29 Mon Sep 17 00:00:00 2001 From: s2quake Date: Mon, 7 Oct 2024 10:02:50 +0900 Subject: [PATCH 070/206] fix: Fix test failures --- .../DelegateValidatorTest.cs | 8 +++- .../RedelegateValidatorTest.cs | 12 +++-- .../UndelegateValidatorTest.cs | 46 +++++++++++++++++-- .../UnjailValidatorTest.cs | 32 ++++++++++++- 4 files changed, 87 insertions(+), 11 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index 42124f2fde..72b9a53db3 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -234,14 +234,18 @@ public void Execute_CannotBeJailedDueToDelegatorDelegating() var world = World; var validatorKey = new PrivateKey(); var delegatorKey = new PrivateKey(); - var validatorGold = NCG * 10; + var validatorCash = NCG * 10; + var validatorGold = NCG * 100; var delegatorGold = NCG * 10; var delegatorBalance = NCG * 100; var actionContext = new ActionContext { }; var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); - world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorCash, height++); + world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, 10, height++); + world = EnsureBondedDelegator(world, validatorKey, validatorKey, validatorCash, height++); + world = EnsureToMintAsset(world, delegatorKey, delegatorBalance, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey, delegatorGold, height++); world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, 10, height++); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs index 84b8a197e4..1460481e15 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs @@ -240,7 +240,7 @@ public void Execute_WithOverShare_Throw() } [Fact] - public void Execute_ToJailedValidator() + public void Execute_FromJailedValidator_ToNotJailedValidator() { // Given var world = World; @@ -281,7 +281,7 @@ public void Execute_ToJailedValidator() var actualBond2 = actualRepository.GetBond(actualDelegatee2, delegatorKey.Address); Assert.Equal(expectedBond1.Share - 10, actualBond1.Share); - Assert.Equal(expectedBond2.Share + 10, actualBond2.Share); + Assert.Equal(expectedBond2.Share + 9, actualBond2.Share); } [Fact] @@ -353,14 +353,18 @@ public void Execute_CannotBeJailedDueToDelegatorRedelegating() var validatorKey1 = new PrivateKey(); var validatorKey2 = new PrivateKey(); var delegatorKey = new PrivateKey(); - var validatorGold = NCG * 10; + var validatorCash = NCG * 10; + var validatorGold = NCG * 100; var delegatorGold = NCG * 10; var delegatorBalance = NCG * 100; var actionContext = new ActionContext { }; var height = 1L; world = EnsureToMintAsset(world, validatorKey1, validatorGold, height++); - world = EnsurePromotedValidator(world, validatorKey1, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey1, validatorCash, height++); + world = EnsureUnbondingDelegator(world, validatorKey1, validatorKey1, 10, height++); + world = EnsureBondedDelegator(world, validatorKey1, validatorKey1, validatorCash, height++); + world = EnsureToMintAsset(world, validatorKey2, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey2, validatorGold, height++); world = EnsureToMintAsset(world, delegatorKey, delegatorBalance, height++); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 3359c31601..4f5e779b78 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -291,13 +291,16 @@ public void Execute_CannotBeJailedDueToDelegatorUndelegating() var world = World; var validatorKey = new PrivateKey(); var delegatorKey = new PrivateKey(); - var validatorGold = NCG * 10; + var validatorCash = NCG * 10; + var validatorGold = NCG * 100; var delegatorGold = NCG * 10; var actionContext = new ActionContext { }; - var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); - world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorCash, height++); + world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, 10, height++); + world = EnsureBondedDelegator(world, validatorKey, validatorKey, validatorCash, height++); + world = EnsureToMintAsset(world, delegatorKey, delegatorGold, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey, delegatorGold, height++); world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, 10, height++); @@ -326,6 +329,43 @@ public void Execute_CannotBeJailedDueToDelegatorUndelegating() Assert.Equal(expectedJailed, actualJailed); } + [Fact] + public void Execute_JailsValidatorWhenUndelegationCausesLowDelegation() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var validatorCash = MinimumDelegation; + var validatorGold = MinimumDelegation; + var actionContext = new ActionContext { }; + var height = 1L; + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorCash, height++); + + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedJailed = expectedDelegatee.Jailed; + + var undelegateValidator = new UndelegateValidator(validatorKey.Address, 10); + actionContext = new ActionContext + { + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height, + }; + world = undelegateValidator.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); + var actualJailed = actualDelegatee.Jailed; + var actualJailedUntil = actualDelegatee.JailedUntil; + + Assert.True(actualJailed); + Assert.NotEqual(expectedJailed, actualJailed); + } + private void ExecuteWithFixture(IUndelegateValidatorFixture fixture) { // Given diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs index ca7f3a0a51..8adbaba247 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using Nekoyume.Action; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.ValidatorDelegation; +using Org.BouncyCastle.Bcpg.OpenPgp; using Xunit; public class UnjailValidatorTest : ValidatorDelegationTestBase @@ -27,8 +28,9 @@ public void Execute() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + var validatorGold = NCG * 100; + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); world = EnsureJailedValidator(world, validatorKey, ref height); // When @@ -142,4 +144,30 @@ public void Execute_OnTombstonedValidator_Throw() Assert.Throws( () => unjailValidator.Execute(actionContext)); } + + [Fact] + public void Execute_OnLowDelegatedValidator_Throw() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var height = 1L; + var validatorGold = MinimumDelegation; + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, 10, height); + + // When + var unjailValidator = new UnjailValidator(); + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = height++, + Signer = validatorKey.Address, + }; + + // Then + Assert.Throws( + () => unjailValidator.Execute(actionContext)); + } } From 7f1af8560417104c649502506f70080e057455c2 Mon Sep 17 00:00:00 2001 From: s2quake Date: Mon, 7 Oct 2024 10:36:52 +0900 Subject: [PATCH 071/206] chore: Remove unused test code --- .../ValidatorDelegation/AllocateRewardTest.cs | 222 ------------------ 1 file changed, 222 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs index 23c1d73857..9f3e106527 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -170,228 +170,6 @@ private static ImmutableArray CreateVotes( return voteList.ToImmutableArray(); } - private void ExecuteWithStaticVariable(int validatorCount, FungibleAssetValue totalReward) - { - if (validatorCount <= 0) - { - throw new ArgumentOutOfRangeException(nameof(validatorCount)); - } - - if (totalReward.Sign <= 0) - { - throw new ArgumentOutOfRangeException(nameof(totalReward)); - } - - // Given - var length = validatorCount; - var world = World; - var actionContext = new ActionContext { }; - var validatorInfos = CreateArray(length, i => new ValidatorInfo - { - Key = new PrivateKey(), - Cash = NCG * (i + 1), - Balance = NCG * 1000, - VoteFlag = i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null, - }); - var validatorKeys = validatorInfos.Select(info => info.Key).ToArray(); - var validatorPromotes = validatorInfos.Select(info => info.Cash).ToArray(); - var validatorBalances = validatorInfos.Select(info => info.Balance).ToArray(); - var height = 1L; - var proposerKey = validatorInfos - .Where(item => item.VoteFlag == VoteFlag.PreCommit) - .Take(ValidatorList.MaxBondedSetSize) - .First() - .Key; - - world = EnsureToMintAssets(world, validatorKeys, validatorBalances, height++); - world = EnsurePromotedValidators(world, validatorKeys, validatorPromotes, height++); - world = EnsureProposer(world, proposerKey, height++); - world = world.MintAsset(actionContext, Addresses.RewardPool, totalReward); - - // Calculate expected values for comparison with actual values. - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedBondedSet = expectedRepository.GetValidatorList().GetBonded(); - var expectedProposer = proposerKey; - var votes = CreateVotes(validatorInfos, expectedBondedSet, height - 1); - var balances = votes - .Select(vote => world.GetBalance(vote.ValidatorPublicKey.Address, NCG)).ToArray(); - var expectedProposerReward - = CalculatePropserReward(totalReward) + CalculateBonusPropserReward(votes, totalReward); - var expectedValidatorsReward = totalReward - expectedProposerReward; - var expectedCommunityFund = CalculateCommunityFund(votes, expectedValidatorsReward); - var expectedAllocatedReward = expectedValidatorsReward - expectedCommunityFund; - - // When - var lastCommit = new BlockCommit(height - 1, round: 0, votes[0].BlockHash, votes); - var allocateReward = new AllocateReward(); - actionContext = new ActionContext - { - PreviousState = world, - Signer = expectedProposer.PublicKey.Address, - LastCommit = lastCommit, - BlockIndex = height++, - }; - world = allocateReward.Execute(actionContext); - - // Then - var totalPower = votes.Select(item => item.ValidatorPower) - .OfType() - .Aggregate(BigInteger.Zero, (accum, next) => accum + next); - var actualRepository = new ValidatorRepository(world, actionContext); - var actualAllocatedReward = NCG * 0; - var actualCommunityFund = world.GetBalance(Addresses.CommunityPool, NCG); - foreach (var (vote, index) in votes.Select((v, i) => (v, i))) - { - if (vote.ValidatorPower is not { } validatorPower) - { - throw new InvalidOperationException("ValidatorPower cannot be null."); - } - - var validatorAddress = vote.ValidatorPublicKey.Address; - var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorAddress); - var validatorRewardAddress = actualDelegatee.CurrentLumpSumRewardsRecordAddress(); - var balance = balances[index]; - var actualBalance = world.GetBalance(validatorAddress, NCG); - var actualReward = world.GetBalance(validatorRewardAddress, NCG); - var isProposer = vote.ValidatorPublicKey.Equals(expectedProposer.PublicKey); - - if (vote.Flag == VoteFlag.Null) - { - Assert.Equal(balance, actualBalance); - Assert.Equal(NCG * 0, actualReward); - Assert.False(isProposer); - continue; - } - - var reward = (expectedValidatorsReward * validatorPower).DivRem(totalPower).Quotient; - var expectedCommission = CalculateCommission(reward, actualDelegatee); - var expectedBalance = isProposer - ? expectedCommission + balance + expectedProposerReward - : expectedCommission + balance; - var expectedReward = reward - expectedCommission; - - Assert.Equal(expectedBalance, actualBalance); - Assert.Equal(expectedReward, actualReward); - - actualAllocatedReward += expectedCommission + expectedReward; - } - - Assert.Equal(expectedAllocatedReward, actualAllocatedReward); - Assert.Equal(expectedCommunityFund, actualCommunityFund); - } - - private void ExecuteWithRandomVariable(int randomSeed) - { - // Given - var random = new Random(randomSeed); - var length = random.Next(1, 200); - var totalReward = GetRandomNCG(random); - var world = World; - var actionContext = new ActionContext { }; - var voteCount = 0; - var validatorInfos = CreateArray(length, i => - { - var promote = GetRandomNCG(random); - var flag = voteCount < 1 || random.Next() % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null; - voteCount += flag == VoteFlag.PreCommit ? 1 : 0; - return new ValidatorInfo - { - Key = new PrivateKey(), - Cash = promote, - Balance = promote + GetRandomNCG(random), - VoteFlag = flag, - }; - }); - var validatorKeys = validatorInfos.Select(info => info.Key).ToArray(); - var validatorPromotes = validatorInfos.Select(info => info.Cash).ToArray(); - var validatorBalances = validatorInfos.Select(info => info.Balance).ToArray(); - var height = 1L; - var proposerKey = validatorInfos - .Where(item => item.VoteFlag == VoteFlag.PreCommit) - .Take(ValidatorList.MaxBondedSetSize) - .First() - .Key; - - world = EnsureToMintAssets(world, validatorKeys, validatorBalances, height++); - world = EnsurePromotedValidators(world, validatorKeys, validatorPromotes, height++); - world = EnsureProposer(world, proposerKey, height++); - world = world.MintAsset(actionContext, Addresses.RewardPool, totalReward); - - // Calculate expected values for comparison with actual values. - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedBondedSet = expectedRepository.GetValidatorList().GetBonded(); - var expectedProposer = proposerKey; - var votes = CreateVotes(validatorInfos, expectedBondedSet, height - 1); - var balances = votes - .Select(vote => world.GetBalance(vote.ValidatorPublicKey.Address, NCG)).ToArray(); - var expectedProposerReward - = CalculatePropserReward(totalReward) + CalculateBonusPropserReward(votes, totalReward); - var expectedValidatorsReward = totalReward - expectedProposerReward; - var expectedCommunityFund = CalculateCommunityFund(votes, expectedValidatorsReward); - var expectedAllocatedReward = expectedValidatorsReward - expectedCommunityFund; - - // When - var lastCommit = new BlockCommit(height - 1, round: 0, votes[0].BlockHash, votes); - var allocateReward = new AllocateReward(); - actionContext = new ActionContext - { - PreviousState = world, - Signer = expectedProposer.PublicKey.Address, - LastCommit = lastCommit, - BlockIndex = height++, - }; - world = allocateReward.Execute(actionContext); - - // Then - var totalPower = votes.Select(item => item.ValidatorPower) - .OfType() - .Aggregate(BigInteger.Zero, (accum, next) => accum + next); - var actualRepository = new ValidatorRepository(world, actionContext); - var actualAllocatedReward = NCG * 0; - var actualCommunityFund = world.GetBalance(Addresses.CommunityPool, NCG); - foreach (var (vote, index) in votes.Select((v, i) => (v, i))) - { - if (vote.ValidatorPower is not { } validatorPower) - { - throw new InvalidOperationException("ValidatorPower cannot be null."); - } - - var validatorAddress = vote.ValidatorPublicKey.Address; - var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorAddress); - var validatorRewardAddress = actualDelegatee.CurrentLumpSumRewardsRecordAddress(); - var balance = balances[index]; - var actualBalance = world.GetBalance(validatorAddress, NCG); - var actualReward = world.GetBalance(validatorRewardAddress, NCG); - var isProposer = vote.ValidatorPublicKey.Equals(expectedProposer.PublicKey); - - if (vote.Flag == VoteFlag.Null) - { - Assert.Equal(balance, actualBalance); - Assert.Equal(NCG * 0, actualReward); - Assert.False(isProposer); - continue; - } - - var reward = (expectedValidatorsReward * validatorPower).DivRem(totalPower).Quotient; - var expectedCommission = CalculateCommission(reward, actualDelegatee); - var expectedBalance = isProposer - ? expectedCommission + balance + expectedProposerReward - : expectedCommission + balance; - var expectedReward = reward - expectedCommission; - - Assert.Equal(expectedBalance, actualBalance); - Assert.Equal(expectedReward, actualReward); - - Assert.Equal(expectedBalance, actualBalance); - Assert.Equal(expectedReward, actualReward); - - actualAllocatedReward += expectedCommission + expectedReward; - } - - Assert.Equal(expectedAllocatedReward, actualAllocatedReward); - Assert.Equal(expectedCommunityFund, actualCommunityFund); - } - private void ExecuteWithFixture(IAllocateRewardFixture fixture) { // Given From 08115cce4342d9d92c8fff8fb0313a0c0dc0bd47 Mon Sep 17 00:00:00 2001 From: s2quake Date: Mon, 7 Oct 2024 11:28:28 +0900 Subject: [PATCH 072/206] test: Add test code to verify that tombstoned validator is excluded from validator list --- .../UpdateValidatorsTest.cs | 78 +++++++++++++------ 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs index 813731c693..9ab953629a 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs @@ -25,37 +25,29 @@ public void Serialization() [Fact] public void Execute() { + // Given const int length = 10; var world = World; - var privateKeys = CreateArray(length, _ => new PrivateKey()); - var golds = CreateArray(length, i => NCG * Random.Shared.Next(1, length + 1)); + var validatorKeys = CreateArray(length, _ => new PrivateKey()); + var validatorGolds = CreateArray(length, i => NCG * Random.Shared.Next(1, length + 1)); + var height = 1L; + var actionContext = new ActionContext { }; + world = EnsureToMintAssets(world, validatorKeys, validatorGolds, height++); + world = EnsurePromotedValidators(world, validatorKeys, validatorGolds, height); - for (int i = 0; i < length; i++) - { - var signer = privateKeys[i]; - var gold = golds[i]; - var promoteValidator = new PromoteValidator(signer.PublicKey, gold); - var actionContext = new ActionContext - { - PreviousState = world.MintAsset(new ActionContext(), signer.Address, NCG * 1000), - Signer = signer.Address, - BlockIndex = 10L, - }; - world = promoteValidator.Execute(actionContext); - } - - var blockActionContext = new ActionContext + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedValidators = expectedRepository.GetValidatorList() + .GetBonded().OrderBy(item => item.OperatorAddress).ToList(); + actionContext = new ActionContext { - BlockIndex = 10L, + BlockIndex = height, PreviousState = world, Signer = AdminKey.Address, }; - var expectedRepository = new ValidatorRepository(world, blockActionContext); - var expectedValidators = expectedRepository.GetValidatorList() - .GetBonded().OrderBy(item => item.OperatorAddress).ToList(); - - world = new UpdateValidators().Execute(blockActionContext); + world = new UpdateValidators().Execute(actionContext); + // Then var actualValidators = world.GetValidatorSet().Validators; Assert.Equal(expectedValidators.Count, actualValidators.Count); for (var i = 0; i < expectedValidators.Count; i++) @@ -65,4 +57,44 @@ public void Execute() Assert.Equal(expectedValidator, actualValidator); } } + + [Fact] + public void Execute_ExcludesTombstonedValidator() + { + // Given + const int length = 10; + var world = World; + var validatorKeys = CreateArray(length, _ => new PrivateKey()); + var validatorGolds = CreateArray(length, i => NCG * 100); + var height = 1L; + var validatorGold = NCG * 100; + var actionContext = new ActionContext { }; + world = EnsureToMintAssets(world, validatorKeys, validatorGolds, height++); + world = EnsurePromotedValidators(world, validatorKeys, validatorGolds, height++); + + // When + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedValidators = expectedRepository.GetValidatorList() + .GetBonded().OrderBy(item => item.OperatorAddress).ToList(); + var updateValidators = new UpdateValidators(); + world = EnsureTombstonedValidator(world, validatorKeys[0], height); + actionContext = new ActionContext + { + BlockIndex = height, + PreviousState = world, + Signer = AdminKey.Address, + }; + world = updateValidators.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualValidators = actualRepository.GetValidatorList() + .GetBonded().OrderBy(item => item.OperatorAddress).ToList(); + var tombstonedValidator = actualRepository.GetValidatorDelegatee(validatorKeys[0].Address); + + Assert.True(tombstonedValidator.Tombstoned); + Assert.Equal(expectedValidators.Count - 1, actualValidators.Count); + Assert.Contains(expectedValidators, v => v.PublicKey == validatorKeys[0].PublicKey); + Assert.DoesNotContain(actualValidators, v => v.PublicKey == validatorKeys[0].PublicKey); + } } From af36106d6e1029c7e1e0e3ea813affbe7dabc618 Mon Sep 17 00:00:00 2001 From: s2quake Date: Mon, 7 Oct 2024 13:47:27 +0900 Subject: [PATCH 073/206] test: Add test for SetValidatorCommission --- .../ValidatorDelegation/ConstantTest.cs | 2 + .../SetValidatorCommissionTest.cs | 240 ++++++++++++++++-- .../ValidatorDelegationTestBase.cs | 19 ++ 3 files changed, 235 insertions(+), 26 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ConstantTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ConstantTest.cs index b0269dec6c..95039f7586 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ConstantTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ConstantTest.cs @@ -10,5 +10,7 @@ public class ConstantTest public void StaticPropertyTest() { Assert.True(ValidatorDelegatee.ValidatorUnbondingPeriod > 0); + Assert.True(ValidatorDelegatee.MaxCommissionPercentage < int.MaxValue); + Assert.True(ValidatorDelegatee.MaxCommissionPercentage >= 0); } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs index b4281a3bed..a37ab75122 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs @@ -1,6 +1,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation { using System; + using System.Collections.Generic; using System.Linq; using System.Numerics; using Libplanet.Action.State; @@ -14,8 +15,44 @@ namespace Lib9c.Tests.Action.ValidatorDelegation using Nekoyume.ValidatorDelegation; using Xunit; - public class SetValidatorCommissionTest + public class SetValidatorCommissionTest : ValidatorDelegationTestBase { + /// + /// Tested that ValidatorDelegatee.MaxCommissionPercentage is less than int.MaxValue + /// in ConstantTest.cs file. + /// + private static readonly int MaxCommissionPercentage + = (int)ValidatorDelegatee.MaxCommissionPercentage; + + private static readonly int CommissionPercentageChangePeriod + = 10; + + public static IEnumerable RandomCommisionPercentage => new List + { + new object[] { Random.Shared.Next(MaxCommissionPercentage) }, + new object[] { Random.Shared.Next(MaxCommissionPercentage) }, + new object[] { Random.Shared.Next(MaxCommissionPercentage) }, + }; + + public static IEnumerable RandomInvalidCommisionPercentage => new List + { + new object[] { Random.Shared.Next(MaxCommissionPercentage, int.MaxValue) }, + new object[] { Random.Shared.Next(MaxCommissionPercentage, int.MaxValue) }, + new object[] { Random.Shared.Next(MaxCommissionPercentage, int.MaxValue) }, + }; + + public static IEnumerable InvalidCommisionPercentagePeriod => new List + { + new object[] { 0 }, + new object[] { CommissionPercentageChangePeriod - 1 }, + }; + + public static IEnumerable ValidCommisionPercentagePeriod => new List + { + new object[] { CommissionPercentageChangePeriod }, + new object[] { CommissionPercentageChangePeriod + 1 }, + }; + [Fact] public void Serialization() { @@ -33,34 +70,185 @@ public void Serialization() [Fact] public void Execute() { - IWorld world = new World(MockUtil.MockModernWorldState); - var context = new ActionContext { }; - var ncg = Currency.Uncapped("NCG", 2, null); - // TODO: Use Currencies.GuildGold when it's available. - // var gg = Currencies.GuildGold; - var gg = ncg; - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + // Given + var world = World; + var validatorKey = new PrivateKey(); + var validatorGold = NCG * 10; + var height = 1L; + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); - var validatorPublicKey = new PrivateKey().PublicKey; - world = world.MintAsset(context, validatorPublicKey.Address, gg * 100); - var promoteFAV = gg * 10; - world = new PromoteValidator(validatorPublicKey, promoteFAV).Execute(new ActionContext + // When + var setValidatorCommission = new SetValidatorCommission( + validatorKey.Address, commissionPercentage: 11); + var actionContext = new ActionContext { PreviousState = world, - Signer = validatorPublicKey.Address, - }); + Signer = validatorKey.Address, + BlockIndex = height++, + }; + world = setValidatorCommission.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); + var actualPercentage = actualDelegatee.CommissionPercentage; + + Assert.Equal(11, actualPercentage); + } - world = new SetValidatorCommission(validatorPublicKey.Address, 11).Execute(new ActionContext + [Theory] + // [MemberData(nameof(RandomCommisionPercentage))] + [InlineData(9)] + [InlineData(11)] + public void Execute_Theory(int commissionPercentage) + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var validatorGold = NCG * 10; + var height = 1L; + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + + // When + var setValidatorCommission = new SetValidatorCommission( + validatorKey.Address, + commissionPercentage); + var actionContext = new ActionContext { PreviousState = world, - Signer = validatorPublicKey.Address, - }); + Signer = validatorKey.Address, + BlockIndex = height++, + }; + world = setValidatorCommission.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); + var actualPercentage = actualDelegatee.CommissionPercentage; - var repository = new ValidatorRepository(world, context); - var validator = repository.GetValidatorDelegatee(validatorPublicKey.Address); - Assert.Equal(11, validator.CommissionPercentage); + Assert.Equal(commissionPercentage, actualPercentage); + } + + [Theory] + [MemberData(nameof(RandomInvalidCommisionPercentage))] + public void Execute_Theory_WithValueGreaterThanMaximum_Throw(int commissionPercentage) + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var validatorGold = NCG * 10; + var height = 1L; + + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height++, + }; + var setValidatorCommission = new SetValidatorCommission( + validatorKey.Address, + commissionPercentage); + + // Then + Assert.Throws( + () => setValidatorCommission.Execute(actionContext)); + } + + [Theory] + [InlineData(-1)] + [InlineData(-2)] + public void Execute_Theory_WithNegative_Throw(int commissionPercentage) + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var validatorGold = NCG * 10; + var height = 1L; + + world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height++, + }; + var setValidatorCommission = new SetValidatorCommission( + validatorKey.Address, + commissionPercentage); + + // Then + Assert.Throws( + () => setValidatorCommission.Execute(actionContext)); + } + + [Theory] + [MemberData(nameof(InvalidCommisionPercentagePeriod))] + public void Execute_Theory_WithInvalidValue_Throw(int period) + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var validatorGold = NCG * 10; + var height = 1L; + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + world = EnsureCommissionChangedValidator(world, validatorKey, 11, height); + + // When + var actionContext = new ActionContext + { + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height + period, + }; + var setValidatorCommission = new SetValidatorCommission( + validatorKey.Address, commissionPercentage: 12); + + // Then + Assert.Throws( + () => setValidatorCommission.Execute(actionContext)); + } + + [Theory] + [MemberData(nameof(ValidCommisionPercentagePeriod))] + public void Execute_Theory_WitValue(int period) + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var validatorGold = NCG * 10; + var height = 1L; + world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + world = EnsureCommissionChangedValidator(world, validatorKey, 11, height); + + // When + var expectedCommission = 12; + var actionContext = new ActionContext + { + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height + period, + }; + var setValidatorCommission = new SetValidatorCommission( + validatorKey.Address, commissionPercentage: expectedCommission); + world = setValidatorCommission.Execute(actionContext); + + // Then + var actualRepository = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); + var actualPercentage = actualDelegatee.CommissionPercentage; + + Assert.Equal(expectedCommission, actualPercentage); } [Fact] @@ -96,10 +284,10 @@ public void CannotExceedMaxPercentage() Assert.Throws( () => new SetValidatorCommission(validatorPublicKey.Address, 31).Execute(new ActionContext - { - PreviousState = world, - Signer = validatorPublicKey.Address, - })); + { + PreviousState = world, + Signer = validatorPublicKey.Address, + })); } [Fact] diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 021d64b8b6..f6e94e239d 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -351,6 +351,25 @@ protected static IWorld EnsureRewardAllocatedValidator( return world; } + protected static IWorld EnsureCommissionChangedValidator( + IWorld world, PrivateKey validatorKey, BigInteger commissionPercentage, long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } + + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight, + Signer = validatorKey.Address, + }; + var setValidatorCommission = new SetValidatorCommission( + validatorKey.Address, commissionPercentage); + return setValidatorCommission.Execute(actionContext); + } + protected static Vote CreateNullVote( PrivateKey privateKey, long blockHeight) { From 08a117b6dd3d22ec0f65277bf717ca80b69dde16 Mon Sep 17 00:00:00 2001 From: s2quake Date: Mon, 7 Oct 2024 18:07:32 +0900 Subject: [PATCH 074/206] test: Improve SetValidatorCommission test code --- .../SetValidatorCommissionTest.cs | 127 ++++-------------- .../UndelegateValidatorTest.cs | 5 + .../ValidatorDelegationTestBase.cs | 54 ++++++-- 3 files changed, 75 insertions(+), 111 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs index a37ab75122..9dc8348735 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs @@ -2,16 +2,9 @@ namespace Lib9c.Tests.Action.ValidatorDelegation { using System; using System.Collections.Generic; - using System.Linq; using System.Numerics; - using Libplanet.Action.State; using Libplanet.Crypto; - using Libplanet.Mocks; - using Libplanet.Types.Assets; - using Nekoyume; using Nekoyume.Action.ValidatorDelegation; - using Nekoyume.Model.State; - using Nekoyume.Module; using Nekoyume.ValidatorDelegation; using Xunit; @@ -24,8 +17,8 @@ public class SetValidatorCommissionTest : ValidatorDelegationTestBase private static readonly int MaxCommissionPercentage = (int)ValidatorDelegatee.MaxCommissionPercentage; - private static readonly int CommissionPercentageChangePeriod - = 10; + private static readonly long CommissionPercentageChangeCooldown + = ValidatorDelegatee.CommissionPercentageUpdateCooldown; public static IEnumerable RandomCommisionPercentage => new List { @@ -41,16 +34,16 @@ private static readonly int CommissionPercentageChangePeriod new object[] { Random.Shared.Next(MaxCommissionPercentage, int.MaxValue) }, }; - public static IEnumerable InvalidCommisionPercentagePeriod => new List + public static IEnumerable InvalidCommisionPercentageCooldown => new List { new object[] { 0 }, - new object[] { CommissionPercentageChangePeriod - 1 }, + new object[] { CommissionPercentageChangeCooldown - 1 }, }; public static IEnumerable ValidCommisionPercentagePeriod => new List { - new object[] { CommissionPercentageChangePeriod }, - new object[] { CommissionPercentageChangePeriod + 1 }, + new object[] { CommissionPercentageChangeCooldown }, + new object[] { CommissionPercentageChangeCooldown + 1 }, }; [Fact] @@ -76,7 +69,7 @@ public void Execute() var validatorGold = NCG * 10; var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); - world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + world = EnsurePromotedValidator(world, validatorKey, validatorGold, height); // When var setValidatorCommission = new SetValidatorCommission( @@ -85,7 +78,7 @@ public void Execute() { PreviousState = world, Signer = validatorKey.Address, - BlockIndex = height++, + BlockIndex = height + CommissionPercentageChangeCooldown, }; world = setValidatorCommission.Execute(actionContext); @@ -98,10 +91,11 @@ public void Execute() } [Theory] - // [MemberData(nameof(RandomCommisionPercentage))] - [InlineData(9)] - [InlineData(11)] - public void Execute_Theory(int commissionPercentage) + [InlineData(9, 10)] + [InlineData(9, 8)] + [InlineData(0, 1)] + [InlineData(20, 19)] + public void Execute_Theory(int oldCommissionPercentage, int newCommissionPercentage) { // Given var world = World; @@ -110,16 +104,18 @@ public void Execute_Theory(int commissionPercentage) var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); + world = EnsureCommissionChangedValidator( + world, validatorKey, oldCommissionPercentage, ref height); // When var setValidatorCommission = new SetValidatorCommission( validatorKey.Address, - commissionPercentage); + newCommissionPercentage); var actionContext = new ActionContext { PreviousState = world, Signer = validatorKey.Address, - BlockIndex = height++, + BlockIndex = height + CommissionPercentageChangeCooldown, }; world = setValidatorCommission.Execute(actionContext); @@ -128,7 +124,7 @@ public void Execute_Theory(int commissionPercentage) var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); var actualPercentage = actualDelegatee.CommissionPercentage; - Assert.Equal(commissionPercentage, actualPercentage); + Assert.Equal(newCommissionPercentage, actualPercentage); } [Theory] @@ -142,14 +138,14 @@ public void Execute_Theory_WithValueGreaterThanMaximum_Throw(int commissionPerce var height = 1L; world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height); // When var actionContext = new ActionContext { PreviousState = world, Signer = validatorKey.Address, - BlockIndex = height++, + BlockIndex = height + CommissionPercentageChangeCooldown, }; var setValidatorCommission = new SetValidatorCommission( validatorKey.Address, @@ -179,7 +175,7 @@ public void Execute_Theory_WithNegative_Throw(int commissionPercentage) { PreviousState = world, Signer = validatorKey.Address, - BlockIndex = height++, + BlockIndex = height + CommissionPercentageChangeCooldown, }; var setValidatorCommission = new SetValidatorCommission( validatorKey.Address, @@ -191,8 +187,8 @@ public void Execute_Theory_WithNegative_Throw(int commissionPercentage) } [Theory] - [MemberData(nameof(InvalidCommisionPercentagePeriod))] - public void Execute_Theory_WithInvalidValue_Throw(int period) + [MemberData(nameof(InvalidCommisionPercentageCooldown))] + public void Execute_Theory_WithInvalidValue_Throw(int cooldown) { // Given var world = World; @@ -201,17 +197,17 @@ public void Execute_Theory_WithInvalidValue_Throw(int period) var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); - world = EnsureCommissionChangedValidator(world, validatorKey, 11, height); + world = EnsureCommissionChangedValidator(world, validatorKey, 15, ref height); // When var actionContext = new ActionContext { PreviousState = world, Signer = validatorKey.Address, - BlockIndex = height + period, + BlockIndex = height + cooldown, }; var setValidatorCommission = new SetValidatorCommission( - validatorKey.Address, commissionPercentage: 12); + validatorKey.Address, commissionPercentage: 14); // Then Assert.Throws( @@ -229,7 +225,7 @@ public void Execute_Theory_WitValue(int period) var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); - world = EnsureCommissionChangedValidator(world, validatorKey, 11, height); + world = EnsureCommissionChangedValidator(world, validatorKey, 11, ref height); // When var expectedCommission = 12; @@ -250,74 +246,5 @@ public void Execute_Theory_WitValue(int period) Assert.Equal(expectedCommission, actualPercentage); } - - [Fact] - public void CannotExceedMaxPercentage() - { - IWorld world = new World(MockUtil.MockModernWorldState); - var context = new ActionContext { }; - var ncg = Currency.Uncapped("NCG", 2, null); - // TODO: Use Currencies.GuildGold when it's available. - // var gg = Currencies.GuildGold; - var gg = ncg; - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - var validatorPublicKey = new PrivateKey().PublicKey; - world = world.MintAsset(context, validatorPublicKey.Address, gg * 100); - var promoteFAV = gg * 10; - world = new PromoteValidator(validatorPublicKey, promoteFAV).Execute(new ActionContext - { - PreviousState = world, - Signer = validatorPublicKey.Address, - }); - - foreach (var percentage in Enumerable.Range(11, 10)) - { - world = new SetValidatorCommission(validatorPublicKey.Address, percentage).Execute(new ActionContext - { - PreviousState = world, - Signer = validatorPublicKey.Address, - }); - } - - Assert.Throws( - () => new SetValidatorCommission(validatorPublicKey.Address, 31).Execute(new ActionContext - { - PreviousState = world, - Signer = validatorPublicKey.Address, - })); - } - - [Fact] - public void CannotExceedMaxChange() - { - IWorld world = new World(MockUtil.MockModernWorldState); - var context = new ActionContext { }; - var ncg = Currency.Uncapped("NCG", 2, null); - // TODO: Use Currencies.GuildGold when it's available. - // var gg = Currencies.GuildGold; - var gg = ncg; - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - var validatorPublicKey = new PrivateKey().PublicKey; - world = world.MintAsset(context, validatorPublicKey.Address, gg * 100); - var promoteFAV = gg * 10; - world = new PromoteValidator(validatorPublicKey, promoteFAV).Execute(new ActionContext - { - PreviousState = world, - Signer = validatorPublicKey.Address, - }); - - Assert.Throws( - () => new SetValidatorCommission(validatorPublicKey.Address, 12).Execute(new ActionContext - { - PreviousState = world, - Signer = validatorPublicKey.Address, - })); - } } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 4f5e779b78..768d1fecba 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -116,6 +116,7 @@ public void Execute_Theory(int delegatorCount) [InlineData(0)] [InlineData(1181126949)] [InlineData(793705868)] + [InlineData(17046502)] public void Execute_Theory_WithStaticSeed(int randomSeed) { var fixture = new RandomFixture(randomSeed); @@ -442,6 +443,10 @@ public ValidatorInfo(Random random) Balance = GetRandomNCG(random); Cash = GetRandomCash(random, Balance); SubtractShare = GetRandomCash(random, Cash).RawValue; + if (SubtractShare == 0) + { + Console.WriteLine("123"); + } } public PrivateKey Key { get; set; } = new PrivateKey(); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index f6e94e239d..589dc8dbbc 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Numerics; using Libplanet.Action.State; @@ -352,22 +353,46 @@ protected static IWorld EnsureRewardAllocatedValidator( } protected static IWorld EnsureCommissionChangedValidator( - IWorld world, PrivateKey validatorKey, BigInteger commissionPercentage, long blockHeight) + IWorld world, + PrivateKey validatorKey, + BigInteger commissionPercentage, + ref long blockHeight) { if (blockHeight < 0) { throw new ArgumentOutOfRangeException(nameof(blockHeight)); } - var actionContext = new ActionContext + if (commissionPercentage < ValidatorDelegatee.MinCommissionPercentage + || commissionPercentage > ValidatorDelegatee.MaxCommissionPercentage) { - PreviousState = world, - BlockIndex = blockHeight, - Signer = validatorKey.Address, - }; - var setValidatorCommission = new SetValidatorCommission( - validatorKey.Address, commissionPercentage); - return setValidatorCommission.Execute(actionContext); + throw new ArgumentOutOfRangeException(nameof(commissionPercentage)); + } + + var cooldown = ValidatorDelegatee.CommissionPercentageUpdateCooldown; + var repository = new ValidatorRepository(world, new ActionContext()); + var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); + var currentCommission = delegatee.CommissionPercentage; + var increment = commissionPercentage > currentCommission ? 1 : -1; + var preferredHeight = delegatee.CommissionPercentageLastUpdateHeight + cooldown; + + while (commissionPercentage != currentCommission) + { + blockHeight = Math.Min(preferredHeight, blockHeight + cooldown); + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight, + Signer = validatorKey.Address, + }; + var setValidatorCommission = new SetValidatorCommission( + validatorKey.Address, currentCommission + increment); + world = setValidatorCommission.Execute(actionContext); + currentCommission += increment; + preferredHeight = blockHeight + cooldown; + } + + return world; } protected static Vote CreateNullVote( @@ -539,8 +564,15 @@ protected static FungibleAssetValue GetRandomNCG(Random random, decimal min, dec protected static FungibleAssetValue GetRandomCash(Random random, FungibleAssetValue fav) { - var num = random.Next(1, 10000); - var cash = (fav * num).DivRem(10000).Quotient; + if (fav.RawValue >= long.MaxValue) + { + throw new ArgumentOutOfRangeException( + nameof(fav), "Fungible asset value is too large."); + } + + var num = random.NextInt64(1, (long)fav.RawValue); + var cash = FungibleAssetValue.FromRawValue(fav.Currency, num); + if (cash.Sign < 0 || cash > fav) { throw new InvalidOperationException("Invalid cash value."); From 21e144862299117d080e4f2c52ed4602ecba85ea Mon Sep 17 00:00:00 2001 From: s2quake Date: Tue, 8 Oct 2024 13:26:11 +0900 Subject: [PATCH 075/206] chore: Remove unnecessary code --- .../Action/ValidatorDelegation/DelegateValidatorTest.cs | 4 +++- .../Action/ValidatorDelegation/RedelegateValidatorTest.cs | 1 - .../Action/ValidatorDelegation/UndelegateValidatorTest.cs | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index 72b9a53db3..460caf813f 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -3,12 +3,15 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System; using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Action; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.ValidatorDelegation; +using Org.BouncyCastle.Crypto.Modes; using Xunit; public class DelegateValidatorTest : ValidatorDelegationTestBase @@ -248,7 +251,6 @@ public void Execute_CannotBeJailedDueToDelegatorDelegating() world = EnsureToMintAsset(world, delegatorKey, delegatorBalance, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey, delegatorGold, height++); - world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, 10, height++); world = EnsureUnjailedValidator(world, validatorKey, ref height); // When diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs index 1460481e15..ac6056a327 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs @@ -369,7 +369,6 @@ public void Execute_CannotBeJailedDueToDelegatorRedelegating() world = EnsurePromotedValidator(world, validatorKey2, validatorGold, height++); world = EnsureToMintAsset(world, delegatorKey, delegatorBalance, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, delegatorGold, height++); - world = EnsureUnbondingDelegator(world, validatorKey1, validatorKey1, 10, height++); world = EnsureUnjailedValidator(world, validatorKey1, ref height); height++; diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 768d1fecba..9671194bd4 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -304,7 +304,6 @@ public void Execute_CannotBeJailedDueToDelegatorUndelegating() world = EnsureToMintAsset(world, delegatorKey, delegatorGold, height++); world = EnsureBondedDelegator(world, delegatorKey, validatorKey, delegatorGold, height++); - world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, 10, height++); world = EnsureUnjailedValidator(world, validatorKey, ref height); // When From 46d6596fd3555e16d06932674d380b367e7663ac Mon Sep 17 00:00:00 2001 From: ilgyu Date: Tue, 15 Oct 2024 18:20:15 +0900 Subject: [PATCH 076/206] chore: Clean Guild Modules --- Lib9c/Action/Guild/AcceptGuildApplication.cs | 2 +- Lib9c/Module/Guild/GuildApplicationModule.cs | 3 +- Lib9c/Module/Guild/GuildModule.cs | 10 ++- Lib9c/Module/Guild/GuildParticipantModule.cs | 86 +++++++++++--------- 4 files changed, 57 insertions(+), 44 deletions(-) diff --git a/Lib9c/Action/Guild/AcceptGuildApplication.cs b/Lib9c/Action/Guild/AcceptGuildApplication.cs index 30878f8e1d..a86640bf35 100644 --- a/Lib9c/Action/Guild/AcceptGuildApplication.cs +++ b/Lib9c/Action/Guild/AcceptGuildApplication.cs @@ -53,7 +53,7 @@ public override IWorld Execute(IActionContext context) var repository = new GuildRepository(world, context); var signer = context.GetAgentAddress(); - repository.AcceptGuildApplication(context, signer, Target); + repository.AcceptGuildApplication(signer, Target, context.BlockIndex); return repository.World; } } diff --git a/Lib9c/Module/Guild/GuildApplicationModule.cs b/Lib9c/Module/Guild/GuildApplicationModule.cs index 355cb649cd..3b03971b2b 100644 --- a/Lib9c/Module/Guild/GuildApplicationModule.cs +++ b/Lib9c/Module/Guild/GuildApplicationModule.cs @@ -2,7 +2,6 @@ using System; using System.Diagnostics.CodeAnalysis; using Bencodex.Types; -using Libplanet.Action; using Nekoyume.Action; using Nekoyume.Extensions; using Nekoyume.Model.Guild; @@ -73,7 +72,7 @@ public static void CancelGuildApplication( } public static void AcceptGuildApplication( - this GuildRepository repository, IActionContext context, AgentAddress signer, AgentAddress target) + this GuildRepository repository, AgentAddress signer, AgentAddress target, long height) { if (!repository.TryGetGuildApplication(target, out var guildApplication)) { diff --git a/Lib9c/Module/Guild/GuildModule.cs b/Lib9c/Module/Guild/GuildModule.cs index aa9dcabd50..47880ce94d 100644 --- a/Lib9c/Module/Guild/GuildModule.cs +++ b/Lib9c/Module/Guild/GuildModule.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics.CodeAnalysis; using Lib9c; +using Libplanet.Action.State; using Libplanet.Action; using Nekoyume.Extensions; using Nekoyume.Model.Guild; @@ -11,6 +12,9 @@ namespace Nekoyume.Module.Guild { public static class GuildModule { + public static GuildRepository GetGuildRepository(this IWorld world, IActionContext context) + => new GuildRepository(world, context); + public static bool TryGetGuild( this GuildRepository repository, GuildAddress guildAddress, @@ -86,11 +90,11 @@ public static GuildRepository RemoveGuild( public static GuildRepository CollectRewardGuild( this GuildRepository repository, - IActionContext context, - GuildAddress guildAddress) + GuildAddress guildAddress, + long height) { var guild = repository.GetGuild(guildAddress); - guild.CollectRewards(context.BlockIndex); + guild.CollectRewards(height); repository.SetGuild(guild); return repository; diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index 4c9a422a7b..b085eed3c7 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using System.Numerics; using Lib9c; -using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Types.Assets; using Nekoyume.Model.Guild; @@ -13,9 +12,6 @@ namespace Nekoyume.Module.Guild { public static class GuildParticipantModule { - public static GuildRepository GetGuildRepository(this IWorld world, IActionContext context) - => new GuildRepository(world, context); - // Returns `null` when it didn't join any guild. // Returns `GuildAddress` when it joined a guild. public static GuildAddress? GetJoinedGuild(this GuildRepository repository, AgentAddress agentAddress) @@ -27,36 +23,50 @@ public static GuildRepository GetGuildRepository(this IWorld world, IActionConte public static GuildRepository JoinGuildWithDelegate( this GuildRepository repository, - IActionContext context, - GuildAddress guildAddress) + AgentAddress guildParticipantAddress, + GuildAddress guildAddress, + long height) => repository - .JoinGuild(guildAddress, new AgentAddress(context.Signer)) - .Delegate(context, guildAddress, repository.World.GetBalance(context.Signer, Currencies.GuildGold)); + .JoinGuild(guildAddress, new AgentAddress(guildParticipantAddress)) + .Delegate( + guildParticipantAddress, + guildAddress, + repository.World.GetBalance(guildParticipantAddress, Currencies.GuildGold), + height); public static GuildRepository LeaveGuildWithUndelegate( this GuildRepository repository, - IActionContext context) + AgentAddress guildParticipantAddress, + long height) { - var guild = repository.GetJoinedGuild(new AgentAddress(context.Signer)) is GuildAddress guildAddr + var guild = repository.GetJoinedGuild(guildParticipantAddress) is GuildAddress guildAddr ? repository.GetGuild(guildAddr) : throw new InvalidOperationException("The signer does not join any guild."); return repository - .Undelegate(context, guildAddr, repository.GetBond(guild, context.Signer).Share) - .LeaveGuild(new AgentAddress(context.Signer)); + .Undelegate(guildParticipantAddress, + guildAddr, + repository.GetBond(guild, guildParticipantAddress).Share, + height) + .LeaveGuild(guildParticipantAddress); } public static GuildRepository MoveGuildWithRedelegate( this GuildRepository repository, - IActionContext context, + AgentAddress guildParticipantAddress, GuildAddress srcGuildAddress, - GuildAddress dstGuildAddress) + GuildAddress dstGuildAddress, + long height) { - var agentAddress = new AgentAddress(context.Signer); var srcGuild = repository.GetGuild(srcGuildAddress); - repository.Redelegate(context, srcGuildAddress, dstGuildAddress, repository.GetBond(srcGuild, agentAddress).Share); - repository.LeaveGuild(agentAddress); - repository.JoinGuild(dstGuildAddress, agentAddress); + repository.Redelegate( + guildParticipantAddress, + srcGuildAddress, + dstGuildAddress, + repository.GetBond(srcGuild, guildParticipantAddress).Share, + height); + repository.LeaveGuild(guildParticipantAddress); + repository.JoinGuild(dstGuildAddress, guildParticipantAddress); return repository; } @@ -129,57 +139,57 @@ private static bool TryGetGuildParticipant( private static GuildRepository Delegate( this GuildRepository repository, - IActionContext context, + AgentAddress guildParticipantAddress, GuildAddress guildAddress, - FungibleAssetValue fav) + FungibleAssetValue fav, + long height) { - var agentAddress = new AgentAddress(context.Signer); - var guildParticipant = repository.GetGuildParticipant(agentAddress); + var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); var guild = repository.GetGuild(guildAddress); - guildParticipant.Delegate(guild, fav, context.BlockIndex); + guildParticipant.Delegate(guild, fav, height); return repository; } private static GuildRepository Undelegate( this GuildRepository repository, - IActionContext context, + AgentAddress guildParticipantAddress, GuildAddress guildAddress, - BigInteger share) + BigInteger share, + long height) { - var agentAddress = new AgentAddress(context.Signer); - var guildParticipant = repository.GetGuildParticipant(agentAddress); + var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); var guild = repository.GetGuild(guildAddress); - guildParticipant.Undelegate(guild, share, context.BlockIndex); + guildParticipant.Undelegate(guild, share, height); return repository; } public static GuildRepository Redelegate( this GuildRepository repository, - IActionContext context, + AgentAddress guildParticipantAddress, GuildAddress srcGuildAddress, GuildAddress dstGuildAddress, - BigInteger share) + BigInteger share, + long height) { - var agentAddress = new AgentAddress(context.Signer); - var guildParticipant = repository.GetGuildParticipant(agentAddress); + var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); var srcGuild = repository.GetGuild(srcGuildAddress); var dstGuild = repository.GetGuild(dstGuildAddress); - guildParticipant.Redelegate(srcGuild, dstGuild, share, context.BlockIndex); + guildParticipant.Redelegate(srcGuild, dstGuild, share, height); return repository; } private static GuildRepository ClaimReward( this GuildRepository repository, - IActionContext context, - GuildAddress guildAddress) + AgentAddress guildParticipantAddress, + GuildAddress guildAddress, + long height) { - var agentAddress = new AgentAddress(context.Signer); - var guildParticipant = repository.GetGuildParticipant(agentAddress); + var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); var guild = repository.GetGuild(guildAddress); - guildParticipant.ClaimReward(guild, context.BlockIndex); + guildParticipant.ClaimReward(guild, height); return repository; } From 030901eef1b17b6969cee0a21a2464308e1f5ada Mon Sep 17 00:00:00 2001 From: Lee Dogeon Date: Thu, 29 Aug 2024 18:06:04 +0900 Subject: [PATCH 077/206] Rename `StakeState` to `LegacyStakeState` --- .Lib9c.Tests/Action/ClaimStakeRewardTest.cs | 20 +++++----- .Lib9c.Tests/Action/HackAndSlashSweepTest.cs | 8 ++-- .Lib9c.Tests/Action/HackAndSlashTest.cs | 8 ++-- .../Action/MigrateMonsterCollectionTest.cs | 8 ++-- .../Scenario/StakeAndClaimScenarioTest.cs | 16 ++++---- .Lib9c.Tests/Action/Stake2Test.cs | 32 ++++++++-------- .Lib9c.Tests/Action/StakeTest.cs | 38 +++++++++---------- .Lib9c.Tests/Action/TransferAssetTest.cs | 12 +++--- .Lib9c.Tests/Action/TransferAssetsTest.cs | 12 +++--- .../Model/Stake/StakeStateUtilsTest.cs | 2 +- .Lib9c.Tests/Model/Stake/StakeStateV2Test.cs | 6 +-- ...keStateTest.cs => LegacyStakeStateTest.cs} | 24 ++++++------ Lib9c/Action/ClaimStakeReward.cs | 4 +- Lib9c/Action/MigrateMonsterCollection.cs | 10 ++--- Lib9c/Action/Stake.cs | 24 +++++++++--- Lib9c/Action/Stake2.cs | 8 ++-- Lib9c/Action/TransferAsset.cs | 2 +- Lib9c/Model/Stake/StakeStateUtils.cs | 6 +-- Lib9c/Model/Stake/StakeStateV2.cs | 8 ++-- .../{StakeState.cs => LegacyStakeState.cs} | 8 ++-- Lib9c/Module/LegacyModule.cs | 10 ++--- 21 files changed, 139 insertions(+), 127 deletions(-) rename .Lib9c.Tests/Model/State/{StakeStateTest.cs => LegacyStakeStateTest.cs} (91%) rename Lib9c/Model/State/{StakeState.cs => LegacyStakeState.cs} (98%) diff --git a/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs b/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs index 82ae98e4eb..f5ad29f3cf 100644 --- a/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs +++ b/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs @@ -373,19 +373,19 @@ public void Execute_Throw_FailedLoadStateException_When_Staking_State_Null() [Theory] [InlineData(0, null, 0)] - [InlineData(0, null, StakeState.RewardInterval - 1)] - [InlineData(0, StakeState.RewardInterval - 2, StakeState.RewardInterval - 1)] - [InlineData(0, StakeState.RewardInterval, StakeState.RewardInterval + 1)] - [InlineData(0, StakeState.RewardInterval, StakeState.RewardInterval * 2 - 1)] - [InlineData(0, StakeState.RewardInterval * 2 - 2, StakeState.RewardInterval * 2 - 1)] - [InlineData(0, StakeState.RewardInterval * 2, StakeState.RewardInterval * 2 + 1)] + [InlineData(0, null, LegacyStakeState.RewardInterval - 1)] + [InlineData(0, LegacyStakeState.RewardInterval - 2, LegacyStakeState.RewardInterval - 1)] + [InlineData(0, LegacyStakeState.RewardInterval, LegacyStakeState.RewardInterval + 1)] + [InlineData(0, LegacyStakeState.RewardInterval, LegacyStakeState.RewardInterval * 2 - 1)] + [InlineData(0, LegacyStakeState.RewardInterval * 2 - 2, LegacyStakeState.RewardInterval * 2 - 1)] + [InlineData(0, LegacyStakeState.RewardInterval * 2, LegacyStakeState.RewardInterval * 2 + 1)] public void Execute_Throw_RequiredBlockIndexException_With_StakeState( long startedBlockIndex, long? receivedBlockIndex, long blockIndex) { - var stakeAddr = StakeState.DeriveAddress(AgentAddr); - var stakeState = new StakeState(stakeAddr, startedBlockIndex); + var stakeAddr = LegacyStakeState.DeriveAddress(AgentAddr); + var stakeState = new LegacyStakeState(stakeAddr, startedBlockIndex); if (receivedBlockIndex is not null) { stakeState.Claim((long)receivedBlockIndex); @@ -570,8 +570,8 @@ public void Execute_Success_With_StakeState( (Address balanceAddr, FungibleAssetValue fav)[] expectedBalances, (int itemSheetId, int count)[] expectedItems) { - var stakeAddr = StakeState.DeriveAddress(AgentAddr); - var stakeState = new StakeState(stakeAddr, startedBlockIndex); + var stakeAddr = LegacyStakeState.DeriveAddress(AgentAddr); + var stakeState = new LegacyStakeState(stakeAddr, startedBlockIndex); if (receivedBlockIndex is not null) { stakeState.Claim((long)receivedBlockIndex); diff --git a/.Lib9c.Tests/Action/HackAndSlashSweepTest.cs b/.Lib9c.Tests/Action/HackAndSlashSweepTest.cs index d3d4a9d957..55ef05c0d1 100644 --- a/.Lib9c.Tests/Action/HackAndSlashSweepTest.cs +++ b/.Lib9c.Tests/Action/HackAndSlashSweepTest.cs @@ -545,8 +545,8 @@ public void ExecuteWithStake(int stakingLevel) var apStone = ItemFactory.CreateTradableMaterial(itemRow); avatarState.inventory.AddItem(apStone); - var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); - var stakeState = new StakeState(stakeStateAddress, 1); + var stakeStateAddress = LegacyStakeState.DeriveAddress(_agentAddress); + var stakeState = new LegacyStakeState(stakeStateAddress, 1); var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows .FirstOrDefault(r => r.Level == stakingLevel)?.RequiredGold ?? 0; var context = new ActionContext(); @@ -695,8 +695,8 @@ public void ExecuteDuplicatedException(int slotIndex, int runeId, int slotIndex2 var apStone = ItemFactory.CreateTradableMaterial(itemRow); avatarState.inventory.AddItem(apStone); - var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); - var stakeState = new StakeState(stakeStateAddress, 1); + var stakeStateAddress = LegacyStakeState.DeriveAddress(_agentAddress); + var stakeState = new LegacyStakeState(stakeStateAddress, 1); var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows .FirstOrDefault(r => r.Level == stakingLevel)?.RequiredGold ?? 0; var context = new ActionContext(); diff --git a/.Lib9c.Tests/Action/HackAndSlashTest.cs b/.Lib9c.Tests/Action/HackAndSlashTest.cs index 9d61aab639..c2d404e189 100644 --- a/.Lib9c.Tests/Action/HackAndSlashTest.cs +++ b/.Lib9c.Tests/Action/HackAndSlashTest.cs @@ -1304,8 +1304,8 @@ public void CheckUsedApByStaking(int level, int playCount) _tableSheets.WorldSheet, clearedStageId); - var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); - var stakeState = new StakeState(stakeStateAddress, 1); + var stakeStateAddress = LegacyStakeState.DeriveAddress(_agentAddress); + var stakeState = new LegacyStakeState(stakeStateAddress, 1); var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows .FirstOrDefault(r => r.Level == level)?.RequiredGold ?? 0; var context = new ActionContext(); @@ -1372,8 +1372,8 @@ public void CheckUsingApStoneWithStaking(int level, int apStoneCount, int totalR r.ItemSubType == ItemSubType.ApStone); var apStone = ItemFactory.CreateTradableMaterial(apStoneRow); previousAvatarState.inventory.AddItem(apStone, apStoneCount); - var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); - var stakeState = new StakeState(stakeStateAddress, 1); + var stakeStateAddress = LegacyStakeState.DeriveAddress(_agentAddress); + var stakeState = new LegacyStakeState(stakeStateAddress, 1); var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows .FirstOrDefault(r => r.Level == level)?.RequiredGold ?? 0; var context = new ActionContext(); diff --git a/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs b/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs index 838f889080..5b82558a7c 100644 --- a/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs +++ b/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs @@ -69,10 +69,10 @@ public void Execute_ThrowsIfAlreadyStakeStateExists() Address monsterCollectionAddress = MonsterCollectionState.DeriveAddress(_signer, 0); var monsterCollectionState = new MonsterCollectionState( monsterCollectionAddress, 1, 0); - Address stakeStateAddress = StakeState.DeriveAddress(_signer); + Address stakeStateAddress = LegacyStakeState.DeriveAddress(_signer); var states = _state.SetLegacyState( - stakeStateAddress, new StakeState(stakeStateAddress, 0).SerializeV2()) - .SetLegacyState(monsterCollectionAddress, monsterCollectionState.Serialize()); + stakeStateAddress, new LegacyStakeState(stakeStateAddress, 0).SerializeV2()) + .SetLegacyState(monsterCollectionAddress, monsterCollectionState.SerializeV2()); MigrateMonsterCollection action = new MigrateMonsterCollection(_avatarAddress); Assert.Throws(() => action.Execute(new ActionContext { @@ -138,7 +138,7 @@ public void Execute(int collectionLevel, long claimBlockIndex, long receivedBloc RandomSeed = 0, }); - Assert.True(states.TryGetStakeState(_signer, out StakeState stakeState)); + Assert.True(states.TryGetStakeState(_signer, out LegacyStakeState stakeState)); Assert.Equal( 0 * currency, states.GetBalance(monsterCollectionState.address, currency)); diff --git a/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs index e4c0b7bedb..f5ed523960 100644 --- a/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs @@ -95,7 +95,7 @@ public void Test() state, _agentAddr, _avatarAddr, - stake2BlockIndex + StakeState.LockupInterval); + stake2BlockIndex + LegacyStakeState.LockupInterval); // Validate staked. ValidateStakedStateV2( @@ -105,14 +105,14 @@ public void Test() stake2BlockIndex, "StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", - StakeState.RewardInterval, - StakeState.LockupInterval); + LegacyStakeState.RewardInterval, + LegacyStakeState.LockupInterval); // Withdraw stake via stake3. - state = Stake3(state, _agentAddr, 0, stake2BlockIndex + StakeState.LockupInterval + 1); + state = Stake3(state, _agentAddr, 0, stake2BlockIndex + LegacyStakeState.LockupInterval + 1); // Stake 50 NCG via stake3 before patching. - const long firstStake3BlockIndex = stake2BlockIndex + StakeState.LockupInterval + 1; + const long firstStake3BlockIndex = stake2BlockIndex + LegacyStakeState.LockupInterval + 1; state = Stake3( state, _agentAddr, @@ -222,10 +222,10 @@ private static void ValidateStakedState( FungibleAssetValue expectStakedAmount, long expectStartedBlockIndex) { - var stakeAddr = StakeState.DeriveAddress(agentAddr); + var stakeAddr = LegacyStakeState.DeriveAddress(agentAddr); var actualStakedAmount = state.GetBalance(stakeAddr, expectStakedAmount.Currency); Assert.Equal(expectStakedAmount, actualStakedAmount); - var stakeState = new StakeState((Dictionary)state.GetLegacyState(stakeAddr)); + var stakeState = new LegacyStakeState((Dictionary)state.GetLegacyState(stakeAddr)); Assert.Equal(expectStartedBlockIndex, stakeState.StartedBlockIndex); } @@ -239,7 +239,7 @@ private static void ValidateStakedStateV2( long expectRewardInterval, long expectLockupInterval) { - var stakeAddr = StakeState.DeriveAddress(agentAddr); + var stakeAddr = LegacyStakeState.DeriveAddress(agentAddr); var actualStakedAmount = state.GetBalance(stakeAddr, expectStakedAmount.Currency); Assert.Equal(expectStakedAmount, actualStakedAmount); var stakeState = new StakeStateV2(state.GetLegacyState(stakeAddr)); diff --git a/.Lib9c.Tests/Action/Stake2Test.cs b/.Lib9c.Tests/Action/Stake2Test.cs index c1e7958343..3fd8ff447f 100644 --- a/.Lib9c.Tests/Action/Stake2Test.cs +++ b/.Lib9c.Tests/Action/Stake2Test.cs @@ -94,10 +94,10 @@ public void Execute_Throws_WhenThereIsMonsterCollection() [Fact] public void Execute_Throws_WhenClaimableExisting() { - Address stakeStateAddress = StakeState.DeriveAddress(_signerAddress); + Address stakeStateAddress = LegacyStakeState.DeriveAddress(_signerAddress); var context = new ActionContext(); var states = _initialState - .SetLegacyState(stakeStateAddress, new StakeState(stakeStateAddress, 0).Serialize()) + .SetLegacyState(stakeStateAddress, new LegacyStakeState(stakeStateAddress, 0).Serialize()) .MintAsset(context, stakeStateAddress, _currency * 50); var action = new Stake2(100); Assert.Throws(() => @@ -105,7 +105,7 @@ public void Execute_Throws_WhenClaimableExisting() { PreviousState = states, Signer = _signerAddress, - BlockIndex = StakeState.RewardInterval, + BlockIndex = LegacyStakeState.RewardInterval, })); } @@ -139,11 +139,11 @@ public void Execute_Throws_WhenCancelOrUpdateWhileLockup() })); // Same (since 4611070) - if (states.TryGetStakeState(_signerAddress, out StakeState stakeState)) + if (states.TryGetStakeState(_signerAddress, out LegacyStakeState stakeState)) { states = states.SetLegacyState( stakeState.address, - new StakeState(stakeState.address, 4611070 - 100).Serialize()); + new LegacyStakeState(stakeState.address, 4611070 - 100).Serialize()); } updateAction = new Stake2(51); @@ -178,11 +178,11 @@ public void Execute() Assert.Equal(_currency * 0, states.GetBalance(_signerAddress, _currency)); Assert.Equal( _currency * 100, - states.GetBalance(StakeState.DeriveAddress(_signerAddress), _currency)); + states.GetBalance(LegacyStakeState.DeriveAddress(_signerAddress), _currency)); - states.TryGetStakeState(_signerAddress, out StakeState stakeState); + states.TryGetStakeState(_signerAddress, out LegacyStakeState stakeState); Assert.Equal(0, stakeState.StartedBlockIndex); - Assert.Equal(0 + StakeState.LockupInterval, stakeState.CancellableBlockIndex); + Assert.Equal(0 + LegacyStakeState.LockupInterval, stakeState.CancellableBlockIndex); Assert.Equal(0, stakeState.ReceivedBlockIndex); Assert.Equal(_currency * 100, states.GetBalance(stakeState.address, _currency)); Assert.Equal(_currency * 0, states.GetBalance(_signerAddress, _currency)); @@ -192,20 +192,20 @@ public void Execute() Assert.False(achievements.Check(0, 1)); Assert.False(achievements.Check(1, 0)); - StakeState producedStakeState = new StakeState( + LegacyStakeState producedLegacyStakeState = new LegacyStakeState( stakeState.address, stakeState.StartedBlockIndex, // Produce a situation that it already received rewards. - StakeState.LockupInterval - 1, + LegacyStakeState.LockupInterval - 1, stakeState.CancellableBlockIndex, stakeState.Achievements); - states = states.SetLegacyState(stakeState.address, producedStakeState.SerializeV2()); + states = states.SetLegacyState(stakeState.address, producedLegacyStakeState.SerializeV2()); var cancelAction = new Stake2(0); states = cancelAction.Execute(new ActionContext { PreviousState = states, Signer = _signerAddress, - BlockIndex = StakeState.LockupInterval, + BlockIndex = LegacyStakeState.LockupInterval, }); Assert.Null(states.GetLegacyState(stakeState.address)); @@ -224,9 +224,9 @@ public void Update() BlockIndex = 0, }); - states.TryGetStakeState(_signerAddress, out StakeState stakeState); + states.TryGetStakeState(_signerAddress, out LegacyStakeState stakeState); Assert.Equal(0, stakeState.StartedBlockIndex); - Assert.Equal(0 + StakeState.LockupInterval, stakeState.CancellableBlockIndex); + Assert.Equal(0 + LegacyStakeState.LockupInterval, stakeState.CancellableBlockIndex); Assert.Equal(0, stakeState.ReceivedBlockIndex); Assert.Equal(_currency * 50, states.GetBalance(stakeState.address, _currency)); Assert.Equal(_currency * 50, states.GetBalance(_signerAddress, _currency)); @@ -241,7 +241,7 @@ public void Update() states.TryGetStakeState(_signerAddress, out stakeState); Assert.Equal(1, stakeState.StartedBlockIndex); - Assert.Equal(1 + StakeState.LockupInterval, stakeState.CancellableBlockIndex); + Assert.Equal(1 + LegacyStakeState.LockupInterval, stakeState.CancellableBlockIndex); Assert.Equal(0, stakeState.ReceivedBlockIndex); Assert.Equal(_currency * 100, states.GetBalance(stakeState.address, _currency)); Assert.Equal(_currency * 0, states.GetBalance(_signerAddress, _currency)); @@ -254,7 +254,7 @@ public void RestrictForStakeStateV2() Assert.Throws(() => action.Execute(new ActionContext { PreviousState = _initialState.SetLegacyState( - StakeState.DeriveAddress(_signerAddress), + LegacyStakeState.DeriveAddress(_signerAddress), new StakeStateV2( new Contract( "StakeRegularFixedRewardSheet_V1", diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index fc42f3e94a..b752021f70 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -222,9 +222,9 @@ public void Execute_Throw_StateNullException_Via_0_Amount() [Theory] // NOTE: minimum required_gold of StakeRegularRewardSheetFixtures.V2 is 50. - [InlineData(0, 50, StakeState.RewardInterval)] + [InlineData(0, 50, LegacyStakeState.RewardInterval)] [InlineData( - long.MaxValue - StakeState.RewardInterval, + long.MaxValue - LegacyStakeState.RewardInterval, long.MaxValue, long.MaxValue)] public void Execute_Throw_StakeExistingClaimableException_With_StakeState( @@ -232,8 +232,8 @@ public void Execute_Throw_StakeExistingClaimableException_With_StakeState( long previousAmount, long blockIndex) { - var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); - var stakeState = new StakeState( + var stakeStateAddr = LegacyStakeState.DeriveAddress(_agentAddr); + var stakeState = new LegacyStakeState( address: stakeStateAddr, startedBlockIndex: previousStartedBlockIndex); Assert.True(stakeState.IsClaimable(blockIndex)); @@ -300,8 +300,8 @@ public void long blockIndex, long reducedAmount) { - var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); - var stakeState = new StakeState( + var stakeStateAddr = LegacyStakeState.DeriveAddress(_agentAddr); + var stakeState = new LegacyStakeState( address: stakeStateAddr, startedBlockIndex: previousStartedBlockIndex); Assert.False(stakeState.IsCancellable(blockIndex)); @@ -380,28 +380,28 @@ public void Execute_Success_When_Staking_State_Null(long amount) // NOTE: minimum required_gold of StakeRegularRewardSheetFixtures.V2 is 50. // NOTE: non claimable, locked up, same amount. [InlineData(0, 50, 0, 50)] - [InlineData(0, 50, StakeState.LockupInterval - 1, 50)] + [InlineData(0, 50, LegacyStakeState.LockupInterval - 1, 50)] [InlineData(0, long.MaxValue, 0, long.MaxValue)] - [InlineData(0, long.MaxValue, StakeState.LockupInterval - 1, long.MaxValue)] + [InlineData(0, long.MaxValue, LegacyStakeState.LockupInterval - 1, long.MaxValue)] // NOTE: non claimable, locked up, increased amount. [InlineData(0, 50, 0, 500)] - [InlineData(0, 50, StakeState.LockupInterval - 1, 500)] + [InlineData(0, 50, LegacyStakeState.LockupInterval - 1, 500)] // NOTE: non claimable, unlocked, same amount. - [InlineData(0, 50, StakeState.LockupInterval, 50)] - [InlineData(0, long.MaxValue, StakeState.LockupInterval, long.MaxValue)] + [InlineData(0, 50, LegacyStakeState.LockupInterval, 50)] + [InlineData(0, long.MaxValue, LegacyStakeState.LockupInterval, long.MaxValue)] // NOTE: non claimable, unlocked, increased amount. - [InlineData(0, 50, StakeState.LockupInterval, 500)] + [InlineData(0, 50, LegacyStakeState.LockupInterval, 500)] // NOTE: non claimable, unlocked, decreased amount. - [InlineData(0, 50, StakeState.LockupInterval, 0)] - [InlineData(0, long.MaxValue, StakeState.LockupInterval, 50)] + [InlineData(0, 50, LegacyStakeState.LockupInterval, 0)] + [InlineData(0, long.MaxValue, LegacyStakeState.LockupInterval, 50)] public void Execute_Success_When_Exist_StakeState( long previousStartedBlockIndex, long previousAmount, long blockIndex, long amount) { - var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); - var stakeState = new StakeState( + var stakeStateAddr = LegacyStakeState.DeriveAddress(_agentAddr); + var stakeState = new LegacyStakeState( address: stakeStateAddr, startedBlockIndex: previousStartedBlockIndex); stakeState.Claim(blockIndex); @@ -442,7 +442,7 @@ public void Execute_Success_When_Exist_StakeState( [InlineData(0, 50, 201_600, 500)] // NOTE: non claimable, unlocked, decreased amount. [InlineData(0, 50, 201_600, 0)] - [InlineData(0, long.MaxValue, StakeState.LockupInterval, 50)] + [InlineData(0, long.MaxValue, LegacyStakeState.LockupInterval, 50)] public void Execute_Success_When_Exist_StakeStateV2( long previousStartedBlockIndex, long previousAmount, @@ -482,7 +482,7 @@ private IWorld Execute( { var previousBalance = previousState.GetBalance(signer, _ncg); var previousStakeBalance = previousState.GetBalance( - StakeState.DeriveAddress(signer), + LegacyStakeState.DeriveAddress(signer), _ncg); var previousTotalBalance = previousBalance + previousStakeBalance; var action = new Stake(amount); @@ -497,7 +497,7 @@ private IWorld Execute( var amountNCG = _ncg * amount; var nextBalance = nextState.GetBalance(signer, _ncg); var nextStakeBalance = nextState.GetBalance( - StakeState.DeriveAddress(signer), + LegacyStakeState.DeriveAddress(signer), _ncg); Assert.Equal(previousTotalBalance - amountNCG, nextBalance); Assert.Equal(amountNCG, nextStakeBalance); diff --git a/.Lib9c.Tests/Action/TransferAssetTest.cs b/.Lib9c.Tests/Action/TransferAssetTest.cs index 4ec020e08e..fb7eb7bd37 100644 --- a/.Lib9c.Tests/Action/TransferAssetTest.cs +++ b/.Lib9c.Tests/Action/TransferAssetTest.cs @@ -290,7 +290,7 @@ public void Execute_Throw_ArgumentException() .SetBalance(_sender, _currency * 1000)); var action = new TransferAsset( sender: _sender, - recipient: StakeState.DeriveAddress(_recipient), + recipient: LegacyStakeState.DeriveAddress(_recipient), amount: _currency * 100 ); // 스테이킹 주소에 송금하려고 하면 실패합니다. @@ -298,8 +298,8 @@ public void Execute_Throw_ArgumentException() { PreviousState = baseState .SetLegacyState( - StakeState.DeriveAddress(_recipient), - new StakeState(StakeState.DeriveAddress(_recipient), 0).SerializeV2()), + LegacyStakeState.DeriveAddress(_recipient), + new LegacyStakeState(LegacyStakeState.DeriveAddress(_recipient), 0).SerializeV2()), Signer = _sender, BlockIndex = 1, })); @@ -307,7 +307,7 @@ public void Execute_Throw_ArgumentException() { PreviousState = baseState .SetLegacyState( - StakeState.DeriveAddress(_recipient), + LegacyStakeState.DeriveAddress(_recipient), new StakeStateV2( new Contract( "StakeRegularFixedRewardSheet_V1", @@ -322,7 +322,7 @@ public void Execute_Throw_ArgumentException() { PreviousState = baseState .SetLegacyState( - StakeState.DeriveAddress(_recipient), + LegacyStakeState.DeriveAddress(_recipient), new MonsterCollectionState( MonsterCollectionState.DeriveAddress(_sender, 0), 1, @@ -338,7 +338,7 @@ public void Execute_Throw_ArgumentException() { PreviousState = baseState .SetLegacyState( - StakeState.DeriveAddress(_recipient), + LegacyStakeState.DeriveAddress(_recipient), new MonsterCollectionState0( MonsterCollectionState.DeriveAddress(_sender, 0), 1, diff --git a/.Lib9c.Tests/Action/TransferAssetsTest.cs b/.Lib9c.Tests/Action/TransferAssetsTest.cs index 70a3169f44..4c391a37eb 100644 --- a/.Lib9c.Tests/Action/TransferAssetsTest.cs +++ b/.Lib9c.Tests/Action/TransferAssetsTest.cs @@ -372,7 +372,7 @@ public void Execute_Throw_ArgumentException() sender: _sender, new List<(Address, FungibleAssetValue)> { - (StakeState.DeriveAddress(_recipient), _currency * 100), + (LegacyStakeState.DeriveAddress(_recipient), _currency * 100), (_recipient2, _currency * 100), } ); @@ -381,8 +381,8 @@ public void Execute_Throw_ArgumentException() { PreviousState = baseState .SetLegacyState( - StakeState.DeriveAddress(_recipient), - new StakeState(StakeState.DeriveAddress(_recipient), 0).SerializeV2()), + LegacyStakeState.DeriveAddress(_recipient), + new LegacyStakeState(LegacyStakeState.DeriveAddress(_recipient), 0).SerializeV2()), Signer = _sender, BlockIndex = 1, })); @@ -390,7 +390,7 @@ public void Execute_Throw_ArgumentException() { PreviousState = baseState .SetLegacyState( - StakeState.DeriveAddress(_recipient), + LegacyStakeState.DeriveAddress(_recipient), new StakeStateV2( new Contract( "StakeRegularFixedRewardSheet_V1", @@ -405,7 +405,7 @@ public void Execute_Throw_ArgumentException() { PreviousState = baseState .SetLegacyState( - StakeState.DeriveAddress(_recipient), + LegacyStakeState.DeriveAddress(_recipient), new MonsterCollectionState( MonsterCollectionState.DeriveAddress(_sender, 0), 1, @@ -421,7 +421,7 @@ public void Execute_Throw_ArgumentException() { PreviousState = baseState .SetLegacyState( - StakeState.DeriveAddress(_recipient), + LegacyStakeState.DeriveAddress(_recipient), new MonsterCollectionState0( MonsterCollectionState.DeriveAddress(_sender, 0), 1, diff --git a/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs b/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs index 37626bee61..a530f7e7e5 100644 --- a/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs +++ b/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs @@ -113,7 +113,7 @@ public void TryMigrate_Return_True_With_StakeState( Addresses.GameConfig, new GameConfigState(GameConfigSheetFixtures.Default).Serialize()); var stakeAddr = new PrivateKey().Address; - var stakeState = new StakeState(stakeAddr, startedBlockIndex); + var stakeState = new LegacyStakeState(stakeAddr, startedBlockIndex); if (receivedBlockIndex is not null) { stakeState.Claim(receivedBlockIndex.Value); diff --git a/.Lib9c.Tests/Model/Stake/StakeStateV2Test.cs b/.Lib9c.Tests/Model/Stake/StakeStateV2Test.cs index e2470170eb..d5c99b50ae 100644 --- a/.Lib9c.Tests/Model/Stake/StakeStateV2Test.cs +++ b/.Lib9c.Tests/Model/Stake/StakeStateV2Test.cs @@ -12,7 +12,7 @@ public class StakeStateV2Test public void DeriveAddress() { var agentAddr = new PrivateKey().Address; - var expectedStakeStateAddr = StakeState.DeriveAddress(agentAddr); + var expectedStakeStateAddr = LegacyStakeState.DeriveAddress(agentAddr); Assert.Equal(expectedStakeStateAddr, StakeStateV2.DeriveAddress(agentAddr)); } @@ -62,7 +62,7 @@ public void Constructor_Throw_ArgumentOutOfRangeException( [InlineData(long.MaxValue, long.MaxValue)] public void Constructor_With_StakeState(long startedBlockIndex, long? receivedBlockIndex) { - var stakeState = new StakeState( + var stakeState = new LegacyStakeState( new PrivateKey().Address, startedBlockIndex); if (receivedBlockIndex.HasValue) @@ -84,7 +84,7 @@ public void Constructor_With_StakeState(long startedBlockIndex, long? receivedBl [Fact] public void Constructor_With_StakeState_Throw_ArgumentNullException() { - var stakeState = new StakeState(new PrivateKey().Address, 0); + var stakeState = new LegacyStakeState(new PrivateKey().Address, 0); var contract = new Contract( Contract.StakeRegularFixedRewardSheetPrefix, Contract.StakeRegularRewardSheetPrefix, diff --git a/.Lib9c.Tests/Model/State/StakeStateTest.cs b/.Lib9c.Tests/Model/State/LegacyStakeStateTest.cs similarity index 91% rename from .Lib9c.Tests/Model/State/StakeStateTest.cs rename to .Lib9c.Tests/Model/State/LegacyStakeStateTest.cs index 82cb4366ef..85c236baa8 100644 --- a/.Lib9c.Tests/Model/State/StakeStateTest.cs +++ b/.Lib9c.Tests/Model/State/LegacyStakeStateTest.cs @@ -6,21 +6,21 @@ namespace Lib9c.Tests.Model.State using Nekoyume.Action; using Nekoyume.Model.State; using Xunit; - using static Nekoyume.Model.State.StakeState; + using static Nekoyume.Model.State.LegacyStakeState; - public class StakeStateTest + public class LegacyStakeStateTest { [Fact] public void IsClaimable() { - Assert.False(new StakeState( + Assert.False(new LegacyStakeState( default, 0, RewardInterval + 1, LockupInterval, new StakeAchievements()) .IsClaimable(RewardInterval * 2)); - Assert.True(new StakeState( + Assert.True(new LegacyStakeState( default, ActionObsoleteConfig.V100290ObsoleteIndex - 100, ActionObsoleteConfig.V100290ObsoleteIndex - 100 + RewardInterval + 1, @@ -32,10 +32,10 @@ public void IsClaimable() [Fact] public void Serialize() { - var state = new StakeState(default, 100); + var state = new LegacyStakeState(default, 100); var serialized = (Dictionary)state.Serialize(); - var deserialized = new StakeState(serialized); + var deserialized = new LegacyStakeState(serialized); Assert.Equal(state.address, deserialized.address); Assert.Equal(state.StartedBlockIndex, deserialized.StartedBlockIndex); @@ -46,10 +46,10 @@ public void Serialize() [Fact] public void SerializeV2() { - var state = new StakeState(default, 100); + var state = new LegacyStakeState(default, 100); var serialized = (Dictionary)state.SerializeV2(); - var deserialized = new StakeState(serialized); + var deserialized = new LegacyStakeState(serialized); Assert.Equal(state.address, deserialized.address); Assert.Equal(state.StartedBlockIndex, deserialized.StartedBlockIndex); @@ -63,7 +63,7 @@ public void SerializeV2() [InlineData(long.MinValue)] public void Claim(long blockIndex) { - var stakeState = new StakeState(new PrivateKey().Address, 0L); + var stakeState = new LegacyStakeState(new PrivateKey().Address, 0L); stakeState.Claim(blockIndex); Assert.Equal(blockIndex, stakeState.ReceivedBlockIndex); } @@ -78,7 +78,7 @@ public void CalculateAccumulateRuneRewards( long blockIndex, int expected) { - var state = new StakeState(default, startedBlockIndex); + var state = new LegacyStakeState(default, startedBlockIndex); Assert.Equal(expected, state.CalculateAccumulatedRuneRewards(blockIndex)); } @@ -120,7 +120,7 @@ public void GetRewardStepV1( long? rewardStartBlockIndex, int expectedStep) { - var stakeState = new StakeState( + var stakeState = new LegacyStakeState( new PrivateKey().Address, startedBlockIndex); stakeState.Claim(receivedBlockIndex); @@ -166,7 +166,7 @@ public void GetRewardStep( long? rewardStartBlockIndex, int expectedStep) { - var stakeState = new StakeState( + var stakeState = new LegacyStakeState( new PrivateKey().Address, startedBlockIndex); stakeState.Claim(receivedBlockIndex); diff --git a/Lib9c/Action/ClaimStakeReward.cs b/Lib9c/Action/ClaimStakeReward.cs index e5e1079aaa..64470a18cd 100644 --- a/Lib9c/Action/ClaimStakeReward.cs +++ b/Lib9c/Action/ClaimStakeReward.cs @@ -57,13 +57,13 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var states = context.PreviousState; var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - var stakeStateAddr = StakeState.DeriveAddress(context.Signer); + var stakeStateAddr = LegacyStakeState.DeriveAddress(context.Signer); if (!states.TryGetStakeStateV2(context.Signer, out var stakeStateV2)) { throw new FailedLoadStateException( ActionTypeText, addressesHex, - typeof(StakeState), + typeof(LegacyStakeState), stakeStateAddr); } diff --git a/Lib9c/Action/MigrateMonsterCollection.cs b/Lib9c/Action/MigrateMonsterCollection.cs index 82c1b22d6b..8d81b95887 100644 --- a/Lib9c/Action/MigrateMonsterCollection.cs +++ b/Lib9c/Action/MigrateMonsterCollection.cs @@ -17,7 +17,7 @@ namespace Nekoyume.Action { /// /// An action to claim remained monster collection rewards and to migrate - /// into without cancellation, to + /// into without cancellation, to /// keep its staked period. /// [ActionType("migrate_monster_collection")] @@ -53,7 +53,7 @@ public override IWorld Execute(IActionContext context) var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); var started = DateTimeOffset.UtcNow; Log.Debug("{AddressesHex}MigrateMonsterCollection exec started", addressesHex); - if (states.TryGetStakeState(context.Signer, out StakeState _)) + if (states.TryGetStakeState(context.Signer, out LegacyStakeState _)) { throw new InvalidOperationException("The user has already staked."); } @@ -80,13 +80,13 @@ public override IWorld Execute(IActionContext context) } var monsterCollectionState = new MonsterCollectionState(stateDict); - var migratedStakeStateAddress = StakeState.DeriveAddress(context.Signer); - var migratedStakeState = new StakeState( + var migratedStakeStateAddress = LegacyStakeState.DeriveAddress(context.Signer); + var migratedStakeState = new LegacyStakeState( migratedStakeStateAddress, monsterCollectionState.StartedBlockIndex, monsterCollectionState.ReceivedBlockIndex, monsterCollectionState.ExpiredBlockIndex, - new StakeState.StakeAchievements()); + new LegacyStakeState.StakeAchievements()); var ended = DateTimeOffset.UtcNow; Log.Debug("{AddressesHex}MigrateMonsterCollection Total Executed Time: {Elapsed}", addressesHex, ended - started); diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index 85953c2968..44f0ad56fd 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Numerics; using Bencodex.Types; +using Lib9c; using Lib9c.Abstractions; using Libplanet.Action; using Libplanet.Action.State; @@ -94,7 +95,7 @@ public override IWorld Execute(IActionContext context) $"The amount must be greater than or equal to {minimumRequiredGold}."); } - var stakeStateAddress = StakeState.DeriveAddress(context.Signer); + var stakeStateAddress = LegacyStakeState.DeriveAddress(context.Signer); var currency = states.GetGoldCurrency(); var currentBalance = states.GetBalance(context.Signer, currency); var stakedBalance = states.GetBalance(stakeStateAddress, currency); @@ -183,16 +184,27 @@ private static IWorld ContractNewStake( var newStakeState = new StakeStateV2(latestStakeContract, context.BlockIndex); if (stakedBalance.HasValue) { - state = state.TransferAsset( - context, - stakeStateAddr, - context.Signer, - stakedBalance.Value); + state = state.BurnAsset( + context, + context.Signer, + GetGuildCoinFromNCG(stakedBalance.Value) + ).TransferAsset( + context, + stakeStateAddr, + context.Signer, + stakedBalance.Value); } return state .TransferAsset(context, context.Signer, stakeStateAddr, targetStakeBalance) + .MintAsset(context, context.Signer, GetGuildCoinFromNCG(targetStakeBalance)) .SetLegacyState(stakeStateAddr, newStakeState.Serialize()); } + + private static FungibleAssetValue GetGuildCoinFromNCG(FungibleAssetValue balance) + { + return FungibleAssetValue.Parse(Currencies.GuildGold, + balance.GetQuantityString(true)); + } } } diff --git a/Lib9c/Action/Stake2.cs b/Lib9c/Action/Stake2.cs index 01aabc2d52..9c30c30029 100644 --- a/Lib9c/Action/Stake2.cs +++ b/Lib9c/Action/Stake2.cs @@ -73,7 +73,7 @@ public override IWorld Execute(IActionContext context) throw new ArgumentOutOfRangeException(nameof(Amount)); } - var stakeStateAddress = StakeState.DeriveAddress(context.Signer); + var stakeStateAddress = LegacyStakeState.DeriveAddress(context.Signer); var currency = states.GetGoldCurrency(); var currentBalance = states.GetBalance(context.Signer, currency); var stakedBalance = states.GetBalance(stakeStateAddress, currency); @@ -87,7 +87,7 @@ public override IWorld Execute(IActionContext context) } // Stake if it doesn't exist yet. - if (!states.TryGetStakeState(context.Signer, out StakeState stakeState)) + if (!states.TryGetStakeState(context.Signer, out LegacyStakeState stakeState)) { if (states.TryGetStakeStateV2(context.Signer, out _)) { @@ -95,7 +95,7 @@ public override IWorld Execute(IActionContext context) $"{context.Signer} has already staked as versions above 2."); } - stakeState = new StakeState(stakeStateAddress, context.BlockIndex); + stakeState = new LegacyStakeState(stakeStateAddress, context.BlockIndex); return states .SetLegacyState( stakeStateAddress, @@ -136,7 +136,7 @@ public override IWorld Execute(IActionContext context) .TransferAsset(context, context.Signer, stakeState.address, targetStakeBalance) .SetLegacyState( stakeState.address, - new StakeState(stakeState.address, context.BlockIndex).SerializeV2()); + new LegacyStakeState(stakeState.address, context.BlockIndex).SerializeV2()); } } } diff --git a/Lib9c/Action/TransferAsset.cs b/Lib9c/Action/TransferAsset.cs index 2f042ab992..0d60c9c10f 100644 --- a/Lib9c/Action/TransferAsset.cs +++ b/Lib9c/Action/TransferAsset.cs @@ -127,7 +127,7 @@ public static void ThrowIfStakeState(IWorld state, Address recipient) { try { - _ = new StakeState(dictionary); + _ = new LegacyStakeState(dictionary); isStakeStateOrMonsterCollectionState = true; } catch (Exception) diff --git a/Lib9c/Model/Stake/StakeStateUtils.cs b/Lib9c/Model/Stake/StakeStateUtils.cs index 55ab65894d..a1598fa77e 100644 --- a/Lib9c/Model/Stake/StakeStateUtils.cs +++ b/Lib9c/Model/Stake/StakeStateUtils.cs @@ -86,7 +86,7 @@ public static bool TryMigrate( // - StakeStateV2.Contract.LockupInterval is StakeState.LockupInterval. // - StakeStateV2.StartedBlockIndex is StakeState.StartedBlockIndex. // - StakeStateV2.ReceivedBlockIndex is StakeState.ReceivedBlockIndex. - var stakeStateV1 = new StakeState(dict); + var stakeStateV1 = new LegacyStakeState(dict); var stakeRegularFixedRewardSheetTableName = stakeStateV1.StartedBlockIndex < gameConfigState.StakeRegularFixedRewardSheet_V2_StartBlockIndex @@ -123,8 +123,8 @@ public static bool TryMigrate( new Contract( stakeRegularFixedRewardSheetTableName: stakeRegularFixedRewardSheetTableName, stakeRegularRewardSheetTableName: stakeRegularRewardSheetTableName, - rewardInterval: StakeState.RewardInterval, - lockupInterval: StakeState.LockupInterval)); + rewardInterval: LegacyStakeState.RewardInterval, + lockupInterval: LegacyStakeState.LockupInterval)); } } } diff --git a/Lib9c/Model/Stake/StakeStateV2.cs b/Lib9c/Model/Stake/StakeStateV2.cs index 7b72cbe23e..ca3b76340d 100644 --- a/Lib9c/Model/Stake/StakeStateV2.cs +++ b/Lib9c/Model/Stake/StakeStateV2.cs @@ -11,7 +11,7 @@ namespace Nekoyume.Model.Stake public const int StateTypeVersion = 2; public static Address DeriveAddress(Address address) => - StakeState.DeriveAddress(address); + LegacyStakeState.DeriveAddress(address); public readonly Contract Contract; public readonly long StartedBlockIndex; @@ -58,12 +58,12 @@ public StakeStateV2( } public StakeStateV2( - StakeState stakeState, + LegacyStakeState legacyStakeState, Contract contract ) : this( contract, - stakeState?.StartedBlockIndex ?? throw new ArgumentNullException(nameof(stakeState)), - stakeState.ReceivedBlockIndex + legacyStakeState?.StartedBlockIndex ?? throw new ArgumentNullException(nameof(legacyStakeState)), + legacyStakeState.ReceivedBlockIndex ) { } diff --git a/Lib9c/Model/State/StakeState.cs b/Lib9c/Model/State/LegacyStakeState.cs similarity index 98% rename from Lib9c/Model/State/StakeState.cs rename to Lib9c/Model/State/LegacyStakeState.cs index 95f46bbc87..75483527da 100644 --- a/Lib9c/Model/State/StakeState.cs +++ b/Lib9c/Model/State/LegacyStakeState.cs @@ -9,7 +9,7 @@ namespace Nekoyume.Model.State { - public class StakeState : State + public class LegacyStakeState : State { public class StakeAchievements { @@ -68,14 +68,14 @@ public void Achieve(int level, int step) public StakeAchievements Achievements { get; private set; } - public StakeState(Address address, long startedBlockIndex) : base(address) + public LegacyStakeState(Address address, long startedBlockIndex) : base(address) { StartedBlockIndex = startedBlockIndex; CancellableBlockIndex = startedBlockIndex + LockupInterval; Achievements = new StakeAchievements(); } - public StakeState( + public LegacyStakeState( Address address, long startedBlockIndex, long receivedBlockIndex, @@ -89,7 +89,7 @@ StakeAchievements achievements Achievements = achievements; } - public StakeState(Dictionary serialized) : base(serialized) + public LegacyStakeState(Dictionary serialized) : base(serialized) { CancellableBlockIndex = (long)serialized[CancellableBlockIndexKey].ToBigInteger(); StartedBlockIndex = (long)serialized[StartedBlockIndexKey].ToBigInteger(); diff --git a/Lib9c/Module/LegacyModule.cs b/Lib9c/Module/LegacyModule.cs index f9cc7dd75d..e82b8745bb 100644 --- a/Lib9c/Module/LegacyModule.cs +++ b/Lib9c/Module/LegacyModule.cs @@ -880,15 +880,15 @@ public static (Address arenaInfoAddress, ArenaInfo arenaInfo, bool isNewArenaInf public static bool TryGetStakeState( this IWorldState worldState, Address agentAddress, - out StakeState stakeState) + out LegacyStakeState legacyStakeState) { - if (TryGetLegacyState(worldState, StakeState.DeriveAddress(agentAddress), out Dictionary dictionary)) + if (TryGetLegacyState(worldState, LegacyStakeState.DeriveAddress(agentAddress), out Dictionary dictionary)) { - stakeState = new StakeState(dictionary); + legacyStakeState = new LegacyStakeState(dictionary); return true; } - stakeState = null; + legacyStakeState = null; return false; } @@ -897,7 +897,7 @@ public static FungibleAssetValue GetStakedAmount( Address agentAddr) { var goldCurrency = GetGoldCurrency(worldState); - return worldState.GetBalance(StakeState.DeriveAddress(agentAddr), goldCurrency); + return worldState.GetBalance(LegacyStakeState.DeriveAddress(agentAddr), goldCurrency); } public static bool TryGetStakeStateV2( From 1aa0cbc2b14e985fb9358040bdea96773b328a63 Mon Sep 17 00:00:00 2001 From: Lee Dogeon Date: Thu, 29 Aug 2024 18:06:33 +0900 Subject: [PATCH 078/206] Rename `StakeStateV2` to `StakeState` --- .Lib9c.Tests/Action/ClaimStakeRewardTest.cs | 24 ++++++++--------- .../Scenario/StakeAndClaimScenarioTest.cs | 2 +- .Lib9c.Tests/Action/Stake2Test.cs | 2 +- .Lib9c.Tests/Action/StakeTest.cs | 12 ++++----- .Lib9c.Tests/Action/TransferAssetTest.cs | 2 +- .Lib9c.Tests/Action/TransferAssetsTest.cs | 2 +- ...{StakeStateV2Test.cs => StakeStateTest.cs} | 26 +++++++++---------- .../Model/Stake/StakeStateUtilsTest.cs | 4 +-- Lib9c/Action/ClaimStakeReward.cs | 2 +- Lib9c/Action/Stake.cs | 2 +- Lib9c/Action/TransferAsset.cs | 2 +- .../Stake/{StakeStateV2.cs => StakeState.cs} | 16 ++++++------ Lib9c/Model/Stake/StakeStateUtils.cs | 18 ++++++------- Lib9c/Module/LegacyModule.cs | 6 ++--- 14 files changed, 60 insertions(+), 60 deletions(-) rename .Lib9c.Tests/Model/Stake/{StakeStateV2Test.cs => StakeStateTest.cs} (84%) rename Lib9c/Model/Stake/{StakeStateV2.cs => StakeState.cs} (90%) diff --git a/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs b/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs index f5ad29f3cf..aa3b292b29 100644 --- a/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs +++ b/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs @@ -361,7 +361,7 @@ public void Execute_Throw_FailedLoadStateException_When_Staking_State_Null() AvatarAddr, 0)); - var stakeAddr = StakeStateV2.DeriveAddress(AgentAddr); + var stakeAddr = StakeState.DeriveAddress(AgentAddr); var previousState = _initialState.RemoveLegacyState(stakeAddr); Assert.Throws(() => Execute( @@ -417,7 +417,7 @@ public void Execute_Throw_RequiredBlockIndexException_With_StakeStateV2( long? receivedBlockIndex, long blockIndex) { - var stakeAddr = StakeStateV2.DeriveAddress(AgentAddr); + var stakeAddr = StakeState.DeriveAddress(AgentAddr); var stakeStateV2 = PrepareStakeStateV2( _stakePolicySheet, startedBlockIndex, @@ -437,7 +437,7 @@ public void Execute_Throw_RequiredBlockIndexException_With_StakeStateV2( [Fact] public void Execute_Throw_FailedLoadStateException_When_Sheet_Null() { - var stakeAddr = StakeStateV2.DeriveAddress(AgentAddr); + var stakeAddr = StakeState.DeriveAddress(AgentAddr); var stakeStateV2 = PrepareStakeStateV2(_stakePolicySheet, 0, null); var blockIndex = stakeStateV2.StartedBlockIndex + stakeStateV2.Contract.RewardInterval; var prevState = _initialState @@ -477,7 +477,7 @@ public void Execute_Throw_FailedLoadStateException_When_Sheet_Null() [InlineData(0)] public void Execute_Throw_InsufficientBalanceException(long stakedBalance) { - var stakeAddr = StakeStateV2.DeriveAddress(AgentAddr); + var stakeAddr = StakeState.DeriveAddress(AgentAddr); var stakeStateV2 = PrepareStakeStateV2(_stakePolicySheet, 0, null); var blockIndex = stakeStateV2.StartedBlockIndex + stakeStateV2.Contract.RewardInterval; var previousState = _initialState.SetLegacyState(stakeAddr, stakeStateV2.Serialize()); @@ -498,7 +498,7 @@ public void Execute_Throw_InsufficientBalanceException(long stakedBalance) [Fact] public void Execute_Throw_ArgumentNullException_When_Reward_CurrencyTicker_Null() { - var stakeAddr = StakeStateV2.DeriveAddress(AgentAddr); + var stakeAddr = StakeState.DeriveAddress(AgentAddr); var stakeStateV2 = PrepareStakeStateV2(_stakePolicySheet, 0, null); var blockIndex = stakeStateV2.StartedBlockIndex + stakeStateV2.Contract.RewardInterval; var prevState = _initialState @@ -528,7 +528,7 @@ public void Execute_Throw_ArgumentNullException_When_Reward_CurrencyTicker_Null( public void Execute_Throw_ArgumentNullException_When_Reward_CurrencyTicker_New_CurrencyDecimalPlaces_Null() { - var stakeAddr = StakeStateV2.DeriveAddress(AgentAddr); + var stakeAddr = StakeState.DeriveAddress(AgentAddr); var stakeStateV2 = PrepareStakeStateV2(_stakePolicySheet, 0, null); var blockIndex = stakeStateV2.StartedBlockIndex + stakeStateV2.Contract.RewardInterval; var prevState = _initialState @@ -606,7 +606,7 @@ public void Execute_Success_With_StakeStateV2( (Address balanceAddr, FungibleAssetValue fav)[] expectedBalances, (int itemSheetId, int count)[] expectedItems) { - var stakeAddr = StakeStateV2.DeriveAddress(AgentAddr); + var stakeAddr = StakeState.DeriveAddress(AgentAddr); var stakeStateV2 = PrepareStakeStateV2( _stakePolicySheet, startedBlockIndex, @@ -634,7 +634,7 @@ public void Execute_Success_With_StakeStateV2( public void Execute_V6() { var prevState = _initialState; - var stakeAddr = StakeStateV2.DeriveAddress(AgentAddr); + var stakeAddr = StakeState.DeriveAddress(AgentAddr); var stakePolicySheet = new StakePolicySheet(); stakePolicySheet.Set(StakePolicySheetFixtures.V6); var stakeStateV2 = PrepareStakeStateV2( @@ -685,15 +685,15 @@ public void Execute_V6() } } - private static StakeStateV2 PrepareStakeStateV2( + private static StakeState PrepareStakeStateV2( StakePolicySheet stakePolicySheet, long startedBlockIndex, long? receivedBlockIndex) { var contract = new Contract(stakePolicySheet); return receivedBlockIndex is null - ? new StakeStateV2(contract, startedBlockIndex) - : new StakeStateV2(contract, startedBlockIndex, receivedBlockIndex.Value); + ? new StakeState(contract, startedBlockIndex) + : new StakeState(contract, startedBlockIndex, receivedBlockIndex.Value); } private static IWorld Execute( @@ -702,7 +702,7 @@ private static IWorld Execute( Address avatarAddr, long blockIndex) { - var stakeAddr = StakeStateV2.DeriveAddress(agentAddr); + var stakeAddr = StakeState.DeriveAddress(agentAddr); var ncg = prevState.GetGoldCurrency(); var prevBalance = prevState.GetBalance(agentAddr, ncg); var prevStakedBalance = prevState.GetBalance(stakeAddr, ncg); diff --git a/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs index f5ed523960..bb8b1761b5 100644 --- a/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs @@ -242,7 +242,7 @@ private static void ValidateStakedStateV2( var stakeAddr = LegacyStakeState.DeriveAddress(agentAddr); var actualStakedAmount = state.GetBalance(stakeAddr, expectStakedAmount.Currency); Assert.Equal(expectStakedAmount, actualStakedAmount); - var stakeState = new StakeStateV2(state.GetLegacyState(stakeAddr)); + var stakeState = new StakeState(state.GetLegacyState(stakeAddr)); Assert.Equal(expectStartedBlockIndex, stakeState.StartedBlockIndex); Assert.Equal( expectStakeRegularFixedRewardSheetName, diff --git a/.Lib9c.Tests/Action/Stake2Test.cs b/.Lib9c.Tests/Action/Stake2Test.cs index 3fd8ff447f..1676135f68 100644 --- a/.Lib9c.Tests/Action/Stake2Test.cs +++ b/.Lib9c.Tests/Action/Stake2Test.cs @@ -255,7 +255,7 @@ public void RestrictForStakeStateV2() { PreviousState = _initialState.SetLegacyState( LegacyStakeState.DeriveAddress(_signerAddress), - new StakeStateV2( + new StakeState( new Contract( "StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index b752021f70..ca70b395ba 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -265,8 +265,8 @@ public void Execute_Throw_StakeExistingClaimableException_With_StakeStateV2( long previousAmount, long blockIndex) { - var stakeStateAddr = StakeStateV2.DeriveAddress(_agentAddr); - var stakeStateV2 = new StakeStateV2( + var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); + var stakeStateV2 = new StakeState( contract: new Contract(_stakePolicySheet), startedBlockIndex: previousStartedBlockIndex); var previousState = _initialState @@ -338,8 +338,8 @@ public void long blockIndex, long reducedAmount) { - var stakeStateAddr = StakeStateV2.DeriveAddress(_agentAddr); - var stakeStateV2 = new StakeStateV2( + var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); + var stakeStateV2 = new StakeState( contract: new Contract(_stakePolicySheet), startedBlockIndex: previousStartedBlockIndex, receivedBlockIndex: blockIndex); @@ -449,8 +449,8 @@ public void Execute_Success_When_Exist_StakeStateV2( long blockIndex, long amount) { - var stakeStateAddr = StakeStateV2.DeriveAddress(_agentAddr); - var stakeStateV2 = new StakeStateV2( + var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); + var stakeStateV2 = new StakeState( contract: new Contract(_stakePolicySheet), startedBlockIndex: previousStartedBlockIndex, receivedBlockIndex: blockIndex); diff --git a/.Lib9c.Tests/Action/TransferAssetTest.cs b/.Lib9c.Tests/Action/TransferAssetTest.cs index fb7eb7bd37..b3b0faac60 100644 --- a/.Lib9c.Tests/Action/TransferAssetTest.cs +++ b/.Lib9c.Tests/Action/TransferAssetTest.cs @@ -308,7 +308,7 @@ public void Execute_Throw_ArgumentException() PreviousState = baseState .SetLegacyState( LegacyStakeState.DeriveAddress(_recipient), - new StakeStateV2( + new StakeState( new Contract( "StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", diff --git a/.Lib9c.Tests/Action/TransferAssetsTest.cs b/.Lib9c.Tests/Action/TransferAssetsTest.cs index 4c391a37eb..a44bdc0e10 100644 --- a/.Lib9c.Tests/Action/TransferAssetsTest.cs +++ b/.Lib9c.Tests/Action/TransferAssetsTest.cs @@ -391,7 +391,7 @@ public void Execute_Throw_ArgumentException() PreviousState = baseState .SetLegacyState( LegacyStakeState.DeriveAddress(_recipient), - new StakeStateV2( + new StakeState( new Contract( "StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", diff --git a/.Lib9c.Tests/Model/Stake/StakeStateV2Test.cs b/.Lib9c.Tests/Model/Stake/StakeStateTest.cs similarity index 84% rename from .Lib9c.Tests/Model/Stake/StakeStateV2Test.cs rename to .Lib9c.Tests/Model/Stake/StakeStateTest.cs index d5c99b50ae..dfb2e67268 100644 --- a/.Lib9c.Tests/Model/Stake/StakeStateV2Test.cs +++ b/.Lib9c.Tests/Model/Stake/StakeStateTest.cs @@ -6,14 +6,14 @@ namespace Lib9c.Tests.Model.Stake using Nekoyume.Model.State; using Xunit; - public class StakeStateV2Test + public class StakeStateTest { [Fact] public void DeriveAddress() { var agentAddr = new PrivateKey().Address; var expectedStakeStateAddr = LegacyStakeState.DeriveAddress(agentAddr); - Assert.Equal(expectedStakeStateAddr, StakeStateV2.DeriveAddress(agentAddr)); + Assert.Equal(expectedStakeStateAddr, StakeState.DeriveAddress(agentAddr)); } [Theory] @@ -26,7 +26,7 @@ public void Constructor(long startedBlockIndex, long receivedBlockIndex) Contract.StakeRegularRewardSheetPrefix, 1, 1); - var state = new StakeStateV2(contract, startedBlockIndex, receivedBlockIndex); + var state = new StakeState(contract, startedBlockIndex, receivedBlockIndex); Assert.Equal(contract, state.Contract); Assert.Equal(startedBlockIndex, state.StartedBlockIndex); Assert.Equal(receivedBlockIndex, state.ReceivedBlockIndex); @@ -40,7 +40,7 @@ public void Constructor_Throw_ArgumentNullException( long receivedBlockIndex) { Assert.Throws(() => - new StakeStateV2(null, startedBlockIndex, receivedBlockIndex)); + new StakeState(null, startedBlockIndex, receivedBlockIndex)); } [Theory] @@ -52,7 +52,7 @@ public void Constructor_Throw_ArgumentOutOfRangeException( long receivedBlockIndex) { Assert.Throws(() => - new StakeStateV2(null, startedBlockIndex, receivedBlockIndex)); + new StakeState(null, startedBlockIndex, receivedBlockIndex)); } [Theory] @@ -75,7 +75,7 @@ public void Constructor_With_StakeState(long startedBlockIndex, long? receivedBl Contract.StakeRegularRewardSheetPrefix, 1, 1); - var stakeStateV2 = new StakeStateV2(stakeState, contract); + var stakeStateV2 = new StakeState(stakeState, contract); Assert.Equal(contract, stakeStateV2.Contract); Assert.Equal(stakeState.StartedBlockIndex, stakeStateV2.StartedBlockIndex); Assert.Equal(stakeState.ReceivedBlockIndex, stakeStateV2.ReceivedBlockIndex); @@ -90,8 +90,8 @@ public void Constructor_With_StakeState_Throw_ArgumentNullException() Contract.StakeRegularRewardSheetPrefix, 1, 1); - Assert.Throws(() => new StakeStateV2(null, contract)); - Assert.Throws(() => new StakeStateV2(stakeState, null)); + Assert.Throws(() => new StakeState(null, contract)); + Assert.Throws(() => new StakeState(stakeState, null)); } [Theory] @@ -104,9 +104,9 @@ public void Serde(long startedBlockIndex, long receivedBlockIndex) Contract.StakeRegularRewardSheetPrefix, 1, 1); - var state = new StakeStateV2(contract, startedBlockIndex, receivedBlockIndex); + var state = new StakeState(contract, startedBlockIndex, receivedBlockIndex); var ser = state.Serialize(); - var des = new StakeStateV2(ser); + var des = new StakeState(ser); Assert.Equal(state.Contract, des.Contract); Assert.Equal(state.StartedBlockIndex, des.StartedBlockIndex); Assert.Equal(state.ReceivedBlockIndex, des.ReceivedBlockIndex); @@ -122,11 +122,11 @@ public void Compare() Contract.StakeRegularRewardSheetPrefix, 1, 1); - var stateL = new StakeStateV2(contract, 0); - var stateR = new StakeStateV2(contract, 0); + var stateL = new StakeState(contract, 0); + var stateR = new StakeState(contract, 0); Assert.Equal(stateL, stateR); Assert.True(stateL == stateR); - stateR = new StakeStateV2(contract, 1); + stateR = new StakeState(contract, 1); Assert.NotEqual(stateL, stateR); Assert.True(stateL != stateR); } diff --git a/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs b/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs index a530f7e7e5..31bb72aa50 100644 --- a/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs +++ b/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs @@ -149,8 +149,8 @@ public void TryMigrate_Return_True_With_StakeStateV2( stakePolicySheet.Set(StakePolicySheetFixtures.V2); var contract = new Contract(stakePolicySheet); var stakeStateV2 = receivedBlockIndex is null - ? new StakeStateV2(contract, startedBlockIndex) - : new StakeStateV2(contract, receivedBlockIndex.Value); + ? new StakeState(contract, startedBlockIndex) + : new StakeState(contract, receivedBlockIndex.Value); state = state.SetLegacyState(stakeAddr, stakeStateV2.Serialize()); Assert.True(StakeStateUtils.TryMigrate(state, stakeAddr, out var result)); diff --git a/Lib9c/Action/ClaimStakeReward.cs b/Lib9c/Action/ClaimStakeReward.cs index 64470a18cd..d15423352f 100644 --- a/Lib9c/Action/ClaimStakeReward.cs +++ b/Lib9c/Action/ClaimStakeReward.cs @@ -148,7 +148,7 @@ public override IWorld Execute(IActionContext context) } // NOTE: update claimed block index. - stakeStateV2 = new StakeStateV2( + stakeStateV2 = new StakeState( stakeStateV2.Contract, stakeStateV2.StartedBlockIndex, context.BlockIndex); diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index 44f0ad56fd..faa24616ba 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -181,7 +181,7 @@ private static IWorld ContractNewStake( FungibleAssetValue targetStakeBalance, Contract latestStakeContract) { - var newStakeState = new StakeStateV2(latestStakeContract, context.BlockIndex); + var newStakeState = new StakeState(latestStakeContract, context.BlockIndex); if (stakedBalance.HasValue) { state = state.BurnAsset( diff --git a/Lib9c/Action/TransferAsset.cs b/Lib9c/Action/TransferAsset.cs index 0d60c9c10f..88781a2d42 100644 --- a/Lib9c/Action/TransferAsset.cs +++ b/Lib9c/Action/TransferAsset.cs @@ -181,7 +181,7 @@ public static void ThrowIfStakeState(IWorld state, Address recipient) { try { - _ = new StakeStateV2(serializedStakeStateV2); + _ = new StakeState(serializedStakeStateV2); isStakeStateOrMonsterCollectionState = true; } catch (Exception) diff --git a/Lib9c/Model/Stake/StakeStateV2.cs b/Lib9c/Model/Stake/StakeState.cs similarity index 90% rename from Lib9c/Model/Stake/StakeStateV2.cs rename to Lib9c/Model/Stake/StakeState.cs index ca3b76340d..d1d8268f1e 100644 --- a/Lib9c/Model/Stake/StakeStateV2.cs +++ b/Lib9c/Model/Stake/StakeState.cs @@ -5,7 +5,7 @@ namespace Nekoyume.Model.Stake { - public readonly struct StakeStateV2 : IState + public readonly struct StakeState : IState { public const string StateTypeName = "stake_state"; public const int StateTypeVersion = 2; @@ -31,7 +31,7 @@ out _ public long ClaimableBlockIndex => ClaimedBlockIndex + Contract.RewardInterval; - public StakeStateV2( + public StakeState( Contract contract, long startedBlockIndex, long receivedBlockIndex = 0) @@ -57,7 +57,7 @@ public StakeStateV2( ReceivedBlockIndex = receivedBlockIndex; } - public StakeStateV2( + public StakeState( LegacyStakeState legacyStakeState, Contract contract ) : this( @@ -68,7 +68,7 @@ Contract contract { } - public StakeStateV2(IValue serialized) + public StakeState(IValue serialized) { if (serialized is not List list) { @@ -102,7 +102,7 @@ list[1] is not Integer stateTypeVersionValue || (Integer)ReceivedBlockIndex ); - public bool Equals(StakeStateV2 other) + public bool Equals(StakeState other) { return Equals(Contract, other.Contract) && StartedBlockIndex == other.StartedBlockIndex && @@ -111,7 +111,7 @@ public bool Equals(StakeStateV2 other) public override bool Equals(object obj) { - return obj is StakeStateV2 other && Equals(other); + return obj is StakeState other && Equals(other); } public override int GetHashCode() @@ -125,12 +125,12 @@ public override int GetHashCode() } } - public static bool operator ==(StakeStateV2 left, StakeStateV2 right) + public static bool operator ==(StakeState left, StakeState right) { return left.Equals(right); } - public static bool operator !=(StakeStateV2 left, StakeStateV2 right) + public static bool operator !=(StakeState left, StakeState right) { return !(left == right); } diff --git a/Lib9c/Model/Stake/StakeStateUtils.cs b/Lib9c/Model/Stake/StakeStateUtils.cs index a1598fa77e..2ad2a6e818 100644 --- a/Lib9c/Model/Stake/StakeStateUtils.cs +++ b/Lib9c/Model/Stake/StakeStateUtils.cs @@ -11,37 +11,37 @@ public static class StakeStateUtils public static bool TryMigrate( IWorldState state, Address stakeStateAddr, - out StakeStateV2 stakeStateV2) + out StakeState stakeState) { var nullableStateState = Migrate(state.GetLegacyState(stakeStateAddr), state.GetGameConfigState()); if (nullableStateState is null) { - stakeStateV2 = default; + stakeState = default; return false; } - stakeStateV2 = nullableStateState.Value; + stakeState = nullableStateState.Value; return true; } public static bool TryMigrate( IValue serialized, GameConfigState gameConfigState, - out StakeStateV2 stakeStateV2) + out StakeState stakeState) { var nullableStateState = Migrate(serialized, gameConfigState); if (nullableStateState is null) { - stakeStateV2 = default; + stakeState = default; return false; } - stakeStateV2 = nullableStateState.Value; + stakeState = nullableStateState.Value; return true; } - public static StakeStateV2? Migrate( + public static StakeState? Migrate( IValue serialized, GameConfigState gameConfigState) { @@ -53,7 +53,7 @@ public static bool TryMigrate( // NOTE: StakeStateV2 is serialized as Bencodex List. if (serialized is List list) { - return new StakeStateV2(list); + return new StakeState(list); } // NOTE: StakeState is serialized as Bencodex Dictionary. @@ -118,7 +118,7 @@ public static bool TryMigrate( stakeRegularRewardSheetTableName = "StakeRegularRewardSheet_V5"; } - return new StakeStateV2( + return new StakeState( stakeStateV1, new Contract( stakeRegularFixedRewardSheetTableName: stakeRegularFixedRewardSheetTableName, diff --git a/Lib9c/Module/LegacyModule.cs b/Lib9c/Module/LegacyModule.cs index e82b8745bb..9d90cc3cff 100644 --- a/Lib9c/Module/LegacyModule.cs +++ b/Lib9c/Module/LegacyModule.cs @@ -903,13 +903,13 @@ public static FungibleAssetValue GetStakedAmount( public static bool TryGetStakeStateV2( this IWorldState worldState, Address agentAddr, - out StakeStateV2 stakeStateV2) + out StakeState stakeState) { - var stakeStateAddr = StakeStateV2.DeriveAddress(agentAddr); + var stakeStateAddr = StakeState.DeriveAddress(agentAddr); return StakeStateUtils.TryMigrate( worldState, stakeStateAddr, - out stakeStateV2); + out stakeState); } public static ArenaParticipants GetArenaParticipants( From 74b29d169191b35d590e3a533adc1bb0012ee626 Mon Sep 17 00:00:00 2001 From: Lee Dogeon Date: Thu, 29 Aug 2024 18:08:35 +0900 Subject: [PATCH 079/206] Rename `TryGetStakeState` to `TryGetLegacyStakeState` --- .Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs | 2 +- .Lib9c.Tests/Action/Stake2Test.cs | 10 +++++----- Lib9c/Action/MigrateMonsterCollection.cs | 2 +- Lib9c/Action/Stake2.cs | 2 +- Lib9c/Module/LegacyModule.cs | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs b/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs index 5b82558a7c..e892851b08 100644 --- a/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs +++ b/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs @@ -138,7 +138,7 @@ public void Execute(int collectionLevel, long claimBlockIndex, long receivedBloc RandomSeed = 0, }); - Assert.True(states.TryGetStakeState(_signer, out LegacyStakeState stakeState)); + Assert.True(states.TryGetLegacyStakeState(_signer, out LegacyStakeState stakeState)); Assert.Equal( 0 * currency, states.GetBalance(monsterCollectionState.address, currency)); diff --git a/.Lib9c.Tests/Action/Stake2Test.cs b/.Lib9c.Tests/Action/Stake2Test.cs index 1676135f68..4cc232559a 100644 --- a/.Lib9c.Tests/Action/Stake2Test.cs +++ b/.Lib9c.Tests/Action/Stake2Test.cs @@ -139,7 +139,7 @@ public void Execute_Throws_WhenCancelOrUpdateWhileLockup() })); // Same (since 4611070) - if (states.TryGetStakeState(_signerAddress, out LegacyStakeState stakeState)) + if (states.TryGetLegacyStakeState(_signerAddress, out LegacyStakeState stakeState)) { states = states.SetLegacyState( stakeState.address, @@ -160,7 +160,7 @@ public void Execute_Throws_WhenCancelOrUpdateWhileLockup() PreviousState = states, Signer = _signerAddress, BlockIndex = 4611070 - 99, - }).TryGetStakeState(_signerAddress, out stakeState)); + }).TryGetLegacyStakeState(_signerAddress, out stakeState)); Assert.Equal(4611070 - 99, stakeState.StartedBlockIndex); } @@ -180,7 +180,7 @@ public void Execute() _currency * 100, states.GetBalance(LegacyStakeState.DeriveAddress(_signerAddress), _currency)); - states.TryGetStakeState(_signerAddress, out LegacyStakeState stakeState); + states.TryGetLegacyStakeState(_signerAddress, out LegacyStakeState stakeState); Assert.Equal(0, stakeState.StartedBlockIndex); Assert.Equal(0 + LegacyStakeState.LockupInterval, stakeState.CancellableBlockIndex); Assert.Equal(0, stakeState.ReceivedBlockIndex); @@ -224,7 +224,7 @@ public void Update() BlockIndex = 0, }); - states.TryGetStakeState(_signerAddress, out LegacyStakeState stakeState); + states.TryGetLegacyStakeState(_signerAddress, out LegacyStakeState stakeState); Assert.Equal(0, stakeState.StartedBlockIndex); Assert.Equal(0 + LegacyStakeState.LockupInterval, stakeState.CancellableBlockIndex); Assert.Equal(0, stakeState.ReceivedBlockIndex); @@ -239,7 +239,7 @@ public void Update() BlockIndex = 1, }); - states.TryGetStakeState(_signerAddress, out stakeState); + states.TryGetLegacyStakeState(_signerAddress, out stakeState); Assert.Equal(1, stakeState.StartedBlockIndex); Assert.Equal(1 + LegacyStakeState.LockupInterval, stakeState.CancellableBlockIndex); Assert.Equal(0, stakeState.ReceivedBlockIndex); diff --git a/Lib9c/Action/MigrateMonsterCollection.cs b/Lib9c/Action/MigrateMonsterCollection.cs index 8d81b95887..1e4a9f0d53 100644 --- a/Lib9c/Action/MigrateMonsterCollection.cs +++ b/Lib9c/Action/MigrateMonsterCollection.cs @@ -53,7 +53,7 @@ public override IWorld Execute(IActionContext context) var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); var started = DateTimeOffset.UtcNow; Log.Debug("{AddressesHex}MigrateMonsterCollection exec started", addressesHex); - if (states.TryGetStakeState(context.Signer, out LegacyStakeState _)) + if (states.TryGetLegacyStakeState(context.Signer, out LegacyStakeState _)) { throw new InvalidOperationException("The user has already staked."); } diff --git a/Lib9c/Action/Stake2.cs b/Lib9c/Action/Stake2.cs index 9c30c30029..30a82fa07c 100644 --- a/Lib9c/Action/Stake2.cs +++ b/Lib9c/Action/Stake2.cs @@ -87,7 +87,7 @@ public override IWorld Execute(IActionContext context) } // Stake if it doesn't exist yet. - if (!states.TryGetStakeState(context.Signer, out LegacyStakeState stakeState)) + if (!states.TryGetLegacyStakeState(context.Signer, out LegacyStakeState stakeState)) { if (states.TryGetStakeStateV2(context.Signer, out _)) { diff --git a/Lib9c/Module/LegacyModule.cs b/Lib9c/Module/LegacyModule.cs index 9d90cc3cff..1747d0ecf0 100644 --- a/Lib9c/Module/LegacyModule.cs +++ b/Lib9c/Module/LegacyModule.cs @@ -877,7 +877,7 @@ public static (Address arenaInfoAddress, ArenaInfo arenaInfo, bool isNewArenaInf return (arenaInfoAddress, arenaInfo, isNew); } - public static bool TryGetStakeState( + public static bool TryGetLegacyStakeState( this IWorldState worldState, Address agentAddress, out LegacyStakeState legacyStakeState) From a74c4b5aee927004ec933b8ddb3bb750be7d8d8e Mon Sep 17 00:00:00 2001 From: Lee Dogeon Date: Thu, 29 Aug 2024 18:09:15 +0900 Subject: [PATCH 080/206] Rename `TryGetStakeStateV2` to `TryGetStakeState` --- .Lib9c.Tests/Action/ClaimStakeRewardTest.cs | 2 +- .Lib9c.Tests/Action/StakeTest.cs | 4 ++-- Lib9c/Action/ClaimStakeReward.cs | 2 +- Lib9c/Action/Stake.cs | 2 +- Lib9c/Action/Stake2.cs | 2 +- Lib9c/Module/LegacyModule.cs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs b/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs index aa3b292b29..77f831b50d 100644 --- a/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs +++ b/.Lib9c.Tests/Action/ClaimStakeRewardTest.cs @@ -717,7 +717,7 @@ private static IWorld Execute( Assert.Equal(prevBalance, nextBalance); var nextStakedBalance = nextState.GetBalance(stakeAddr, ncg); Assert.Equal(prevStakedBalance, nextStakedBalance); - Assert.True(nextState.TryGetStakeStateV2(agentAddr, out var stakeStateV2)); + Assert.True(nextState.TryGetStakeState(agentAddr, out var stakeStateV2)); Assert.Equal(blockIndex, stakeStateV2.ReceivedBlockIndex); Assert.True(stakeStateV2.ClaimedBlockIndex <= blockIndex); Assert.True(stakeStateV2.ClaimableBlockIndex > blockIndex); diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index ca70b395ba..bb202622f8 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -504,11 +504,11 @@ private IWorld Execute( if (amount == 0) { - Assert.False(nextState.TryGetStakeStateV2(_agentAddr, out _)); + Assert.False(nextState.TryGetStakeState(_agentAddr, out _)); } else if (amount > 0) { - Assert.True(nextState.TryGetStakeStateV2(_agentAddr, out var stakeStateV2)); + Assert.True(nextState.TryGetStakeState(_agentAddr, out var stakeStateV2)); Assert.Equal( _stakePolicySheet.StakeRegularFixedRewardSheetValue, stakeStateV2.Contract.StakeRegularFixedRewardSheetTableName); diff --git a/Lib9c/Action/ClaimStakeReward.cs b/Lib9c/Action/ClaimStakeReward.cs index d15423352f..6b13a75d98 100644 --- a/Lib9c/Action/ClaimStakeReward.cs +++ b/Lib9c/Action/ClaimStakeReward.cs @@ -58,7 +58,7 @@ public override IWorld Execute(IActionContext context) var states = context.PreviousState; var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); var stakeStateAddr = LegacyStakeState.DeriveAddress(context.Signer); - if (!states.TryGetStakeStateV2(context.Signer, out var stakeStateV2)) + if (!states.TryGetStakeState(context.Signer, out var stakeStateV2)) { throw new FailedLoadStateException( ActionTypeText, diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index faa24616ba..2eceaf502c 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -111,7 +111,7 @@ public override IWorld Execute(IActionContext context) var latestStakeContract = new Contract(stakePolicySheet); // NOTE: When the staking state is not exist. - if (!states.TryGetStakeStateV2(context.Signer, out var stakeStateV2)) + if (!states.TryGetStakeState(context.Signer, out var stakeStateV2)) { // NOTE: Cannot withdraw staking. if (Amount == 0) diff --git a/Lib9c/Action/Stake2.cs b/Lib9c/Action/Stake2.cs index 30a82fa07c..e330b0e575 100644 --- a/Lib9c/Action/Stake2.cs +++ b/Lib9c/Action/Stake2.cs @@ -89,7 +89,7 @@ public override IWorld Execute(IActionContext context) // Stake if it doesn't exist yet. if (!states.TryGetLegacyStakeState(context.Signer, out LegacyStakeState stakeState)) { - if (states.TryGetStakeStateV2(context.Signer, out _)) + if (states.TryGetStakeState(context.Signer, out _)) { throw new InvalidOperationException( $"{context.Signer} has already staked as versions above 2."); diff --git a/Lib9c/Module/LegacyModule.cs b/Lib9c/Module/LegacyModule.cs index 1747d0ecf0..554e928079 100644 --- a/Lib9c/Module/LegacyModule.cs +++ b/Lib9c/Module/LegacyModule.cs @@ -900,7 +900,7 @@ public static FungibleAssetValue GetStakedAmount( return worldState.GetBalance(LegacyStakeState.DeriveAddress(agentAddr), goldCurrency); } - public static bool TryGetStakeStateV2( + public static bool TryGetStakeState( this IWorldState worldState, Address agentAddr, out StakeState stakeState) From cc3ca5a1ce3d89794ace6ba4a9eb914bbfedae6f Mon Sep 17 00:00:00 2001 From: Lee Dogeon Date: Thu, 29 Aug 2024 19:43:41 +0900 Subject: [PATCH 081/206] Rename `StakeStateUtils.Migrate` to `StakeStateUtils.MigrateV1ToV2` --- Lib9c/Model/Stake/StakeStateUtils.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib9c/Model/Stake/StakeStateUtils.cs b/Lib9c/Model/Stake/StakeStateUtils.cs index 2ad2a6e818..674143513d 100644 --- a/Lib9c/Model/Stake/StakeStateUtils.cs +++ b/Lib9c/Model/Stake/StakeStateUtils.cs @@ -14,7 +14,7 @@ public static bool TryMigrate( out StakeState stakeState) { var nullableStateState = - Migrate(state.GetLegacyState(stakeStateAddr), state.GetGameConfigState()); + MigrateV1ToV2(state.GetLegacyState(stakeStateAddr), state.GetGameConfigState()); if (nullableStateState is null) { stakeState = default; @@ -30,7 +30,7 @@ public static bool TryMigrate( GameConfigState gameConfigState, out StakeState stakeState) { - var nullableStateState = Migrate(serialized, gameConfigState); + var nullableStateState = MigrateV1ToV2(serialized, gameConfigState); if (nullableStateState is null) { stakeState = default; @@ -41,7 +41,7 @@ public static bool TryMigrate( return true; } - public static StakeState? Migrate( + public static StakeState? MigrateV1ToV2( IValue serialized, GameConfigState gameConfigState) { From 6633a0b1a2992c8080f8b054bfa5b89d125f51f6 Mon Sep 17 00:00:00 2001 From: Lee Dogeon Date: Fri, 30 Aug 2024 10:09:31 +0900 Subject: [PATCH 082/206] Mint & Burn GuildCoin when staking --- .Lib9c.Tests/Action/StakeTest.cs | 3 +- .../Model/Stake/StakeStateUtilsTest.cs | 10 ++--- Lib9c/Action/ClaimStakeReward.cs | 16 +++++++ Lib9c/Action/Stake.cs | 43 +++++++++++++------ Lib9c/Model/Stake/StakeState.cs | 21 ++++++--- Lib9c/Model/Stake/StakeStateUtils.cs | 40 ++++++++++++++++- Lib9c/Module/LegacyModule.cs | 2 +- 7 files changed, 107 insertions(+), 28 deletions(-) diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index bb202622f8..58e156dd52 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -453,7 +453,8 @@ public void Execute_Success_When_Exist_StakeStateV2( var stakeStateV2 = new StakeState( contract: new Contract(_stakePolicySheet), startedBlockIndex: previousStartedBlockIndex, - receivedBlockIndex: blockIndex); + receivedBlockIndex: blockIndex, + stateVersion: 2); var previousState = _initialState .MintAsset( new ActionContext(), diff --git a/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs b/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs index 31bb72aa50..a391d01237 100644 --- a/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs +++ b/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs @@ -20,20 +20,20 @@ public class StakeStateUtilsTest public void TryMigrate_Throw_NullReferenceException_When_IAccountDelta_Null() { Assert.Throws(() => - StakeStateUtils.TryMigrate((IWorld)null, default, out _)); + StakeStateUtils.TryMigrateV1ToV2((IWorld)null, default, out _)); } [Fact] public void TryMigrate_Return_False_When_IValue_Null() { - Assert.False(StakeStateUtils.TryMigrate((IValue)null, default, out _)); + Assert.False(StakeStateUtils.TryMigrateV1ToV2((IValue)null, default, out _)); } [Fact] public void TryMigrate_Return_False_When_Staking_State_Null() { var state = new World(MockUtil.MockModernWorldState); - Assert.False(StakeStateUtils.TryMigrate(state, new PrivateKey().Address, out _)); + Assert.False(StakeStateUtils.TryMigrateV1ToV2(state, new PrivateKey().Address, out _)); } [Theory] @@ -120,7 +120,7 @@ public void TryMigrate_Return_True_With_StakeState( } state = state.SetLegacyState(stakeAddr, stakeState.Serialize()); - Assert.True(StakeStateUtils.TryMigrate(state, stakeAddr, out var stakeStateV2)); + Assert.True(StakeStateUtils.TryMigrateV1ToV2(state, stakeAddr, out var stakeStateV2)); Assert.Equal( stakeRegularFixedRewardSheetTableName, stakeStateV2.Contract.StakeRegularFixedRewardSheetTableName); @@ -153,7 +153,7 @@ public void TryMigrate_Return_True_With_StakeStateV2( : new StakeState(contract, receivedBlockIndex.Value); state = state.SetLegacyState(stakeAddr, stakeStateV2.Serialize()); - Assert.True(StakeStateUtils.TryMigrate(state, stakeAddr, out var result)); + Assert.True(StakeStateUtils.TryMigrateV1ToV2(state, stakeAddr, out var result)); Assert.Equal(stakeStateV2.Contract, result.Contract); Assert.Equal(stakeStateV2.StartedBlockIndex, result.StartedBlockIndex); Assert.Equal(stakeStateV2.ReceivedBlockIndex, result.ReceivedBlockIndex); diff --git a/Lib9c/Action/ClaimStakeReward.cs b/Lib9c/Action/ClaimStakeReward.cs index 6b13a75d98..98073aca4a 100644 --- a/Lib9c/Action/ClaimStakeReward.cs +++ b/Lib9c/Action/ClaimStakeReward.cs @@ -67,6 +67,22 @@ public override IWorld Execute(IActionContext context) stakeStateAddr); } + if (stakeStateV2.StateVersion == 2) + { + if (!StakeStateUtils.TryMigrateV2ToV3( + context, + states, + StakeState.DeriveAddress(context.Signer), + stakeStateV2, out var result)) + { + throw new InvalidOperationException( + "Failed to migrate stake state. Unexpected situation."); + } + + states = result.Value.world; + stakeStateV2 = result.Value.newStakeState; + } + if (stakeStateV2.ClaimableBlockIndex > context.BlockIndex) { throw new RequiredBlockIndexException( diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index 2eceaf502c..7733be8048 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -150,12 +150,21 @@ public override IWorld Execute(IActionContext context) } } + if (stakeStateV2.StateVersion == 2) + { + if (!StakeStateUtils.TryMigrateV2ToV3(context, states, stakeStateAddress, stakeStateV2, out var result)) + { + throw new InvalidOperationException("Failed to migration. Unexpected situation."); + } + + states = result.Value.world; + } + // NOTE: Withdraw staking. if (Amount == 0) { - return states - .RemoveLegacyState(stakeStateAddress) - .TransferAsset(context, stakeStateAddress, context.Signer, stakedBalance); + return Refund(context, states, stakeStateAddress, context.Signer, stakedBalance) + .RemoveLegacyState(stakeStateAddress); } // NOTE: Contract a new staking. @@ -182,25 +191,33 @@ private static IWorld ContractNewStake( Contract latestStakeContract) { var newStakeState = new StakeState(latestStakeContract, context.BlockIndex); + if (stakedBalance.HasValue) { - state = state.BurnAsset( - context, - context.Signer, - GetGuildCoinFromNCG(stakedBalance.Value) - ).TransferAsset( - context, - stakeStateAddr, - context.Signer, - stakedBalance.Value); + state = Refund(context, state, stakeStateAddr, context.Signer, stakedBalance.Value); } return state .TransferAsset(context, context.Signer, stakeStateAddr, targetStakeBalance) - .MintAsset(context, context.Signer, GetGuildCoinFromNCG(targetStakeBalance)) + .MintAsset(context, stakeStateAddr, GetGuildCoinFromNCG(targetStakeBalance)) .SetLegacyState(stakeStateAddr, newStakeState.Serialize()); } + private static IWorld Refund( + IActionContext context, IWorld world, Address stakeStateAddress, Address signer, + FungibleAssetValue stakedBalance) + { + return world.BurnAsset( + context, + stakeStateAddress, + GetGuildCoinFromNCG(stakedBalance) + ).TransferAsset( + context, + stakeStateAddress, + signer, + stakedBalance); + } + private static FungibleAssetValue GetGuildCoinFromNCG(FungibleAssetValue balance) { return FungibleAssetValue.Parse(Currencies.GuildGold, diff --git a/Lib9c/Model/Stake/StakeState.cs b/Lib9c/Model/Stake/StakeState.cs index d1d8268f1e..6ac1e628c7 100644 --- a/Lib9c/Model/Stake/StakeState.cs +++ b/Lib9c/Model/Stake/StakeState.cs @@ -8,11 +8,12 @@ namespace Nekoyume.Model.Stake public readonly struct StakeState : IState { public const string StateTypeName = "stake_state"; - public const int StateTypeVersion = 2; + public const int LatestStateTypeVersion = 3; public static Address DeriveAddress(Address address) => LegacyStakeState.DeriveAddress(address); + public readonly int StateVersion; public readonly Contract Contract; public readonly long StartedBlockIndex; public readonly long ReceivedBlockIndex; @@ -34,7 +35,8 @@ out _ public StakeState( Contract contract, long startedBlockIndex, - long receivedBlockIndex = 0) + long receivedBlockIndex = 0, + int stateVersion = LatestStateTypeVersion) { if (startedBlockIndex < 0) { @@ -55,15 +57,18 @@ public StakeState( Contract = contract ?? throw new ArgumentNullException(nameof(contract)); StartedBlockIndex = startedBlockIndex; ReceivedBlockIndex = receivedBlockIndex; + StateVersion = stateVersion; } + // Migration constructor V1 to V2. public StakeState( LegacyStakeState legacyStakeState, Contract contract ) : this( contract, legacyStakeState?.StartedBlockIndex ?? throw new ArgumentNullException(nameof(legacyStakeState)), - legacyStakeState.ReceivedBlockIndex + legacyStakeState.ReceivedBlockIndex, + stateVersion: 2 ) { } @@ -80,7 +85,8 @@ public StakeState(IValue serialized) if (list[0] is not Text stateTypeNameValue || stateTypeNameValue != StateTypeName || list[1] is not Integer stateTypeVersionValue || - stateTypeVersionValue.Value != StateTypeVersion) + stateTypeVersionValue.Value == 1 || + stateTypeVersionValue.Value > LatestStateTypeVersion) { throw new ArgumentException( nameof(serialized), @@ -89,6 +95,7 @@ list[1] is not Integer stateTypeVersionValue || const int reservedCount = 2; + StateVersion = stateTypeVersionValue; Contract = new Contract(list[reservedCount]); StartedBlockIndex = (Integer)list[reservedCount + 1]; ReceivedBlockIndex = (Integer)list[reservedCount + 2]; @@ -96,7 +103,7 @@ list[1] is not Integer stateTypeVersionValue || public IValue Serialize() => new List( (Text)StateTypeName, - (Integer)StateTypeVersion, + (Integer)StateVersion, Contract.Serialize(), (Integer)StartedBlockIndex, (Integer)ReceivedBlockIndex @@ -106,7 +113,8 @@ public bool Equals(StakeState other) { return Equals(Contract, other.Contract) && StartedBlockIndex == other.StartedBlockIndex && - ReceivedBlockIndex == other.ReceivedBlockIndex; + ReceivedBlockIndex == other.ReceivedBlockIndex && + StateVersion == other.StateVersion; } public override bool Equals(object obj) @@ -121,6 +129,7 @@ public override int GetHashCode() var hashCode = (Contract != null ? Contract.GetHashCode() : 0); hashCode = (hashCode * 397) ^ StartedBlockIndex.GetHashCode(); hashCode = (hashCode * 397) ^ ReceivedBlockIndex.GetHashCode(); + hashCode = (hashCode * 397) ^ StateVersion.GetHashCode(); return hashCode; } } diff --git a/Lib9c/Model/Stake/StakeStateUtils.cs b/Lib9c/Model/Stake/StakeStateUtils.cs index 674143513d..92a4b383c6 100644 --- a/Lib9c/Model/Stake/StakeStateUtils.cs +++ b/Lib9c/Model/Stake/StakeStateUtils.cs @@ -1,6 +1,11 @@ +using System; +using System.Diagnostics.CodeAnalysis; using Bencodex.Types; +using Lib9c; +using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; +using Libplanet.Types.Assets; using Nekoyume.Model.State; using Nekoyume.Module; @@ -8,7 +13,7 @@ namespace Nekoyume.Model.Stake { public static class StakeStateUtils { - public static bool TryMigrate( + public static bool TryMigrateV1ToV2( IWorldState state, Address stakeStateAddr, out StakeState stakeState) @@ -25,7 +30,7 @@ public static bool TryMigrate( return true; } - public static bool TryMigrate( + public static bool TryMigrateV1ToV2( IValue serialized, GameConfigState gameConfigState, out StakeState stakeState) @@ -126,5 +131,36 @@ public static bool TryMigrate( rewardInterval: LegacyStakeState.RewardInterval, lockupInterval: LegacyStakeState.LockupInterval)); } + + public static bool TryMigrateV2ToV3( + IActionContext context, + IWorld world, + Address stakeStateAddr, + StakeState stakeState, + [NotNullWhen(true)] + out (IWorld world, StakeState newStakeState)? result + ) + { + if (stakeState.StateVersion != 2) + { + result = null; + return false; + } + + var goldCurrency = world.GetGoldCurrency(); + var goldBalance = world.GetBalance(stakeStateAddr, goldCurrency); + var newStakeState = new StakeState( + stakeState.Contract, + stakeState.StartedBlockIndex, + stakeState.ReceivedBlockIndex, + stateVersion: 3); + + result = ( + world.MintAsset(context, stakeStateAddr, + FungibleAssetValue.Parse(Currencies.GuildGold, + goldBalance.GetQuantityString(true))) + .SetLegacyState(stakeStateAddr, newStakeState.Serialize()), newStakeState); + return true; + } } } diff --git a/Lib9c/Module/LegacyModule.cs b/Lib9c/Module/LegacyModule.cs index 554e928079..f9a9b01769 100644 --- a/Lib9c/Module/LegacyModule.cs +++ b/Lib9c/Module/LegacyModule.cs @@ -906,7 +906,7 @@ public static bool TryGetStakeState( out StakeState stakeState) { var stakeStateAddr = StakeState.DeriveAddress(agentAddr); - return StakeStateUtils.TryMigrate( + return StakeStateUtils.TryMigrateV1ToV2( worldState, stakeStateAddr, out stakeState); From 830ff03dde3692da487544b064a69a1e41936bd1 Mon Sep 17 00:00:00 2001 From: Lee Dogeon Date: Fri, 30 Aug 2024 13:36:20 +0900 Subject: [PATCH 083/206] Complete tests related with staking --- .Lib9c.Tests/Action/StakeTest.cs | 66 ++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index 58e156dd52..6ceba5fd62 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -416,12 +416,20 @@ public void Execute_Success_When_Exist_StakeState( stakeStateAddr, _ncg * previousAmount) .SetLegacyState(stakeStateAddr, stakeState.Serialize()); - Execute( + var nextState = Execute( blockIndex, previousState, new TestRandom(), _agentAddr, amount); + + if (amount == 0) + { + return; + } + + Assert.True(nextState.TryGetStakeState(_agentAddr, out StakeState newStakeState)); + Assert.Equal(3, newStakeState.StateVersion); } [Theory] @@ -466,12 +474,59 @@ public void Execute_Success_When_Exist_StakeStateV2( stakeStateAddr, _ncg * previousAmount) .SetLegacyState(stakeStateAddr, stakeStateV2.Serialize()); - Execute( + var nextState = Execute( blockIndex, previousState, new TestRandom(), _agentAddr, amount); + + if (amount == 0) + { + return; + } + + Assert.True(nextState.TryGetStakeState(_agentAddr, out StakeState stakeState)); + Assert.Equal(3, stakeState.StateVersion); + } + + [Fact] + public void Execute_Success_When_Exist_StakeStateV3() + { + var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); + var stakeStateV2 = new StakeState( + contract: new Contract(_stakePolicySheet), + startedBlockIndex: 0, + receivedBlockIndex: 0, + stateVersion: 3); + + var previousState = _initialState + .MintAsset( + new ActionContext(), + _agentAddr, + _ncg * 100) + .TransferAsset( + new ActionContext(), + _agentAddr, + stakeStateAddr, + _ncg * 50) + .MintAsset(new ActionContext(), stakeStateAddr, Currencies.GuildGold * 50) + .SetLegacyState(stakeStateAddr, stakeStateV2.Serialize()); + + var action = new Stake(100); + var nextState = action.Execute(new ActionContext + { + BlockIndex = 0, + Signer = _agentAddr, + PreviousState = previousState, + }); + + Assert.Equal( + Currencies.GuildGold * 100, + nextState.GetBalance(stakeStateAddr, Currencies.GuildGold)); + Assert.Equal( + _ncg * 100, + nextState.GetBalance(stakeStateAddr, _ncg)); } private IWorld Execute( @@ -495,13 +550,18 @@ private IWorld Execute( Signer = signer, }); + var stakeStateAddress = LegacyStakeState.DeriveAddress(signer); + var amountNCG = _ncg * amount; var nextBalance = nextState.GetBalance(signer, _ncg); var nextStakeBalance = nextState.GetBalance( - LegacyStakeState.DeriveAddress(signer), + stakeStateAddress, _ncg); Assert.Equal(previousTotalBalance - amountNCG, nextBalance); Assert.Equal(amountNCG, nextStakeBalance); + Assert.Equal( + Currencies.GuildGold * amount, + nextState.GetBalance(stakeStateAddress, Currencies.GuildGold)); if (amount == 0) { From 9f55b1a2d8afb9b2a7c85b3a2beb2024982f5dc6 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 16 Oct 2024 19:32:13 +0900 Subject: [PATCH 084/206] fix: Fix namespace change --- Lib9c/Model/Guild/GuildParticipant.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Model/Guild/GuildParticipant.cs b/Lib9c/Model/Guild/GuildParticipant.cs index 632343de0a..ba0c37b9b7 100644 --- a/Lib9c/Model/Guild/GuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildParticipant.cs @@ -4,7 +4,7 @@ using Libplanet.Crypto; using Nekoyume.Action; using Nekoyume.Delegation; -using Nekoyume.Model.State; +using Nekoyume.Model.Stake; using Nekoyume.TypedAddress; namespace Nekoyume.Model.Guild From a99be885144acf42900e801979e184082010acce Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 16 Oct 2024 21:14:14 +0900 Subject: [PATCH 085/206] feat: Add reward address to delegator Co-authored-by: s2quake --- Lib9c/Delegation/Delegatee.cs | 2 +- Lib9c/Delegation/Delegator.cs | 6 +++++- Lib9c/Delegation/DelegatorMetadata.cs | 15 +++++++++++++-- Lib9c/Delegation/IDelegator.cs | 2 ++ 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 8adbb3563a..530ca1d372 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -261,7 +261,7 @@ public void DistributeReward(T delegator, long height) FungibleAssetValue reward = record.RewardsDuringPeriod(share); if (reward.Sign > 0) { - Repository.TransferAsset(record.Address, delegator.Address, reward); + Repository.TransferAsset(record.Address, delegator.RewardAddress, reward); } LumpSumRewardsRecord newRecord = record.RemoveDelegator(delegator.Address); diff --git a/Lib9c/Delegation/Delegator.cs b/Lib9c/Delegation/Delegator.cs index 33ffc6699d..d46d88caf3 100644 --- a/Lib9c/Delegation/Delegator.cs +++ b/Lib9c/Delegation/Delegator.cs @@ -16,12 +16,14 @@ public Delegator( Address address, Address accountAddress, Address delegationPoolAddress, + Address rewardAddress, IDelegationRepository repository) : this( new DelegatorMetadata( address, accountAddress, - delegationPoolAddress), + delegationPoolAddress, + rewardAddress), repository) { } @@ -51,6 +53,8 @@ private Delegator(DelegatorMetadata metadata, IDelegationRepository repository) public Address DelegationPoolAddress => Metadata.DelegationPoolAddress; + public Address RewardAddress => Metadata.RewardAddress; + public ImmutableSortedSet
Delegatees => Metadata.Delegatees; public List MetadataBencoded => Metadata.Bencoded; diff --git a/Lib9c/Delegation/DelegatorMetadata.cs b/Lib9c/Delegation/DelegatorMetadata.cs index bd95e15334..9fe2be2088 100644 --- a/Lib9c/Delegation/DelegatorMetadata.cs +++ b/Lib9c/Delegation/DelegatorMetadata.cs @@ -15,11 +15,13 @@ public class DelegatorMetadata : IDelegatorMetadata public DelegatorMetadata( Address address, Address accountAddress, - Address delegationPoolAddress) + Address delegationPoolAddress, + Address rewardAddress) : this( address, accountAddress, delegationPoolAddress, + rewardAddress, ImmutableSortedSet
.Empty) { } @@ -40,7 +42,8 @@ public DelegatorMetadata( address, accountAddress, new Address(bencoded[0]), - ((List)bencoded[1]).Select(item => new Address(item)).ToImmutableSortedSet()) + new Address(bencoded[1]), + ((List)bencoded[2]).Select(item => new Address(item)).ToImmutableSortedSet()) { } @@ -48,11 +51,13 @@ private DelegatorMetadata( Address address, Address accountAddress, Address delegationPoolAddress, + Address rewardAddress, ImmutableSortedSet
delegatees) { DelegatorAddress = address; DelegatorAccountAddress = accountAddress; DelegationPoolAddress = delegationPoolAddress; + RewardAddress = rewardAddress; Delegatees = delegatees; } @@ -67,11 +72,14 @@ public Address Address public Address DelegationPoolAddress { get; } + public Address RewardAddress { get; } + public ImmutableSortedSet
Delegatees { get; private set; } public List Bencoded => List.Empty .Add(DelegationPoolAddress.Bencoded) + .Add(RewardAddress.Bencoded) .Add(new List(Delegatees.Select(a => a.Bencoded))); IValue IBencodable.Bencoded => Bencoded; @@ -94,6 +102,9 @@ public virtual bool Equals(IDelegator? other) || (other is DelegatorMetadata delegator && GetType() != delegator.GetType() && DelegatorAddress.Equals(delegator.DelegatorAddress) + && DelegatorAccountAddress.Equals(delegator.DelegatorAccountAddress) + && DelegationPoolAddress.Equals(delegator.DelegationPoolAddress) + && RewardAddress.Equals(delegator.RewardAddress) && Delegatees.SequenceEqual(delegator.Delegatees)); public override int GetHashCode() diff --git a/Lib9c/Delegation/IDelegator.cs b/Lib9c/Delegation/IDelegator.cs index 4b42ecb2b3..ee8e375982 100644 --- a/Lib9c/Delegation/IDelegator.cs +++ b/Lib9c/Delegation/IDelegator.cs @@ -15,6 +15,8 @@ public interface IDelegator Address DelegationPoolAddress { get; } + Address RewardAddress { get; } + ImmutableSortedSet
Delegatees { get; } void Delegate( From 4f2588a0e50ea35970f20f2c8afdbdfc1ddc908f Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 16 Oct 2024 21:32:41 +0900 Subject: [PATCH 086/206] feat: Change parameters for Guild, Validators for guild system Co-authored-by: s2quake --- Lib9c/Model/Guild/Guild.cs | 52 +++++++++++-------- Lib9c/Model/Guild/GuildParticipant.cs | 40 +++++++------- .../ValidatorDelegation/ValidatorDelegatee.cs | 3 +- .../ValidatorDelegation/ValidatorDelegator.cs | 2 + 4 files changed, 53 insertions(+), 44 deletions(-) diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index 04c071d3af..667f7ad874 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -10,16 +10,19 @@ namespace Nekoyume.Model.Guild { - public class Guild : Delegatee, IEquatable, IBencodable + public class Guild : Delegatee, IBencodable, IEquatable { private const string StateTypeName = "guild"; private const long StateVersion = 1; public readonly AgentAddress GuildMasterAddress; + public readonly Address ValidatorAddress; + public Guild( - Address address, + GuildAddress address, AgentAddress guildMasterAddress, + Address validatorAddress, Currency rewardCurrency, GuildRepository repository) : base( @@ -30,30 +33,27 @@ public Guild( delegationPoolAddress: address, rewardRemainderPoolAddress: Addresses.CommunityPool, slashedPoolAddress: Addresses.CommunityPool, - unbondingPeriod: 75600L, - maxUnbondLockInEntries: 10, - maxRebondGraceEntries: 10, + unbondingPeriod: 0L, + maxUnbondLockInEntries: 0, + maxRebondGraceEntries: 0, repository: repository) { + ValidatorAddress = validatorAddress; GuildMasterAddress = guildMasterAddress; } public Guild( - Address address, - IValue bencoded, GuildRepository repository) - : this(address: address, bencoded: (List)bencoded, repository: repository) - { - } - - public Guild( - Address address, List bencoded, GuildRepository repository) - : base( - address: address, - repository: repository) + GuildAddress address, + IValue bencoded, + GuildRepository repository) + : base(address: address, repository: repository) { - GuildMasterAddress = new AgentAddress(bencoded[2]); + if (bencoded is not List list) + { + throw new InvalidCastException(); + } - if (bencoded[0] is not Text text || text != StateTypeName || bencoded[1] is not Integer integer) + if (list[0] is not Text text || text != StateTypeName || list[1] is not Integer integer) { throw new InvalidCastException(); } @@ -62,12 +62,18 @@ public Guild( { throw new FailedLoadStateException("Un-deserializable state."); } + + GuildMasterAddress = new AgentAddress(list[2]); + ValidatorAddress = new AgentAddress(list[3]); } + public new GuildAddress Address => new GuildAddress(base.Address); + public List Bencoded => List.Empty .Add(StateTypeName) .Add(StateVersion) - .Add(GuildMasterAddress.Bencoded); + .Add(GuildMasterAddress.Bencoded) + .Add(ValidatorAddress.Bencoded); IValue IBencodable.Bencoded => Bencoded; @@ -75,8 +81,10 @@ public bool Equals(Guild other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return GuildMasterAddress.Equals(other.GuildMasterAddress) - && Metadata.Equals(other.Metadata); + return Address.Equals(other.Address) + && GuildMasterAddress.Equals(other.GuildMasterAddress) + && ValidatorAddress.Equals(other.ValidatorAddress) + && Metadata.Equals(other.Metadata); } public override bool Equals(object obj) @@ -89,7 +97,7 @@ public override bool Equals(object obj) public override int GetHashCode() { - return GuildMasterAddress.GetHashCode(); + return HashCode.Combine(Address, GuildMasterAddress, ValidatorAddress); } } } diff --git a/Lib9c/Model/Guild/GuildParticipant.cs b/Lib9c/Model/Guild/GuildParticipant.cs index ba0c37b9b7..a7439f7f66 100644 --- a/Lib9c/Model/Guild/GuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildParticipant.cs @@ -1,7 +1,6 @@ using System; using Bencodex; using Bencodex.Types; -using Libplanet.Crypto; using Nekoyume.Action; using Nekoyume.Delegation; using Nekoyume.Model.Stake; @@ -17,37 +16,31 @@ public class GuildParticipant : Delegator, IBencodable, public readonly GuildAddress GuildAddress; public GuildParticipant( - Address address, + AgentAddress address, GuildAddress guildAddress, GuildRepository repository) : base( address: address, accountAddress: Addresses.GuildParticipant, - delegationPoolAddress: StakeState.DeriveAddress(address), + delegationPoolAddress: address, + rewardAddress: address, repository: repository) { GuildAddress = guildAddress; } public GuildParticipant( - Address address, + AgentAddress address, IValue bencoded, GuildRepository repository) - : this(address, (List)bencoded, repository) + : base(address: address, repository: repository) { - } - - public GuildParticipant( - Address address, - List bencoded, - GuildRepository repository) - : base( - address: address, - repository: repository) - { - GuildAddress = new GuildAddress(bencoded[2]); + if (bencoded is not List list) + { + throw new InvalidCastException(); + } - if (bencoded[0] is not Text text || text != StateTypeName || bencoded[1] is not Integer integer) + if (list[0] is not Text text || text != StateTypeName || list[1] is not Integer integer) { throw new InvalidCastException(); } @@ -56,8 +49,12 @@ public GuildParticipant( { throw new FailedLoadStateException("Un-deserializable state."); } + + GuildAddress = new GuildAddress(list[2]); } + public new AgentAddress Address => new AgentAddress(base.Address); + public List Bencoded => List.Empty .Add(StateTypeName) .Add(StateVersion) @@ -69,8 +66,9 @@ public bool Equals(GuildParticipant other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return GuildAddress.Equals(other.GuildAddress) - && Metadata.Equals(other.Metadata); + return Address.Equals(other.Address) + && GuildAddress.Equals(other.GuildAddress) + && Metadata.Equals(other.Metadata); } public override bool Equals(object obj) @@ -78,12 +76,12 @@ public override bool Equals(object obj) if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; - return Equals((GuildParticipant)obj); + return Equals((Guild)obj); } public override int GetHashCode() { - return GuildAddress.GetHashCode(); + return HashCode.Combine(Address, GuildAddress); } } } diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index 4994bfd7b4..740f9599f0 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -19,6 +19,7 @@ public sealed class ValidatorDelegatee public ValidatorDelegatee( Address address, PublicKey publicKey, + Currency delegationCurrency, Currency rewardCurrency, BigInteger commissionPercentage, long creationHeight, @@ -26,7 +27,7 @@ public ValidatorDelegatee( : base( address: address, accountAddress: repository.DelegateeAccountAddress, - delegationCurrency: rewardCurrency, + delegationCurrency: delegationCurrency, rewardCurrency: rewardCurrency, delegationPoolAddress: UnbondedPoolAddress, rewardRemainderPoolAddress: Addresses.CommunityPool, diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegator.cs b/Lib9c/ValidatorDelegation/ValidatorDelegator.cs index bdaf71f9e2..b3f045b1b2 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegator.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegator.cs @@ -10,11 +10,13 @@ public sealed class ValidatorDelegator : Delegator Date: Wed, 16 Oct 2024 21:33:44 +0900 Subject: [PATCH 087/206] feat: Change concrete repositories Co-authored-by: s2quake --- Lib9c/Model/Guild/GuildRepository.cs | 7 +++++-- Lib9c/ValidatorDelegation/ValidatorRepository.cs | 10 +++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Lib9c/Model/Guild/GuildRepository.cs b/Lib9c/Model/Guild/GuildRepository.cs index aba15d7d25..329eeec663 100644 --- a/Lib9c/Model/Guild/GuildRepository.cs +++ b/Lib9c/Model/Guild/GuildRepository.cs @@ -29,7 +29,7 @@ public GuildRepository(IWorld world, IActionContext context) public Guild GetGuild(Address address) => delegateeAccount.GetState(address) is IValue bencoded ? new Guild( - new AgentAddress(address), + new GuildAddress(address), bencoded, this) : throw new FailedLoadStateException("Guild does not exist."); @@ -40,7 +40,10 @@ public override IDelegatee GetDelegatee(Address address) public GuildParticipant GetGuildParticipant(Address address) => delegatorAccount.GetState(address) is IValue bencoded - ? new GuildParticipant(address, bencoded, this) + ? new GuildParticipant( + new AgentAddress(address), + bencoded, + this) : throw new FailedLoadStateException("Delegator does not exist."); public override IDelegator GetDelegator(Address address) diff --git a/Lib9c/ValidatorDelegation/ValidatorRepository.cs b/Lib9c/ValidatorDelegation/ValidatorRepository.cs index e8dc5353ff..1dd709a9cf 100644 --- a/Lib9c/ValidatorDelegation/ValidatorRepository.cs +++ b/Lib9c/ValidatorDelegation/ValidatorRepository.cs @@ -46,7 +46,7 @@ public ValidatorDelegatee GetValidatorDelegatee(Address address) public override IDelegatee GetDelegatee(Address address) => GetValidatorDelegatee(address); - public ValidatorDelegator GetValidatorDelegator(Address address) + public ValidatorDelegator GetValidatorDelegator(Address address, Address rewardAddress) { try { @@ -55,12 +55,16 @@ public ValidatorDelegator GetValidatorDelegator(Address address) catch (FailedLoadStateException) { // TODO: delegationPoolAddress have to be changed after guild system is implemented. - return new ValidatorDelegator(address, address, this); + return new ValidatorDelegator( + address, + address, + rewardAddress, + this); } } public override IDelegator GetDelegator(Address address) - => GetValidatorDelegator(address); + => new ValidatorDelegator(address, this); public ValidatorList GetValidatorList() { From 839412679551f8cef97a6041928399ad0ce0dc36 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 16 Oct 2024 21:45:14 +0900 Subject: [PATCH 088/206] feat: Remove GuildApplication Co-authored-by: s2quake --- Lib9c/Action/Guild/AcceptGuildApplication.cs | 60 -------- Lib9c/Action/Guild/ApplyGuild.cs | 62 --------- Lib9c/Action/Guild/CancelGuildApplication.cs | 44 ------ Lib9c/Action/Guild/RejectGuildApplication.cs | 60 -------- Lib9c/Model/Guild/GuildApplication.cs | 41 ------ Lib9c/Module/Guild/GuildApplicationModule.cs | 139 ------------------- Lib9c/Module/Guild/GuildBanModule.cs | 11 +- 7 files changed, 5 insertions(+), 412 deletions(-) delete mode 100644 Lib9c/Action/Guild/AcceptGuildApplication.cs delete mode 100644 Lib9c/Action/Guild/ApplyGuild.cs delete mode 100644 Lib9c/Action/Guild/CancelGuildApplication.cs delete mode 100644 Lib9c/Action/Guild/RejectGuildApplication.cs delete mode 100644 Lib9c/Model/Guild/GuildApplication.cs delete mode 100644 Lib9c/Module/Guild/GuildApplicationModule.cs diff --git a/Lib9c/Action/Guild/AcceptGuildApplication.cs b/Lib9c/Action/Guild/AcceptGuildApplication.cs deleted file mode 100644 index a86640bf35..0000000000 --- a/Lib9c/Action/Guild/AcceptGuildApplication.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using Bencodex.Types; -using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Extensions; -using Nekoyume.Model.Guild; -using Nekoyume.Module.Guild; -using Nekoyume.TypedAddress; - -namespace Nekoyume.Action.Guild -{ - // TODO(GUILD-FEATURE): Enable again when Guild features are enabled. - // [ActionType(TypeIdentifier)] - public class AcceptGuildApplication : ActionBase - { - public const string TypeIdentifier = "accept_guild_application"; - - private const string TargetKey = "t"; - - public AcceptGuildApplication() {} - - public AcceptGuildApplication(AgentAddress target) - { - Target = target; - } - - public AgentAddress Target { get; private set; } - - public override IValue PlainValue => Dictionary.Empty - .Add("type_id", TypeIdentifier) - .Add("values", Dictionary.Empty - .Add(TargetKey, Target.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 Dictionary values || - !values.TryGetValue((Text)TargetKey, out var rawTargetAddress)) - { - throw new InvalidCastException(); - } - - Target = new AgentAddress(rawTargetAddress); - } - - public override IWorld Execute(IActionContext context) - { - GasTracer.UseGas(1); - - var world = context.PreviousState; - var repository = new GuildRepository(world, context); - var signer = context.GetAgentAddress(); - - repository.AcceptGuildApplication(signer, Target, context.BlockIndex); - return repository.World; - } - } -} diff --git a/Lib9c/Action/Guild/ApplyGuild.cs b/Lib9c/Action/Guild/ApplyGuild.cs deleted file mode 100644 index a1b3305595..0000000000 --- a/Lib9c/Action/Guild/ApplyGuild.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using Bencodex.Types; -using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Extensions; -using Nekoyume.Model.Guild; -using Nekoyume.Module.Guild; -using Nekoyume.TypedAddress; - -namespace Nekoyume.Action.Guild -{ - // TODO(GUILD-FEATURE): Enable again when Guild features are enabled. - // [ActionType(TypeIdentifier)] - public class ApplyGuild : ActionBase - { - public const string TypeIdentifier = "apply_guild"; - - private const string GuildAddressKey = "ga"; - - public ApplyGuild() {} - - public ApplyGuild(GuildAddress guildAddress) - { - GuildAddress = guildAddress; - } - - public GuildAddress GuildAddress { get; private set; } - - public override IValue PlainValue => Dictionary.Empty - .Add("type_id", TypeIdentifier) - .Add("values", Dictionary.Empty - .Add(GuildAddressKey, 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 Dictionary values || - !values.TryGetValue((Text)GuildAddressKey, out var rawGuildAddress)) - { - throw new InvalidCastException(); - } - - GuildAddress = new GuildAddress(rawGuildAddress); - } - - public override IWorld Execute(IActionContext context) - { - GasTracer.UseGas(1); - - var world = context.PreviousState; - var repository = new GuildRepository(world, context); - var signer = context.GetAgentAddress(); - - // TODO: Do something related with ConsensusPower delegation. - - repository.ApplyGuild(signer, GuildAddress); - return repository.World; - } - } -} diff --git a/Lib9c/Action/Guild/CancelGuildApplication.cs b/Lib9c/Action/Guild/CancelGuildApplication.cs deleted file mode 100644 index b5f4258bf3..0000000000 --- a/Lib9c/Action/Guild/CancelGuildApplication.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using Bencodex.Types; -using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Extensions; -using Nekoyume.Model.Guild; -using Nekoyume.Module.Guild; - -namespace Nekoyume.Action.Guild -{ - // TODO(GUILD-FEATURE): Enable again when Guild features are enabled. - // [ActionType(TypeIdentifier)] - public class CancelGuildApplication : ActionBase - { - public const string TypeIdentifier = "cancel_guild_application"; - - 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 GuildRepository(world, context); - var signer = context.GetAgentAddress(); - - repository.CancelGuildApplication(signer); - return repository.World; - } - } -} diff --git a/Lib9c/Action/Guild/RejectGuildApplication.cs b/Lib9c/Action/Guild/RejectGuildApplication.cs deleted file mode 100644 index 7242531d55..0000000000 --- a/Lib9c/Action/Guild/RejectGuildApplication.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using Bencodex.Types; -using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Extensions; -using Nekoyume.Model.Guild; -using Nekoyume.Module.Guild; -using Nekoyume.TypedAddress; - -namespace Nekoyume.Action.Guild -{ - // TODO(GUILD-FEATURE): Enable again when Guild features are enabled. - // [ActionType(TypeIdentifier)] - public class RejectGuildApplication : ActionBase - { - public const string TypeIdentifier = "reject_guild_application"; - - private const string TargetKey = "t"; - - public RejectGuildApplication() {} - - public RejectGuildApplication(AgentAddress target) - { - Target = target; - } - - public AgentAddress Target { get; private set; } - - public override IValue PlainValue => Dictionary.Empty - .Add("type_id", TypeIdentifier) - .Add("values", Dictionary.Empty - .Add(TargetKey, Target.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 Dictionary values || - !values.TryGetValue((Text)TargetKey, out var rawTargetAddress)) - { - throw new InvalidCastException(); - } - - Target = new AgentAddress(rawTargetAddress); - } - - public override IWorld Execute(IActionContext context) - { - GasTracer.UseGas(1); - - var world = context.PreviousState; - var repository = new GuildRepository(world, context); - var signer = context.GetAgentAddress(); - - repository.RejectGuildApplication(signer, Target); - return repository.World; - } - } -} diff --git a/Lib9c/Model/Guild/GuildApplication.cs b/Lib9c/Model/Guild/GuildApplication.cs deleted file mode 100644 index 3f6c7e3a68..0000000000 --- a/Lib9c/Model/Guild/GuildApplication.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using Bencodex; -using Bencodex.Types; -using Nekoyume.Action; -using Nekoyume.TypedAddress; - -namespace Nekoyume.Model.Guild -{ - public class GuildApplication : IBencodable - { - private const string StateTypeName = "guild_application"; - private const long StateVersion = 1; - - public readonly GuildAddress GuildAddress; - - public GuildApplication(GuildAddress guildAddress) - { - GuildAddress = guildAddress; - } - - public GuildApplication(List list) : this(new GuildAddress(list[2])) - { - if (list[0] is not Text text || text != StateTypeName || list[1] is not Integer integer) - { - throw new InvalidCastException(); - } - - if (integer > StateVersion) - { - throw new FailedLoadStateException("Un-deserializable state."); - } - } - - public List Bencoded => new( - (Text)StateTypeName, - (Integer)StateVersion, - GuildAddress.Bencoded); - - IValue IBencodable.Bencoded => Bencoded; - } -} diff --git a/Lib9c/Module/Guild/GuildApplicationModule.cs b/Lib9c/Module/Guild/GuildApplicationModule.cs deleted file mode 100644 index 3b03971b2b..0000000000 --- a/Lib9c/Module/Guild/GuildApplicationModule.cs +++ /dev/null @@ -1,139 +0,0 @@ -#nullable enable -using System; -using System.Diagnostics.CodeAnalysis; -using Bencodex.Types; -using Nekoyume.Action; -using Nekoyume.Extensions; -using Nekoyume.Model.Guild; -using Nekoyume.TypedAddress; - -namespace Nekoyume.Module.Guild -{ - public static class GuildApplicationModule - { - public static Model.Guild.GuildApplication GetGuildApplication( - this GuildRepository repository, AgentAddress agentAddress) - { - var value = repository.World.GetAccountState(Addresses.GuildApplication).GetState(agentAddress); - if (value is List list) - { - return new Model.Guild.GuildApplication(list); - } - - throw new FailedLoadStateException("There is no such guild."); - } - - public static bool TryGetGuildApplication(this GuildRepository repository, - AgentAddress agentAddress, [NotNullWhen(true)] out Model.Guild.GuildApplication? guildApplication) - { - try - { - guildApplication = repository.GetGuildApplication(agentAddress); - return true; - } - catch - { - guildApplication = null; - return false; - } - } - - public static void ApplyGuild( - this GuildRepository repository, AgentAddress signer, GuildAddress guildAddress) - { - if (repository.GetJoinedGuild(signer) is not null) - { - throw new InvalidOperationException("The signer is already joined in a guild."); - } - - // NOTE: Check there is such guild. - if (!repository.TryGetGuild(guildAddress, out _)) - { - throw new InvalidOperationException("The guild does not exist."); - } - - if (repository.IsBanned(guildAddress, signer)) - { - throw new InvalidOperationException("The signer is banned from the guild."); - } - - repository.SetGuildApplication(signer, new GuildApplication(guildAddress)); - } - - public static void CancelGuildApplication( - this GuildRepository repository, AgentAddress agentAddress) - { - if (!repository.TryGetGuildApplication(agentAddress, out _)) - { - throw new InvalidOperationException("It may not apply any guild."); - } - - repository.RemoveGuildApplication(agentAddress); - } - - public static void AcceptGuildApplication( - this GuildRepository repository, AgentAddress signer, AgentAddress target, long height) - { - if (!repository.TryGetGuildApplication(target, out var guildApplication)) - { - throw new InvalidOperationException("It may not apply any guild."); - } - - if (!repository.TryGetGuild(guildApplication.GuildAddress, out var guild)) - { - throw new InvalidOperationException( - "There is no such guild now. It may be removed. Please cancel and apply another guild."); - } - - if (signer != guild.GuildMasterAddress) - { - throw new InvalidOperationException("It may not be a guild master."); - } - - repository.RemoveGuildApplication(target); - repository.JoinGuild(guildApplication.GuildAddress, target); - } - -#pragma warning disable S4144 - public static void RejectGuildApplication( -#pragma warning restore S4144 - this GuildRepository repository, AgentAddress signer, AgentAddress target) - { - if (!repository.TryGetGuildApplication(target, out var guildApplication)) - { - throw new InvalidOperationException("It may not apply any guild."); - } - - if (!repository.TryGetGuild(guildApplication.GuildAddress, out var guild)) - { - throw new InvalidOperationException( - "There is no such guild now. It may be removed. Please cancel and apply another guild."); - } - - if (signer != guild.GuildMasterAddress) - { - throw new InvalidOperationException("It may not be a guild master."); - } - - repository.RemoveGuildApplication(target); - } - - public static void SetGuildApplication( - this GuildRepository repository, AgentAddress agentAddress, GuildApplication guildApplication) - { - repository.UpdateWorld( - repository.World.MutateAccount( - Addresses.GuildApplication, - account => account.SetState(agentAddress, guildApplication.Bencoded))); - } - - private static void RemoveGuildApplication( - this GuildRepository repository, AgentAddress agentAddress) - { - repository.UpdateWorld( - repository.World.MutateAccount( - Addresses.GuildApplication, - account => account.RemoveState(agentAddress))); - } - } -} diff --git a/Lib9c/Module/Guild/GuildBanModule.cs b/Lib9c/Module/Guild/GuildBanModule.cs index fd5122fc84..f0280bec35 100644 --- a/Lib9c/Module/Guild/GuildBanModule.cs +++ b/Lib9c/Module/Guild/GuildBanModule.cs @@ -29,7 +29,11 @@ public static bool IsBanned(this GuildRepository repository, GuildAddress guildA return false; } - public static void Ban(this GuildRepository repository, GuildAddress guildAddress, AgentAddress signer, AgentAddress target) + public static void Ban( + this GuildRepository repository, + GuildAddress guildAddress, + AgentAddress signer, + AgentAddress target) { if (!repository.TryGetGuild(guildAddress, out var guild)) { @@ -46,11 +50,6 @@ public static void Ban(this GuildRepository repository, GuildAddress guildAddres throw new InvalidOperationException("The guild master cannot be banned."); } - if (repository.TryGetGuildApplication(target, out var guildApplication) && guildApplication.GuildAddress == guildAddress) - { - repository.RejectGuildApplication(signer, target); - } - if (repository.GetJoinedGuild(target) == guildAddress) { repository.LeaveGuild(target); From 001d96d7094a1f346587ca3a311f3ceb7ddc074e Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 16 Oct 2024 21:54:55 +0900 Subject: [PATCH 089/206] feat: Reveal ActionContext of Repository Co-authored-by: s2quake --- Lib9c/Delegation/DelegationRepository.cs | 9 +++++---- Lib9c/Delegation/IDelegationRepository.cs | 3 +++ Lib9c/Model/Guild/GuildRepository.cs | 4 ++-- Lib9c/ValidatorDelegation/ValidatorRepository.cs | 4 ++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Lib9c/Delegation/DelegationRepository.cs b/Lib9c/Delegation/DelegationRepository.cs index 374d531d2c..c4002c2372 100644 --- a/Lib9c/Delegation/DelegationRepository.cs +++ b/Lib9c/Delegation/DelegationRepository.cs @@ -11,7 +11,6 @@ namespace Nekoyume.Delegation public abstract class DelegationRepository : IDelegationRepository { protected IWorld previousWorld; - protected IActionContext actionContext; protected IAccount delegateeAccount; protected IAccount delegatorAccount; protected IAccount delegateeMetadataAccount; @@ -24,7 +23,7 @@ public abstract class DelegationRepository : IDelegationRepository public DelegationRepository( IWorld world, - IActionContext context, + IActionContext actionContext, Address delegateeAccountAddress, Address delegatorAccountAddress, Address delegateeMetadataAccountAddress, @@ -36,7 +35,7 @@ public DelegationRepository( Address lumpSumRewardRecordAccountAddress) { previousWorld = world; - actionContext = context; + ActionContext = actionContext; DelegateeAccountAddress = delegateeAccountAddress; DelegatorAccountAddress = delegatorAccountAddress; DelegateeMetadataAccountAddress = delegateeMetadataAccountAddress; @@ -69,6 +68,8 @@ public DelegationRepository( .SetAccount(UnbondingSetAccountAddress, unbondingSetAccount) .SetAccount(LumpSumRewardsRecordAccountAddress, lumpSumRewardsRecordAccount); + public IActionContext ActionContext { get; } + public Address DelegateeAccountAddress { get; } public Address DelegatorAccountAddress { get; } @@ -231,7 +232,7 @@ public void SetLumpSumRewardsRecord(LumpSumRewardsRecord lumpSumRewardsRecord) } public void TransferAsset(Address sender, Address recipient, FungibleAssetValue value) - => previousWorld = previousWorld.TransferAsset(actionContext, sender, recipient, value); + => previousWorld = previousWorld.TransferAsset(ActionContext, sender, recipient, value); public virtual void UpdateWorld(IWorld world) { diff --git a/Lib9c/Delegation/IDelegationRepository.cs b/Lib9c/Delegation/IDelegationRepository.cs index 7b86472252..6d2c7a2f19 100644 --- a/Lib9c/Delegation/IDelegationRepository.cs +++ b/Lib9c/Delegation/IDelegationRepository.cs @@ -1,4 +1,5 @@ #nullable enable +using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; @@ -13,6 +14,8 @@ public interface IDelegationRepository IWorld World { get; } + IActionContext ActionContext { get; } + IDelegatee GetDelegatee(Address address); IDelegator GetDelegator(Address address); diff --git a/Lib9c/Model/Guild/GuildRepository.cs b/Lib9c/Model/Guild/GuildRepository.cs index 329eeec663..f8ebc11009 100644 --- a/Lib9c/Model/Guild/GuildRepository.cs +++ b/Lib9c/Model/Guild/GuildRepository.cs @@ -10,10 +10,10 @@ namespace Nekoyume.Model.Guild { public class GuildRepository : DelegationRepository { - public GuildRepository(IWorld world, IActionContext context) + public GuildRepository(IWorld world, IActionContext actionContext) : base( world: world, - context: context, + actionContext: actionContext, delegateeAccountAddress: Addresses.Guild, delegatorAccountAddress: Addresses.GuildParticipant, delegateeMetadataAccountAddress: Addresses.GuildMetadata, diff --git a/Lib9c/ValidatorDelegation/ValidatorRepository.cs b/Lib9c/ValidatorDelegation/ValidatorRepository.cs index 1dd709a9cf..164b3e5525 100644 --- a/Lib9c/ValidatorDelegation/ValidatorRepository.cs +++ b/Lib9c/ValidatorDelegation/ValidatorRepository.cs @@ -15,10 +15,10 @@ public sealed class ValidatorRepository : DelegationRepository private IAccount _validatorList; - public ValidatorRepository(IWorld world, IActionContext context) + public ValidatorRepository(IWorld world, IActionContext actionContext) : base( world, - context, + actionContext, Addresses.ValidatorDelegatee, Addresses.ValidatorDelegator, Addresses.ValidatorDelegateeMetadata, From 1c276f9c68e677b8e9255c42b6549f52fb19edc7 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 16 Oct 2024 21:56:13 +0900 Subject: [PATCH 090/206] feat: Update validator modules Co-authored-by: s2quake --- .../ValidatorDelegateeModule.cs | 3 +- .../ValidatorDelegatorModule.cs | 74 +++++++++++++++---- 2 files changed, 62 insertions(+), 15 deletions(-) diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs index 474780690b..c757553e57 100644 --- a/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Numerics; +using Lib9c; using Libplanet.Action; using Libplanet.Crypto; using Nekoyume.ValidatorDelegation; @@ -43,7 +44,7 @@ public static ValidatorRepository CreateValidatorDelegatee( } var validatorDelegatee = new ValidatorDelegatee( - signer, publicKey, repository.World.GetGoldCurrency(), commissionPercentage, context.BlockIndex, repository); + signer, publicKey, Currencies.GuildGold, repository.World.GetGoldCurrency(), commissionPercentage, context.BlockIndex, repository); repository.SetValidatorDelegatee(validatorDelegatee); diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs index 83fc3d8f7c..adaa837d21 100644 --- a/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs @@ -4,6 +4,8 @@ using Libplanet.Action; using Libplanet.Crypto; using Libplanet.Types.Assets; +using Nekoyume.Action; +using Nekoyume.Model.Guild; using Nekoyume.ValidatorDelegation; namespace Nekoyume.Module.ValidatorDelegation @@ -13,11 +15,22 @@ public static class ValidatorDelegatorModule public static ValidatorRepository DelegateValidator( this ValidatorRepository repository, IActionContext context, - Address address, + Address validatorAddress, + FungibleAssetValue fav) + => DelegateValidator(repository, context, validatorAddress, validatorAddress, fav); + + + public static ValidatorRepository DelegateValidator( + this ValidatorRepository repository, + IActionContext context, + Address validatorAddress, + Address rewardAddress, FungibleAssetValue fav) { - var validatorDelegator = repository.GetValidatorDelegator(context.Signer); - var validatorDelegatee = repository.GetValidatorDelegatee(address); + var delegatorAddress = context.Signer; + var validatorDelegator = repository.GetValidatorDelegator( + delegatorAddress, rewardAddress); + var validatorDelegatee = repository.GetValidatorDelegatee(validatorAddress); validatorDelegator.Delegate(validatorDelegatee, fav, context.BlockIndex); return repository; @@ -26,37 +39,67 @@ public static ValidatorRepository DelegateValidator( public static ValidatorRepository UndelegateValidator( this ValidatorRepository repository, IActionContext context, - Address address, + Address validatorAddress, + BigInteger share) + => UndelegateValidator(repository, context, validatorAddress, validatorAddress, share); + + public static ValidatorRepository UndelegateValidator( + this ValidatorRepository repository, + IActionContext context, + Address validatorAddress, + Address rewardAddress, BigInteger share) { - var validatorDelegator = repository.GetValidatorDelegator(context.Signer); - var validatorDelegatee = repository.GetValidatorDelegatee(address); + var delegatorAddress = context.Signer; + var validatorDelegator = repository.GetValidatorDelegator( + delegatorAddress, rewardAddress); + var validatorDelegatee = repository.GetValidatorDelegatee(validatorAddress); validatorDelegator.Undelegate(validatorDelegatee, share, context.BlockIndex); return repository; } + public static ValidatorRepository RedelegateValidator( + this ValidatorRepository repository, + IActionContext context, + Address srcValidatorAddress, + Address dstValidatorAddress, + BigInteger share) + => RedelegateValidator(repository, context, srcValidatorAddress, dstValidatorAddress, dstValidatorAddress, share); + public static ValidatorRepository RedelegateValidator( this ValidatorRepository repository, IActionContext context, - Address srcAddress, - Address dstAddress, + Address srcValidatorAddress, + Address dstValidatorAddress, + Address rewardAddress, BigInteger share) { - var validatorDelegator = repository.GetValidatorDelegator(context.Signer); - var srcValidatorDelegatee = repository.GetValidatorDelegatee(srcAddress); - var dstValidatorDelegatee = repository.GetValidatorDelegatee(dstAddress); + var delegatorAddress = context.Signer; + var validatorDelegator = repository.GetValidatorDelegator( + delegatorAddress, rewardAddress); + var srcValidatorDelegatee = repository.GetValidatorDelegatee(srcValidatorAddress); + var dstValidatorDelegatee = repository.GetValidatorDelegatee(dstValidatorAddress); validatorDelegator.Redelegate(srcValidatorDelegatee, dstValidatorDelegatee, share, context.BlockIndex); return repository; } + public static ValidatorRepository ClaimRewardValidator( + this ValidatorRepository repository, + IActionContext context, + Address validatorAddress) + => ClaimRewardValidator(repository, context, validatorAddress, validatorAddress); + public static ValidatorRepository ClaimRewardValidator( this ValidatorRepository repository, IActionContext context, - Address address) + Address address, + Address rewardAddress) { - var validatorDelegator = repository.GetValidatorDelegator(context.Signer); + var delegatorAddress = context.Signer; + var validatorDelegator = repository.GetValidatorDelegator( + delegatorAddress, rewardAddress); var validatorDelegatee = repository.GetValidatorDelegatee(address); validatorDelegator.ClaimReward(validatorDelegatee, context.BlockIndex); @@ -66,12 +109,15 @@ public static ValidatorRepository ClaimRewardValidator( public static bool TryGetValidatorDelegator( this ValidatorRepository repository, + IActionContext context, Address address, + Address rewardAddress, [NotNullWhen(true)] out ValidatorDelegator? validatorDelegator) { try { - validatorDelegator = repository.GetValidatorDelegator(address); + validatorDelegator = repository.GetValidatorDelegator( + address, rewardAddress); return true; } catch From a14a7ca0956ab45d97a4b652649b2381ffc85ae3 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 16 Oct 2024 21:57:23 +0900 Subject: [PATCH 091/206] feat: Update guild modules Co-authored-by: s2quake --- Lib9c/Module/Guild/GuildBanModule.cs | 1 - Lib9c/Module/Guild/GuildModule.cs | 12 +- Lib9c/Module/Guild/GuildParticipantModule.cs | 179 +++++++++++-------- Lib9c/Module/Guild/GuildUnbondingModule.cs | 72 ++++---- 4 files changed, 147 insertions(+), 117 deletions(-) diff --git a/Lib9c/Module/Guild/GuildBanModule.cs b/Lib9c/Module/Guild/GuildBanModule.cs index f0280bec35..810fb02fd8 100644 --- a/Lib9c/Module/Guild/GuildBanModule.cs +++ b/Lib9c/Module/Guild/GuildBanModule.cs @@ -1,5 +1,4 @@ using System; -using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; using Nekoyume.Extensions; diff --git a/Lib9c/Module/Guild/GuildModule.cs b/Lib9c/Module/Guild/GuildModule.cs index 47880ce94d..07936baea8 100644 --- a/Lib9c/Module/Guild/GuildModule.cs +++ b/Lib9c/Module/Guild/GuildModule.cs @@ -7,6 +7,7 @@ using Nekoyume.Extensions; using Nekoyume.Model.Guild; using Nekoyume.TypedAddress; +using Libplanet.Crypto; namespace Nekoyume.Module.Guild { @@ -35,7 +36,8 @@ public static bool TryGetGuild( public static GuildRepository MakeGuild( this GuildRepository repository, GuildAddress guildAddress, - AgentAddress signer) + AgentAddress signer, + Address validatorAddress) { if (repository.GetJoinedGuild(signer) is not null) { @@ -48,7 +50,7 @@ public static GuildRepository MakeGuild( } var guild = new Model.Guild.Guild( - guildAddress, signer, Currencies.GuildGold, repository); + guildAddress, signer, validatorAddress, repository.World.GetGoldCurrency(), repository); repository.SetGuild(guild); repository.JoinGuild(guildAddress, signer); @@ -57,7 +59,8 @@ public static GuildRepository MakeGuild( public static GuildRepository RemoveGuild( this GuildRepository repository, - AgentAddress signer) + AgentAddress signer, + long height) { if (repository.GetJoinedGuild(signer) is not { } guildAddress) { @@ -79,7 +82,8 @@ public static GuildRepository RemoveGuild( throw new InvalidOperationException("There are remained participants in the guild."); } - repository.RawLeaveGuild(signer); + repository.RemoveGuildParticipant(signer); + repository.DecreaseGuildMemberCount(guild.Address); repository.UpdateWorld( repository.World.MutateAccount( Addresses.Guild, account => account.RemoveState(guildAddress))); diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index b085eed3c7..19ce138f4b 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -3,10 +3,11 @@ using System.Diagnostics.CodeAnalysis; using System.Numerics; using Lib9c; -using Libplanet.Action.State; +using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Model.Guild; using Nekoyume.TypedAddress; +using Nekoyume.ValidatorDelegation; namespace Nekoyume.Module.Guild { @@ -21,64 +22,40 @@ public static class GuildParticipantModule : null; } - public static GuildRepository JoinGuildWithDelegate( + public static GuildRepository JoinGuild( this GuildRepository repository, - AgentAddress guildParticipantAddress, GuildAddress guildAddress, - long height) - => repository - .JoinGuild(guildAddress, new AgentAddress(guildParticipantAddress)) - .Delegate( - guildParticipantAddress, - guildAddress, - repository.World.GetBalance(guildParticipantAddress, Currencies.GuildGold), - height); - - public static GuildRepository LeaveGuildWithUndelegate( - this GuildRepository repository, - AgentAddress guildParticipantAddress, - long height) - { - var guild = repository.GetJoinedGuild(guildParticipantAddress) is GuildAddress guildAddr - ? repository.GetGuild(guildAddr) - : throw new InvalidOperationException("The signer does not join any guild."); - - return repository - .Undelegate(guildParticipantAddress, - guildAddr, - repository.GetBond(guild, guildParticipantAddress).Share, - height) - .LeaveGuild(guildParticipantAddress); - } - - public static GuildRepository MoveGuildWithRedelegate( - this GuildRepository repository, - AgentAddress guildParticipantAddress, - GuildAddress srcGuildAddress, - GuildAddress dstGuildAddress, - long height) + AgentAddress target) { - var srcGuild = repository.GetGuild(srcGuildAddress); - repository.Redelegate( - guildParticipantAddress, - srcGuildAddress, - dstGuildAddress, - repository.GetBond(srcGuild, guildParticipantAddress).Share, - height); - repository.LeaveGuild(guildParticipantAddress); - repository.JoinGuild(dstGuildAddress, guildParticipantAddress); + var guildParticipant = new GuildParticipant(target, guildAddress, repository); + var guildGold = repository.GetBalance(guildAddress, Currencies.GuildGold); + repository.SetGuildParticipant(guildParticipant); + repository.IncreaseGuildMemberCount(guildAddress); + repository.Delegate(target, guildGold); return repository; } - public static GuildRepository JoinGuild( + public static GuildRepository MoveGuild( this GuildRepository repository, - GuildAddress guildAddress, - AgentAddress target) + AgentAddress guildParticipantAddress, + GuildAddress dstGuildAddress) { - var guildParticipant = new Model.Guild.GuildParticipant(target, guildAddress, repository); + var dstGuild = repository.GetGuild(dstGuildAddress); + var validatorRepository = new ValidatorRepository(repository.World, repository.ActionContext); + var dstValidatorDelegatee = validatorRepository.GetValidatorDelegatee(dstGuild.ValidatorAddress); + if (dstValidatorDelegatee.Tombstoned) + { + throw new InvalidOperationException("The validator of the guild to move to has been tombstoned."); + } + + var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); + repository.RemoveGuildParticipant(guildParticipantAddress); + repository.DecreaseGuildMemberCount(guildParticipant.GuildAddress); repository.SetGuildParticipant(guildParticipant); - repository.IncreaseGuildMemberCount(guildAddress); + repository.IncreaseGuildMemberCount(dstGuildAddress); + repository.Redelegate( + guildParticipantAddress, dstGuildAddress); return repository; } @@ -104,22 +81,26 @@ public static GuildRepository LeaveGuild( "The signer is a guild master. Guild master cannot quit the guild."); } - return repository.RawLeaveGuild(target); - } - - public static GuildRepository RawLeaveGuild(this GuildRepository repository, AgentAddress target) - { - if (!repository.TryGetGuildParticipant(target, out var guildParticipant)) - { - throw new InvalidOperationException("It may not join any guild."); - } - repository.RemoveGuildParticipant(target); - repository.DecreaseGuildMemberCount(guildParticipant.GuildAddress); + repository.DecreaseGuildMemberCount(guild.Address); + repository.Undelegate(target); return repository; } + // public static GuildRepository RawLeaveGuild(this GuildRepository repository, AgentAddress target) + // { + // if (!repository.TryGetGuildParticipant(target, out var guildParticipant)) + // { + // throw new InvalidOperationException("It may not join any guild."); + // } + + // repository.RemoveGuildParticipant(target); + // repository.DecreaseGuildMemberCount(guildParticipant.GuildAddress); + + // return repository; + // } + private static bool TryGetGuildParticipant( this GuildRepository repository, AgentAddress agentAddress, @@ -140,13 +121,41 @@ private static bool TryGetGuildParticipant( private static GuildRepository Delegate( this GuildRepository repository, AgentAddress guildParticipantAddress, - GuildAddress guildAddress, - FungibleAssetValue fav, - long height) + FungibleAssetValue fav) { + var height = repository.ActionContext.BlockIndex; var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); - var guild = repository.GetGuild(guildAddress); - guildParticipant.Delegate(guild, fav, height); + 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); + + return repository; + } + + private static GuildRepository Undelegate( + this GuildRepository repository, + AgentAddress guildParticipantAddress) + { + 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); return repository; } @@ -154,13 +163,20 @@ private static GuildRepository Delegate( private static GuildRepository Undelegate( this GuildRepository repository, AgentAddress guildParticipantAddress, - GuildAddress guildAddress, - BigInteger share, - long height) + BigInteger share) { + var height = repository.ActionContext.BlockIndex; var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); - var guild = repository.GetGuild(guildAddress); - guildParticipant.Undelegate(guild, share, height); + 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); return repository; } @@ -168,15 +184,26 @@ private static GuildRepository Undelegate( public static GuildRepository Redelegate( this GuildRepository repository, AgentAddress guildParticipantAddress, - GuildAddress srcGuildAddress, - GuildAddress dstGuildAddress, - BigInteger share, - long height) + GuildAddress dstGuildAddress) { + var height = repository.ActionContext.BlockIndex; var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); - var srcGuild = repository.GetGuild(srcGuildAddress); + var guild = repository.GetGuild(guildParticipant.GuildAddress); + var srcValidatorAddress = guild.ValidatorAddress; var dstGuild = repository.GetGuild(dstGuildAddress); - guildParticipant.Redelegate(srcGuild, dstGuild, share, height); + 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); return repository; } diff --git a/Lib9c/Module/Guild/GuildUnbondingModule.cs b/Lib9c/Module/Guild/GuildUnbondingModule.cs index c29f1b9c16..390fa8ba4b 100644 --- a/Lib9c/Module/Guild/GuildUnbondingModule.cs +++ b/Lib9c/Module/Guild/GuildUnbondingModule.cs @@ -1,42 +1,42 @@ -using System; -using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Delegation; -using Nekoyume.Model.Guild; +// using System; +// using Libplanet.Action; +// using Libplanet.Action.State; +// using Nekoyume.Delegation; +// using Nekoyume.Model.Guild; -namespace Nekoyume.Module.Guild -{ - public static class GuildUnbondingModule - { - public static IWorld ReleaseUnbondings(this IWorld world, IActionContext context) - { - var repository = new GuildRepository(world, context); - var unbondingSet = repository.GetUnbondingSet(); - var unbondings = unbondingSet.UnbondingsToRelease(context.BlockIndex); +// namespace Nekoyume.Module.Guild +// { +// public static class GuildUnbondingModule +// { +// public static IWorld ReleaseUnbondings(this IWorld world, IActionContext context) +// { +// var repository = new GuildRepository(world, context); +// var unbondingSet = repository.GetUnbondingSet(); +// var unbondings = unbondingSet.UnbondingsToRelease(context.BlockIndex); - IUnbonding released; - foreach (var unbonding in unbondings) - { - released = unbonding.Release(context.BlockIndex); +// IUnbonding released; +// foreach (var unbonding in unbondings) +// { +// released = unbonding.Release(context.BlockIndex); - switch (released) - { - case UnbondLockIn unbondLockIn: - repository.SetUnbondLockIn(unbondLockIn); - break; - case RebondGrace rebondGrace: - repository.SetRebondGrace(rebondGrace); - break; - default: - throw new ArgumentException("Invalid unbonding type."); - } +// switch (released) +// { +// case UnbondLockIn unbondLockIn: +// repository.SetUnbondLockIn(unbondLockIn); +// break; +// case RebondGrace rebondGrace: +// repository.SetRebondGrace(rebondGrace); +// break; +// default: +// throw new ArgumentException("Invalid unbonding type."); +// } - unbondingSet = unbondingSet.SetUnbonding(released); - } +// unbondingSet = unbondingSet.SetUnbonding(released); +// } - repository.SetUnbondingSet(unbondingSet); +// repository.SetUnbondingSet(unbondingSet); - return repository.World; - } - } -} +// return repository.World; +// } +// } +// } From eb2d7cf065c9c2fddff853eb209d21680db3d257 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 16 Oct 2024 21:58:25 +0900 Subject: [PATCH 092/206] feat: Update Guild/Validator Actions Co-authored-by: s2quake --- Lib9c/Action/Guild/JoinGuild.cs | 62 +++++++++++++++++++ Lib9c/Action/Guild/MakeGuild.cs | 31 ++++++++-- Lib9c/Action/Guild/MoveGuild.cs | 62 +++++++++++++++++++ Lib9c/Action/Guild/RemoveGuild.cs | 3 +- Lib9c/Action/InitializeStates.cs | 7 ++- .../ValidatorDelegation/PromoteValidator.cs | 10 ++- 6 files changed, 165 insertions(+), 10 deletions(-) create mode 100644 Lib9c/Action/Guild/JoinGuild.cs create mode 100644 Lib9c/Action/Guild/MoveGuild.cs diff --git a/Lib9c/Action/Guild/JoinGuild.cs b/Lib9c/Action/Guild/JoinGuild.cs new file mode 100644 index 0000000000..38d4a256ac --- /dev/null +++ b/Lib9c/Action/Guild/JoinGuild.cs @@ -0,0 +1,62 @@ +using System; +using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.State; +using Nekoyume.Extensions; +using Nekoyume.Model.Guild; +using Nekoyume.Module.Guild; +using Nekoyume.TypedAddress; + +namespace Nekoyume.Action.Guild +{ + // TODO(GUILD-FEATURE): Enable again when Guild features are enabled. + // [ActionType(TypeIdentifier)] + public class JoinGuild : ActionBase + { + public const string TypeIdentifier = "join_guild"; + + private const string GuildAddressKey = "ga"; + + public JoinGuild() {} + + public JoinGuild(GuildAddress guildAddress) + { + GuildAddress = guildAddress; + } + + public GuildAddress GuildAddress { get; private set; } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", Dictionary.Empty + .Add(GuildAddressKey, 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 Dictionary values || + !values.TryGetValue((Text)GuildAddressKey, out var rawGuildAddress)) + { + throw new InvalidCastException(); + } + + GuildAddress = new GuildAddress(rawGuildAddress); + } + + public override IWorld Execute(IActionContext context) + { + GasTracer.UseGas(1); + + var world = context.PreviousState; + var repository = new GuildRepository(world, context); + var target = context.GetAgentAddress(); + var guildAddress = GuildAddress; + + repository.JoinGuild(guildAddress, target); + + return repository.World; + } + } +} diff --git a/Lib9c/Action/Guild/MakeGuild.cs b/Lib9c/Action/Guild/MakeGuild.cs index 3bba2f2fbc..73e19d1777 100644 --- a/Lib9c/Action/Guild/MakeGuild.cs +++ b/Lib9c/Action/Guild/MakeGuild.cs @@ -15,18 +15,37 @@ public class MakeGuild : ActionBase { public const string TypeIdentifier = "make_guild"; + private const string ValidatorAddressKey = "va"; + + public MakeGuild() { } + + public MakeGuild(Address validatorAddress) + { + + } + + public GuildAddress GuildAddress { get; private set; } + + public Address ValidatorAddress { get; private set; } + + public bool IsNew{get; private set;} + public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) - .Add("values", Null.Value); + .Add("values", Dictionary.Empty + .Add(ValidatorAddressKey, ValidatorAddress.Bencoded)); public override void LoadPlainValue(IValue plainValue) { if (plainValue is not Dictionary root || !root.TryGetValue((Text)"values", out var rawValues) || - rawValues is not Null) + rawValues is not Dictionary values || + !values.TryGetValue((Text)ValidatorAddressKey, out var rawValidatorAddress)) { throw new InvalidCastException(); } + + ValidatorAddress = new Address(rawValidatorAddress); } public override IWorld Execute(IActionContext context) @@ -36,6 +55,9 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; var repository = new GuildRepository(world, context); var random = context.GetRandom(); + var guildAddress = GuildAddress; + var guildMasterAddress = new AgentAddress(context.Signer); + var validatorAddress = ValidatorAddress; // TODO: Remove this check when to deliver features to users. if (context.Signer != GuildConfig.PlanetariumGuildOwner) @@ -44,10 +66,7 @@ public override IWorld Execute(IActionContext context) $"This action is not allowed for {context.Signer}."); } - var guildAddress = new GuildAddress(random.GenerateAddress()); - var signer = context.GetAgentAddress(); - - repository.MakeGuild(guildAddress, signer); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); return repository.World; } } diff --git a/Lib9c/Action/Guild/MoveGuild.cs b/Lib9c/Action/Guild/MoveGuild.cs new file mode 100644 index 0000000000..84ede7baae --- /dev/null +++ b/Lib9c/Action/Guild/MoveGuild.cs @@ -0,0 +1,62 @@ +using System; +using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.State; +using Nekoyume.Extensions; +using Nekoyume.Model.Guild; +using Nekoyume.Module.Guild; +using Nekoyume.TypedAddress; + +namespace Nekoyume.Action.Guild +{ + // TODO(GUILD-FEATURE): Enable again when Guild features are enabled. + // [ActionType(TypeIdentifier)] + public class MoveGuild : ActionBase + { + public const string TypeIdentifier = "move_guild"; + + private const string GuildAddressKey = "ga"; + + public MoveGuild() {} + + public MoveGuild(GuildAddress guildAddress) + { + GuildAddress = guildAddress; + } + + public GuildAddress GuildAddress { get; private set; } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", Dictionary.Empty + .Add(GuildAddressKey, 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 Dictionary values || + !values.TryGetValue((Text)GuildAddressKey, out var rawGuildAddress)) + { + throw new InvalidCastException(); + } + + GuildAddress = new GuildAddress(rawGuildAddress); + } + + public override IWorld Execute(IActionContext context) + { + GasTracer.UseGas(1); + + var world = context.PreviousState; + var repository = new GuildRepository(world, context); + var target = context.GetAgentAddress(); + var guildAddress = GuildAddress; + + repository.MoveGuild(target, guildAddress); + + return repository.World; + } + } +} diff --git a/Lib9c/Action/Guild/RemoveGuild.cs b/Lib9c/Action/Guild/RemoveGuild.cs index 65fbcc21c6..b5c42cc5a7 100644 --- a/Lib9c/Action/Guild/RemoveGuild.cs +++ b/Lib9c/Action/Guild/RemoveGuild.cs @@ -38,10 +38,11 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; var repository = new GuildRepository(world, context); var signer = context.GetAgentAddress(); + var height = context.BlockIndex; // TODO: Do something to return 'Power' token; - repository.RemoveGuild(signer); + repository.RemoveGuild(signer, height); return repository.World; } } diff --git a/Lib9c/Action/InitializeStates.cs b/Lib9c/Action/InitializeStates.cs index 394ad46114..6cdc273675 100644 --- a/Lib9c/Action/InitializeStates.cs +++ b/Lib9c/Action/InitializeStates.cs @@ -12,6 +12,8 @@ using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.ValidatorDelegation; +using Nekoyume.Action.Guild; +using Lib9c; namespace Nekoyume.Action { @@ -205,13 +207,16 @@ public override IWorld Execute(IActionContext context) var validatorDelegatee = new ValidatorDelegatee( validator.OperatorAddress, validator.PublicKey, + delegationCurrency: Currencies.GuildGold, repository.World.GetGoldCurrency(), ValidatorDelegatee.DefaultCommissionPercentage, context.BlockIndex, repository); var delegationFAV = FungibleAssetValue.FromRawValue( validatorDelegatee.DelegationCurrency, validator.Power); - var validatorDelegator = repository.GetValidatorDelegator(validator.OperatorAddress); + var validatorOperatorAddress = validator.OperatorAddress; + var validatorDelegator = repository.GetValidatorDelegator( + validatorOperatorAddress, validatorOperatorAddress); repository.SetValidatorDelegatee(validatorDelegatee); repository.TransferAsset( diff --git a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs index 9ab13b7f46..229aec991a 100644 --- a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs @@ -27,6 +27,7 @@ public PromoteValidator(PublicKey publicKey, FungibleAssetValue fav, BigInteger PublicKey = publicKey; FAV = fav; CommissionPercentage = commissionPercentage; + RewardAddress = publicKey.Address; } public PublicKey PublicKey { get; private set; } @@ -35,12 +36,15 @@ public PromoteValidator(PublicKey publicKey, FungibleAssetValue fav, BigInteger public BigInteger CommissionPercentage { get; private set; } + public Address RewardAddress { get; private set; } + public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) .Add("values", List.Empty .Add(PublicKey.Format(true)) .Add(FAV.Serialize()) - .Add(CommissionPercentage)); + .Add(CommissionPercentage) + .Add(RewardAddress.Bencoded)); public override void LoadPlainValue(IValue plainValue) { @@ -55,6 +59,7 @@ public override void LoadPlainValue(IValue plainValue) PublicKey = new PublicKey(((Binary)values[0]).ByteArray); FAV = new FungibleAssetValue(values[1]); CommissionPercentage = (Integer)values[2]; + RewardAddress = new Address(values[3]); } public override IWorld Execute(IActionContext context) @@ -63,9 +68,10 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; var repository = new ValidatorRepository(world, context); + var rewardAddress = RewardAddress; repository.CreateValidatorDelegatee(context, PublicKey, CommissionPercentage); - repository.DelegateValidator(context, context.Signer, FAV); + repository.DelegateValidator(context, context.Signer, rewardAddress, FAV); return repository.World; } From bd6c2eb622210239d09fd84e5dcffe806353770f Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 16 Oct 2024 21:59:43 +0900 Subject: [PATCH 093/206] test: Update tests and benchmarks Co-authored-by: s2quake --- .Lib9c.Benchmarks/Actions/AutoJoinGuild.cs | 4 +- .../Actions/MigratePledgeToGuild.cs | 4 +- .../Guild/AcceptGuildApplicationTest.cs | 96 --------------- .../Action/Guild/BanGuildMemberTest.cs | 42 ++----- .../Guild/CancelGuildApplicationTest.cs | 76 ------------ .../{ApplyGuildTest.cs => JoinGuildTest.cs} | 34 ++--- .../Guild/Migration/GuildMigrationCtrlTest.cs | 10 +- .../Migration/MigratePledgeToGuildTest.cs | 10 +- .Lib9c.Tests/Action/Guild/MoveGuildTest.cs | 63 ++++++++++ .Lib9c.Tests/Action/Guild/QuitGuildTest.cs | 4 +- .../Guild/RejectGuildApplicationTest.cs | 93 -------------- .Lib9c.Tests/Action/Guild/RemoveGuildTest.cs | 13 +- .../Action/Guild/UnbanGuildMemberTest.cs | 3 +- .../ValidatorDelegation/AllocateRewardTest.cs | 36 +++--- .../ClaimRewardValidatorTest.cs | 42 +++---- .../DelegateValidatorTest.cs | 60 ++++----- .../PromoteValidatorTest.cs | 16 +-- .../RedelegateValidatorTest.cs | 116 +++++++++--------- .../ReleaseValidatorUnbondingsTest.cs | 12 +- .../SetValidatorCommissionTest.cs | 20 +-- .../ValidatorDelegation/SlashValidatorTest.cs | 18 +-- .../UndelegateValidatorTest.cs | 72 +++++------ .../UnjailValidatorTest.cs | 14 +-- .../UpdateValidatorsTest.cs | 6 +- .../ValidatorDelegationTestBase.cs | 19 +-- .Lib9c.Tests/Delegation/DummyDelegator.cs | 2 +- .Lib9c.Tests/Delegation/DummyRepository.cs | 2 +- .Lib9c.Tests/Delegation/TestDelegator.cs | 2 +- .Lib9c.Tests/Delegation/TestRepository.cs | 2 +- .../Model/Guild/GuildApplicationTest.cs | 38 ------ .Lib9c.Tests/Model/Guild/GuildTest.cs | 9 +- .../Tx/Begin/AutoJoinGuildTest.cs | 7 +- 32 files changed, 349 insertions(+), 596 deletions(-) delete mode 100644 .Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs delete mode 100644 .Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs rename .Lib9c.Tests/Action/Guild/{ApplyGuildTest.cs => JoinGuildTest.cs} (55%) create mode 100644 .Lib9c.Tests/Action/Guild/MoveGuildTest.cs delete mode 100644 .Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs delete mode 100644 .Lib9c.Tests/Model/Guild/GuildApplicationTest.cs diff --git a/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs b/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs index 0e693d38d6..2f14d093b3 100644 --- a/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs +++ b/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs @@ -3,6 +3,7 @@ using Lib9c.Tests.Action; using Lib9c.Tests.Util; using Libplanet.Action.State; +using Libplanet.Crypto; using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action.Guild; @@ -33,8 +34,9 @@ public void Setup() var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); + var validatorAddress = new PrivateKey().Address; var repository = new GuildRepository(worldWithPledge, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); worldWithPledgeAndGuild = repository.World; repository.JoinGuild(guildAddress, signer); worldAfterMigration = repository.World; diff --git a/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs b/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs index e01e2aa102..9a0d5020e3 100644 --- a/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs +++ b/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs @@ -3,6 +3,7 @@ using Lib9c.Tests.Action; using Lib9c.Tests.Util; using Libplanet.Action.State; +using Libplanet.Crypto; using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action.Guild; @@ -34,9 +35,10 @@ public void Setup() var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); + var validatorAddress = new PrivateKey().Address; var repository = new GuildRepository(worldWithPledge, new ActionContext()); worldWithPledgeAndGuild = repository - .MakeGuild(guildAddress, guildMasterAddress).World; + .MakeGuild(guildAddress, guildMasterAddress, validatorAddress).World; worldAfterMigration = repository .JoinGuild(guildAddress, signer).World; } diff --git a/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs b/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs deleted file mode 100644 index 5b5663b795..0000000000 --- a/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs +++ /dev/null @@ -1,96 +0,0 @@ -namespace Lib9c.Tests.Action.Guild -{ - using System; - using Lib9c.Tests.Util; - using Libplanet.Action.State; - 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; - - public class AcceptGuildApplicationTest - { - [Fact] - public void Serialization() - { - var agentAddress = AddressUtil.CreateAgentAddress(); - var action = new AcceptGuildApplication(agentAddress); - var plainValue = action.PlainValue; - - var deserialized = new AcceptGuildApplication(); - deserialized.LoadPlainValue(plainValue); - Assert.Equal(agentAddress, deserialized.Target); - } - - [Fact] - public void Execute() - { - var appliedMemberAddress = AddressUtil.CreateAgentAddress(); - var nonAppliedMemberAddress = AddressUtil.CreateAgentAddress(); - var guildMasterAddress = AddressUtil.CreateAgentAddress(); - var guildAddress = AddressUtil.CreateGuildAddress(); - - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.ApplyGuild(appliedMemberAddress, guildAddress); - world = repository.World; - - // These cases should fail because the member didn't apply the guild and - // non-guild-master-addresses cannot accept the guild application. - Assert.Throws( - () => new AcceptGuildApplication(nonAppliedMemberAddress).Execute(new ActionContext - { - PreviousState = world, - Signer = guildMasterAddress, - })); - Assert.Throws( - () => new AcceptGuildApplication(nonAppliedMemberAddress).Execute(new ActionContext - { - PreviousState = world, - Signer = appliedMemberAddress, - })); - Assert.Throws( - () => new AcceptGuildApplication(nonAppliedMemberAddress).Execute(new ActionContext - { - PreviousState = world, - Signer = nonAppliedMemberAddress, - })); - - // These cases should fail because non-guild-master-addresses cannot accept the guild application. - Assert.Throws( - () => new AcceptGuildApplication(appliedMemberAddress).Execute(new ActionContext - { - PreviousState = world, - Signer = appliedMemberAddress, - })); - Assert.Throws( - () => new AcceptGuildApplication(appliedMemberAddress).Execute(new ActionContext - { - PreviousState = world, - Signer = nonAppliedMemberAddress, - })); - - world = new AcceptGuildApplication(appliedMemberAddress).Execute(new ActionContext - { - PreviousState = world, - Signer = guildMasterAddress, - }); - - repository.UpdateWorld(world); - - Assert.False(repository.TryGetGuildApplication(appliedMemberAddress, out _)); - Assert.Equal(guildAddress, repository.GetJoinedGuild(appliedMemberAddress)); - } - } -} diff --git a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs index 385a0e4c9c..e4593369bb 100644 --- a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs +++ b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Action.Guild using System; using Lib9c.Tests.Util; using Libplanet.Action.State; + using Libplanet.Crypto; using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; @@ -37,6 +38,8 @@ public void Ban_By_GuildMaster() var otherGuildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); var otherGuildAddress = AddressUtil.CreateGuildAddress(); + var validatorAddress = new PrivateKey().Address; + var otherValidatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -44,9 +47,9 @@ public void Ban_By_GuildMaster() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); repository.JoinGuild(guildAddress, guildMemberAddress); - repository.MakeGuild(otherGuildAddress, otherGuildMasterAddress); + repository.MakeGuild(otherGuildAddress, otherGuildMasterAddress, otherValidatorAddress); repository.JoinGuild(otherGuildAddress, otherGuildMemberAddress); // Guild @@ -134,6 +137,7 @@ public void Ban_By_GuildMember() var otherAddress = AddressUtil.CreateAgentAddress(); var targetGuildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); + var validatorAddress = new PrivateKey().Address; var action = new BanGuildMember(targetGuildMemberAddress); @@ -143,7 +147,7 @@ public void Ban_By_GuildMember() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); repository.JoinGuild(guildAddress, guildMemberAddress); repository.JoinGuild(guildAddress, targetGuildMemberAddress); @@ -180,6 +184,7 @@ public void Ban_By_Other() var otherAddress = AddressUtil.CreateAgentAddress(); var targetGuildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); + var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -187,7 +192,7 @@ public void Ban_By_Other() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); repository.JoinGuild(guildAddress, targetGuildMemberAddress); // Other tries to ban GuildMember. @@ -206,34 +211,5 @@ public void Ban_By_Other() Signer = otherAddress, })); } - - [Fact] - public void RejectGuildApplication() - { - var guildAddress = AddressUtil.CreateGuildAddress(); - var guildMasterAddress = AddressUtil.CreateAgentAddress(); - var agentAddress = AddressUtil.CreateAgentAddress(); - - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.ApplyGuild(agentAddress, guildAddress); - Assert.True(repository.TryGetGuildApplication(agentAddress, out _)); - - var action = new BanGuildMember(agentAddress); - world = action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = guildMasterAddress, - }); - - repository.UpdateWorld(world); - Assert.True(repository.IsBanned(guildAddress, agentAddress)); - Assert.False(repository.TryGetGuildApplication(agentAddress, out _)); - } } } diff --git a/.Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs b/.Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs deleted file mode 100644 index f6f2c1d670..0000000000 --- a/.Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Lib9c.Tests.Action.Guild -{ - using System; - 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 Nekoyume.TypedAddress; - using Xunit; - - public class CancelGuildApplicationTest - { - [Fact] - public void Serialization() - { - var action = new CancelGuildApplication(); - var plainValue = action.PlainValue; - - var deserialized = new CancelGuildApplication(); - deserialized.LoadPlainValue(plainValue); - } - - [Fact] - public void Execute() - { - var privateKey = new PrivateKey(); - var signer = new AgentAddress(privateKey.Address); - var guildMasterAddress = AddressUtil.CreateAgentAddress(); - var guildAddress = AddressUtil.CreateGuildAddress(); - - var action = new CancelGuildApplication(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - Assert.Throws( - () => action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = signer, - })); - - var otherAddress = AddressUtil.CreateAgentAddress(); - repository.ApplyGuild(otherAddress, guildAddress); - - // It should fail because other agent applied the guild but the signer didn't apply. - Assert.Throws( - () => action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = signer, - })); - - repository.ApplyGuild(signer, guildAddress); - world = action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = signer, - }); - - repository.UpdateWorld(world); - - Assert.False(repository.TryGetGuildApplication(signer, out _)); - } - } -} diff --git a/.Lib9c.Tests/Action/Guild/ApplyGuildTest.cs b/.Lib9c.Tests/Action/Guild/JoinGuildTest.cs similarity index 55% rename from .Lib9c.Tests/Action/Guild/ApplyGuildTest.cs rename to .Lib9c.Tests/Action/Guild/JoinGuildTest.cs index 07c0c831dc..726e448f29 100644 --- a/.Lib9c.Tests/Action/Guild/ApplyGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/JoinGuildTest.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Action.Guild using System; using Lib9c.Tests.Util; using Libplanet.Action.State; + using Libplanet.Crypto; using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; @@ -13,16 +14,16 @@ namespace Lib9c.Tests.Action.Guild using Nekoyume.Module.Guild; using Xunit; - public class ApplyGuildTest + public class JoinGuildTest { [Fact] public void Serialization() { var guildAddress = AddressUtil.CreateGuildAddress(); - var action = new ApplyGuild(guildAddress); + var action = new JoinGuild(guildAddress); var plainValue = action.PlainValue; - var deserialized = new ApplyGuild(); + var deserialized = new JoinGuild(); deserialized.LoadPlainValue(plainValue); Assert.Equal(guildAddress, deserialized.GuildAddress); } @@ -33,35 +34,22 @@ public void Execute() var agentAddress = AddressUtil.CreateAgentAddress(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - var action = new ApplyGuild(guildAddress); + var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.JoinGuild(guildAddress, guildMasterAddress); - var bannedRepository = new GuildRepository(repository.World, new ActionContext()); - bannedRepository.Ban(guildAddress, guildMasterAddress, agentAddress); - // This case should fail because the agent is banned by the guild. - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = bannedRepository.World, - Signer = agentAddress, - })); + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.JoinGuild(guildAddress, agentAddress); - world = action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = agentAddress, - }); + var guild = repository.GetGuild(agentAddress); - repository.UpdateWorld(world); - Assert.True(repository.TryGetGuildApplication(agentAddress, out var application)); - Assert.Equal(guildAddress, application.GuildAddress); + Assert.Equal(guildMasterAddress, guild.GuildMasterAddress); + Assert.Equal(guildAddress, guild.Address); } } } diff --git a/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs b/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs index 664ece747b..1f60eb7997 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Action.Guild.Migration using Bencodex.Types; using Lib9c.Tests.Util; using Libplanet.Action.State; + using Libplanet.Crypto; using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; @@ -25,6 +26,7 @@ public void MigratePlanetariumPledgeToGuild_When_WithUnapprovedPledgeContract() { var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); + var validatorAddress = new PrivateKey().Address; var target = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); IWorld world = new World(MockUtil.MockModernWorldState); @@ -33,7 +35,7 @@ public void MigratePlanetariumPledgeToGuild_When_WithUnapprovedPledgeContract() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); repository.JoinGuild(guildAddress, guildMasterAddress); repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( MeadConfig.PatronAddress.Serialize(), @@ -50,6 +52,7 @@ public void MigratePlanetariumPledgeToGuild_When_WithPledgeContract() { var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); + var validatorAddress = new PrivateKey().Address; var target = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); IWorld world = new World(MockUtil.MockModernWorldState); @@ -58,7 +61,7 @@ public void MigratePlanetariumPledgeToGuild_When_WithPledgeContract() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); repository.JoinGuild(guildAddress, guildMasterAddress); repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( MeadConfig.PatronAddress.Serialize(), @@ -78,6 +81,7 @@ public void MigratePlanetariumPledgeToGuild_When_WithoutPledgeContract() { var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); + var validatorAddress = new PrivateKey().Address; var target = AddressUtil.CreateAgentAddress(); IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -85,7 +89,7 @@ public void MigratePlanetariumPledgeToGuild_When_WithoutPledgeContract() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); repository.JoinGuild(guildAddress, guildMasterAddress); Assert.Null(repository.GetJoinedGuild(target)); diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs index 8c8eef9d16..57bff02993 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action.Guild.Migration using Lib9c.Tests.Action; using Lib9c.Tests.Util; using Libplanet.Action.State; + using Libplanet.Crypto; using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; @@ -44,13 +45,14 @@ public void Execute_When_WithPledgeContract() var target = AddressUtil.CreateAgentAddress(); var caller = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); + var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); repository.JoinGuild(guildAddress, guildMasterAddress); repository.UpdateWorld(repository.World.SetLegacyState( pledgeAddress, @@ -95,13 +97,14 @@ public void Execute_When_WithUnapprovedPledgeContract() var target = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); var caller = AddressUtil.CreateAgentAddress(); + var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); repository.JoinGuild(guildAddress, guildMasterAddress); repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( MeadConfig.PatronAddress.Serialize(), @@ -131,6 +134,7 @@ public void Execute_When_WithoutPledgeContract() { var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); + var validatorAddress = new PrivateKey().Address; var target = AddressUtil.CreateAgentAddress(); var caller = AddressUtil.CreateAgentAddress(); IWorld world = new World(MockUtil.MockModernWorldState); @@ -139,7 +143,7 @@ public void Execute_When_WithoutPledgeContract() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); repository.JoinGuild(guildAddress, guildMasterAddress); Assert.Null(repository.GetJoinedGuild(target)); diff --git a/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs b/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs new file mode 100644 index 0000000000..cfd1db9dfa --- /dev/null +++ b/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs @@ -0,0 +1,63 @@ +namespace Lib9c.Tests.Action.Guild +{ + using System; + 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; + + public class MoveGuildTest + { + [Fact] + public void Serialization() + { + var guildAddress = AddressUtil.CreateGuildAddress(); + var action = new MoveGuild(guildAddress); + var plainValue = action.PlainValue; + + var deserialized = new MoveGuild(); + deserialized.LoadPlainValue(plainValue); + Assert.Equal(guildAddress, deserialized.GuildAddress); + } + + [Fact] + public void Execute() + { + var agentAddress = AddressUtil.CreateAgentAddress(); + var guildMasterAddress1 = AddressUtil.CreateAgentAddress(); + var guildMasterAddress2 = AddressUtil.CreateAgentAddress(); + var guildAddress1 = AddressUtil.CreateGuildAddress(); + var guildAddress2 = AddressUtil.CreateGuildAddress(); + var validatorAddress1 = new PrivateKey().Address; + var validatorAddress2 = new PrivateKey().Address; + + IWorld world = new World(MockUtil.MockModernWorldState); + var ncg = Currency.Uncapped("NCG", 2, null); + var goldCurrencyState = new GoldCurrencyState(ncg); + world = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + var repository = new GuildRepository(world, new ActionContext()); + repository.MakeGuild(guildAddress1, guildMasterAddress1, validatorAddress1); + repository.MakeGuild(guildAddress2, guildMasterAddress2, validatorAddress2); + repository.JoinGuild(guildAddress1, agentAddress); + var guild1 = repository.GetGuild(agentAddress); + + repository.MoveGuild(agentAddress, guildAddress2); + + var guild2 = repository.GetGuild(agentAddress); + + Assert.NotEqual(guild1.Address, guild2.Address); + Assert.Equal(guildMasterAddress2, guild2.GuildMasterAddress); + Assert.Equal(guildAddress2, guild2.Address); + } + } +} diff --git a/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs b/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs index dad2cc54a2..fae173654b 100644 --- a/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Action.Guild using System; using Lib9c.Tests.Util; using Libplanet.Action.State; + using Libplanet.Crypto; using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; @@ -31,6 +32,7 @@ public void Execute() var agentAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); + var validatorAddress = new PrivateKey().Address; var action = new QuitGuild(); IWorld world = new World(MockUtil.MockModernWorldState); @@ -39,7 +41,7 @@ public void Execute() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); // This case should fail because guild master cannot quit the guild. Assert.Throws(() => action.Execute(new ActionContext diff --git a/.Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs b/.Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs deleted file mode 100644 index 02d002f2f9..0000000000 --- a/.Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs +++ /dev/null @@ -1,93 +0,0 @@ -namespace Lib9c.Tests.Action.Guild -{ - using System; - using Lib9c.Tests.Util; - using Libplanet.Action.State; - 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; - - public class RejectGuildApplicationTest - { - [Fact] - public void Serialization() - { - var agentAddress = AddressUtil.CreateAgentAddress(); - var action = new RejectGuildApplication(agentAddress); - var plainValue = action.PlainValue; - - var deserialized = new RejectGuildApplication(); - deserialized.LoadPlainValue(plainValue); - Assert.Equal(agentAddress, deserialized.Target); - } - - [Fact] - public void Execute() - { - var appliedMemberAddress = AddressUtil.CreateAgentAddress(); - var nonAppliedMemberAddress = AddressUtil.CreateAgentAddress(); - var guildMasterAddress = AddressUtil.CreateAgentAddress(); - var guildAddress = AddressUtil.CreateGuildAddress(); - - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.ApplyGuild(appliedMemberAddress, guildAddress); - - // These cases should fail because the member didn't apply the guild and - // non-guild-master-addresses cannot reject the guild application. - Assert.Throws( - () => new RejectGuildApplication(nonAppliedMemberAddress).Execute(new ActionContext - { - PreviousState = repository.World, - Signer = guildMasterAddress, - })); - Assert.Throws( - () => new RejectGuildApplication(nonAppliedMemberAddress).Execute(new ActionContext - { - PreviousState = repository.World, - Signer = appliedMemberAddress, - })); - Assert.Throws( - () => new RejectGuildApplication(nonAppliedMemberAddress).Execute(new ActionContext - { - PreviousState = repository.World, - Signer = nonAppliedMemberAddress, - })); - - // These cases should fail because non-guild-master-addresses cannot reject the guild application. - Assert.Throws( - () => new RejectGuildApplication(appliedMemberAddress).Execute(new ActionContext - { - PreviousState = repository.World, - Signer = appliedMemberAddress, - })); - Assert.Throws( - () => new RejectGuildApplication(appliedMemberAddress).Execute(new ActionContext - { - PreviousState = repository.World, - Signer = nonAppliedMemberAddress, - })); - - world = new RejectGuildApplication(appliedMemberAddress).Execute(new ActionContext - { - PreviousState = repository.World, - Signer = guildMasterAddress, - }); - - repository.UpdateWorld(world); - Assert.False(repository.TryGetGuildApplication(appliedMemberAddress, out _)); - Assert.Null(repository.GetJoinedGuild(appliedMemberAddress)); - } - } -} diff --git a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs index ecf6705b08..50065fafc4 100644 --- a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Action.Guild using System; using Lib9c.Tests.Util; using Libplanet.Action.State; + using Libplanet.Crypto; using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; @@ -33,6 +34,7 @@ public void Execute_By_GuildMember() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); + var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -40,7 +42,7 @@ public void Execute_By_GuildMember() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); Assert.Throws(() => action.Execute(new ActionContext { @@ -56,6 +58,7 @@ public void Execute_By_GuildMaster() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); + var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -63,7 +66,7 @@ public void Execute_By_GuildMaster() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); var changedWorld = action.Execute(new ActionContext { @@ -84,6 +87,7 @@ public void Execute_By_Other() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var otherAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); + var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -91,7 +95,7 @@ public void Execute_By_Other() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); Assert.Throws(() => action.Execute(new ActionContext { @@ -108,6 +112,7 @@ public void ResetBannedAddresses() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); var bannedAddress = AddressUtil.CreateAgentAddress(); + var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -115,7 +120,7 @@ public void ResetBannedAddresses() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); repository.Ban(guildAddress, guildMasterAddress, bannedAddress); Assert.True(repository.IsBanned(guildAddress, bannedAddress)); diff --git a/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs index 4fe7f7e912..17c1760fbf 100644 --- a/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs +++ b/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs @@ -68,6 +68,7 @@ public void Unban_By_GuildMaster() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var targetGuildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); + var validatorAddress = new PrivateKey().Address; var action = new UnbanGuildMember(targetGuildMemberAddress); @@ -77,7 +78,7 @@ public void Unban_By_GuildMaster() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); repository.Ban(guildAddress, guildMasterAddress, targetGuildMemberAddress); Assert.True(repository.IsBanned(guildAddress, targetGuildMemberAddress)); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs index 9f3e106527..0aa5d51a07 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -69,12 +69,12 @@ public void Execute() { var fixture = new StaticFixture { - TotalReward = NCG * 1000, + TotalReward = GG * 1000, ValidatorsInfos = CreateArray(4, i => new ValidatorInfo { Key = new PrivateKey(), - Cash = NCG * 10, - Balance = NCG * 100, + Cash = GG * 10, + Balance = GG * 100, VoteFlag = i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null, }), Delegatorinfos = Array.Empty(), @@ -95,12 +95,12 @@ public void Execute_Theory(int validatorCount, double totalReward) { var fixture = new StaticFixture { - TotalReward = FungibleAssetValue.Parse(NCG, $"{totalReward:R}"), + TotalReward = FungibleAssetValue.Parse(GG, $"{totalReward:R}"), ValidatorsInfos = CreateArray(validatorCount, i => new ValidatorInfo { Key = new PrivateKey(), - Cash = NCG * 10, - Balance = NCG * 100, + Cash = GG * 10, + Balance = GG * 100, VoteFlag = i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null, }), Delegatorinfos = Array.Empty(), @@ -113,12 +113,12 @@ public void Execute_WithoutReward_Throw() { var fixture = new StaticFixture { - TotalReward = NCG * 0, + TotalReward = GG * 0, ValidatorsInfos = CreateArray(4, i => new ValidatorInfo { Key = new PrivateKey(), - Cash = NCG * 10, - Balance = NCG * 100, + Cash = GG * 10, + Balance = GG * 100, VoteFlag = i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null, }), Delegatorinfos = Array.Empty(), @@ -196,7 +196,7 @@ private void ExecuteWithFixture(IAllocateRewardFixture fixture) var expectedProposer = proposerKey; var votes = CreateVotes(validatorInfos, expectedBondedSet, height - 1); var balances = votes - .Select(vote => world.GetBalance(vote.ValidatorPublicKey.Address, NCG)).ToArray(); + .Select(vote => world.GetBalance(vote.ValidatorPublicKey.Address, GG)).ToArray(); var expectedProposerReward = CalculatePropserReward(totalReward) + CalculateBonusPropserReward(votes, totalReward); var expectedValidatorsReward = totalReward - expectedProposerReward; @@ -220,8 +220,8 @@ var expectedProposerReward .OfType() .Aggregate(BigInteger.Zero, (accum, next) => accum + next); var actualRepository = new ValidatorRepository(world, actionContext); - var actualAllocatedReward = NCG * 0; - var actualCommunityFund = world.GetBalance(Addresses.CommunityPool, NCG); + var actualAllocatedReward = GG * 0; + var actualCommunityFund = world.GetBalance(Addresses.CommunityPool, GG); foreach (var (vote, index) in votes.Select((v, i) => (v, i))) { if (vote.ValidatorPower is not { } validatorPower) @@ -233,14 +233,14 @@ var expectedProposerReward var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorAddress); var validatorRewardAddress = actualDelegatee.CurrentLumpSumRewardsRecordAddress(); var balance = balances[index]; - var actualBalance = world.GetBalance(validatorAddress, NCG); - var actualReward = world.GetBalance(validatorRewardAddress, NCG); + var actualBalance = world.GetBalance(validatorAddress, GG); + var actualReward = world.GetBalance(validatorRewardAddress, GG); var isProposer = vote.ValidatorPublicKey.Equals(expectedProposer.PublicKey); if (vote.Flag == VoteFlag.Null) { Assert.Equal(balance, actualBalance); - Assert.Equal(NCG * 0, actualReward); + Assert.Equal(GG * 0, actualReward); Assert.False(isProposer); continue; } @@ -301,10 +301,10 @@ private class RandomFixture : IAllocateRewardFixture public RandomFixture(int randomSeed) { _random = new Random(randomSeed); - TotalReward = GetRandomNCG(_random); + TotalReward = GetRandomGG(_random); ValidatorsInfos = CreateArray(_random.Next(1, 200), i => { - var balance = GetRandomNCG(_random); + var balance = GetRandomGG(_random); var flag = _random.Next() % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null; return new ValidatorInfo { @@ -316,7 +316,7 @@ public RandomFixture(int randomSeed) }); Delegatorinfos = CreateArray(_random.Next(1, 200), i => { - var balance = GetRandomNCG(_random); + var balance = GetRandomGG(_random); return new DelegatorInfo { Key = new PrivateKey(), diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs index 9fd1183c83..d4f3b82228 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -59,8 +59,8 @@ public void Execute() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - var validatorGold = NCG * 10; - var allocatedReward = NCG * 100; + var validatorGold = GG * 10; + var allocatedReward = GG * 100; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); @@ -79,7 +79,7 @@ public void Execute() world = claimRewardValidator.Execute(actionContext); // Then - var actualBalance = world.GetBalance(validatorKey.Address, NCG); + var actualBalance = world.GetBalance(validatorKey.Address, GG); Assert.Equal(expectedBalance, actualBalance); } @@ -94,17 +94,17 @@ public void Execute_Theory_OneDelegator(decimal totalReward) var fixture = new StaticFixture { DelegatorLength = 1, - TotalReward = FungibleAssetValue.Parse(NCG, $"{totalReward}"), + TotalReward = FungibleAssetValue.Parse(GG, $"{totalReward}"), ValidatorKey = new PrivateKey(), - ValidatorBalance = NCG * 100, - ValidatorCash = NCG * 10, + ValidatorBalance = GG * 100, + ValidatorCash = GG * 10, DelegatorInfos = new[] { new DelegatorInfo { Key = new PrivateKey(), - Balance = NCG * 100, - Cash = NCG * 10, + Balance = GG * 100, + Cash = GG * 10, }, }, }; @@ -130,23 +130,23 @@ public void Execute_Theory_TwoDelegators(decimal totalReward) var fixture = new StaticFixture { DelegatorLength = 2, - TotalReward = FungibleAssetValue.Parse(NCG, $"{totalReward}"), + TotalReward = FungibleAssetValue.Parse(GG, $"{totalReward}"), ValidatorKey = new PrivateKey(), - ValidatorBalance = NCG * 100, - ValidatorCash = NCG * 10, + ValidatorBalance = GG * 100, + ValidatorCash = GG * 10, DelegatorInfos = new[] { new DelegatorInfo { Key = new PrivateKey(), - Balance = NCG * 100, - Cash = NCG * 10, + Balance = GG * 100, + Cash = GG * 10, }, new DelegatorInfo { Key = new PrivateKey(), - Balance = NCG * 100, - Cash = NCG * 10, + Balance = GG * 100, + Cash = GG * 10, }, }, }; @@ -254,10 +254,10 @@ var expectedProposerReward // Then var repository = new ValidatorRepository(world, actionContext); var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); - var actualRemainReward = world.GetBalance(delegatee.RewardRemainderPoolAddress, NCG); - var actualValidatorBalance = world.GetBalance(validatorKey.Address, NCG); + var actualRemainReward = world.GetBalance(delegatee.RewardRemainderPoolAddress, GG); + var actualValidatorBalance = world.GetBalance(validatorKey.Address, GG); var actualDelegatorBalances = delegatorKeys - .Select(item => world.GetBalance(item.Address, NCG)) + .Select(item => world.GetBalance(item.Address, GG)) .ToArray(); Assert.Equal(expectedRemainReward, actualRemainReward); Assert.Equal(expectedValidatorBalance, actualValidatorBalance); @@ -297,12 +297,12 @@ public RandomFixture(int randomSeed) _random = new Random(randomSeed); DelegatorLength = _random.Next(3, 100); ValidatorKey = new PrivateKey(); - TotalReward = GetRandomNCG(_random); - ValidatorBalance = GetRandomNCG(_random); + TotalReward = GetRandomGG(_random); + ValidatorBalance = GetRandomGG(_random); ValidatorCash = GetRandomCash(_random, ValidatorBalance); DelegatorInfos = CreateArray(DelegatorLength, _ => { - var balance = GetRandomNCG(_random); + var balance = GetRandomGG(_random); return new DelegatorInfo { Key = new PrivateKey(), diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index 460caf813f..694068b9b8 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -37,7 +37,7 @@ private interface IDelegateValidatorFixture public void Serialization() { var address = new PrivateKey().Address; - var gold = NCG * 10; + var gold = GG * 10; var action = new DelegateValidator(address, gold); var plainValue = action.PlainValue; @@ -55,11 +55,11 @@ public void Execute() var validatorKey = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - var validatorGold = NCG * 10; - var delegatorGold = NCG * 20; + var validatorGold = GG * 10; + var delegatorGold = GG * 20; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); - world = EnsureToMintAsset(world, delegatorKey, NCG * 100, height++); + world = EnsureToMintAsset(world, delegatorKey, GG * 100, height++); // When var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorGold); @@ -80,7 +80,7 @@ public void Execute() Assert.Equal(delegatorGold.RawValue, bond.Share); Assert.Equal(validatorGold.RawValue + delegatorGold.RawValue, validator.Validator.Power); Assert.Equal(validator.Validator, Assert.Single(validatorList.Validators)); - Assert.Equal(NCG * 80, world.GetBalance(delegatorKey.Address, NCG)); + Assert.Equal(GG * 80, world.GetBalance(delegatorKey.Address, GG)); } [Theory] @@ -94,15 +94,15 @@ public void Execute_Theory(int delegatorCount) ValidatorInfo = new ValidatorInfo { Key = new PrivateKey(), - Cash = NCG * 10, - Balance = NCG * 100, + Cash = GG * 10, + Balance = GG * 100, }, DelegatorInfos = Enumerable.Range(0, delegatorCount) .Select(_ => new DelegatorInfo { Key = new PrivateKey(), - Cash = NCG * 20, - Balance = NCG * 100, + Cash = GG * 20, + Balance = GG * 100, }) .ToArray(), }; @@ -135,7 +135,7 @@ public void Execute_WithInvalidCurrency_Throw() var validatorKey = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - var validatorGold = NCG * 10; + var validatorGold = GG * 10; var delegatorDollar = Dollar * 20; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); @@ -161,8 +161,8 @@ public void Execute_WithInsufficientBalance_Throw() var world = World; var validatorKey = new PrivateKey(); var delegatorKey = new PrivateKey(); - var validatorGold = NCG * 10; - var delegatorGold = NCG * 10; + var validatorGold = GG * 10; + var delegatorGold = GG * 10; var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); @@ -175,7 +175,7 @@ public void Execute_WithInsufficientBalance_Throw() Signer = delegatorKey.Address, BlockIndex = height++, }; - var delegateValidator = new DelegateValidator(validatorKey.Address, NCG * 11); + var delegateValidator = new DelegateValidator(validatorKey.Address, GG * 11); // Then Assert.Throws(() => delegateValidator.Execute(actionContext)); @@ -189,7 +189,7 @@ public void Execute_ToInvalidValidator_Throw() var validatorKey = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, delegatorKey, NCG * 100, height++); + world = EnsureToMintAsset(world, delegatorKey, GG * 100, height++); // When var actionContext = new ActionContext @@ -197,7 +197,7 @@ public void Execute_ToInvalidValidator_Throw() PreviousState = world, Signer = delegatorKey.Address, }; - var delegateValidator = new DelegateValidator(validatorKey.Address, NCG * 10); + var delegateValidator = new DelegateValidator(validatorKey.Address, GG * 10); // Then Assert.Throws(() => delegateValidator.Execute(actionContext)); @@ -211,8 +211,8 @@ public void Execute_ToTombstonedValidator_Throw() var validatorKey = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - var validatorGold = NCG * 10; - var delegatorGold = NCG * 10; + var validatorGold = GG * 10; + var delegatorGold = GG * 10; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); world = EnsureTombstonedValidator(world, validatorKey, height++); @@ -237,10 +237,10 @@ public void Execute_CannotBeJailedDueToDelegatorDelegating() var world = World; var validatorKey = new PrivateKey(); var delegatorKey = new PrivateKey(); - var validatorCash = NCG * 10; - var validatorGold = NCG * 100; - var delegatorGold = NCG * 10; - var delegatorBalance = NCG * 100; + var validatorCash = GG * 10; + var validatorGold = GG * 100; + var delegatorGold = GG * 10; + var delegatorBalance = GG * 100; var actionContext = new ActionContext { }; var height = 1L; @@ -258,7 +258,7 @@ public void Execute_CannotBeJailedDueToDelegatorDelegating() var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); var expectedJailed = expectedDelegatee.Jailed; - var delegateValidator = new DelegateValidator(validatorKey.Address, 1 * NCG); + var delegateValidator = new DelegateValidator(validatorKey.Address, 1 * GG); actionContext = new ActionContext { PreviousState = world, @@ -316,9 +316,9 @@ private void ExecuteWithFixture(IDelegateValidatorFixture fixture) // Then var actualRepository = new ValidatorRepository(world, actionContext); var actualValidator = actualRepository.GetValidatorDelegatee(validatorKey.Address); - var actualValidatorBalance = world.GetBalance(validatorKey.Address, NCG); + var actualValidatorBalance = world.GetBalance(validatorKey.Address, GG); var actualDelegatorBalances = delegatorKeys - .Select(k => world.GetBalance(k.Address, NCG)).ToArray(); + .Select(k => world.GetBalance(k.Address, GG)).ToArray(); var actualPower = actualValidator.Power; for (var i = 0; i < delegatorKeys.Length; i++) @@ -342,15 +342,15 @@ public ValidatorInfo() public ValidatorInfo(Random random) { - Balance = GetRandomNCG(random); + Balance = GetRandomGG(random); Cash = GetRandomCash(random, Balance); } public PrivateKey Key { get; set; } = new PrivateKey(); - public FungibleAssetValue Cash { get; set; } = NCG * 10; + public FungibleAssetValue Cash { get; set; } = GG * 10; - public FungibleAssetValue Balance { get; set; } = NCG * 100; + public FungibleAssetValue Balance { get; set; } = GG * 100; } private struct DelegatorInfo @@ -361,15 +361,15 @@ public DelegatorInfo() public DelegatorInfo(Random random) { - Balance = GetRandomNCG(random); + Balance = GetRandomGG(random); Cash = GetRandomCash(random, Balance); } public PrivateKey Key { get; set; } = new PrivateKey(); - public FungibleAssetValue Cash { get; set; } = NCG * 10; + public FungibleAssetValue Cash { get; set; } = GG * 10; - public FungibleAssetValue Balance { get; set; } = NCG * 100; + public FungibleAssetValue Balance { get; set; } = GG * 100; } private struct StaticFixture : IDelegateValidatorFixture diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs index eb1c0c33ee..deb2c9a165 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs @@ -13,7 +13,7 @@ public class PromoteValidatorTest : ValidatorDelegationTestBase public void Serialization() { var publicKey = new PrivateKey().PublicKey; - var gold = NCG * 10; + var gold = GG * 10; var action = new PromoteValidator(publicKey, gold); var plainValue = action.PlainValue; @@ -30,8 +30,8 @@ public void Execute() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - var gold = NCG * 10; - world = EnsureToMintAsset(world, validatorKey, NCG * 100, height++); + var gold = GG * 10; + world = EnsureToMintAsset(world, validatorKey, GG * 100, height++); // When var actionContext = new ActionContext @@ -53,7 +53,7 @@ public void Execute() Assert.Equal(gold.RawValue, bond.Share); Assert.Equal(validator.Validator, Assert.Single(validatorList.Validators)); Assert.Equal(validator.Validator, Assert.Single(validatorList.GetBonded())); - Assert.Equal(NCG * 90, world.GetBalance(validatorKey.Address, NCG)); + Assert.Equal(GG * 90, world.GetBalance(validatorKey.Address, GG)); Assert.Empty(validatorList.GetUnbonded()); } @@ -65,8 +65,8 @@ public void Execute_ToInvalidValidator_Throw() var context = new ActionContext { }; var validatorKey = new PrivateKey(); var height = 1L; - var gold = NCG * 10; - world = EnsureToMintAsset(world, validatorKey, NCG * 100, height++); + var gold = GG * 10; + world = EnsureToMintAsset(world, validatorKey, GG * 100, height++); // When var actionContext = new ActionContext @@ -113,7 +113,7 @@ public void Execute_WithInsufficientBalance_Throw() var world = World; var validatorKey = new PrivateKey().PublicKey; var height = 1L; - var gold = NCG * 10; + var gold = GG * 10; // When var actionContext = new ActionContext @@ -136,7 +136,7 @@ public void Execute_PromotedValidator_Throw() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - var validatorGold = NCG * 10; + var validatorGold = GG * 10; world = EnsureToMintAsset(world, validatorKey, validatorGold * 2, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs index ac6056a327..cf76b74a15 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs @@ -58,10 +58,10 @@ public void Execute() var srcPrivateKey = new PrivateKey(); var dstPrivateKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, srcPrivateKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, srcPrivateKey, NCG * 10, height++); - world = EnsureToMintAsset(world, dstPrivateKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, dstPrivateKey, NCG * 10, height++); + world = EnsureToMintAsset(world, srcPrivateKey, GG * 10, height++); + world = EnsurePromotedValidator(world, srcPrivateKey, GG * 10, height++); + world = EnsureToMintAsset(world, dstPrivateKey, GG * 10, height++); + world = EnsurePromotedValidator(world, dstPrivateKey, GG * 10, height++); // When var expectedRepository = new ValidatorRepository(world, actionContext); @@ -87,10 +87,10 @@ public void Execute() Assert.Contains(srcPrivateKey.Address, actualDstValidator.Delegators); Assert.Single(actualValidatorList.Validators); Assert.Equal(actualDstValidator.Validator, actualValidatorList.Validators[0]); - Assert.Equal((NCG * 10).RawValue, actualDstBond.Share); - Assert.Equal(NCG * 20, actualDstValidator.TotalDelegated); - Assert.Equal((NCG * 20).RawValue, actualDstValidator.TotalShares); - Assert.Equal((NCG * 20).RawValue, actualDstValidator.Power); + Assert.Equal((GG * 10).RawValue, actualDstBond.Share); + Assert.Equal(GG * 20, actualDstValidator.TotalDelegated); + Assert.Equal((GG * 20).RawValue, actualDstValidator.TotalShares); + Assert.Equal((GG * 20).RawValue, actualDstValidator.Power); } [Theory] @@ -104,21 +104,21 @@ public void Execute_Theory(int delegatorCount) ValidatorInfo1 = new ValidatorInfo { Key = new PrivateKey(), - Cash = NCG * 10, - Balance = NCG * 100, + Cash = GG * 10, + Balance = GG * 100, }, ValidatorInfo2 = new ValidatorInfo { Key = new PrivateKey(), - Cash = NCG * 10, - Balance = NCG * 100, + Cash = GG * 10, + Balance = GG * 100, }, DelegatorInfos = Enumerable.Range(0, delegatorCount) .Select(_ => new DelegatorInfo { Key = new PrivateKey(), - Cash = NCG * 20, - Balance = NCG * 100, + Cash = GG * 20, + Balance = GG * 100, Redelegating = 20, }) .ToArray(), @@ -152,10 +152,10 @@ public void Execute_ToInvalidValidator_Throw() var validatorKey = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, GG * 10, height++); // When var actionContext = new ActionContext @@ -183,12 +183,12 @@ public void Execute_WithNotPositiveShare_Throw(long share) var validatorKey2 = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey1, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, height++); - world = EnsureToMintAsset(world, validatorKey2, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey1, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey1, GG * 10, height++); + world = EnsureToMintAsset(world, validatorKey2, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey2, GG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, GG * 10, height++); // When var actionContext = new ActionContext @@ -214,12 +214,12 @@ public void Execute_WithOverShare_Throw() var validatorKey2 = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey1, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, height++); - world = EnsureToMintAsset(world, validatorKey2, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey1, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey1, GG * 10, height++); + world = EnsureToMintAsset(world, validatorKey2, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey2, GG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, GG * 10, height++); // When var actionContext = new ActionContext @@ -248,12 +248,12 @@ public void Execute_FromJailedValidator_ToNotJailedValidator() var validatorKey1 = new PrivateKey(); var validatorKey2 = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey1, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey1, NCG * 10, height++); - world = EnsureToMintAsset(world, validatorKey2, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey2, NCG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey1, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey1, GG * 10, height++); + world = EnsureToMintAsset(world, validatorKey2, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey2, GG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, GG * 10, height++); world = EnsureJailedValidator(world, validatorKey1, ref height); // When @@ -293,10 +293,10 @@ public void Execute_ToTombstonedValidator_Throw() var srcPrivateKey = new PrivateKey(); var dstPrivateKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, srcPrivateKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, srcPrivateKey, NCG * 10, height++); - world = EnsureToMintAsset(world, dstPrivateKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, dstPrivateKey, NCG * 10, height++); + world = EnsureToMintAsset(world, srcPrivateKey, GG * 10, height++); + world = EnsurePromotedValidator(world, srcPrivateKey, GG * 10, height++); + world = EnsureToMintAsset(world, dstPrivateKey, GG * 10, height++); + world = EnsurePromotedValidator(world, dstPrivateKey, GG * 10, height++); world = EnsureTombstonedValidator(world, dstPrivateKey, height++); // When @@ -325,10 +325,10 @@ public void Execute_SrcAndDstAddressAreSame_Throw() var validatorKey = new PrivateKey(); var delegatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, GG * 10, height++); // When var actionContext = new ActionContext @@ -353,10 +353,10 @@ public void Execute_CannotBeJailedDueToDelegatorRedelegating() var validatorKey1 = new PrivateKey(); var validatorKey2 = new PrivateKey(); var delegatorKey = new PrivateKey(); - var validatorCash = NCG * 10; - var validatorGold = NCG * 100; - var delegatorGold = NCG * 10; - var delegatorBalance = NCG * 100; + var validatorCash = GG * 10; + var validatorGold = GG * 100; + var delegatorGold = GG * 10; + var delegatorBalance = GG * 100; var actionContext = new ActionContext { }; var height = 1L; @@ -429,7 +429,7 @@ private void ExecuteWithFixture(IRedelegateValidatorFixture fixture) var expectedValidatorBalance1 = validatorBalance1 - validatorCash1; var expectedValidatorBalance2 = validatorBalance2 - validatorCash2; var expectedDelegatorBalances = delegatorKeys - .Select(k => world.GetBalance(k.Address, NCG)).ToArray(); + .Select(k => world.GetBalance(k.Address, GG)).ToArray(); var expectedShares1 = delegatorCashes .Select((c, i) => c.RawValue - delegatorRedelegatings[i]).ToArray(); var expectedShares2 = delegatorRedelegatings; @@ -456,10 +456,10 @@ private void ExecuteWithFixture(IRedelegateValidatorFixture fixture) var actualRepository = new ValidatorRepository(world, actionContext); var actualValidator1 = actualRepository.GetValidatorDelegatee(validatorKey1.Address); var actualValidator2 = actualRepository.GetValidatorDelegatee(validatorKey2.Address); - var actualValidatorBalance1 = world.GetBalance(validatorKey1.Address, NCG); - var actualValidatorBalance2 = world.GetBalance(validatorKey2.Address, NCG); + var actualValidatorBalance1 = world.GetBalance(validatorKey1.Address, GG); + var actualValidatorBalance2 = world.GetBalance(validatorKey2.Address, GG); var actualDelegatorBalances = delegatorKeys - .Select(k => world.GetBalance(k.Address, NCG)).ToArray(); + .Select(k => world.GetBalance(k.Address, GG)).ToArray(); var actualPower1 = actualValidator1.Power; var actualPower2 = actualValidator2.Power; @@ -489,15 +489,15 @@ public ValidatorInfo() public ValidatorInfo(Random random) { - Balance = GetRandomNCG(random); + Balance = GetRandomGG(random); Cash = GetRandomCash(random, Balance); } public PrivateKey Key { get; set; } = new PrivateKey(); - public FungibleAssetValue Cash { get; set; } = NCG * 10; + public FungibleAssetValue Cash { get; set; } = GG * 10; - public FungibleAssetValue Balance { get; set; } = NCG * 100; + public FungibleAssetValue Balance { get; set; } = GG * 100; } private struct DelegatorInfo @@ -508,16 +508,16 @@ public DelegatorInfo() public DelegatorInfo(Random random) { - Balance = GetRandomNCG(random); + Balance = GetRandomGG(random); Cash = GetRandomCash(random, Balance); Redelegating = GetRandomCash(random, Cash).RawValue; } public PrivateKey Key { get; set; } = new PrivateKey(); - public FungibleAssetValue Cash { get; set; } = NCG * 10; + public FungibleAssetValue Cash { get; set; } = GG * 10; - public FungibleAssetValue Balance { get; set; } = NCG * 100; + public FungibleAssetValue Balance { get; set; } = GG * 100; public BigInteger Redelegating { get; set; } = 100; } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs index ef695b9881..34999626f4 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs @@ -26,8 +26,8 @@ public void Execute() // Given var world = World; var validatorKey = new PrivateKey(); - var validatorGold = NCG * 50; - var validatorBalance = NCG * 100; + var validatorGold = GG * 50; + var validatorBalance = GG * 100; var share = new BigInteger(10); var height = 1L; var actionContext = new ActionContext { }; @@ -42,7 +42,7 @@ public void Execute() var expectedUnbondingSet = expectedRepository.GetUnbondingSet(); var expectedReleaseCount = expectedUnbondingSet.UnbondingRefs.Count; var expectedDepositGold = expectedDelegatee.FAVFromShare(share); - var expectedBalance = world.GetBalance(validatorKey.Address, NCG) + expectedDepositGold; + var expectedBalance = world.GetBalance(validatorKey.Address, GG) + expectedDepositGold; var releaseValidatorUnbondings = new ReleaseValidatorUnbondings(validatorKey.Address); actionContext = new ActionContext @@ -55,7 +55,7 @@ public void Execute() // Then var actualRepository = new ValidatorRepository(world, actionContext); - var actualBalance = world.GetBalance(validatorKey.Address, NCG); + var actualBalance = world.GetBalance(validatorKey.Address, GG); var actualUnbondingSet = actualRepository.GetUnbondingSet(); var actualReleaseCount = actualUnbondingSet.UnbondingRefs.Count; @@ -75,8 +75,8 @@ public void Execute_ThereIsNoUnbonding_AtEarlyHeight() var actionContext = new ActionContext { }; var share = new BigInteger(10); - world = EnsureToMintAsset(world, validatorKey, NCG * 100, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 50, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 100, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 50, height++); world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, share, height); // When diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs index 9dc8348735..8459c4f13f 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs @@ -66,7 +66,7 @@ public void Execute() // Given var world = World; var validatorKey = new PrivateKey(); - var validatorGold = NCG * 10; + var validatorGold = GG * 10; var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height); @@ -100,7 +100,7 @@ public void Execute_Theory(int oldCommissionPercentage, int newCommissionPercent // Given var world = World; var validatorKey = new PrivateKey(); - var validatorGold = NCG * 10; + var validatorGold = GG * 10; var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); @@ -134,11 +134,11 @@ public void Execute_Theory_WithValueGreaterThanMaximum_Throw(int commissionPerce // Given var world = World; var validatorKey = new PrivateKey(); - var validatorGold = NCG * 10; + var validatorGold = GG * 10; var height = 1L; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height); // When var actionContext = new ActionContext @@ -164,11 +164,11 @@ public void Execute_Theory_WithNegative_Throw(int commissionPercentage) // Given var world = World; var validatorKey = new PrivateKey(); - var validatorGold = NCG * 10; + var validatorGold = GG * 10; var height = 1L; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); // When var actionContext = new ActionContext @@ -193,7 +193,7 @@ public void Execute_Theory_WithInvalidValue_Throw(int cooldown) // Given var world = World; var validatorKey = new PrivateKey(); - var validatorGold = NCG * 10; + var validatorGold = GG * 10; var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); @@ -221,7 +221,7 @@ public void Execute_Theory_WitValue(int period) // Given var world = World; var validatorKey = new PrivateKey(); - var validatorGold = NCG * 10; + var validatorGold = GG * 10; var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs index b1b85326ff..2a103fde41 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs @@ -34,13 +34,13 @@ public void Execute() const int length = 10; var world = World; var validatorKey = new PrivateKey(); - var validatorGold = NCG * 10; + var validatorGold = GG * 10; var deletatorKeys = CreateArray(length, _ => new PrivateKey()); - var delegatorGolds = CreateArray(length, i => NCG * Random.Shared.Next(10, 100)); + var delegatorGolds = CreateArray(length, i => GG * Random.Shared.Next(10, 100)); var height = 1L; var actionContext = new ActionContext { }; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); world = EnsureToMintAssets(world, deletatorKeys, delegatorGolds, height++); world = EnsureBondedDelegators( world, deletatorKeys, validatorKey, delegatorGolds, height++); @@ -92,7 +92,7 @@ public void Execute() world = slashValidator.Execute(actionContext); // Then - var balance = world.GetBalance(validatorKey.Address, NCG); + var balance = world.GetBalance(validatorKey.Address, GG); var actualRepository = new ValidatorRepository(world, actionContext); var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); var actualValidatorShare = actualRepository.GetBond(actualDelegatee, validatorKey.Address).Share; @@ -138,8 +138,8 @@ public void Execute_ByAbstain() var validatorKey = new PrivateKey(); var actionContext = new ActionContext { }; var height = 1L; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); // When for (var i = 0L; i <= AbstainHistory.MaxAbstainAllowance; i++) @@ -176,8 +176,8 @@ public void Execute_ToJailedValidator_ThenNothingHappens() var validatorKey = new PrivateKey(); var height = 1L; var actionContext = new ActionContext(); - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); world = EnsureTombstonedValidator(world, validatorKey, height++); // When diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 9671194bd4..03b67f78c7 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -53,8 +53,8 @@ public void Execute() var actionContext = new ActionContext { }; var validatorKey = new PrivateKey(); var height = 1L; - var validatorGold = NCG * 10; - world = EnsureToMintAsset(world, validatorKey, NCG * 100, height++); + var validatorGold = GG * 10; + world = EnsureToMintAsset(world, validatorKey, GG * 100, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); // When @@ -95,16 +95,16 @@ public void Execute_Theory(int delegatorCount) ValidatorInfo = new ValidatorInfo { Key = new PrivateKey(), - Cash = NCG * 10, - Balance = NCG * 100, + Cash = GG * 10, + Balance = GG * 100, SubtractShare = 10, }, DelegatorInfos = Enumerable.Range(0, delegatorCount) .Select(_ => new DelegatorInfo { Key = new PrivateKey(), - Cash = NCG * 20, - Balance = NCG * 100, + Cash = GG * 20, + Balance = GG * 100, SubtractShare = 20, }) .ToArray(), @@ -139,10 +139,10 @@ public void Execute_FromInvalidValidtor_Throw() var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, GG * 10, height++); // When var actionContext = new ActionContext @@ -168,10 +168,10 @@ public void Execute_WithNotPositiveShare_Throw(long share) var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, GG * 10, height++); // When var actionContext = new ActionContext @@ -195,8 +195,8 @@ public void Execute_WithoutDelegating_Throw() var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); // When var actionContext = new ActionContext @@ -221,10 +221,10 @@ public void Execute_FromJailedValidator() var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, GG * 10, height++); world = EnsureJailedValidator(world, validatorKey, ref height); // When @@ -257,10 +257,10 @@ public void Execute_FromTombstonedValidator() var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, NCG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); + world = EnsureBondedDelegator(world, delegatorKey, validatorKey, GG * 10, height++); world = EnsureTombstonedValidator(world, validatorKey, height++); // When @@ -292,9 +292,9 @@ public void Execute_CannotBeJailedDueToDelegatorUndelegating() var world = World; var validatorKey = new PrivateKey(); var delegatorKey = new PrivateKey(); - var validatorCash = NCG * 10; - var validatorGold = NCG * 100; - var delegatorGold = NCG * 10; + var validatorCash = GG * 10; + var validatorGold = GG * 100; + var delegatorGold = GG * 10; var actionContext = new ActionContext { }; var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); @@ -390,7 +390,7 @@ private void ExecuteWithFixture(IUndelegateValidatorFixture fixture) var expectedValidator = expectedRepository.GetValidatorDelegatee(validatorKey.Address); var expectedValidatorBalance = validatorBalance - validatorCash; var expectedDelegatorBalances = delegatorKeys - .Select(k => world.GetBalance(k.Address, NCG)).ToArray(); + .Select(k => world.GetBalance(k.Address, GG)).ToArray(); var expectedShares = delegatorCashes .Select((c, i) => c.RawValue - delegatorSubtractShares[i]).ToArray(); var expectedPower = expectedValidator.Power - delegatorSubtractShares.Aggregate( @@ -413,9 +413,9 @@ private void ExecuteWithFixture(IUndelegateValidatorFixture fixture) // Then var actualRepository = new ValidatorRepository(world, actionContext); var actualValidator = actualRepository.GetValidatorDelegatee(validatorKey.Address); - var actualValidatorBalance = world.GetBalance(validatorKey.Address, NCG); + var actualValidatorBalance = world.GetBalance(validatorKey.Address, GG); var actualDelegatorBalances = delegatorKeys - .Select(k => world.GetBalance(k.Address, NCG)).ToArray(); + .Select(k => world.GetBalance(k.Address, GG)).ToArray(); var actualPower = actualValidator.Power; for (var i = 0; i < delegatorKeys.Length; i++) @@ -439,7 +439,7 @@ public ValidatorInfo() public ValidatorInfo(Random random) { - Balance = GetRandomNCG(random); + Balance = GetRandomGG(random); Cash = GetRandomCash(random, Balance); SubtractShare = GetRandomCash(random, Cash).RawValue; if (SubtractShare == 0) @@ -450,9 +450,9 @@ public ValidatorInfo(Random random) public PrivateKey Key { get; set; } = new PrivateKey(); - public FungibleAssetValue Cash { get; set; } = NCG * 10; + public FungibleAssetValue Cash { get; set; } = GG * 10; - public FungibleAssetValue Balance { get; set; } = NCG * 100; + public FungibleAssetValue Balance { get; set; } = GG * 100; public BigInteger SubtractShare { get; set; } = 100; } @@ -465,16 +465,16 @@ public DelegatorInfo() public DelegatorInfo(Random random) { - Balance = GetRandomNCG(random); + Balance = GetRandomGG(random); Cash = GetRandomCash(random, Balance); SubtractShare = GetRandomCash(random, Cash).RawValue; } public PrivateKey Key { get; set; } = new PrivateKey(); - public FungibleAssetValue Cash { get; set; } = NCG * 10; + public FungibleAssetValue Cash { get; set; } = GG * 10; - public FungibleAssetValue Balance { get; set; } = NCG * 100; + public FungibleAssetValue Balance { get; set; } = GG * 100; public BigInteger SubtractShare { get; set; } = 100; } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs index 8adbaba247..2247ba5364 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs @@ -28,7 +28,7 @@ public void Execute() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - var validatorGold = NCG * 100; + var validatorGold = GG * 100; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); world = EnsureJailedValidator(world, validatorKey, ref height); @@ -79,8 +79,8 @@ public void Execute_OnNotJailedValidator_Throw() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); // When var unjailValidator = new UnjailValidator(); @@ -102,8 +102,8 @@ public void Execute_TooEarly_Throw() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); world = EnsureJailedValidator(world, validatorKey, ref height); // When @@ -127,8 +127,8 @@ public void Execute_OnTombstonedValidator_Throw() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, NCG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, NCG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); world = EnsureTombstonedValidator(world, validatorKey, height++); // When diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs index 9ab953629a..17297db452 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs @@ -29,7 +29,7 @@ public void Execute() const int length = 10; var world = World; var validatorKeys = CreateArray(length, _ => new PrivateKey()); - var validatorGolds = CreateArray(length, i => NCG * Random.Shared.Next(1, length + 1)); + var validatorGolds = CreateArray(length, i => GG * Random.Shared.Next(1, length + 1)); var height = 1L; var actionContext = new ActionContext { }; world = EnsureToMintAssets(world, validatorKeys, validatorGolds, height++); @@ -65,9 +65,9 @@ public void Execute_ExcludesTombstonedValidator() const int length = 10; var world = World; var validatorKeys = CreateArray(length, _ => new PrivateKey()); - var validatorGolds = CreateArray(length, i => NCG * 100); + var validatorGolds = CreateArray(length, i => GG * 100); var height = 1L; - var validatorGold = NCG * 100; + var validatorGold = GG * 100; var actionContext = new ActionContext { }; world = EnsureToMintAssets(world, validatorKeys, validatorGolds, height++); world = EnsurePromotedValidators(world, validatorKeys, validatorGolds, height++); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 589dc8dbbc..583b7a022b 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -22,13 +22,13 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; public class ValidatorDelegationTestBase { - protected static readonly Currency NCG = Currency.Uncapped("NCG", 2, null); + protected static readonly Currency GG = Currencies.GuildGold; protected static readonly Currency Dollar = Currency.Uncapped("dollar", 2, null); public ValidatorDelegationTestBase() { var world = new World(MockUtil.MockModernWorldState); - var goldCurrencyState = new GoldCurrencyState(NCG); + var goldCurrencyState = new GoldCurrencyState(GG); World = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); } @@ -40,7 +40,7 @@ public ValidatorDelegationTestBase() protected IWorld World { get; } - protected FungibleAssetValue MinimumDelegation { get; } = NCG * 10; + protected FungibleAssetValue MinimumDelegation { get; } = GG * 10; protected static T[] CreateArray(int length, Func creator) => Enumerable.Range(0, length).Select(creator).ToArray(); @@ -53,7 +53,8 @@ protected static IWorld EnsureToMintAsset( PreviousState = world, BlockIndex = blockHeight, }; - return world.MintAsset(actionContext, privateKey.Address, amount); + var address = privateKey.Address; + return world.MintAsset(actionContext, address, amount); } protected static IWorld EnsureToMintAssets( @@ -549,17 +550,17 @@ protected static FungibleAssetValue CalculateCommunityFund(ImmutableArray return communityFund; } - protected static FungibleAssetValue GetRandomNCG() => GetRandomNCG(Random.Shared, 1, 100000); + protected static FungibleAssetValue GetRandomGG() => GetRandomGG(Random.Shared, 1, 100000); - protected static FungibleAssetValue GetRandomNCG(Random random) - => GetRandomNCG(random, 0.01m, 1000.0m); + protected static FungibleAssetValue GetRandomGG(Random random) + => GetRandomGG(random, 0.01m, 1000.0m); - protected static FungibleAssetValue GetRandomNCG(Random random, decimal min, decimal max) + protected static FungibleAssetValue GetRandomGG(Random random, decimal min, decimal max) { var minLong = (int)(min * 100); var maxLong = (int)(max * 100); var value = Math.Round(random.Next(minLong, maxLong) / 100.0, 2); - return FungibleAssetValue.Parse(NCG, $"{value:R}"); + return FungibleAssetValue.Parse(GG, $"{value:R}"); } protected static FungibleAssetValue GetRandomCash(Random random, FungibleAssetValue fav) diff --git a/.Lib9c.Tests/Delegation/DummyDelegator.cs b/.Lib9c.Tests/Delegation/DummyDelegator.cs index bbf5c8e128..d7b0223f56 100644 --- a/.Lib9c.Tests/Delegation/DummyDelegator.cs +++ b/.Lib9c.Tests/Delegation/DummyDelegator.cs @@ -9,7 +9,7 @@ public DummyDelegator(Address address, IDelegationRepository repository) } public DummyDelegator(Address address, Address accountAddress, DummyRepository repo) - : base(address, accountAddress, address, repo) + : base(address, accountAddress, address, address, repo) { } } diff --git a/.Lib9c.Tests/Delegation/DummyRepository.cs b/.Lib9c.Tests/Delegation/DummyRepository.cs index 11af6050e1..3e19cf8a05 100644 --- a/.Lib9c.Tests/Delegation/DummyRepository.cs +++ b/.Lib9c.Tests/Delegation/DummyRepository.cs @@ -12,7 +12,7 @@ public class DummyRepository : DelegationRepository public DummyRepository(IWorld world, IActionContext context) : base( world: world, - context: context, + actionContext: context, delegateeAccountAddress: new Address("1000000000000000000000000000000000000000"), delegatorAccountAddress: new Address("1000000000000000000000000000000000000001"), delegateeMetadataAccountAddress: new Address("0000000000000000000000000000000000000002"), diff --git a/.Lib9c.Tests/Delegation/TestDelegator.cs b/.Lib9c.Tests/Delegation/TestDelegator.cs index 20fbade1f1..fa1c4c2e99 100644 --- a/.Lib9c.Tests/Delegation/TestDelegator.cs +++ b/.Lib9c.Tests/Delegation/TestDelegator.cs @@ -11,7 +11,7 @@ public TestDelegator(Address address, TestRepository repo) } public TestDelegator(Address address, Address accountAddress, TestRepository repo) - : base(address, accountAddress, address, repo) + : base(address, accountAddress, address, address, repo) { } } diff --git a/.Lib9c.Tests/Delegation/TestRepository.cs b/.Lib9c.Tests/Delegation/TestRepository.cs index befff670a2..6a00b476f5 100644 --- a/.Lib9c.Tests/Delegation/TestRepository.cs +++ b/.Lib9c.Tests/Delegation/TestRepository.cs @@ -13,7 +13,7 @@ public class TestRepository : DelegationRepository public TestRepository(IWorld world, IActionContext context) : base( world: world, - context: context, + actionContext: context, delegateeAccountAddress: new Address("0000000000000000000000000000000000000000"), delegatorAccountAddress: new Address("0000000000000000000000000000000000000001"), delegateeMetadataAccountAddress: new Address("0000000000000000000000000000000000000002"), diff --git a/.Lib9c.Tests/Model/Guild/GuildApplicationTest.cs b/.Lib9c.Tests/Model/Guild/GuildApplicationTest.cs deleted file mode 100644 index 3879932184..0000000000 --- a/.Lib9c.Tests/Model/Guild/GuildApplicationTest.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Lib9c.Tests.Model.Guild -{ - using System.Threading.Tasks; - using Lib9c.Tests.Util; - using Nekoyume.TypedAddress; - using VerifyTests; - using VerifyXunit; - using Xunit; - - [UsesVerify] - public class GuildApplicationTest - { - public GuildApplicationTest() - { - VerifierSettings.SortPropertiesAlphabetically(); - } - - [Fact] - public Task Snapshot() - { - var guildApplication = new Nekoyume.Model.Guild.GuildApplication( - new GuildAddress("0xd928ae87311dead490c986c24cc23c37eff892f2")); - - return Verifier.Verify(guildApplication.Bencoded); - } - - [Fact] - public void Serialization() - { - var guildApplication = new Nekoyume.Model.Guild.GuildApplication( - AddressUtil.CreateGuildAddress()); - var newGuildApplication = - new Nekoyume.Model.Guild.GuildApplication(guildApplication.Bencoded); - - Assert.Equal(guildApplication.GuildAddress, newGuildApplication.GuildAddress); - } - } -} diff --git a/.Lib9c.Tests/Model/Guild/GuildTest.cs b/.Lib9c.Tests/Model/Guild/GuildTest.cs index b0ff757ef2..1ccf7bb2c8 100644 --- a/.Lib9c.Tests/Model/Guild/GuildTest.cs +++ b/.Lib9c.Tests/Model/Guild/GuildTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Model.Guild using Lib9c.Tests.Action; using Lib9c.Tests.Util; using Libplanet.Action.State; + using Libplanet.Crypto; using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume.TypedAddress; @@ -24,9 +25,11 @@ public Task Snapshot() { IWorld world = new World(MockUtil.MockModernWorldState); var repository = new Nekoyume.Model.Guild.GuildRepository(world, new ActionContext()); + var validatorAddress = new PrivateKey().Address; var guild = new Nekoyume.Model.Guild.Guild( - AddressUtil.CreateAgentAddress(), + AddressUtil.CreateGuildAddress(), new AgentAddress("0xd928ae87311dead490c986c24cc23c37eff892f2"), + validatorAddress, Currency.Legacy("NCG", 2, null), repository); @@ -38,10 +41,12 @@ public void Serialization() { IWorld world = new World(MockUtil.MockModernWorldState); var repository = new Nekoyume.Model.Guild.GuildRepository(world, new ActionContext()); - var guildAddress = AddressUtil.CreateAgentAddress(); + var guildAddress = AddressUtil.CreateGuildAddress(); + var validatorAddress = new PrivateKey().Address; var guild = new Nekoyume.Model.Guild.Guild( guildAddress, AddressUtil.CreateAgentAddress(), + validatorAddress, Currency.Legacy("NCG", 2, null), repository); repository.SetGuild(guild); diff --git a/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs b/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs index 9dbd733c65..3f2bb4ef98 100644 --- a/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs +++ b/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.PolicyAction.Tx.Begin using Lib9c.Tests.Action; using Lib9c.Tests.Util; using Libplanet.Action.State; + using Libplanet.Crypto; using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; @@ -38,13 +39,14 @@ public void Execute_When_WithPledgeContract() var guildAddress = AddressUtil.CreateGuildAddress(); var agentAddress = AddressUtil.CreateAgentAddress(); var pledgeAddress = agentAddress.GetPledgeAddress(); + var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); repository.JoinGuild(guildAddress, guildMasterAddress); repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( MeadConfig.PatronAddress.Serialize(), @@ -72,13 +74,14 @@ public void Execute_When_WithoutPledgeContract() var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); var agentAddress = AddressUtil.CreateAgentAddress(); + var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); repository.JoinGuild(guildAddress, guildMasterAddress); Assert.Null(repository.GetJoinedGuild(agentAddress)); From 23faf4ee7144812713e1e308f33824c679228199 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 16 Oct 2024 22:45:32 +0900 Subject: [PATCH 094/206] refactor: Remove redundancies --- Lib9c/Module/Guild/GuildModule.cs | 12 +++--- Lib9c/Module/Guild/GuildParticipantModule.cs | 1 - Lib9c/Module/Guild/GuildUnbondingModule.cs | 42 ------------------- .../ValidatorDelegateeModule.cs | 7 ++-- .../ValidatorDelegatorModule.cs | 27 +++++------- .../ValidatorUnbondingModule.cs | 4 +- .../ValidatorDelegation/ValidatorDelegatee.cs | 8 ++-- 7 files changed, 24 insertions(+), 77 deletions(-) delete mode 100644 Lib9c/Module/Guild/GuildUnbondingModule.cs diff --git a/Lib9c/Module/Guild/GuildModule.cs b/Lib9c/Module/Guild/GuildModule.cs index 07936baea8..280c1b4683 100644 --- a/Lib9c/Module/Guild/GuildModule.cs +++ b/Lib9c/Module/Guild/GuildModule.cs @@ -36,9 +36,9 @@ public static bool TryGetGuild( public static GuildRepository MakeGuild( this GuildRepository repository, GuildAddress guildAddress, - AgentAddress signer, Address validatorAddress) { + var signer = new AgentAddress(repository.ActionContext.Signer); if (repository.GetJoinedGuild(signer) is not null) { throw new InvalidOperationException("The signer already has a guild."); @@ -58,10 +58,9 @@ public static GuildRepository MakeGuild( } public static GuildRepository RemoveGuild( - this GuildRepository repository, - AgentAddress signer, - long height) + this GuildRepository repository) { + var signer = new AgentAddress(repository.ActionContext.Signer); if (repository.GetJoinedGuild(signer) is not { } guildAddress) { throw new InvalidOperationException("The signer does not join any guild."); @@ -94,11 +93,10 @@ public static GuildRepository RemoveGuild( public static GuildRepository CollectRewardGuild( this GuildRepository repository, - GuildAddress guildAddress, - long height) + GuildAddress guildAddress) { var guild = repository.GetGuild(guildAddress); - guild.CollectRewards(height); + guild.CollectRewards(repository.ActionContext.BlockIndex); repository.SetGuild(guild); return repository; diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index 19ce138f4b..17c87970b8 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using System.Numerics; using Lib9c; -using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Model.Guild; using Nekoyume.TypedAddress; diff --git a/Lib9c/Module/Guild/GuildUnbondingModule.cs b/Lib9c/Module/Guild/GuildUnbondingModule.cs deleted file mode 100644 index 390fa8ba4b..0000000000 --- a/Lib9c/Module/Guild/GuildUnbondingModule.cs +++ /dev/null @@ -1,42 +0,0 @@ -// using System; -// using Libplanet.Action; -// using Libplanet.Action.State; -// using Nekoyume.Delegation; -// using Nekoyume.Model.Guild; - -// namespace Nekoyume.Module.Guild -// { -// public static class GuildUnbondingModule -// { -// public static IWorld ReleaseUnbondings(this IWorld world, IActionContext context) -// { -// var repository = new GuildRepository(world, context); -// var unbondingSet = repository.GetUnbondingSet(); -// var unbondings = unbondingSet.UnbondingsToRelease(context.BlockIndex); - -// IUnbonding released; -// foreach (var unbonding in unbondings) -// { -// released = unbonding.Release(context.BlockIndex); - -// switch (released) -// { -// case UnbondLockIn unbondLockIn: -// repository.SetUnbondLockIn(unbondLockIn); -// break; -// case RebondGrace rebondGrace: -// repository.SetRebondGrace(rebondGrace); -// break; -// default: -// throw new ArgumentException("Invalid unbonding type."); -// } - -// unbondingSet = unbondingSet.SetUnbonding(released); -// } - -// repository.SetUnbondingSet(unbondingSet); - -// return repository.World; -// } -// } -// } diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs index c757553e57..558772159e 100644 --- a/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs @@ -2,8 +2,6 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Numerics; -using Lib9c; -using Libplanet.Action; using Libplanet.Crypto; using Nekoyume.ValidatorDelegation; @@ -29,8 +27,9 @@ public static bool TryGetValidatorDelegatee( } public static ValidatorRepository CreateValidatorDelegatee( - this ValidatorRepository repository, IActionContext context, PublicKey publicKey, BigInteger commissionPercentage) + this ValidatorRepository repository, PublicKey publicKey, BigInteger commissionPercentage) { + var context = repository.ActionContext; var signer = context.Signer; if (!publicKey.Address.Equals(signer)) @@ -44,7 +43,7 @@ public static ValidatorRepository CreateValidatorDelegatee( } var validatorDelegatee = new ValidatorDelegatee( - signer, publicKey, Currencies.GuildGold, repository.World.GetGoldCurrency(), commissionPercentage, context.BlockIndex, repository); + signer, publicKey, commissionPercentage, context.BlockIndex, repository); repository.SetValidatorDelegatee(validatorDelegatee); diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs index adaa837d21..c221130384 100644 --- a/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs @@ -1,11 +1,8 @@ #nullable enable using System.Diagnostics.CodeAnalysis; using System.Numerics; -using Libplanet.Action; using Libplanet.Crypto; using Libplanet.Types.Assets; -using Nekoyume.Action; -using Nekoyume.Model.Guild; using Nekoyume.ValidatorDelegation; namespace Nekoyume.Module.ValidatorDelegation @@ -14,19 +11,18 @@ public static class ValidatorDelegatorModule { public static ValidatorRepository DelegateValidator( this ValidatorRepository repository, - IActionContext context, Address validatorAddress, FungibleAssetValue fav) - => DelegateValidator(repository, context, validatorAddress, validatorAddress, fav); + => DelegateValidator(repository, validatorAddress, validatorAddress, fav); public static ValidatorRepository DelegateValidator( this ValidatorRepository repository, - IActionContext context, Address validatorAddress, Address rewardAddress, FungibleAssetValue fav) { + var context = repository.ActionContext; var delegatorAddress = context.Signer; var validatorDelegator = repository.GetValidatorDelegator( delegatorAddress, rewardAddress); @@ -38,18 +34,17 @@ public static ValidatorRepository DelegateValidator( public static ValidatorRepository UndelegateValidator( this ValidatorRepository repository, - IActionContext context, Address validatorAddress, BigInteger share) - => UndelegateValidator(repository, context, validatorAddress, validatorAddress, share); + => UndelegateValidator(repository, validatorAddress, validatorAddress, share); public static ValidatorRepository UndelegateValidator( this ValidatorRepository repository, - IActionContext context, Address validatorAddress, Address rewardAddress, BigInteger share) { + var context = repository.ActionContext; var delegatorAddress = context.Signer; var validatorDelegator = repository.GetValidatorDelegator( delegatorAddress, rewardAddress); @@ -61,20 +56,19 @@ public static ValidatorRepository UndelegateValidator( public static ValidatorRepository RedelegateValidator( this ValidatorRepository repository, - IActionContext context, Address srcValidatorAddress, Address dstValidatorAddress, BigInteger share) - => RedelegateValidator(repository, context, srcValidatorAddress, dstValidatorAddress, dstValidatorAddress, share); + => RedelegateValidator(repository, srcValidatorAddress, dstValidatorAddress, dstValidatorAddress, share); public static ValidatorRepository RedelegateValidator( this ValidatorRepository repository, - IActionContext context, Address srcValidatorAddress, Address dstValidatorAddress, Address rewardAddress, BigInteger share) { + var context = repository.ActionContext; var delegatorAddress = context.Signer; var validatorDelegator = repository.GetValidatorDelegator( delegatorAddress, rewardAddress); @@ -87,16 +81,15 @@ public static ValidatorRepository RedelegateValidator( public static ValidatorRepository ClaimRewardValidator( this ValidatorRepository repository, - IActionContext context, Address validatorAddress) - => ClaimRewardValidator(repository, context, validatorAddress, validatorAddress); + => ClaimRewardValidator(repository, validatorAddress, validatorAddress); public static ValidatorRepository ClaimRewardValidator( this ValidatorRepository repository, - IActionContext context, Address address, Address rewardAddress) { + var context = repository.ActionContext; var delegatorAddress = context.Signer; var validatorDelegator = repository.GetValidatorDelegator( delegatorAddress, rewardAddress); @@ -109,7 +102,6 @@ public static ValidatorRepository ClaimRewardValidator( public static bool TryGetValidatorDelegator( this ValidatorRepository repository, - IActionContext context, Address address, Address rewardAddress, [NotNullWhen(true)] out ValidatorDelegator? validatorDelegator) @@ -117,7 +109,8 @@ public static bool TryGetValidatorDelegator( try { validatorDelegator = repository.GetValidatorDelegator( - address, rewardAddress); + address, + rewardAddress); return true; } catch diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorUnbondingModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorUnbondingModule.cs index 7ce6fd0c45..f1a6030c0f 100644 --- a/Lib9c/Module/ValidatorDelegation/ValidatorUnbondingModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorUnbondingModule.cs @@ -1,5 +1,4 @@ using System; -using Libplanet.Action; using Nekoyume.Delegation; using Nekoyume.ValidatorDelegation; @@ -8,8 +7,9 @@ namespace Nekoyume.Module.ValidatorDelegation public static class ValidatorUnbondingModule { public static ValidatorRepository ReleaseUnbondings( - this ValidatorRepository repository, IActionContext context) + this ValidatorRepository repository) { + var context = repository.ActionContext; var unbondingSet = repository.GetUnbondingSet(); var unbondings = unbondingSet.UnbondingsToRelease(context.BlockIndex); diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index 740f9599f0..3d1d04a767 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -19,16 +19,14 @@ public sealed class ValidatorDelegatee public ValidatorDelegatee( Address address, PublicKey publicKey, - Currency delegationCurrency, - Currency rewardCurrency, BigInteger commissionPercentage, long creationHeight, ValidatorRepository repository) : base( address: address, accountAddress: repository.DelegateeAccountAddress, - delegationCurrency: delegationCurrency, - rewardCurrency: rewardCurrency, + delegationCurrency: ValidatorDelegationCurrency, + rewardCurrency: ValidatorRewardCurrency, delegationPoolAddress: UnbondedPoolAddress, rewardRemainderPoolAddress: Addresses.CommunityPool, slashedPoolAddress: Addresses.CommunityPool, @@ -88,6 +86,8 @@ public ValidatorDelegatee( public static Currency ValidatorDelegationCurrency => Currencies.GuildGold; + public static Currency ValidatorRewardCurrency => Currencies.Mead; + public static long ValidatorUnbondingPeriod => 10L; public static int ValidatorMaxUnbondLockInEntries => 10; From 76f0b2b289965ad5b018a7d5e6d9a9b216d4b4e6 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 16 Oct 2024 23:14:44 +0900 Subject: [PATCH 095/206] fix: Build fix from refactoring --- Lib9c/Action/Guild/MakeGuild.cs | 3 +-- Lib9c/Action/Guild/RemoveGuild.cs | 2 +- Lib9c/Action/InitializeStates.cs | 2 -- .../ValidatorDelegation/ClaimRewardValidator.cs | 2 +- .../Action/ValidatorDelegation/DelegateValidator.cs | 2 +- Lib9c/Action/ValidatorDelegation/PromoteValidator.cs | 12 +++--------- .../ValidatorDelegation/RedelegateValidator.cs | 2 +- .../ValidatorDelegation/UndelegateValidator.cs | 2 +- 8 files changed, 9 insertions(+), 18 deletions(-) diff --git a/Lib9c/Action/Guild/MakeGuild.cs b/Lib9c/Action/Guild/MakeGuild.cs index 73e19d1777..dd8a63587a 100644 --- a/Lib9c/Action/Guild/MakeGuild.cs +++ b/Lib9c/Action/Guild/MakeGuild.cs @@ -56,7 +56,6 @@ public override IWorld Execute(IActionContext context) var repository = new GuildRepository(world, context); var random = context.GetRandom(); var guildAddress = GuildAddress; - var guildMasterAddress = new AgentAddress(context.Signer); var validatorAddress = ValidatorAddress; // TODO: Remove this check when to deliver features to users. @@ -66,7 +65,7 @@ public override IWorld Execute(IActionContext context) $"This action is not allowed for {context.Signer}."); } - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, validatorAddress); return repository.World; } } diff --git a/Lib9c/Action/Guild/RemoveGuild.cs b/Lib9c/Action/Guild/RemoveGuild.cs index b5c42cc5a7..ccd45ed7b9 100644 --- a/Lib9c/Action/Guild/RemoveGuild.cs +++ b/Lib9c/Action/Guild/RemoveGuild.cs @@ -42,7 +42,7 @@ public override IWorld Execute(IActionContext context) // TODO: Do something to return 'Power' token; - repository.RemoveGuild(signer, height); + repository.RemoveGuild(); return repository.World; } } diff --git a/Lib9c/Action/InitializeStates.cs b/Lib9c/Action/InitializeStates.cs index 6cdc273675..b5b230490d 100644 --- a/Lib9c/Action/InitializeStates.cs +++ b/Lib9c/Action/InitializeStates.cs @@ -207,8 +207,6 @@ public override IWorld Execute(IActionContext context) var validatorDelegatee = new ValidatorDelegatee( validator.OperatorAddress, validator.PublicKey, - delegationCurrency: Currencies.GuildGold, - repository.World.GetGoldCurrency(), ValidatorDelegatee.DefaultCommissionPercentage, context.BlockIndex, repository); diff --git a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs index 91f471ecba..13dc8767b6 100644 --- a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs @@ -46,7 +46,7 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; var repository = new ValidatorRepository(world, context); - repository.ClaimRewardValidator(context, ValidatorDelegatee); + repository.ClaimRewardValidator(ValidatorDelegatee); return repository.World; } diff --git a/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs index 16c57c4b22..3642ce4714 100644 --- a/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs @@ -52,7 +52,7 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; var repository = new ValidatorRepository(world, context); - repository.DelegateValidator(context, ValidatorDelegatee, FAV); + repository.DelegateValidator(ValidatorDelegatee, FAV); return repository.World; } diff --git a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs index 229aec991a..88f3f8776a 100644 --- a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs @@ -27,7 +27,6 @@ public PromoteValidator(PublicKey publicKey, FungibleAssetValue fav, BigInteger PublicKey = publicKey; FAV = fav; CommissionPercentage = commissionPercentage; - RewardAddress = publicKey.Address; } public PublicKey PublicKey { get; private set; } @@ -36,15 +35,12 @@ public PromoteValidator(PublicKey publicKey, FungibleAssetValue fav, BigInteger public BigInteger CommissionPercentage { get; private set; } - public Address RewardAddress { get; private set; } - public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) .Add("values", List.Empty .Add(PublicKey.Format(true)) .Add(FAV.Serialize()) - .Add(CommissionPercentage) - .Add(RewardAddress.Bencoded)); + .Add(CommissionPercentage)); public override void LoadPlainValue(IValue plainValue) { @@ -59,7 +55,6 @@ public override void LoadPlainValue(IValue plainValue) PublicKey = new PublicKey(((Binary)values[0]).ByteArray); FAV = new FungibleAssetValue(values[1]); CommissionPercentage = (Integer)values[2]; - RewardAddress = new Address(values[3]); } public override IWorld Execute(IActionContext context) @@ -68,10 +63,9 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; var repository = new ValidatorRepository(world, context); - var rewardAddress = RewardAddress; - repository.CreateValidatorDelegatee(context, PublicKey, CommissionPercentage); - repository.DelegateValidator(context, context.Signer, rewardAddress, FAV); + repository.CreateValidatorDelegatee(PublicKey, CommissionPercentage); + repository.DelegateValidator(context.Signer, FAV); return repository.World; } diff --git a/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs index fdd52c8db6..98870a12eb 100644 --- a/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs @@ -63,7 +63,7 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; var repository = new ValidatorRepository(world, context); - repository.RedelegateValidator(context, SrcValidatorDelegatee, DstValidatorDelegatee, Share); + repository.RedelegateValidator(SrcValidatorDelegatee, DstValidatorDelegatee, Share); return repository.World; } diff --git a/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs index d6a4af1018..09e4ba172b 100644 --- a/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs @@ -52,7 +52,7 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; var repository = new ValidatorRepository(world, context); - repository.UndelegateValidator(context, ValidatorDelegatee, Share); + repository.UndelegateValidator(ValidatorDelegatee, Share); return repository.World; } From a69e166f068f4d8a0ca597b7f36421d6610fae6e Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 17 Oct 2024 09:49:11 +0900 Subject: [PATCH 096/206] fix: Fix test code build errors. --- .Lib9c.Benchmarks/Actions/AutoJoinGuild.cs | 3 +-- .Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs | 3 +-- .Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs | 12 ++++-------- .Lib9c.Tests/Action/Guild/JoinGuildTest.cs | 3 +-- .../Action/Guild/Migration/GuildMigrationCtrlTest.cs | 9 +++------ .../Guild/Migration/MigratePledgeToGuildTest.cs | 9 +++------ .Lib9c.Tests/Action/Guild/MoveGuildTest.cs | 6 ++---- .Lib9c.Tests/Action/Guild/QuitGuildTest.cs | 3 +-- .Lib9c.Tests/Action/Guild/RemoveGuildTest.cs | 12 ++++-------- .Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs | 3 +-- .Lib9c.Tests/Delegation/DummyRepository.cs | 3 --- .Lib9c.Tests/Delegation/TestRepository.cs | 5 ++++- .../PolicyAction/Tx/Begin/AutoJoinGuildTest.cs | 6 ++---- 13 files changed, 27 insertions(+), 50 deletions(-) diff --git a/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs b/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs index 2f14d093b3..fd1b5a3ece 100644 --- a/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs +++ b/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs @@ -34,9 +34,8 @@ public void Setup() var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); - var validatorAddress = new PrivateKey().Address; var repository = new GuildRepository(worldWithPledge, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); worldWithPledgeAndGuild = repository.World; repository.JoinGuild(guildAddress, signer); worldAfterMigration = repository.World; diff --git a/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs b/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs index 9a0d5020e3..5fe9343d25 100644 --- a/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs +++ b/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs @@ -35,10 +35,9 @@ public void Setup() var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); - var validatorAddress = new PrivateKey().Address; var repository = new GuildRepository(worldWithPledge, new ActionContext()); worldWithPledgeAndGuild = repository - .MakeGuild(guildAddress, guildMasterAddress, validatorAddress).World; + .MakeGuild(guildAddress, guildMasterAddress).World; worldAfterMigration = repository .JoinGuild(guildAddress, signer).World; } diff --git a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs index e4593369bb..5cf73cc3f9 100644 --- a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs +++ b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs @@ -38,8 +38,6 @@ public void Ban_By_GuildMaster() var otherGuildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); var otherGuildAddress = AddressUtil.CreateGuildAddress(); - var validatorAddress = new PrivateKey().Address; - var otherValidatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -47,9 +45,9 @@ public void Ban_By_GuildMaster() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, guildMemberAddress); - repository.MakeGuild(otherGuildAddress, otherGuildMasterAddress, otherValidatorAddress); + repository.MakeGuild(otherGuildAddress, otherGuildMasterAddress); repository.JoinGuild(otherGuildAddress, otherGuildMemberAddress); // Guild @@ -137,7 +135,6 @@ public void Ban_By_GuildMember() var otherAddress = AddressUtil.CreateAgentAddress(); var targetGuildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - var validatorAddress = new PrivateKey().Address; var action = new BanGuildMember(targetGuildMemberAddress); @@ -147,7 +144,7 @@ public void Ban_By_GuildMember() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, guildMemberAddress); repository.JoinGuild(guildAddress, targetGuildMemberAddress); @@ -184,7 +181,6 @@ public void Ban_By_Other() var otherAddress = AddressUtil.CreateAgentAddress(); var targetGuildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -192,7 +188,7 @@ public void Ban_By_Other() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, targetGuildMemberAddress); // Other tries to ban GuildMember. diff --git a/.Lib9c.Tests/Action/Guild/JoinGuildTest.cs b/.Lib9c.Tests/Action/Guild/JoinGuildTest.cs index 726e448f29..088d7368c7 100644 --- a/.Lib9c.Tests/Action/Guild/JoinGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/JoinGuildTest.cs @@ -34,7 +34,6 @@ public void Execute() var agentAddress = AddressUtil.CreateAgentAddress(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -43,7 +42,7 @@ public void Execute() .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, agentAddress); var guild = repository.GetGuild(agentAddress); diff --git a/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs b/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs index 1f60eb7997..babd632aac 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs @@ -26,7 +26,6 @@ public void MigratePlanetariumPledgeToGuild_When_WithUnapprovedPledgeContract() { var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); - var validatorAddress = new PrivateKey().Address; var target = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); IWorld world = new World(MockUtil.MockModernWorldState); @@ -35,7 +34,7 @@ public void MigratePlanetariumPledgeToGuild_When_WithUnapprovedPledgeContract() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, guildMasterAddress); repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( MeadConfig.PatronAddress.Serialize(), @@ -52,7 +51,6 @@ public void MigratePlanetariumPledgeToGuild_When_WithPledgeContract() { var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); - var validatorAddress = new PrivateKey().Address; var target = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); IWorld world = new World(MockUtil.MockModernWorldState); @@ -61,7 +59,7 @@ public void MigratePlanetariumPledgeToGuild_When_WithPledgeContract() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, guildMasterAddress); repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( MeadConfig.PatronAddress.Serialize(), @@ -81,7 +79,6 @@ public void MigratePlanetariumPledgeToGuild_When_WithoutPledgeContract() { var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); - var validatorAddress = new PrivateKey().Address; var target = AddressUtil.CreateAgentAddress(); IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -89,7 +86,7 @@ public void MigratePlanetariumPledgeToGuild_When_WithoutPledgeContract() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, guildMasterAddress); Assert.Null(repository.GetJoinedGuild(target)); diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs index 57bff02993..753acac82a 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs @@ -45,14 +45,13 @@ public void Execute_When_WithPledgeContract() var target = AddressUtil.CreateAgentAddress(); var caller = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); - var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, guildMasterAddress); repository.UpdateWorld(repository.World.SetLegacyState( pledgeAddress, @@ -97,14 +96,13 @@ public void Execute_When_WithUnapprovedPledgeContract() var target = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); var caller = AddressUtil.CreateAgentAddress(); - var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, guildMasterAddress); repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( MeadConfig.PatronAddress.Serialize(), @@ -134,7 +132,6 @@ public void Execute_When_WithoutPledgeContract() { var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); - var validatorAddress = new PrivateKey().Address; var target = AddressUtil.CreateAgentAddress(); var caller = AddressUtil.CreateAgentAddress(); IWorld world = new World(MockUtil.MockModernWorldState); @@ -143,7 +140,7 @@ public void Execute_When_WithoutPledgeContract() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, guildMasterAddress); Assert.Null(repository.GetJoinedGuild(target)); diff --git a/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs b/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs index cfd1db9dfa..d8a06dcc93 100644 --- a/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs @@ -36,8 +36,6 @@ public void Execute() var guildMasterAddress2 = AddressUtil.CreateAgentAddress(); var guildAddress1 = AddressUtil.CreateGuildAddress(); var guildAddress2 = AddressUtil.CreateGuildAddress(); - var validatorAddress1 = new PrivateKey().Address; - var validatorAddress2 = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -46,8 +44,8 @@ public void Execute() .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress1, guildMasterAddress1, validatorAddress1); - repository.MakeGuild(guildAddress2, guildMasterAddress2, validatorAddress2); + repository.MakeGuild(guildAddress1, guildMasterAddress1); + repository.MakeGuild(guildAddress2, guildMasterAddress2); repository.JoinGuild(guildAddress1, agentAddress); var guild1 = repository.GetGuild(agentAddress); diff --git a/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs b/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs index fae173654b..3ed26056ee 100644 --- a/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs @@ -32,7 +32,6 @@ public void Execute() var agentAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); - var validatorAddress = new PrivateKey().Address; var action = new QuitGuild(); IWorld world = new World(MockUtil.MockModernWorldState); @@ -41,7 +40,7 @@ public void Execute() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); // This case should fail because guild master cannot quit the guild. Assert.Throws(() => action.Execute(new ActionContext diff --git a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs index 50065fafc4..e22d95f116 100644 --- a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs @@ -34,7 +34,6 @@ public void Execute_By_GuildMember() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -42,7 +41,7 @@ public void Execute_By_GuildMember() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); Assert.Throws(() => action.Execute(new ActionContext { @@ -58,7 +57,6 @@ public void Execute_By_GuildMaster() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -66,7 +64,7 @@ public void Execute_By_GuildMaster() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); var changedWorld = action.Execute(new ActionContext { @@ -87,7 +85,6 @@ public void Execute_By_Other() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var otherAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -95,7 +92,7 @@ public void Execute_By_Other() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); Assert.Throws(() => action.Execute(new ActionContext { @@ -112,7 +109,6 @@ public void ResetBannedAddresses() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); var bannedAddress = AddressUtil.CreateAgentAddress(); - var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); @@ -120,7 +116,7 @@ public void ResetBannedAddresses() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); repository.Ban(guildAddress, guildMasterAddress, bannedAddress); Assert.True(repository.IsBanned(guildAddress, bannedAddress)); diff --git a/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs index 17c1760fbf..4fe7f7e912 100644 --- a/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs +++ b/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs @@ -68,7 +68,6 @@ public void Unban_By_GuildMaster() var guildMasterAddress = AddressUtil.CreateAgentAddress(); var targetGuildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - var validatorAddress = new PrivateKey().Address; var action = new UnbanGuildMember(targetGuildMemberAddress); @@ -78,7 +77,7 @@ public void Unban_By_GuildMaster() world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); repository.Ban(guildAddress, guildMasterAddress, targetGuildMemberAddress); Assert.True(repository.IsBanned(guildAddress, targetGuildMemberAddress)); diff --git a/.Lib9c.Tests/Delegation/DummyRepository.cs b/.Lib9c.Tests/Delegation/DummyRepository.cs index 3e19cf8a05..85332a4785 100644 --- a/.Lib9c.Tests/Delegation/DummyRepository.cs +++ b/.Lib9c.Tests/Delegation/DummyRepository.cs @@ -54,8 +54,5 @@ public override void SetDelegatee(IDelegatee delegatee) public override void SetDelegator(IDelegator delegator) => SetDelegatorMetadata(((DummyDelegator)delegator).Metadata); - - public void MintAsset(Address recipient, FungibleAssetValue value) - => previousWorld = previousWorld.MintAsset(actionContext, recipient, value); } } diff --git a/.Lib9c.Tests/Delegation/TestRepository.cs b/.Lib9c.Tests/Delegation/TestRepository.cs index 6a00b476f5..b072d9ce86 100644 --- a/.Lib9c.Tests/Delegation/TestRepository.cs +++ b/.Lib9c.Tests/Delegation/TestRepository.cs @@ -10,6 +10,8 @@ namespace Nekoyume.Delegation public class TestRepository : DelegationRepository { + private readonly IActionContext _context; + public TestRepository(IWorld world, IActionContext context) : base( world: world, @@ -24,6 +26,7 @@ public TestRepository(IWorld world, IActionContext context) unbondingSetAccountAddress: new Address("0000000000000000000000000000000000000007"), lumpSumRewardRecordAccountAddress: new Address("0000000000000000000000000000000000000008")) { + _context = context; } public override TestDelegatee GetDelegatee(Address address) @@ -57,6 +60,6 @@ public override void SetDelegator(IDelegator delegator) => SetDelegatorMetadata(((TestDelegator)delegator).Metadata); public void MintAsset(Address recipient, FungibleAssetValue value) - => previousWorld = previousWorld.MintAsset(actionContext, recipient, value); + => previousWorld = previousWorld.MintAsset(_context, recipient, value); } } diff --git a/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs b/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs index 3f2bb4ef98..85e988a202 100644 --- a/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs +++ b/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs @@ -39,14 +39,13 @@ public void Execute_When_WithPledgeContract() var guildAddress = AddressUtil.CreateGuildAddress(); var agentAddress = AddressUtil.CreateAgentAddress(); var pledgeAddress = agentAddress.GetPledgeAddress(); - var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, guildMasterAddress); repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( MeadConfig.PatronAddress.Serialize(), @@ -74,14 +73,13 @@ public void Execute_When_WithoutPledgeContract() var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); var agentAddress = AddressUtil.CreateAgentAddress(); - var validatorAddress = new PrivateKey().Address; IWorld world = new World(MockUtil.MockModernWorldState); var ncg = Currency.Uncapped("NCG", 2, null); var goldCurrencyState = new GoldCurrencyState(ncg); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress, validatorAddress); + repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, guildMasterAddress); Assert.Null(repository.GetJoinedGuild(agentAddress)); From dcc149e562bef1aa4994c8243839ed9375c927a0 Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 17 Oct 2024 14:05:17 +0900 Subject: [PATCH 097/206] test: guild action test --- .../Action/Guild/BanGuildMemberTest.cs | 41 ++++++- .Lib9c.Tests/Action/Guild/GuildTestBase.cs | 103 ++++++++++++++++++ .Lib9c.Tests/Action/Guild/JoinGuildTest.cs | 28 ++--- .Lib9c.Tests/Action/Guild/MakeGuildTest.cs | 14 +-- .Lib9c.Tests/Action/Guild/MoveGuildTest.cs | 38 ++++--- .Lib9c.Tests/Action/Guild/QuitGuildTest.cs | 49 +++------ .Lib9c.Tests/Action/Guild/RemoveGuildTest.cs | 28 ++++- .../Action/Guild/UnbanGuildMemberTest.cs | 30 ++++- .../Guild/GuildTest.Snapshot.received.txt | 34 ------ .../Guild/GuildTest.Snapshot.verified.txt | 22 ++++ .Lib9c.Tests/Model/Guild/GuildTest.cs | 2 +- Lib9c/Action/Guild/MakeGuild.cs | 1 - Lib9c/Module/Guild/GuildParticipantModule.cs | 40 +++++-- 13 files changed, 301 insertions(+), 129 deletions(-) create mode 100644 .Lib9c.Tests/Action/Guild/GuildTestBase.cs delete mode 100644 .Lib9c.Tests/Model/Guild/GuildTest.Snapshot.received.txt diff --git a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs index 5cf73cc3f9..a038c9d9e6 100644 --- a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs +++ b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs @@ -14,7 +14,7 @@ namespace Lib9c.Tests.Action.Guild using Nekoyume.Module.Guild; using Xunit; - public class BanGuildMemberTest + public class BanGuildMemberTest : GuildTestBase { [Fact] public void Serialization() @@ -28,6 +28,33 @@ public void Serialization() Assert.Equal(guildMemberAddress, deserialized.Target); } + [Fact] + public void Execute() + { + var validatorKey = new PrivateKey(); + var guildMasterAddress = AddressUtil.CreateAgentAddress(); + var targetGuildMemberAddress = AddressUtil.CreateAgentAddress(); + var guildAddress = AddressUtil.CreateGuildAddress(); + + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + world = EnsureToJoinGuild(world, targetGuildMemberAddress, guildAddress); + + var banGuildMember = new BanGuildMember(targetGuildMemberAddress); + var actionContext = new ActionContext + { + PreviousState = world, + Signer = guildMasterAddress, + }; + world = banGuildMember.Execute(actionContext); + + var repository = new GuildRepository(world, actionContext); + Assert.True(repository.IsBanned(guildAddress, targetGuildMemberAddress)); + Assert.Null(repository.GetJoinedGuild(targetGuildMemberAddress)); + } + // Expected use-case. [Fact] public void Ban_By_GuildMaster() @@ -177,16 +204,18 @@ public void Ban_By_Other() { // NOTE: It assumes 'other' hasn't any guild. If 'other' has its own guild, // it should be assumed as a guild master. + var validatorKey = new PrivateKey(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); var otherAddress = AddressUtil.CreateAgentAddress(); var targetGuildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + world = EnsureToJoinGuild(world, targetGuildMemberAddress, guildAddress); + var repository = new GuildRepository(world, new ActionContext()); repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, targetGuildMemberAddress); diff --git a/.Lib9c.Tests/Action/Guild/GuildTestBase.cs b/.Lib9c.Tests/Action/Guild/GuildTestBase.cs new file mode 100644 index 0000000000..c6238ff356 --- /dev/null +++ b/.Lib9c.Tests/Action/Guild/GuildTestBase.cs @@ -0,0 +1,103 @@ +namespace Lib9c.Tests.Action.Guild +{ + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; + using Nekoyume.Model.Guild; + using Nekoyume.Model.State; + using Nekoyume.Module; + using Nekoyume.Module.Guild; + using Nekoyume.Module.ValidatorDelegation; + using Nekoyume.TypedAddress; + using Nekoyume.ValidatorDelegation; + + public abstract class GuildTestBase + { + protected static readonly Currency GG = Currencies.GuildGold; + protected static readonly Currency Mead = Currencies.Mead; + protected static readonly Currency NCG = Currency.Uncapped("NCG", 2, null); + + public GuildTestBase() + { + var world = new World(MockUtil.MockModernWorldState); + var goldCurrencyState = new GoldCurrencyState(NCG); + World = world + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + } + + protected IWorld World { get; } + + protected static IWorld EnsureToMintAsset( + IWorld world, Address address, FungibleAssetValue amount) + { + var actionContext = new ActionContext + { + PreviousState = world, + }; + return world.MintAsset(actionContext, address, amount); + } + + protected static IWorld EnsureToCreateValidator( + IWorld world, + PublicKey validatorPublicKey) + { + var validatorAddress = validatorPublicKey.Address; + var commissionPercentage = 10; + var actionContext = new ActionContext + { + Signer = validatorAddress, + }; + + var validatorRepository = new ValidatorRepository(world, actionContext); + validatorRepository.CreateValidatorDelegatee(validatorPublicKey, commissionPercentage); + + return validatorRepository.World; + } + + protected static IWorld EnsureToMakeGuild( + IWorld world, + GuildAddress guildAddress, + AgentAddress guildMasterAddress, + Address validatorAddress) + { + var actionContext = new ActionContext + { + Signer = guildMasterAddress, + }; + var repository = new GuildRepository(world, actionContext); + repository.MakeGuild(guildAddress, validatorAddress); + return repository.World; + } + + protected static IWorld EnsureToJoinGuild( + IWorld world, + AgentAddress agentAddress, + GuildAddress guildAddress) + { + var actionContext = new ActionContext + { + Signer = agentAddress, + }; + var repository = new GuildRepository(world, actionContext); + repository.JoinGuild(guildAddress, agentAddress); + return repository.World; + } + + protected static IWorld EnsureToBanGuildMember( + IWorld world, + GuildAddress guildAddress, + AgentAddress guildMasterAddress, + AgentAddress agentAddress) + { + var actionContext = new ActionContext + { + Signer = agentAddress, + }; + var repository = new GuildRepository(world, actionContext); + repository.Ban(guildAddress, guildMasterAddress, agentAddress); + return repository.World; + } + } +} diff --git a/.Lib9c.Tests/Action/Guild/JoinGuildTest.cs b/.Lib9c.Tests/Action/Guild/JoinGuildTest.cs index 088d7368c7..df981b1a74 100644 --- a/.Lib9c.Tests/Action/Guild/JoinGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/JoinGuildTest.cs @@ -1,20 +1,15 @@ namespace Lib9c.Tests.Action.Guild { - using System; 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 Nekoyume.TypedAddress; using Xunit; - public class JoinGuildTest + public class JoinGuildTest : GuildTestBase { [Fact] public void Serialization() @@ -31,24 +26,25 @@ public void Serialization() [Fact] public void Execute() { + var validatorKey = new PrivateKey(); var agentAddress = AddressUtil.CreateAgentAddress(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); - var repository = new GuildRepository(world, new ActionContext()); + var repository = new GuildRepository(world, new ActionContext + { + Signer = guildMasterAddress, + }); repository.MakeGuild(guildAddress, guildMasterAddress); repository.JoinGuild(guildAddress, agentAddress); - var guild = repository.GetGuild(agentAddress); + var guildParticipant = repository.GetGuildParticipant(agentAddress); - Assert.Equal(guildMasterAddress, guild.GuildMasterAddress); - Assert.Equal(guildAddress, guild.Address); + Assert.Equal(agentAddress, guildParticipant.Address); } } } diff --git a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs index ea5f700a49..6d7ebb1556 100644 --- a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action.Guild using System.Collections.Generic; using Lib9c.Tests.Util; using Libplanet.Action.State; + using Libplanet.Crypto; using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; @@ -15,7 +16,7 @@ namespace Lib9c.Tests.Action.Guild using Nekoyume.TypedAddress; using Xunit; - public class MakeGuildTest + public class MakeGuildTest : GuildTestBase { public static IEnumerable TestCases => new[] { @@ -46,12 +47,11 @@ public void Serialization() [MemberData(nameof(TestCases))] public void Execute(AgentAddress guildMasterAddress, bool fail) { - var action = new MakeGuild(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + IWorld world = World; + var validatorPrivateKey = new PrivateKey(); + world = EnsureToMintAsset(world, validatorPrivateKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorPrivateKey.PublicKey); + var action = new MakeGuild(validatorPrivateKey.Address); if (fail) { diff --git a/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs b/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs index d8a06dcc93..43bcb23dec 100644 --- a/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs @@ -14,7 +14,7 @@ namespace Lib9c.Tests.Action.Guild using Nekoyume.Module.Guild; using Xunit; - public class MoveGuildTest + public class MoveGuildTest : GuildTestBase { [Fact] public void Serialization() @@ -31,31 +31,35 @@ public void Serialization() [Fact] public void Execute() { + var validatorKey1 = new PrivateKey(); + var validatorKey2 = new PrivateKey(); var agentAddress = AddressUtil.CreateAgentAddress(); var guildMasterAddress1 = AddressUtil.CreateAgentAddress(); var guildMasterAddress2 = AddressUtil.CreateAgentAddress(); var guildAddress1 = AddressUtil.CreateGuildAddress(); var guildAddress2 = AddressUtil.CreateGuildAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey1.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey1.PublicKey); + world = EnsureToMintAsset(world, validatorKey2.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey2.PublicKey); + world = EnsureToMakeGuild(world, guildAddress1, guildMasterAddress1, validatorKey1.Address); + world = EnsureToMakeGuild(world, guildAddress2, guildMasterAddress2, validatorKey2.Address); + world = EnsureToJoinGuild(world, agentAddress, guildAddress1); - var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress1, guildMasterAddress1); - repository.MakeGuild(guildAddress2, guildMasterAddress2); - repository.JoinGuild(guildAddress1, agentAddress); - var guild1 = repository.GetGuild(agentAddress); + var moveGuild = new MoveGuild(guildAddress2); + var actionContext = new ActionContext + { + PreviousState = world, + Signer = agentAddress, + }; + world = moveGuild.Execute(actionContext); - repository.MoveGuild(agentAddress, guildAddress2); + var repository = new GuildRepository(world, actionContext); + var guildParticipant = repository.GetGuildParticipant(agentAddress); - var guild2 = repository.GetGuild(agentAddress); - - Assert.NotEqual(guild1.Address, guild2.Address); - Assert.Equal(guildMasterAddress2, guild2.GuildMasterAddress); - Assert.Equal(guildAddress2, guild2.Address); + Assert.Equal(guildAddress2, guildParticipant.GuildAddress); } } } diff --git a/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs b/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs index 3ed26056ee..0b51d24a54 100644 --- a/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/QuitGuildTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action.Guild using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; + using Nekoyume.Action; using Nekoyume.Action.Guild; using Nekoyume.Model.Guild; using Nekoyume.Model.State; @@ -14,7 +15,7 @@ namespace Lib9c.Tests.Action.Guild using Nekoyume.Module.Guild; using Xunit; - public class QuitGuildTest + public class QuitGuildTest : GuildTestBase { [Fact] public void Serialization() @@ -29,46 +30,28 @@ public void Serialization() [Fact] public void Execute() { + var validatorKey = new PrivateKey(); var agentAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); - var action = new QuitGuild(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - - // This case should fail because guild master cannot quit the guild. - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = guildMasterAddress, - })); - - // This case should fail because the agent is not a member of the guild. - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = agentAddress, - })); - - // Join the guild. - repository.JoinGuild(guildAddress, agentAddress); - Assert.NotNull(repository.GetJoinedGuild(agentAddress)); + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + world = EnsureToJoinGuild(world, agentAddress, guildAddress); - // This case should fail because the agent is not a member of the guild. - world = action.Execute(new ActionContext + var quitGuild = new QuitGuild(); + var actionContext = new ActionContext { - PreviousState = repository.World, + PreviousState = world, Signer = agentAddress, - }); + }; + world = quitGuild.Execute(actionContext); - repository.UpdateWorld(world); - Assert.Null(repository.GetJoinedGuild(agentAddress)); + var repository = new GuildRepository(world, actionContext); + Assert.Throws( + () => repository.GetGuildParticipant(agentAddress)); } } } diff --git a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs index e22d95f116..b3e2242c45 100644 --- a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action.Guild using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; + using Nekoyume.Action; using Nekoyume.Action.Guild; using Nekoyume.Model.Guild; using Nekoyume.Model.State; @@ -14,7 +15,7 @@ namespace Lib9c.Tests.Action.Guild using Nekoyume.Module.Guild; using Xunit; - public class RemoveGuildTest + public class RemoveGuildTest : GuildTestBase { [Fact] public void Serialization() @@ -26,6 +27,31 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } + [Fact] + public void Execute() + { + var validatorKey = new PrivateKey(); + var guildMasterAddress = AddressUtil.CreateAgentAddress(); + var targetGuildMemberAddress = AddressUtil.CreateAgentAddress(); + var guildAddress = AddressUtil.CreateGuildAddress(); + + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + + var removeGuild = new RemoveGuild(); + var actionContext = new ActionContext + { + PreviousState = world, + Signer = guildMasterAddress, + }; + world = removeGuild.Execute(actionContext); + + var repository = new GuildRepository(world, actionContext); + Assert.Throws(() => repository.GetGuild(guildAddress)); + } + [Fact] public void Execute_By_GuildMember() { diff --git a/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs index 4fe7f7e912..ff8251182c 100644 --- a/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs +++ b/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs @@ -14,7 +14,7 @@ namespace Lib9c.Tests.Action.Guild using Nekoyume.Module.Guild; using Xunit; - public class UnbanGuildMemberTest + public class UnbanGuildMemberTest : GuildTestBase { [Fact] public void Serialization() @@ -28,6 +28,34 @@ public void Serialization() Assert.Equal(guildMemberAddress, deserialized.Target); } + [Fact] + public void Execute() + { + var validatorKey = new PrivateKey(); + var guildMasterAddress = AddressUtil.CreateAgentAddress(); + var targetGuildMemberAddress = AddressUtil.CreateAgentAddress(); + var guildAddress = AddressUtil.CreateGuildAddress(); + + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + world = EnsureToJoinGuild(world, targetGuildMemberAddress, guildAddress); + world = EnsureToBanGuildMember(world, guildAddress, guildMasterAddress, targetGuildMemberAddress); + + var unbanGuildMember = new UnbanGuildMember(targetGuildMemberAddress); + var actionContext = new ActionContext + { + PreviousState = world, + Signer = guildMasterAddress, + }; + world = unbanGuildMember.Execute(actionContext); + + var repository = new GuildRepository(world, actionContext); + Assert.False(repository.IsBanned(guildAddress, targetGuildMemberAddress)); + Assert.Null(repository.GetJoinedGuild(targetGuildMemberAddress)); + } + [Fact] public void Unban_By_GuildMember() { diff --git a/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.received.txt b/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.received.txt deleted file mode 100644 index 4cb9594719..0000000000 --- a/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.received.txt +++ /dev/null @@ -1,34 +0,0 @@ -[ - { - EncodingLength: 8, - Kind: Text, - Value: guild - }, - { - EncodingLength: 3, - Kind: Integer, - Value: 1 - }, - [ - 217, - 40, - 174, - 135, - 49, - 29, - 234, - 212, - 144, - 201, - 134, - 194, - 76, - 194, - 60, - 55, - 239, - 248, - 146, - 242 - ] -] \ No newline at end of file diff --git a/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt b/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt index 14587b1dd9..4abde14fa4 100644 --- a/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt +++ b/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt @@ -30,5 +30,27 @@ 248, 146, 242 + ], + [ + 97, + 75, + 191, + 60, + 231, + 134, + 87, + 182, + 225, + 103, + 60, + 167, + 121, + 151, + 173, + 223, + 81, + 5, + 56, + 223 ] ] diff --git a/.Lib9c.Tests/Model/Guild/GuildTest.cs b/.Lib9c.Tests/Model/Guild/GuildTest.cs index 1ccf7bb2c8..769034c41b 100644 --- a/.Lib9c.Tests/Model/Guild/GuildTest.cs +++ b/.Lib9c.Tests/Model/Guild/GuildTest.cs @@ -25,7 +25,7 @@ public Task Snapshot() { IWorld world = new World(MockUtil.MockModernWorldState); var repository = new Nekoyume.Model.Guild.GuildRepository(world, new ActionContext()); - var validatorAddress = new PrivateKey().Address; + var validatorAddress = new Address("0x614bBf3cE78657b6E1673cA77997adDf510538Df"); var guild = new Nekoyume.Model.Guild.Guild( AddressUtil.CreateGuildAddress(), new AgentAddress("0xd928ae87311dead490c986c24cc23c37eff892f2"), diff --git a/Lib9c/Action/Guild/MakeGuild.cs b/Lib9c/Action/Guild/MakeGuild.cs index dd8a63587a..b481aa97bc 100644 --- a/Lib9c/Action/Guild/MakeGuild.cs +++ b/Lib9c/Action/Guild/MakeGuild.cs @@ -54,7 +54,6 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; var repository = new GuildRepository(world, context); - var random = context.GetRandom(); var guildAddress = GuildAddress; var validatorAddress = ValidatorAddress; diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index 17c87970b8..327535785e 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -27,10 +27,13 @@ public static GuildRepository JoinGuild( AgentAddress target) { var guildParticipant = new GuildParticipant(target, guildAddress, repository); - var guildGold = repository.GetBalance(guildAddress, Currencies.GuildGold); + var guildGold = repository.GetBalance(target, Currencies.GuildGold); repository.SetGuildParticipant(guildParticipant); repository.IncreaseGuildMemberCount(guildAddress); - repository.Delegate(target, guildGold); + if (guildGold.RawValue > 0) + { + repository.Delegate(target, guildGold); + } return repository; } @@ -40,30 +43,36 @@ public static GuildRepository MoveGuild( AgentAddress guildParticipantAddress, GuildAddress dstGuildAddress) { + var guildParticipant1 = repository.GetGuildParticipant(guildParticipantAddress); + var srcGuild = repository.GetGuild(guildParticipant1.GuildAddress); var dstGuild = repository.GetGuild(dstGuildAddress); var validatorRepository = new ValidatorRepository(repository.World, repository.ActionContext); + var srcValidatorDelegatee = validatorRepository.GetValidatorDelegatee(srcGuild.ValidatorAddress); var dstValidatorDelegatee = validatorRepository.GetValidatorDelegatee(dstGuild.ValidatorAddress); if (dstValidatorDelegatee.Tombstoned) { throw new InvalidOperationException("The validator of the guild to move to has been tombstoned."); } - var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); + var guildParticipant2 = new GuildParticipant(guildParticipantAddress, dstGuildAddress, repository); + var bond = validatorRepository.GetBond(srcValidatorDelegatee, guildParticipantAddress); repository.RemoveGuildParticipant(guildParticipantAddress); - repository.DecreaseGuildMemberCount(guildParticipant.GuildAddress); - repository.SetGuildParticipant(guildParticipant); + repository.DecreaseGuildMemberCount(guildParticipant1.GuildAddress); + repository.SetGuildParticipant(guildParticipant2); repository.IncreaseGuildMemberCount(dstGuildAddress); - repository.Redelegate( - guildParticipantAddress, dstGuildAddress); + if (bond.Share > 0) + { + repository.Redelegate(guildParticipantAddress, dstGuildAddress); + } return repository; } public static GuildRepository LeaveGuild( this GuildRepository repository, - AgentAddress target) + AgentAddress agentAddress) { - if (repository.GetJoinedGuild(target) is not { } guildAddress) + if (repository.GetJoinedGuild(agentAddress) is not { } guildAddress) { throw new InvalidOperationException("The signer does not join any guild."); } @@ -74,15 +83,22 @@ public static GuildRepository LeaveGuild( "There is no such guild."); } - if (guild.GuildMasterAddress == target) + if (guild.GuildMasterAddress == agentAddress) { throw new InvalidOperationException( "The signer is a guild master. Guild master cannot quit the guild."); } - repository.RemoveGuildParticipant(target); + var validatorRepository = new ValidatorRepository(repository.World, repository.ActionContext); + var validatorDelegatee = validatorRepository.GetValidatorDelegatee(guild.ValidatorAddress); + var bond = validatorRepository.GetBond(validatorDelegatee, agentAddress); + + repository.RemoveGuildParticipant(agentAddress); repository.DecreaseGuildMemberCount(guild.Address); - repository.Undelegate(target); + if (bond.Share > 0) + { + repository.Undelegate(agentAddress); + } return repository; } From 9b2d964de19ae6a7f35f14f791d86dd6ec97cd16 Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 17 Oct 2024 15:53:53 +0900 Subject: [PATCH 098/206] test: Fix and add test code for Guild Action --- .../Action/Guild/BanGuildMemberTest.cs | 41 +++---- .Lib9c.Tests/Action/Guild/GuildTestBase.cs | 4 +- .Lib9c.Tests/Action/Guild/MoveGuildTest.cs | 2 +- .Lib9c.Tests/Action/Guild/QuitGuildTest.cs | 2 +- .Lib9c.Tests/Action/Guild/RemoveGuildTest.cs | 110 ++++++++---------- .../Action/Guild/UnbanGuildMemberTest.cs | 32 ++--- Lib9c/Action/Guild/RemoveGuild.cs | 3 - Lib9c/Module/Guild/GuildModule.cs | 9 ++ 8 files changed, 100 insertions(+), 103 deletions(-) diff --git a/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/BanGuildMemberTest.cs index a038c9d9e6..cb31fc17b0 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, targetGuildMemberAddress, guildAddress); + world = EnsureToJoinGuild(world, guildAddress, targetGuildMemberAddress); var banGuildMember = new BanGuildMember(targetGuildMemberAddress); var actionContext = new ActionContext @@ -59,6 +59,7 @@ public void Execute() [Fact] public void Ban_By_GuildMaster() { + var validatorKey = new PrivateKey(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); var otherGuildMasterAddress = AddressUtil.CreateAgentAddress(); var guildMemberAddress = AddressUtil.CreateAgentAddress(); @@ -66,17 +67,15 @@ public void Ban_By_GuildMaster() var guildAddress = AddressUtil.CreateGuildAddress(); var otherGuildAddress = AddressUtil.CreateGuildAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.JoinGuild(guildAddress, guildMemberAddress); - repository.MakeGuild(otherGuildAddress, otherGuildMasterAddress); - repository.JoinGuild(otherGuildAddress, otherGuildMemberAddress); + IWorld world = World; + 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 = EnsureToMakeGuild(world, otherGuildAddress, otherGuildMasterAddress, validatorKey.Address); + world = EnsureToJoinGuild(world, otherGuildAddress, otherGuildMemberAddress); + var repository = new GuildRepository(world, new ActionContext()); // Guild Assert.False(repository.IsBanned(guildAddress, guildMasterAddress)); Assert.Equal(guildAddress, repository.GetJoinedGuild(guildMasterAddress)); @@ -157,6 +156,7 @@ public void Ban_By_GuildMaster() [Fact] public void Ban_By_GuildMember() { + var validatorKey = new PrivateKey(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildMemberAddress = AddressUtil.CreateAgentAddress(); var otherAddress = AddressUtil.CreateAgentAddress(); @@ -165,15 +165,14 @@ public void Ban_By_GuildMember() var action = new BanGuildMember(targetGuildMemberAddress); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + IWorld world = World; + 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); + var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.JoinGuild(guildAddress, guildMemberAddress); - repository.JoinGuild(guildAddress, targetGuildMemberAddress); // GuildMember tries to ban other guild member. Assert.Throws(() => action.Execute(new ActionContext @@ -214,11 +213,9 @@ 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, targetGuildMemberAddress, guildAddress); + world = EnsureToJoinGuild(world, guildAddress, targetGuildMemberAddress); var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.JoinGuild(guildAddress, targetGuildMemberAddress); // Other tries to ban GuildMember. var action = new BanGuildMember(targetGuildMemberAddress); diff --git a/.Lib9c.Tests/Action/Guild/GuildTestBase.cs b/.Lib9c.Tests/Action/Guild/GuildTestBase.cs index c6238ff356..b24b8a4e38 100644 --- a/.Lib9c.Tests/Action/Guild/GuildTestBase.cs +++ b/.Lib9c.Tests/Action/Guild/GuildTestBase.cs @@ -73,8 +73,8 @@ protected static IWorld EnsureToMakeGuild( protected static IWorld EnsureToJoinGuild( IWorld world, - AgentAddress agentAddress, - GuildAddress guildAddress) + GuildAddress guildAddress, + AgentAddress agentAddress) { var actionContext = new ActionContext { diff --git a/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs b/.Lib9c.Tests/Action/Guild/MoveGuildTest.cs index 43bcb23dec..06ce2738c4 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, agentAddress, guildAddress1); + world = EnsureToJoinGuild(world, guildAddress1, agentAddress); 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 0b51d24a54..5542804221 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, agentAddress, guildAddress); + world = EnsureToJoinGuild(world, guildAddress, agentAddress); 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 b3e2242c45..8ab47ee731 100644 --- a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs @@ -53,107 +53,99 @@ public void Execute() } [Fact] - public void Execute_By_GuildMember() + public void Execute_ByGuildMember_Throw() { - var action = new RemoveGuild(); - + var validatorKey = new PrivateKey(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + world = EnsureToJoinGuild(world, guildAddress, guildMemberAddress); - Assert.Throws(() => action.Execute(new ActionContext + var actionContext = new ActionContext { - PreviousState = repository.World, + PreviousState = world, Signer = guildMemberAddress, - })); + }; + var removeGuild = new RemoveGuild(); + + Assert.Throws(() => removeGuild.Execute(actionContext)); } [Fact] - public void Execute_By_GuildMaster() + public void Execute_WhenDelegationExists_Throw() { - var action = new RemoveGuild(); - + var validatorKey = new PrivateKey(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMintAsset(world, guildMasterAddress, GG * 100); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - var changedWorld = action.Execute(new ActionContext + var actionContext = new ActionContext { - PreviousState = repository.World, + PreviousState = world, Signer = guildMasterAddress, - }); + }; + var removeGuild = new RemoveGuild(); - repository.UpdateWorld(changedWorld); - Assert.False(repository.TryGetGuild(guildAddress, out _)); - Assert.Null(repository.GetJoinedGuild(guildMasterAddress)); + Assert.Throws(() => removeGuild.Execute(actionContext)); } [Fact] - public void Execute_By_Other() + public void Execute_ByNonGuildMember_Throw() { - var action = new RemoveGuild(); - + var validatorKey = new PrivateKey(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); var otherAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - Assert.Throws(() => action.Execute(new ActionContext + var actionContext = new ActionContext { - PreviousState = repository.World, + PreviousState = world, Signer = otherAddress, - })); + }; + var removeGuild = new RemoveGuild(); + + Assert.Throws(() => removeGuild.Execute(actionContext)); } [Fact] - public void ResetBannedAddresses() + public void Execute_ResetBannedAddresses() { - var action = new RemoveGuild(); - + var validatorKey = new PrivateKey(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); var bannedAddress = AddressUtil.CreateAgentAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.Ban(guildAddress, guildMasterAddress, bannedAddress); - - Assert.True(repository.IsBanned(guildAddress, bannedAddress)); + IWorld world = World; + 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 = EnsureToBanGuildMember(world, guildAddress, guildMasterAddress, bannedAddress); - world = action.Execute(new ActionContext + var actionContext = new ActionContext { - PreviousState = repository.World, + PreviousState = world, Signer = guildMasterAddress, - }); + }; + var removeGuild = new RemoveGuild(); + world = removeGuild.Execute(actionContext); - repository.UpdateWorld(world); + var repository = new GuildRepository(world, actionContext); Assert.False(repository.IsBanned(guildAddress, bannedAddress)); } } diff --git a/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs b/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs index ff8251182c..34af378fea 100644 --- a/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.cs +++ b/.Lib9c.Tests/Action/Guild/UnbanGuildMemberTest.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, targetGuildMemberAddress, guildAddress); + world = EnsureToJoinGuild(world, guildAddress, targetGuildMemberAddress); world = EnsureToBanGuildMember(world, guildAddress, guildMasterAddress, targetGuildMemberAddress); var unbanGuildMember = new UnbanGuildMember(targetGuildMemberAddress); @@ -59,6 +59,7 @@ public void Execute() [Fact] public void Unban_By_GuildMember() { + var validatorKey = new PrivateKey(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); var guildMemberAddress = AddressUtil.CreateAgentAddress(); var targetGuildMemberAddress = AddressUtil.CreateAgentAddress(); @@ -66,14 +67,14 @@ public void Unban_By_GuildMember() var action = new UnbanGuildMember(targetGuildMemberAddress); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + IWorld world = World; + 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 = EnsureToBanGuildMember(world, guildAddress, guildMasterAddress, targetGuildMemberAddress); + var repository = new GuildRepository(world, new ActionContext()); - repository.JoinGuild(guildAddress, guildMemberAddress); - repository.JoinGuild(guildAddress, targetGuildMemberAddress); // GuildMember tries to ban other guild member. Assert.Throws(() => action.Execute(new ActionContext @@ -93,20 +94,21 @@ public void Unban_By_GuildMember() [Fact] public void Unban_By_GuildMaster() { + var validatorKey = new PrivateKey(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); var targetGuildMemberAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); var action = new UnbanGuildMember(targetGuildMemberAddress); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + IWorld world = World; + 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 = EnsureToBanGuildMember(world, guildAddress, guildMasterAddress, targetGuildMemberAddress); + var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.Ban(guildAddress, guildMasterAddress, targetGuildMemberAddress); Assert.True(repository.IsBanned(guildAddress, targetGuildMemberAddress)); Assert.Null(repository.GetJoinedGuild(targetGuildMemberAddress)); diff --git a/Lib9c/Action/Guild/RemoveGuild.cs b/Lib9c/Action/Guild/RemoveGuild.cs index ccd45ed7b9..39bd0652e7 100644 --- a/Lib9c/Action/Guild/RemoveGuild.cs +++ b/Lib9c/Action/Guild/RemoveGuild.cs @@ -37,11 +37,8 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; var repository = new GuildRepository(world, context); - var signer = context.GetAgentAddress(); - var height = context.BlockIndex; // TODO: Do something to return 'Power' token; - repository.RemoveGuild(); return repository.World; } diff --git a/Lib9c/Module/Guild/GuildModule.cs b/Lib9c/Module/Guild/GuildModule.cs index 280c1b4683..3bbfd053eb 100644 --- a/Lib9c/Module/Guild/GuildModule.cs +++ b/Lib9c/Module/Guild/GuildModule.cs @@ -8,6 +8,7 @@ using Nekoyume.Model.Guild; using Nekoyume.TypedAddress; using Libplanet.Crypto; +using Nekoyume.ValidatorDelegation; namespace Nekoyume.Module.Guild { @@ -81,6 +82,14 @@ public static GuildRepository RemoveGuild( throw new InvalidOperationException("There are remained participants in the guild."); } + var validatorRepository = new ValidatorRepository(repository.World, repository.ActionContext); + var validatorDelegatee = validatorRepository.GetValidatorDelegatee(guild.ValidatorAddress); + var bond = validatorRepository.GetBond(validatorDelegatee, signer); + if (bond.Share > 0) + { + throw new InvalidOperationException("The signer has a bond with the validator."); + } + repository.RemoveGuildParticipant(signer); repository.DecreaseGuildMemberCount(guild.Address); repository.UpdateWorld( From 45caa747899e976990c49d8b1c70377ca6cd05ec Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 17 Oct 2024 16:15:16 +0900 Subject: [PATCH 099/206] test: Fix errors of MigratePledgeToGuildTest and GuildMigrationCtrlTest --- .../Guild/Migration/GuildMigrationCtrlTest.cs | 41 +++++++++--------- .../Migration/MigratePledgeToGuildTest.cs | 42 +++++++++---------- 2 files changed, 39 insertions(+), 44 deletions(-) diff --git a/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs b/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs index babd632aac..54b11496cc 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs @@ -19,23 +19,22 @@ namespace Lib9c.Tests.Action.Guild.Migration using Nekoyume.TypedAddress; using Xunit; - public class GuildMigrationCtrlTest + public class GuildMigrationCtrlTest : GuildTestBase { [Fact] public void MigratePlanetariumPledgeToGuild_When_WithUnapprovedPledgeContract() { + var validatorKey = new PrivateKey(); var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); var target = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.JoinGuild(guildAddress, guildMasterAddress); repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( MeadConfig.PatronAddress.Serialize(), false.Serialize(), // Unapproved @@ -49,18 +48,17 @@ public void MigratePlanetariumPledgeToGuild_When_WithUnapprovedPledgeContract() [Fact] public void MigratePlanetariumPledgeToGuild_When_WithPledgeContract() { + var validatorKey = new PrivateKey(); var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); var target = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.JoinGuild(guildAddress, guildMasterAddress); repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( MeadConfig.PatronAddress.Serialize(), true.Serialize(), @@ -77,17 +75,16 @@ public void MigratePlanetariumPledgeToGuild_When_WithPledgeContract() [Fact] public void MigratePlanetariumPledgeToGuild_When_WithoutPledgeContract() { + var validatorKey = new PrivateKey(); var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); var target = AddressUtil.CreateAgentAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.JoinGuild(guildAddress, guildMasterAddress); Assert.Null(repository.GetJoinedGuild(target)); Assert.Throws(() => diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs index 753acac82a..d273846b88 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Action.Guild.Migration using System; using Bencodex.Types; using Lib9c.Tests.Action; + using Lib9c.Tests.Model.Guild; using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Crypto; @@ -21,7 +22,7 @@ namespace Lib9c.Tests.Action.Guild.Migration using Nekoyume.TypedAddress; using Xunit; - public class MigratePledgeToGuildTest + public class MigratePledgeToGuildTest : GuildTestBase { [Fact] public void Serialization() @@ -40,19 +41,18 @@ public void Serialization() [Fact] public void Execute_When_WithPledgeContract() { + var validatorKey = new PrivateKey(); var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); var target = AddressUtil.CreateAgentAddress(); var caller = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.JoinGuild(guildAddress, guildMasterAddress); repository.UpdateWorld(repository.World.SetLegacyState( pledgeAddress, new List( @@ -91,19 +91,18 @@ public void Execute_When_WithPledgeContract() [Fact] public void Execute_When_WithUnapprovedPledgeContract() { + var validatorKey = new PrivateKey(); var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); var target = AddressUtil.CreateAgentAddress(); var pledgeAddress = target.GetPledgeAddress(); var caller = AddressUtil.CreateAgentAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.JoinGuild(guildAddress, guildMasterAddress); repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( MeadConfig.PatronAddress.Serialize(), false.Serialize(), @@ -130,18 +129,17 @@ public void Execute_When_WithUnapprovedPledgeContract() [Fact] public void Execute_When_WithoutPledgeContract() { + var validatorKey = new PrivateKey(); var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); var target = AddressUtil.CreateAgentAddress(); var caller = AddressUtil.CreateAgentAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + IWorld world = World; + world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); + world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.JoinGuild(guildAddress, guildMasterAddress); Assert.Null(repository.GetJoinedGuild(target)); var action = new MigratePledgeToGuild(target); From faaa485d3dc1217a1e94ade97b47da8b6a119ec5 Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 17 Oct 2024 16:47:38 +0900 Subject: [PATCH 100/206] test: Fix test code for AutoJoinGuildTest --- .../Tx/Begin/AutoJoinGuildTest.cs | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs b/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs index 85e988a202..ead01b8df9 100644 --- a/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs +++ b/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs @@ -16,8 +16,10 @@ namespace Lib9c.Tests.PolicyAction.Tx.Begin using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.Module.Guild; + using Nekoyume.Module.ValidatorDelegation; using Nekoyume.PolicyAction.Tx.Begin; using Nekoyume.TypedAddress; + using Nekoyume.ValidatorDelegation; using Xunit; public class AutoJoinGuildTest @@ -35,18 +37,29 @@ public void RunAsPolicyActionOnly() [Fact] public void Execute_When_WithPledgeContract() { + var validatorKey = new PrivateKey(); var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); var agentAddress = AddressUtil.CreateAgentAddress(); var pledgeAddress = agentAddress.GetPledgeAddress(); IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); + var goldCurrencyState = new GoldCurrencyState(Currencies.GuildGold); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.JoinGuild(guildAddress, guildMasterAddress); + + world = world.MintAsset(new ActionContext(), validatorKey.Address, Currencies.GuildGold * 100); + var validatorRepository = new ValidatorRepository(world, new ActionContext + { + Signer = validatorKey.Address, + }); + validatorRepository.CreateValidatorDelegatee(validatorKey.PublicKey, 10); + world = validatorRepository.World; + + var repository = new GuildRepository(world, new ActionContext + { + Signer = guildMasterAddress, + }); + repository.MakeGuild(guildAddress, validatorKey.Address); repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( MeadConfig.PatronAddress.Serialize(), true.Serialize(), @@ -70,17 +83,24 @@ public void Execute_When_WithPledgeContract() [Fact] public void Execute_When_WithoutPledgeContract() { + var validatorKey = new PrivateKey(); var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; var guildAddress = AddressUtil.CreateGuildAddress(); var agentAddress = AddressUtil.CreateAgentAddress(); IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); + var goldCurrencyState = new GoldCurrencyState(Currencies.GuildGold); world = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + world = world.MintAsset(new ActionContext(), validatorKey.Address, Currencies.GuildGold * 100); + var validatorRepository = new ValidatorRepository(world, new ActionContext + { + Signer = validatorKey.Address, + }); + validatorRepository.CreateValidatorDelegatee(validatorKey.PublicKey, 10); + world = validatorRepository.World; + var repository = new GuildRepository(world, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.JoinGuild(guildAddress, guildMasterAddress); Assert.Null(repository.GetJoinedGuild(agentAddress)); var action = new AutoJoinGuild(); From 64e2a7f963d6369ba52780ae5e580622d218e323 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 18 Oct 2024 11:02:35 +0900 Subject: [PATCH 101/206] fix: Restrict (Un)Delegation to validator --- .../ValidatorDelegation/AllocateRewardTest.cs | 77 ++- .../ClaimRewardValidatorTest.cs | 65 +- .../DelegateValidatorTest.cs | 204 ++----- .../PromoteValidatorTest.cs | 16 +- .../ValidatorDelegation/RecordProposerTest.cs | 1 - .../RedelegateValidatorTest.cs | 554 ------------------ .../ReleaseValidatorUnbondingsTest.cs | 16 +- .../SetValidatorCommissionTest.cs | 20 +- .../ValidatorDelegation/SlashValidatorTest.cs | 26 +- .../UndelegateValidatorTest.cs | 232 ++------ .../UnjailValidatorTest.cs | 17 +- .../UpdateValidatorsTest.cs | 6 +- .../ValidatorDelegationTestBase.cs | 128 ++-- .../ValidatorDelegation/AllocateReward.cs | 3 +- .../ValidatorDelegation/DelegateValidator.cs | 8 + .../RedelegateValidator.cs | 71 --- .../UndelegateValidator.cs | 8 + Lib9c/Model/Guild/GuildParticipant.cs | 1 - 18 files changed, 299 insertions(+), 1154 deletions(-) delete mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs delete mode 100644 Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs index 0aa5d51a07..6dd938eb44 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -34,10 +34,11 @@ private interface IAllocateRewardFixture FungibleAssetValue[] ValidatorBalances => ValidatorsInfos.Select(info => info.Balance).ToArray(); - PrivateKey GetProposerKey() + PrivateKey GetProposerKey(List validators) { return ValidatorsInfos .Where(item => item.VoteFlag == VoteFlag.PreCommit) + .Where(item => validators.Any(v => v.PublicKey.Equals(item.Key.PublicKey))) .Take(ValidatorList.MaxBondedSetSize) .First() .Key; @@ -69,12 +70,12 @@ public void Execute() { var fixture = new StaticFixture { - TotalReward = GG * 1000, + TotalReward = RewardCurrency * 1000, ValidatorsInfos = CreateArray(4, i => new ValidatorInfo { Key = new PrivateKey(), - Cash = GG * 10, - Balance = GG * 100, + Cash = DelegationCurrency * 10, + Balance = DelegationCurrency * 100, VoteFlag = i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null, }), Delegatorinfos = Array.Empty(), @@ -95,12 +96,12 @@ public void Execute_Theory(int validatorCount, double totalReward) { var fixture = new StaticFixture { - TotalReward = FungibleAssetValue.Parse(GG, $"{totalReward:R}"), + TotalReward = FungibleAssetValue.Parse(RewardCurrency, $"{totalReward:R}"), ValidatorsInfos = CreateArray(validatorCount, i => new ValidatorInfo { Key = new PrivateKey(), - Cash = GG * 10, - Balance = GG * 100, + Cash = DelegationCurrency * 10, + Balance = DelegationCurrency * 100, VoteFlag = i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null, }), Delegatorinfos = Array.Empty(), @@ -113,12 +114,12 @@ public void Execute_WithoutReward_Throw() { var fixture = new StaticFixture { - TotalReward = GG * 0, + TotalReward = RewardCurrency * 0, ValidatorsInfos = CreateArray(4, i => new ValidatorInfo { Key = new PrivateKey(), - Cash = GG * 10, - Balance = GG * 100, + Cash = DelegationCurrency * 10, + Balance = DelegationCurrency * 100, VoteFlag = i % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null, }), Delegatorinfos = Array.Empty(), @@ -183,25 +184,22 @@ private void ExecuteWithFixture(IAllocateRewardFixture fixture) var validatorKeys = fixture.ValidatorKeys; var validatorCashes = fixture.ValidatorCashes; var validatorBalances = fixture.ValidatorBalances; - var proposerKey = fixture.GetProposerKey(); var height = 1L; world = EnsureToMintAssets(world, validatorKeys, validatorBalances, height++); world = EnsurePromotedValidators(world, validatorKeys, validatorCashes, height++); - world = EnsureProposer(world, proposerKey, height++); world = world.MintAsset(actionContext, Addresses.RewardPool, totalReward); + var repository = new ValidatorRepository(world, actionContext); + var bondedSet = repository.GetValidatorList().GetBonded(); + var proposerKey = fixture.GetProposerKey(bondedSet); + world = EnsureProposer(world, proposerKey, height++); // Calculate expected values for comparison with actual values. - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedBondedSet = expectedRepository.GetValidatorList().GetBonded(); - var expectedProposer = proposerKey; - var votes = CreateVotes(validatorInfos, expectedBondedSet, height - 1); - var balances = votes - .Select(vote => world.GetBalance(vote.ValidatorPublicKey.Address, GG)).ToArray(); + var votes = CreateVotes(validatorInfos, bondedSet, height - 1); var expectedProposerReward = CalculatePropserReward(totalReward) + CalculateBonusPropserReward(votes, totalReward); var expectedValidatorsReward = totalReward - expectedProposerReward; var expectedCommunityFund = CalculateCommunityFund(votes, expectedValidatorsReward); - var expectedAllocatedReward = expectedValidatorsReward - expectedCommunityFund; + var expectedAllocatedReward = totalReward - expectedCommunityFund; // When var lastCommit = new BlockCommit(height - 1, round: 0, votes[0].BlockHash, votes); @@ -209,7 +207,7 @@ var expectedProposerReward actionContext = new ActionContext { PreviousState = world, - Signer = expectedProposer.PublicKey.Address, + Signer = proposerKey.PublicKey.Address, LastCommit = lastCommit, BlockIndex = height++, }; @@ -220,8 +218,8 @@ var expectedProposerReward .OfType() .Aggregate(BigInteger.Zero, (accum, next) => accum + next); var actualRepository = new ValidatorRepository(world, actionContext); - var actualAllocatedReward = GG * 0; - var actualCommunityFund = world.GetBalance(Addresses.CommunityPool, GG); + var actualAllocatedReward = RewardCurrency * 0; + var actualCommunityFund = world.GetBalance(Addresses.CommunityPool, RewardCurrency); foreach (var (vote, index) in votes.Select((v, i) => (v, i))) { if (vote.ValidatorPower is not { } validatorPower) @@ -232,33 +230,30 @@ var expectedProposerReward var validatorAddress = vote.ValidatorPublicKey.Address; var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorAddress); var validatorRewardAddress = actualDelegatee.CurrentLumpSumRewardsRecordAddress(); - var balance = balances[index]; - var actualBalance = world.GetBalance(validatorAddress, GG); - var actualReward = world.GetBalance(validatorRewardAddress, GG); - var isProposer = vote.ValidatorPublicKey.Equals(expectedProposer.PublicKey); + var actualDelegationBalance = world.GetBalance(validatorAddress, DelegationCurrency); + var actualCommission = world.GetBalance(validatorAddress, RewardCurrency); + var actualUnclaimedReward = world.GetBalance(validatorRewardAddress, RewardCurrency); + var isProposer = vote.ValidatorPublicKey.Equals(proposerKey.PublicKey); if (vote.Flag == VoteFlag.Null) { - Assert.Equal(balance, actualBalance); - Assert.Equal(GG * 0, actualReward); + Assert.Equal(RewardCurrency * 0, actualCommission); + Assert.Equal(RewardCurrency * 0, actualUnclaimedReward); Assert.False(isProposer); continue; } var reward = (expectedValidatorsReward * validatorPower).DivRem(totalPower).Quotient; var expectedCommission = CalculateCommission(reward, actualDelegatee); - var expectedBalance = isProposer - ? expectedCommission + balance + expectedProposerReward - : expectedCommission + balance; - var expectedReward = reward - expectedCommission; - - Assert.Equal(expectedBalance, actualBalance); - Assert.Equal(expectedReward, actualReward); + var expectedUnclaimedReward = reward - expectedCommission; + expectedCommission = isProposer + ? expectedCommission + expectedProposerReward + : expectedCommission; - Assert.Equal(expectedBalance, actualBalance); - Assert.Equal(expectedReward, actualReward); + Assert.Equal(expectedCommission, actualCommission); + Assert.Equal(expectedUnclaimedReward, actualUnclaimedReward); - actualAllocatedReward += expectedCommission + expectedReward; + actualAllocatedReward += expectedCommission + expectedUnclaimedReward; } Assert.Equal(expectedAllocatedReward, actualAllocatedReward); @@ -301,10 +296,10 @@ private class RandomFixture : IAllocateRewardFixture public RandomFixture(int randomSeed) { _random = new Random(randomSeed); - TotalReward = GetRandomGG(_random); + TotalReward = GetRandomFAV(RewardCurrency, _random); ValidatorsInfos = CreateArray(_random.Next(1, 200), i => { - var balance = GetRandomGG(_random); + var balance = GetRandomFAV(DelegationCurrency, _random); var flag = _random.Next() % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null; return new ValidatorInfo { @@ -316,7 +311,7 @@ public RandomFixture(int randomSeed) }); Delegatorinfos = CreateArray(_random.Next(1, 200), i => { - var balance = GetRandomGG(_random); + var balance = GetRandomFAV(DelegationCurrency, _random); return new DelegatorInfo { Key = new PrivateKey(), diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs index d4f3b82228..b3a87e2baa 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -28,8 +28,6 @@ private interface IClaimRewardFixture PrivateKey[] DelegatorKeys => DelegatorInfos.Select(i => i.Key).ToArray(); FungibleAssetValue[] DelegatorBalances => DelegatorInfos.Select(i => i.Balance).ToArray(); - - FungibleAssetValue[] DelegatorCashes => DelegatorInfos.Select(i => i.Cash).ToArray(); } public static IEnumerable RandomSeeds => new List @@ -59,8 +57,8 @@ public void Execute() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - var validatorGold = GG * 10; - var allocatedReward = GG * 100; + var validatorGold = DelegationCurrency * 10; + var allocatedReward = RewardCurrency * 100; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); @@ -79,7 +77,7 @@ public void Execute() world = claimRewardValidator.Execute(actionContext); // Then - var actualBalance = world.GetBalance(validatorKey.Address, GG); + var actualBalance = world.GetBalance(validatorKey.Address, RewardCurrency); Assert.Equal(expectedBalance, actualBalance); } @@ -94,17 +92,16 @@ public void Execute_Theory_OneDelegator(decimal totalReward) var fixture = new StaticFixture { DelegatorLength = 1, - TotalReward = FungibleAssetValue.Parse(GG, $"{totalReward}"), + TotalReward = FungibleAssetValue.Parse(RewardCurrency, $"{totalReward}"), ValidatorKey = new PrivateKey(), - ValidatorBalance = GG * 100, - ValidatorCash = GG * 10, + ValidatorBalance = DelegationCurrency * 100, + ValidatorCash = DelegationCurrency * 10, DelegatorInfos = new[] { new DelegatorInfo { Key = new PrivateKey(), - Balance = GG * 100, - Cash = GG * 10, + Balance = DelegationCurrency * 100, }, }, }; @@ -130,23 +127,21 @@ public void Execute_Theory_TwoDelegators(decimal totalReward) var fixture = new StaticFixture { DelegatorLength = 2, - TotalReward = FungibleAssetValue.Parse(GG, $"{totalReward}"), + TotalReward = FungibleAssetValue.Parse(RewardCurrency, $"{totalReward}"), ValidatorKey = new PrivateKey(), - ValidatorBalance = GG * 100, - ValidatorCash = GG * 10, + ValidatorBalance = DelegationCurrency * 100, + ValidatorCash = DelegationCurrency * 10, DelegatorInfos = new[] { new DelegatorInfo { Key = new PrivateKey(), - Balance = GG * 100, - Cash = GG * 10, + Balance = DelegationCurrency * 100, }, new DelegatorInfo { Key = new PrivateKey(), - Balance = GG * 100, - Cash = GG * 10, + Balance = DelegationCurrency * 100, }, }, }; @@ -179,7 +174,6 @@ private void ExecuteWithFixture(IClaimRewardFixture fixture) var validatorKey = fixture.ValidatorKey; var delegatorKeys = fixture.DelegatorKeys; var delegatorBalances = fixture.DelegatorBalances; - var delegatorCashes = fixture.DelegatorCashes; var height = 1L; var actionContext = new ActionContext(); var validatorBalance = fixture.ValidatorBalance; @@ -188,8 +182,8 @@ private void ExecuteWithFixture(IClaimRewardFixture fixture) world = EnsureToMintAsset(world, validatorKey, validatorBalance, height++); world = EnsurePromotedValidator(world, validatorKey, validatorCash, height++); world = EnsureToMintAssets(world, delegatorKeys, delegatorBalances, height++); - world = EnsureBondedDelegators( - world, delegatorKeys, validatorKey, delegatorCashes, height++); + world = delegatorKeys.Aggregate(world, (w, d) => EnsureMakeGuild(w, d.Address, validatorKey.Address, height++)); + world = EnsureRewardAllocatedValidator(world, validatorKey, totalReward, ref height); // Calculate expected values for comparison with actual values. @@ -214,12 +208,10 @@ var expectedProposerReward i => CalculateClaim(expectedDelegatorShares[i], expectedTotalShares, expectedClaim)); var expectedValidatorBalance = validatorBalance; expectedValidatorBalance -= validatorCash; - expectedValidatorBalance += expectedProposerReward; - expectedValidatorBalance += expectedCommission; - expectedValidatorBalance += expectedValidatorClaim; - var expectedDelegatorBalances = CreateArray( - length, - i => delegatorBalances[i] - delegatorCashes[i] + expectedDelegatorClaims[i]); + var expectedValidatorReward = expectedProposerReward; + expectedValidatorReward += expectedCommission; + expectedValidatorReward += expectedValidatorClaim; + var expectedDelegatorBalances = CreateArray(length, i => DelegationCurrency * 0); var expectedRemainReward = totalReward; expectedRemainReward -= expectedProposerReward; expectedRemainReward -= expectedCommission; @@ -254,22 +246,26 @@ var expectedProposerReward // Then var repository = new ValidatorRepository(world, actionContext); var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); - var actualRemainReward = world.GetBalance(delegatee.RewardRemainderPoolAddress, GG); - var actualValidatorBalance = world.GetBalance(validatorKey.Address, GG); + var actualRemainReward = world.GetBalance(delegatee.RewardRemainderPoolAddress, RewardCurrency); + var actualValidatorBalance = world.GetBalance(validatorKey.Address, DelegationCurrency); + var actualValidatorReward = world.GetBalance(validatorKey.Address, RewardCurrency); var actualDelegatorBalances = delegatorKeys - .Select(item => world.GetBalance(item.Address, GG)) + .Select(item => world.GetBalance(item.Address, DelegationCurrency)) + .ToArray(); + var actualDelegatorRewards = delegatorKeys + .Select(item => world.GetBalance(item.Address, RewardCurrency)) .ToArray(); Assert.Equal(expectedRemainReward, actualRemainReward); Assert.Equal(expectedValidatorBalance, actualValidatorBalance); Assert.Equal(expectedDelegatorBalances, actualDelegatorBalances); + Assert.Equal(expectedValidatorReward, actualValidatorReward); + Assert.Equal(expectedDelegatorClaims, actualDelegatorRewards); } private struct DelegatorInfo { public PrivateKey Key { get; set; } - public FungibleAssetValue Cash { get; set; } - public FungibleAssetValue Balance { get; set; } } @@ -297,17 +293,16 @@ public RandomFixture(int randomSeed) _random = new Random(randomSeed); DelegatorLength = _random.Next(3, 100); ValidatorKey = new PrivateKey(); - TotalReward = GetRandomGG(_random); - ValidatorBalance = GetRandomGG(_random); + TotalReward = GetRandomFAV(RewardCurrency, _random); + ValidatorBalance = GetRandomFAV(DelegationCurrency, _random); ValidatorCash = GetRandomCash(_random, ValidatorBalance); DelegatorInfos = CreateArray(DelegatorLength, _ => { - var balance = GetRandomGG(_random); + var balance = GetRandomFAV(DelegationCurrency, _random); return new DelegatorInfo { Key = new PrivateKey(), Balance = balance, - Cash = GetRandomCash(_random, balance), }; }); } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index 694068b9b8..bbacce54b3 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -3,15 +3,12 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System; using System.Collections.Generic; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Action; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.ValidatorDelegation; -using Org.BouncyCastle.Crypto.Modes; using Xunit; public class DelegateValidatorTest : ValidatorDelegationTestBase @@ -19,8 +16,6 @@ public class DelegateValidatorTest : ValidatorDelegationTestBase private interface IDelegateValidatorFixture { ValidatorInfo ValidatorInfo { get; } - - DelegatorInfo[] DelegatorInfos { get; } } public static IEnumerable RandomSeeds => new List @@ -37,7 +32,7 @@ private interface IDelegateValidatorFixture public void Serialization() { var address = new PrivateKey().Address; - var gold = GG * 10; + var gold = DelegationCurrency * 10; var action = new DelegateValidator(address, gold); var plainValue = action.PlainValue; @@ -53,58 +48,46 @@ public void Execute() // Given var world = World; var validatorKey = new PrivateKey(); - var delegatorKey = new PrivateKey(); var height = 1L; - var validatorGold = GG * 10; - var delegatorGold = GG * 20; + var validatorGold = DelegationCurrency * 10; + var delegatorGold = DelegationCurrency * 20; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); - world = EnsureToMintAsset(world, delegatorKey, GG * 100, height++); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 100, height++); // When var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorGold); var actionContext = new ActionContext { PreviousState = world, - Signer = delegatorKey.Address, + Signer = validatorKey.Address, + BlockIndex = height++, }; world = delegateValidator.Execute(actionContext); // Then var repository = new ValidatorRepository(world, actionContext); var validator = repository.GetValidatorDelegatee(validatorKey.Address); - var bond = repository.GetBond(validator, delegatorKey.Address); + var bond = repository.GetBond(validator, validatorKey.Address); var validatorList = repository.GetValidatorList(); - Assert.Contains(delegatorKey.Address, validator.Delegators); - Assert.Equal(delegatorGold.RawValue, bond.Share); + Assert.Equal(validatorGold.RawValue + delegatorGold.RawValue, bond.Share); Assert.Equal(validatorGold.RawValue + delegatorGold.RawValue, validator.Validator.Power); Assert.Equal(validator.Validator, Assert.Single(validatorList.Validators)); - Assert.Equal(GG * 80, world.GetBalance(delegatorKey.Address, GG)); + Assert.Equal(DelegationCurrency * 80, world.GetBalance(validatorKey.Address, DelegationCurrency)); } - [Theory] - [InlineData(2)] - [InlineData(3)] - [InlineData(9)] - public void Execute_Theory(int delegatorCount) + [Fact] + public void Execute_Fact() { var fixture = new StaticFixture { ValidatorInfo = new ValidatorInfo { Key = new PrivateKey(), - Cash = GG * 10, - Balance = GG * 100, + Cash = DelegationCurrency * 10, + Balance = DelegationCurrency * 100, }, - DelegatorInfos = Enumerable.Range(0, delegatorCount) - .Select(_ => new DelegatorInfo - { - Key = new PrivateKey(), - Cash = GG * 20, - Balance = GG * 100, - }) - .ToArray(), }; ExecuteWithFixture(fixture); } @@ -113,7 +96,7 @@ public void Execute_Theory(int delegatorCount) [InlineData(0)] [InlineData(1181126949)] [InlineData(793705868)] - public void Execute_Theory_WithStaticSeed(int randomSeed) + public void Execute_Fact_WithStaticSeed(int randomSeed) { var fixture = new RandomFixture(randomSeed); ExecuteWithFixture(fixture); @@ -121,7 +104,7 @@ public void Execute_Theory_WithStaticSeed(int randomSeed) [Theory] [MemberData(nameof(RandomSeeds))] - public void Execute_Theory_WithRandomSeed(int randomSeed) + public void Execute_Fact_WithRandomSeed(int randomSeed) { var fixture = new RandomFixture(randomSeed); ExecuteWithFixture(fixture); @@ -133,19 +116,18 @@ public void Execute_WithInvalidCurrency_Throw() // Given var world = World; var validatorKey = new PrivateKey(); - var delegatorKey = new PrivateKey(); var height = 1L; - var validatorGold = GG * 10; + var validatorGold = DelegationCurrency * 10; var delegatorDollar = Dollar * 20; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); - world = EnsureToMintAsset(world, delegatorKey, delegatorDollar, height++); + world = EnsureToMintAsset(world, validatorKey, delegatorDollar, height++); // When var actionContext = new ActionContext { PreviousState = world, - Signer = delegatorKey.Address, + Signer = validatorKey.Address, BlockIndex = height++, }; var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorDollar); @@ -160,22 +142,21 @@ public void Execute_WithInsufficientBalance_Throw() // Given var world = World; var validatorKey = new PrivateKey(); - var delegatorKey = new PrivateKey(); - var validatorGold = GG * 10; - var delegatorGold = GG * 10; + var validatorGold = DelegationCurrency * 10; + var delegatorGold = DelegationCurrency * 10; var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); - world = EnsureToMintAsset(world, delegatorKey, delegatorGold, height++); + world = EnsureToMintAsset(world, validatorKey, delegatorGold, height++); // When var actionContext = new ActionContext { PreviousState = world, - Signer = delegatorKey.Address, + Signer = validatorKey.Address, BlockIndex = height++, }; - var delegateValidator = new DelegateValidator(validatorKey.Address, GG * 11); + var delegateValidator = new DelegateValidator(validatorKey.Address, DelegationCurrency * 11); // Then Assert.Throws(() => delegateValidator.Execute(actionContext)); @@ -187,17 +168,16 @@ public void Execute_ToInvalidValidator_Throw() // Given var world = World; var validatorKey = new PrivateKey(); - var delegatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, delegatorKey, GG * 100, height++); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 100, height++); // When var actionContext = new ActionContext { PreviousState = world, - Signer = delegatorKey.Address, + Signer = validatorKey.Address, }; - var delegateValidator = new DelegateValidator(validatorKey.Address, GG * 10); + var delegateValidator = new DelegateValidator(validatorKey.Address, DelegationCurrency * 10); // Then Assert.Throws(() => delegateValidator.Execute(actionContext)); @@ -209,20 +189,19 @@ public void Execute_ToTombstonedValidator_Throw() // Given var world = World; var validatorKey = new PrivateKey(); - var delegatorKey = new PrivateKey(); var height = 1L; - var validatorGold = GG * 10; - var delegatorGold = GG * 10; + var validatorGold = DelegationCurrency * 10; + var delegatorGold = DelegationCurrency * 10; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); world = EnsureTombstonedValidator(world, validatorKey, height++); - world = EnsureToMintAsset(world, delegatorKey, delegatorGold, height++); + world = EnsureToMintAsset(world, validatorKey, delegatorGold, height++); // When var actionContext = new ActionContext { PreviousState = world, - Signer = delegatorKey.Address, + Signer = validatorKey.Address, }; var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorGold); @@ -230,108 +209,44 @@ public void Execute_ToTombstonedValidator_Throw() Assert.Throws(() => delegateValidator.Execute(actionContext)); } - [Fact] - public void Execute_CannotBeJailedDueToDelegatorDelegating() - { - // Given - var world = World; - var validatorKey = new PrivateKey(); - var delegatorKey = new PrivateKey(); - var validatorCash = GG * 10; - var validatorGold = GG * 100; - var delegatorGold = GG * 10; - var delegatorBalance = GG * 100; - var actionContext = new ActionContext { }; - - var height = 1L; - world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); - world = EnsurePromotedValidator(world, validatorKey, validatorCash, height++); - world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, 10, height++); - world = EnsureBondedDelegator(world, validatorKey, validatorKey, validatorCash, height++); - - world = EnsureToMintAsset(world, delegatorKey, delegatorBalance, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, delegatorGold, height++); - world = EnsureUnjailedValidator(world, validatorKey, ref height); - - // When - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); - var expectedJailed = expectedDelegatee.Jailed; - - var delegateValidator = new DelegateValidator(validatorKey.Address, 1 * GG); - actionContext = new ActionContext - { - PreviousState = world, - Signer = delegatorKey.Address, - BlockIndex = height, - }; - world = delegateValidator.Execute(actionContext); - - // Then - var actualRepository = new ValidatorRepository(world, actionContext); - var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); - var actualJailed = actualDelegatee.Jailed; - - Assert.False(actualJailed); - Assert.Equal(expectedJailed, actualJailed); - } - private void ExecuteWithFixture(IDelegateValidatorFixture fixture) { // Given var world = World; var validatorKey = fixture.ValidatorInfo.Key; var height = 1L; + var validatorCashToDelegate = fixture.ValidatorInfo.CashToDelegate; var validatorCash = fixture.ValidatorInfo.Cash; var validatorBalance = fixture.ValidatorInfo.Balance; - var delegatorKeys = fixture.DelegatorInfos.Select(i => i.Key).ToArray(); - var delegatorCashes = fixture.DelegatorInfos.Select(i => i.Cash).ToArray(); - var delegatorBalances = fixture.DelegatorInfos.Select(i => i.Balance).ToArray(); var actionContext = new ActionContext { }; world = EnsureToMintAsset(world, validatorKey, validatorBalance, height++); world = EnsurePromotedValidator(world, validatorKey, validatorCash, height++); - world = EnsureToMintAssets(world, delegatorKeys, delegatorBalances, height++); // When var expectedRepository = new ValidatorRepository(world, new ActionContext()); var expectedValidator = expectedRepository.GetValidatorDelegatee(validatorKey.Address); - var expectedValidatorBalance = validatorBalance - validatorCash; - var expectedDelegatorBalances = delegatorBalances - .Select((b, i) => b - delegatorCashes[i]).ToArray(); - var expectedPower = delegatorCashes.Aggregate( - validatorCash.RawValue, (a, b) => a + b.RawValue); + var expectedValidatorBalance = validatorBalance - validatorCash - validatorCashToDelegate; + var expectedPower = validatorCash.RawValue + validatorCashToDelegate.RawValue; - for (var i = 0; i < delegatorKeys.Length; i++) + var delegateValidator = new DelegateValidator(validatorKey.Address, validatorCashToDelegate); + actionContext = new ActionContext { - var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorCashes[i]); - actionContext = new ActionContext - { - PreviousState = world, - Signer = delegatorKeys[i].Address, - BlockIndex = height++, - }; - world = delegateValidator.Execute(actionContext); - } + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height++, + }; + world = delegateValidator.Execute(actionContext); // Then var actualRepository = new ValidatorRepository(world, actionContext); var actualValidator = actualRepository.GetValidatorDelegatee(validatorKey.Address); - var actualValidatorBalance = world.GetBalance(validatorKey.Address, GG); - var actualDelegatorBalances = delegatorKeys - .Select(k => world.GetBalance(k.Address, GG)).ToArray(); + var actualValidatorBalance = world.GetBalance(validatorKey.Address, DelegationCurrency); var actualPower = actualValidator.Power; + var actualBond = actualRepository.GetBond(actualValidator, validatorKey.Address); - for (var i = 0; i < delegatorKeys.Length; i++) - { - var actualBond = actualRepository.GetBond(actualValidator, delegatorKeys[i].Address); - Assert.Contains(delegatorKeys[i].Address, actualValidator.Delegators); - Assert.Equal(delegatorCashes[i].RawValue, actualBond.Share); - Assert.Equal(expectedDelegatorBalances[i], actualDelegatorBalances[i]); - } - + Assert.Equal(expectedPower, actualBond.Share); Assert.Equal(expectedValidatorBalance, actualValidatorBalance); Assert.Equal(expectedPower, actualPower); - Assert.Equal(expectedDelegatorBalances, actualDelegatorBalances); } private struct ValidatorInfo @@ -342,34 +257,18 @@ public ValidatorInfo() public ValidatorInfo(Random random) { - Balance = GetRandomGG(random); + Balance = GetRandomFAV(DelegationCurrency, random); Cash = GetRandomCash(random, Balance); + CashToDelegate = GetRandomCash(random, Balance - Cash); } public PrivateKey Key { get; set; } = new PrivateKey(); - public FungibleAssetValue Cash { get; set; } = GG * 10; - - public FungibleAssetValue Balance { get; set; } = GG * 100; - } + public FungibleAssetValue CashToDelegate { get; set; } = DelegationCurrency * 10; - private struct DelegatorInfo - { - public DelegatorInfo() - { - } + public FungibleAssetValue Cash { get; set; } = DelegationCurrency * 10; - public DelegatorInfo(Random random) - { - Balance = GetRandomGG(random); - Cash = GetRandomCash(random, Balance); - } - - public PrivateKey Key { get; set; } = new PrivateKey(); - - public FungibleAssetValue Cash { get; set; } = GG * 10; - - public FungibleAssetValue Balance { get; set; } = GG * 100; + public FungibleAssetValue Balance { get; set; } = DelegationCurrency * 100; } private struct StaticFixture : IDelegateValidatorFixture @@ -377,8 +276,6 @@ private struct StaticFixture : IDelegateValidatorFixture public DelegateValidator DelegateValidator { get; set; } public ValidatorInfo ValidatorInfo { get; set; } - - public DelegatorInfo[] DelegatorInfos { get; set; } } private class RandomFixture : IDelegateValidatorFixture @@ -389,13 +286,8 @@ public RandomFixture(int randomSeed) { _random = new Random(randomSeed); ValidatorInfo = new ValidatorInfo(_random); - DelegatorInfos = Enumerable.Range(0, _random.Next(1, 10)) - .Select(_ => new DelegatorInfo(_random)) - .ToArray(); } public ValidatorInfo ValidatorInfo { get; } - - public DelegatorInfo[] DelegatorInfos { get; } } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs index deb2c9a165..3d008cb8be 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs @@ -13,7 +13,7 @@ public class PromoteValidatorTest : ValidatorDelegationTestBase public void Serialization() { var publicKey = new PrivateKey().PublicKey; - var gold = GG * 10; + var gold = DelegationCurrency * 10; var action = new PromoteValidator(publicKey, gold); var plainValue = action.PlainValue; @@ -30,8 +30,8 @@ public void Execute() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - var gold = GG * 10; - world = EnsureToMintAsset(world, validatorKey, GG * 100, height++); + var gold = DelegationCurrency * 10; + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 100, height++); // When var actionContext = new ActionContext @@ -53,7 +53,7 @@ public void Execute() Assert.Equal(gold.RawValue, bond.Share); Assert.Equal(validator.Validator, Assert.Single(validatorList.Validators)); Assert.Equal(validator.Validator, Assert.Single(validatorList.GetBonded())); - Assert.Equal(GG * 90, world.GetBalance(validatorKey.Address, GG)); + Assert.Equal(DelegationCurrency * 90, world.GetBalance(validatorKey.Address, DelegationCurrency)); Assert.Empty(validatorList.GetUnbonded()); } @@ -65,8 +65,8 @@ public void Execute_ToInvalidValidator_Throw() var context = new ActionContext { }; var validatorKey = new PrivateKey(); var height = 1L; - var gold = GG * 10; - world = EnsureToMintAsset(world, validatorKey, GG * 100, height++); + var gold = DelegationCurrency * 10; + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 100, height++); // When var actionContext = new ActionContext @@ -113,7 +113,7 @@ public void Execute_WithInsufficientBalance_Throw() var world = World; var validatorKey = new PrivateKey().PublicKey; var height = 1L; - var gold = GG * 10; + var gold = DelegationCurrency * 10; // When var actionContext = new ActionContext @@ -136,7 +136,7 @@ public void Execute_PromotedValidator_Throw() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - var validatorGold = GG * 10; + var validatorGold = DelegationCurrency * 10; world = EnsureToMintAsset(world, validatorKey, validatorGold * 2, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RecordProposerTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RecordProposerTest.cs index 1c83b5296f..5f0e6b7697 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RecordProposerTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/RecordProposerTest.cs @@ -2,7 +2,6 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System; -using Libplanet.Action.State; using Libplanet.Crypto; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Module.ValidatorDelegation; diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs deleted file mode 100644 index cf76b74a15..0000000000 --- a/.Lib9c.Tests/Action/ValidatorDelegation/RedelegateValidatorTest.cs +++ /dev/null @@ -1,554 +0,0 @@ -namespace Lib9c.Tests.Action.ValidatorDelegation; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Action; -using Nekoyume.Action.ValidatorDelegation; -using Nekoyume.ValidatorDelegation; -using Xunit; - -public class RedelegateValidatorTest : ValidatorDelegationTestBase -{ - private interface IRedelegateValidatorFixture - { - ValidatorInfo ValidatorInfo1 { get; } - - ValidatorInfo ValidatorInfo2 { get; } - - DelegatorInfo[] DelegatorInfos { get; } - } - - public static IEnumerable RandomSeeds => new List - { - new object[] { Random.Shared.Next() }, - new object[] { Random.Shared.Next() }, - new object[] { Random.Shared.Next() }, - new object[] { Random.Shared.Next() }, - new object[] { Random.Shared.Next() }, - new object[] { Random.Shared.Next() }, - }; - - [Fact] - public void Serialization() - { - var srcAddress = new PrivateKey().Address; - var dstAddress = new PrivateKey().Address; - var share = BigInteger.One; - var action = new RedelegateValidator(srcAddress, dstAddress, share); - var plainValue = action.PlainValue; - - var deserialized = new RedelegateValidator(); - deserialized.LoadPlainValue(plainValue); - Assert.Equal(srcAddress, deserialized.SrcValidatorDelegatee); - Assert.Equal(dstAddress, deserialized.DstValidatorDelegatee); - Assert.Equal(share, deserialized.Share); - } - - [Fact] - public void Execute() - { - // Given - var world = World; - var actionContext = new ActionContext { }; - var srcPrivateKey = new PrivateKey(); - var dstPrivateKey = new PrivateKey(); - var height = 1L; - world = EnsureToMintAsset(world, srcPrivateKey, GG * 10, height++); - world = EnsurePromotedValidator(world, srcPrivateKey, GG * 10, height++); - world = EnsureToMintAsset(world, dstPrivateKey, GG * 10, height++); - world = EnsurePromotedValidator(world, dstPrivateKey, GG * 10, height++); - - // When - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedSrcValidator = expectedRepository.GetValidatorDelegatee(srcPrivateKey.Address); - var expectedBond = expectedRepository.GetBond(expectedSrcValidator, srcPrivateKey.Address); - var redelegateValidator = new RedelegateValidator( - srcPrivateKey.Address, dstPrivateKey.Address, expectedBond.Share); - actionContext = new ActionContext - { - PreviousState = world, - Signer = srcPrivateKey.Address, - BlockIndex = height++, - }; - - world = redelegateValidator.Execute(actionContext); - - // Then - var actualRepository = new ValidatorRepository(world, actionContext); - var actualDstValidator = actualRepository.GetValidatorDelegatee(dstPrivateKey.Address); - var actualValidatorList = actualRepository.GetValidatorList(); - var actualDstBond = actualRepository.GetBond(actualDstValidator, srcPrivateKey.Address); - - Assert.Contains(srcPrivateKey.Address, actualDstValidator.Delegators); - Assert.Single(actualValidatorList.Validators); - Assert.Equal(actualDstValidator.Validator, actualValidatorList.Validators[0]); - Assert.Equal((GG * 10).RawValue, actualDstBond.Share); - Assert.Equal(GG * 20, actualDstValidator.TotalDelegated); - Assert.Equal((GG * 20).RawValue, actualDstValidator.TotalShares); - Assert.Equal((GG * 20).RawValue, actualDstValidator.Power); - } - - [Theory] - [InlineData(2)] - [InlineData(3)] - [InlineData(9)] - public void Execute_Theory(int delegatorCount) - { - var fixture = new StaticFixture - { - ValidatorInfo1 = new ValidatorInfo - { - Key = new PrivateKey(), - Cash = GG * 10, - Balance = GG * 100, - }, - ValidatorInfo2 = new ValidatorInfo - { - Key = new PrivateKey(), - Cash = GG * 10, - Balance = GG * 100, - }, - DelegatorInfos = Enumerable.Range(0, delegatorCount) - .Select(_ => new DelegatorInfo - { - Key = new PrivateKey(), - Cash = GG * 20, - Balance = GG * 100, - Redelegating = 20, - }) - .ToArray(), - }; - ExecuteWithFixture(fixture); - } - - [Theory] - [InlineData(0)] - [InlineData(1181126949)] - [InlineData(793705868)] - public void Execute_Theory_WithStaticSeed(int randomSeed) - { - var fixture = new RandomFixture(randomSeed); - ExecuteWithFixture(fixture); - } - - [Theory] - [MemberData(nameof(RandomSeeds))] - public void Execute_Theory_WithRandomSeed(int randomSeed) - { - var fixture = new RandomFixture(randomSeed); - ExecuteWithFixture(fixture); - } - - [Fact] - public void Execute_ToInvalidValidator_Throw() - { - // Given - var world = World; - var validatorKey = new PrivateKey(); - var delegatorKey = new PrivateKey(); - var height = 1L; - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, GG * 10, height++); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = height++, - Signer = delegatorKey.Address, - }; - var invalidAddress = new PrivateKey().Address; - var redelegateValidator = new RedelegateValidator(validatorKey.Address, invalidAddress, 10); - - // Then - Assert.Throws( - () => redelegateValidator.Execute(actionContext)); - } - - [Theory] - [InlineData(0)] - [InlineData(-1)] - public void Execute_WithNotPositiveShare_Throw(long share) - { - // Given - var world = World; - var validatorKey1 = new PrivateKey(); - var validatorKey2 = new PrivateKey(); - var delegatorKey = new PrivateKey(); - var height = 1L; - world = EnsureToMintAsset(world, validatorKey1, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey1, GG * 10, height++); - world = EnsureToMintAsset(world, validatorKey2, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey2, GG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, GG * 10, height++); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = height++, - Signer = delegatorKey.Address, - }; - var redelegateValidator = new RedelegateValidator( - validatorKey1.Address, validatorKey2.Address, share); - - // Then - Assert.Throws( - () => redelegateValidator.Execute(actionContext)); - } - - [Fact] - public void Execute_WithOverShare_Throw() - { - // Given - var world = World; - var validatorKey1 = new PrivateKey(); - var validatorKey2 = new PrivateKey(); - var delegatorKey = new PrivateKey(); - var height = 1L; - world = EnsureToMintAsset(world, validatorKey1, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey1, GG * 10, height++); - world = EnsureToMintAsset(world, validatorKey2, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey2, GG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, GG * 10, height++); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = height++, - Signer = delegatorKey.Address, - }; - var repository = new ValidatorRepository(world, actionContext); - var delegatee = repository.GetDelegatee(validatorKey1.Address); - var bond = repository.GetBond(delegatee, delegatorKey.Address); - var redelegateValidator = new RedelegateValidator( - validatorKey1.Address, validatorKey2.Address, bond.Share + 1); - - // Then - Assert.Throws( - () => redelegateValidator.Execute(actionContext)); - } - - [Fact] - public void Execute_FromJailedValidator_ToNotJailedValidator() - { - // Given - var world = World; - var delegatorKey = new PrivateKey(); - var validatorKey1 = new PrivateKey(); - var validatorKey2 = new PrivateKey(); - var height = 1L; - world = EnsureToMintAsset(world, validatorKey1, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey1, GG * 10, height++); - world = EnsureToMintAsset(world, validatorKey2, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey2, GG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, GG * 10, height++); - world = EnsureJailedValidator(world, validatorKey1, ref height); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - Signer = delegatorKey.Address, - BlockIndex = height++, - }; - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedDelegatee1 = expectedRepository.GetValidatorDelegatee(validatorKey1.Address); - var expectedDelegatee2 = expectedRepository.GetValidatorDelegatee(validatorKey2.Address); - var expectedBond1 = expectedRepository.GetBond(expectedDelegatee1, delegatorKey.Address); - var expectedBond2 = expectedRepository.GetBond(expectedDelegatee2, delegatorKey.Address); - - var redelegateValidator = new RedelegateValidator( - validatorKey1.Address, validatorKey2.Address, 10); - world = redelegateValidator.Execute(actionContext); - - // Then - var actualRepository = new ValidatorRepository(world, actionContext); - var actualDelegatee1 = actualRepository.GetValidatorDelegatee(validatorKey1.Address); - var actualDelegatee2 = actualRepository.GetValidatorDelegatee(validatorKey2.Address); - var actualBond1 = actualRepository.GetBond(actualDelegatee1, delegatorKey.Address); - var actualBond2 = actualRepository.GetBond(actualDelegatee2, delegatorKey.Address); - - Assert.Equal(expectedBond1.Share - 10, actualBond1.Share); - Assert.Equal(expectedBond2.Share + 9, actualBond2.Share); - } - - [Fact] - public void Execute_ToTombstonedValidator_Throw() - { - // Given - var world = World; - var actionContext = new ActionContext { }; - var srcPrivateKey = new PrivateKey(); - var dstPrivateKey = new PrivateKey(); - var height = 1L; - world = EnsureToMintAsset(world, srcPrivateKey, GG * 10, height++); - world = EnsurePromotedValidator(world, srcPrivateKey, GG * 10, height++); - world = EnsureToMintAsset(world, dstPrivateKey, GG * 10, height++); - world = EnsurePromotedValidator(world, dstPrivateKey, GG * 10, height++); - world = EnsureTombstonedValidator(world, dstPrivateKey, height++); - - // When - var repository = new ValidatorRepository(world, actionContext); - var srcValidator = repository.GetValidatorDelegatee(srcPrivateKey.Address); - var bone = repository.GetBond(srcValidator, srcPrivateKey.Address); - var redelegateValidator = new RedelegateValidator( - srcPrivateKey.Address, dstPrivateKey.Address, bone.Share); - actionContext = new ActionContext - { - PreviousState = world, - Signer = srcPrivateKey.Address, - BlockIndex = height++, - }; - - // Then - Assert.Throws( - () => redelegateValidator.Execute(actionContext)); - } - - [Fact] - public void Execute_SrcAndDstAddressAreSame_Throw() - { - // Given - var world = World; - var validatorKey = new PrivateKey(); - var delegatorKey = new PrivateKey(); - var height = 1L; - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, GG * 10, height++); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = height++, - Signer = delegatorKey.Address, - }; - var redelegateValidator = new RedelegateValidator( - validatorKey.Address, validatorKey.Address, 10); - - // Then - Assert.Throws( - () => redelegateValidator.Execute(actionContext)); - } - - [Fact] - public void Execute_CannotBeJailedDueToDelegatorRedelegating() - { - // Given - var world = World; - var validatorKey1 = new PrivateKey(); - var validatorKey2 = new PrivateKey(); - var delegatorKey = new PrivateKey(); - var validatorCash = GG * 10; - var validatorGold = GG * 100; - var delegatorGold = GG * 10; - var delegatorBalance = GG * 100; - var actionContext = new ActionContext { }; - - var height = 1L; - world = EnsureToMintAsset(world, validatorKey1, validatorGold, height++); - world = EnsurePromotedValidator(world, validatorKey1, validatorCash, height++); - world = EnsureUnbondingDelegator(world, validatorKey1, validatorKey1, 10, height++); - world = EnsureBondedDelegator(world, validatorKey1, validatorKey1, validatorCash, height++); - - world = EnsureToMintAsset(world, validatorKey2, validatorGold, height++); - world = EnsurePromotedValidator(world, validatorKey2, validatorGold, height++); - world = EnsureToMintAsset(world, delegatorKey, delegatorBalance, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey1, delegatorGold, height++); - world = EnsureUnjailedValidator(world, validatorKey1, ref height); - height++; - - // When - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey1.Address); - var expectedJailed = expectedDelegatee.Jailed; - - var redelegateValidator = new RedelegateValidator( - srcValidatorDelegatee: validatorKey1.Address, - dstValidatorDelegatee: validatorKey2.Address, - share: 10); - actionContext = new ActionContext - { - PreviousState = world, - Signer = delegatorKey.Address, - BlockIndex = height, - }; - world = redelegateValidator.Execute(actionContext); - - // Then - var actualRepository = new ValidatorRepository(world, actionContext); - var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey1.Address); - var actualJailed = actualDelegatee.Jailed; - - Assert.False(actualJailed); - Assert.Equal(expectedJailed, actualJailed); - } - - private void ExecuteWithFixture(IRedelegateValidatorFixture fixture) - { - // Given - var world = World; - var validatorKey1 = fixture.ValidatorInfo1.Key; - var validatorKey2 = fixture.ValidatorInfo2.Key; - var height = 1L; - var validatorCash1 = fixture.ValidatorInfo1.Cash; - var validatorBalance1 = fixture.ValidatorInfo1.Balance; - var validatorCash2 = fixture.ValidatorInfo2.Cash; - var validatorBalance2 = fixture.ValidatorInfo2.Balance; - var delegatorKeys = fixture.DelegatorInfos.Select(i => i.Key).ToArray(); - var delegatorCashes = fixture.DelegatorInfos.Select(i => i.Cash).ToArray(); - var delegatorBalances = fixture.DelegatorInfos.Select(i => i.Balance).ToArray(); - var delegatorRedelegatings = fixture.DelegatorInfos.Select(i => i.Redelegating).ToArray(); - var actionContext = new ActionContext { }; - world = EnsureToMintAsset(world, validatorKey1, validatorBalance1, height++); - world = EnsurePromotedValidator(world, validatorKey1, validatorCash1, height++); - world = EnsureToMintAsset(world, validatorKey2, validatorBalance2, height++); - world = EnsurePromotedValidator(world, validatorKey2, validatorCash2, height++); - world = EnsureToMintAssets(world, delegatorKeys, delegatorBalances, height++); - world = EnsureBondedDelegators( - world, delegatorKeys, validatorKey1, delegatorCashes, height++); - - // When - var expectedRepository = new ValidatorRepository(world, new ActionContext()); - var expectedValidator1 = expectedRepository.GetValidatorDelegatee(validatorKey1.Address); - var expectedValidator2 = expectedRepository.GetValidatorDelegatee(validatorKey2.Address); - var expectedValidatorBalance1 = validatorBalance1 - validatorCash1; - var expectedValidatorBalance2 = validatorBalance2 - validatorCash2; - var expectedDelegatorBalances = delegatorKeys - .Select(k => world.GetBalance(k.Address, GG)).ToArray(); - var expectedShares1 = delegatorCashes - .Select((c, i) => c.RawValue - delegatorRedelegatings[i]).ToArray(); - var expectedShares2 = delegatorRedelegatings; - var expectedPower1 = expectedValidator1.Power - delegatorRedelegatings.Aggregate( - BigInteger.Zero, (a, b) => a + b); - var expectedPower2 = expectedValidator2.Power + delegatorRedelegatings.Aggregate( - BigInteger.Zero, (a, b) => a + b); - - for (var i = 0; i < delegatorKeys.Length; i++) - { - var redelegating = fixture.DelegatorInfos[i].Redelegating; - var redelegateValidator = new RedelegateValidator( - validatorKey1.Address, validatorKey2.Address, redelegating); - actionContext = new ActionContext - { - PreviousState = world, - Signer = delegatorKeys[i].Address, - BlockIndex = height++, - }; - world = redelegateValidator.Execute(actionContext); - } - - // Then - var actualRepository = new ValidatorRepository(world, actionContext); - var actualValidator1 = actualRepository.GetValidatorDelegatee(validatorKey1.Address); - var actualValidator2 = actualRepository.GetValidatorDelegatee(validatorKey2.Address); - var actualValidatorBalance1 = world.GetBalance(validatorKey1.Address, GG); - var actualValidatorBalance2 = world.GetBalance(validatorKey2.Address, GG); - var actualDelegatorBalances = delegatorKeys - .Select(k => world.GetBalance(k.Address, GG)).ToArray(); - var actualPower1 = actualValidator1.Power; - var actualPower2 = actualValidator2.Power; - - for (var i = 0; i < delegatorKeys.Length; i++) - { - var actualBond1 = actualRepository.GetBond(actualValidator1, delegatorKeys[i].Address); - var actualBond2 = actualRepository.GetBond(actualValidator2, delegatorKeys[i].Address); - Assert.Contains(delegatorKeys[i].Address, actualValidator1.Delegators); - Assert.Contains(delegatorKeys[i].Address, actualValidator2.Delegators); - Assert.Equal(expectedShares1[i], actualBond1.Share); - Assert.Equal(expectedShares2[i], actualBond2.Share); - Assert.Equal(expectedDelegatorBalances[i], actualDelegatorBalances[i]); - } - - Assert.Equal(expectedValidatorBalance1, actualValidatorBalance1); - Assert.Equal(expectedValidatorBalance2, actualValidatorBalance2); - Assert.Equal(expectedPower1, actualPower1); - Assert.Equal(expectedPower2, actualPower2); - Assert.Equal(expectedDelegatorBalances, actualDelegatorBalances); - } - - private struct ValidatorInfo - { - public ValidatorInfo() - { - } - - public ValidatorInfo(Random random) - { - Balance = GetRandomGG(random); - Cash = GetRandomCash(random, Balance); - } - - public PrivateKey Key { get; set; } = new PrivateKey(); - - public FungibleAssetValue Cash { get; set; } = GG * 10; - - public FungibleAssetValue Balance { get; set; } = GG * 100; - } - - private struct DelegatorInfo - { - public DelegatorInfo() - { - } - - public DelegatorInfo(Random random) - { - Balance = GetRandomGG(random); - Cash = GetRandomCash(random, Balance); - Redelegating = GetRandomCash(random, Cash).RawValue; - } - - public PrivateKey Key { get; set; } = new PrivateKey(); - - public FungibleAssetValue Cash { get; set; } = GG * 10; - - public FungibleAssetValue Balance { get; set; } = GG * 100; - - public BigInteger Redelegating { get; set; } = 100; - } - - private struct StaticFixture : IRedelegateValidatorFixture - { - public ValidatorInfo ValidatorInfo1 { get; set; } - - public ValidatorInfo ValidatorInfo2 { get; set; } - - public DelegatorInfo[] DelegatorInfos { get; set; } - } - - private class RandomFixture : IRedelegateValidatorFixture - { - private readonly Random _random; - - public RandomFixture(int randomSeed) - { - _random = new Random(randomSeed); - ValidatorInfo1 = new ValidatorInfo(_random); - ValidatorInfo2 = new ValidatorInfo(_random); - DelegatorInfos = Enumerable.Range(0, _random.Next(1, 10)) - .Select(_ => new DelegatorInfo(_random)) - .ToArray(); - } - - public ValidatorInfo ValidatorInfo1 { get; } - - public ValidatorInfo ValidatorInfo2 { get; } - - public DelegatorInfo[] DelegatorInfos { get; } - } -} diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs index 34999626f4..d95ad3e77d 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs @@ -26,15 +26,15 @@ public void Execute() // Given var world = World; var validatorKey = new PrivateKey(); - var validatorGold = GG * 50; - var validatorBalance = GG * 100; + var validatorGold = DelegationCurrency * 50; + var validatorBalance = DelegationCurrency * 100; var share = new BigInteger(10); var height = 1L; var actionContext = new ActionContext { }; world = EnsureToMintAsset(world, validatorKey, validatorBalance, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); - world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, share, height); + world = EnsureUnbondingValidator(world, validatorKey.Address, share, height); // When var expectedRepository = new ValidatorRepository(world, actionContext); @@ -42,7 +42,7 @@ public void Execute() var expectedUnbondingSet = expectedRepository.GetUnbondingSet(); var expectedReleaseCount = expectedUnbondingSet.UnbondingRefs.Count; var expectedDepositGold = expectedDelegatee.FAVFromShare(share); - var expectedBalance = world.GetBalance(validatorKey.Address, GG) + expectedDepositGold; + var expectedBalance = world.GetBalance(validatorKey.Address, DelegationCurrency) + expectedDepositGold; var releaseValidatorUnbondings = new ReleaseValidatorUnbondings(validatorKey.Address); actionContext = new ActionContext @@ -55,7 +55,7 @@ public void Execute() // Then var actualRepository = new ValidatorRepository(world, actionContext); - var actualBalance = world.GetBalance(validatorKey.Address, GG); + var actualBalance = world.GetBalance(validatorKey.Address, DelegationCurrency); var actualUnbondingSet = actualRepository.GetUnbondingSet(); var actualReleaseCount = actualUnbondingSet.UnbondingRefs.Count; @@ -75,9 +75,9 @@ public void Execute_ThereIsNoUnbonding_AtEarlyHeight() var actionContext = new ActionContext { }; var share = new BigInteger(10); - world = EnsureToMintAsset(world, validatorKey, GG * 100, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 50, height++); - world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, share, height); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 100, height++); + world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 50, height++); + world = EnsureUnbondingValidator(world, validatorKey.Address, share, height); // When var expectedRepository = new ValidatorRepository(world, actionContext); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs index 8459c4f13f..6886fa952c 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs @@ -66,7 +66,7 @@ public void Execute() // Given var world = World; var validatorKey = new PrivateKey(); - var validatorGold = GG * 10; + var validatorGold = DelegationCurrency * 10; var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height); @@ -100,7 +100,7 @@ public void Execute_Theory(int oldCommissionPercentage, int newCommissionPercent // Given var world = World; var validatorKey = new PrivateKey(); - var validatorGold = GG * 10; + var validatorGold = DelegationCurrency * 10; var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); @@ -134,11 +134,11 @@ public void Execute_Theory_WithValueGreaterThanMaximum_Throw(int commissionPerce // Given var world = World; var validatorKey = new PrivateKey(); - var validatorGold = GG * 10; + var validatorGold = DelegationCurrency * 10; var height = 1L; - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 10, height); // When var actionContext = new ActionContext @@ -164,11 +164,11 @@ public void Execute_Theory_WithNegative_Throw(int commissionPercentage) // Given var world = World; var validatorKey = new PrivateKey(); - var validatorGold = GG * 10; + var validatorGold = DelegationCurrency * 10; var height = 1L; - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 10, height++); // When var actionContext = new ActionContext @@ -193,7 +193,7 @@ public void Execute_Theory_WithInvalidValue_Throw(int cooldown) // Given var world = World; var validatorKey = new PrivateKey(); - var validatorGold = GG * 10; + var validatorGold = DelegationCurrency * 10; var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); @@ -221,7 +221,7 @@ public void Execute_Theory_WitValue(int period) // Given var world = World; var validatorKey = new PrivateKey(); - var validatorGold = GG * 10; + var validatorGold = DelegationCurrency * 10; var height = 1L; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs index 2a103fde41..454eb36b5e 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Numerics; using Libplanet.Action.State; using Libplanet.Crypto; @@ -34,16 +35,15 @@ public void Execute() const int length = 10; var world = World; var validatorKey = new PrivateKey(); - var validatorGold = GG * 10; - var deletatorKeys = CreateArray(length, _ => new PrivateKey()); - var delegatorGolds = CreateArray(length, i => GG * Random.Shared.Next(10, 100)); + var validatorGold = DelegationCurrency * 10; + var delegatorKeys = CreateArray(length, _ => new PrivateKey()); + var delegatorGolds = CreateArray(length, i => DelegationCurrency * Random.Shared.Next(10, 100)); var height = 1L; var actionContext = new ActionContext { }; - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); - world = EnsureToMintAssets(world, deletatorKeys, delegatorGolds, height++); - world = EnsureBondedDelegators( - world, deletatorKeys, validatorKey, delegatorGolds, height++); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsureToMintAssets(world, delegatorKeys, delegatorGolds, height++); + world = delegatorKeys.Aggregate(world, (w, d) => EnsureMakeGuild(w, d.Address, validatorKey.Address, height++)); // When var expectedRepository = new ValidatorRepository(world, actionContext); @@ -92,7 +92,7 @@ public void Execute() world = slashValidator.Execute(actionContext); // Then - var balance = world.GetBalance(validatorKey.Address, GG); + var balance = world.GetBalance(validatorKey.Address, DelegationCurrency); var actualRepository = new ValidatorRepository(world, actionContext); var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); var actualValidatorShare = actualRepository.GetBond(actualDelegatee, validatorKey.Address).Share; @@ -138,8 +138,8 @@ public void Execute_ByAbstain() var validatorKey = new PrivateKey(); var actionContext = new ActionContext { }; var height = 1L; - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 10, height++); // When for (var i = 0L; i <= AbstainHistory.MaxAbstainAllowance; i++) @@ -176,8 +176,8 @@ public void Execute_ToJailedValidator_ThenNothingHappens() var validatorKey = new PrivateKey(); var height = 1L; var actionContext = new ActionContext(); - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 10, height++); world = EnsureTombstonedValidator(world, validatorKey, height++); // When diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 03b67f78c7..745901ccf1 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -17,8 +17,6 @@ public class UndelegateValidatorTest : ValidatorDelegationTestBase private interface IUndelegateValidatorFixture { ValidatorInfo ValidatorInfo { get; } - - DelegatorInfo[] DelegatorInfos { get; } } public static IEnumerable RandomSeeds => new List @@ -53,8 +51,8 @@ public void Execute() var actionContext = new ActionContext { }; var validatorKey = new PrivateKey(); var height = 1L; - var validatorGold = GG * 10; - world = EnsureToMintAsset(world, validatorKey, GG * 100, height++); + var validatorGold = DelegationCurrency * 10; + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 100, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); // When @@ -84,30 +82,18 @@ public void Execute() Assert.Equal(BigInteger.Zero, actualBond.Share); } - [Theory] - [InlineData(2)] - [InlineData(3)] - [InlineData(9)] - public void Execute_Theory(int delegatorCount) + [Fact] + public void Execute_Theory() { var fixture = new StaticFixture { ValidatorInfo = new ValidatorInfo { Key = new PrivateKey(), - Cash = GG * 10, - Balance = GG * 100, + Cash = DelegationCurrency * 10, + Balance = DelegationCurrency * 100, SubtractShare = 10, }, - DelegatorInfos = Enumerable.Range(0, delegatorCount) - .Select(_ => new DelegatorInfo - { - Key = new PrivateKey(), - Cash = GG * 20, - Balance = GG * 100, - SubtractShare = 20, - }) - .ToArray(), }; ExecuteWithFixture(fixture); } @@ -136,25 +122,22 @@ public void Execute_FromInvalidValidtor_Throw() { // Given var world = World; - var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 10, height++); // When var actionContext = new ActionContext { PreviousState = world, - Signer = delegatorKey.Address, + Signer = validatorKey.Address, BlockIndex = height++, }; var undelegateValidator = new UndelegateValidator(new PrivateKey().Address, 10); // Then - Assert.Throws( + Assert.Throws( () => undelegateValidator.Execute(actionContext)); } @@ -165,19 +148,17 @@ public void Execute_WithNotPositiveShare_Throw(long share) { // Given var world = World; - var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); // When var actionContext = new ActionContext { PreviousState = world, - Signer = delegatorKey.Address, + Signer = validatorKey.Address, BlockIndex = height++, }; var undelegateValidator = new UndelegateValidator(validatorKey.Address, share); @@ -187,56 +168,27 @@ public void Execute_WithNotPositiveShare_Throw(long share) () => undelegateValidator.Execute(actionContext)); } - [Fact] - public void Execute_WithoutDelegating_Throw() - { - // Given - var world = World; - var delegatorKey = new PrivateKey(); - var validatorKey = new PrivateKey(); - var height = 1L; - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); - - // When - var actionContext = new ActionContext - { - PreviousState = world, - Signer = delegatorKey.Address, - BlockIndex = height++, - }; - var undelegateValidator = new UndelegateValidator( - validatorKey.Address, 10); - - // Then - Assert.Throws( - () => undelegateValidator.Execute(actionContext)); - } - [Fact] public void Execute_FromJailedValidator() { // Given var world = World; - var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 10, height++); world = EnsureJailedValidator(world, validatorKey, ref height); // When var actionContext = new ActionContext { PreviousState = world, - Signer = delegatorKey.Address, + Signer = validatorKey.Address, BlockIndex = height, }; var expectedRepository = new ValidatorRepository(world, actionContext); var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); - var expectedBond = expectedRepository.GetBond(expectedDelegatee, delegatorKey.Address); + var expectedBond = expectedRepository.GetBond(expectedDelegatee, validatorKey.Address); var undelegateValidator = new UndelegateValidator(validatorKey.Address, 10); world = undelegateValidator.Execute(actionContext); @@ -244,7 +196,7 @@ public void Execute_FromJailedValidator() // Then var actualRepository = new ValidatorRepository(world, actionContext); var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); - var actualBond = actualRepository.GetBond(actualDelegatee, delegatorKey.Address); + var actualBond = actualRepository.GetBond(actualDelegatee, validatorKey.Address); Assert.Equal(expectedBond.Share - 10, actualBond.Share); } @@ -254,25 +206,22 @@ public void Execute_FromTombstonedValidator() { // Given var world = World; - var delegatorKey = new PrivateKey(); var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); - world = EnsureToMintAsset(world, delegatorKey, GG * 10, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 10, height++); world = EnsureTombstonedValidator(world, validatorKey, height++); // When var actionContext = new ActionContext { PreviousState = world, - Signer = delegatorKey.Address, + Signer = validatorKey.Address, BlockIndex = height, }; var expectedRepository = new ValidatorRepository(world, actionContext); var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); - var expectedBond = expectedRepository.GetBond(expectedDelegatee, delegatorKey.Address); + var expectedBond = expectedRepository.GetBond(expectedDelegatee, validatorKey.Address); var undelegateValidator = new UndelegateValidator(validatorKey.Address, 10); world = undelegateValidator.Execute(actionContext); @@ -280,55 +229,11 @@ public void Execute_FromTombstonedValidator() // Then var actualRepository = new ValidatorRepository(world, actionContext); var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); - var actualBond = actualRepository.GetBond(actualDelegatee, delegatorKey.Address); + var actualBond = actualRepository.GetBond(actualDelegatee, validatorKey.Address); Assert.Equal(expectedBond.Share - 10, actualBond.Share); } - [Fact] - public void Execute_CannotBeJailedDueToDelegatorUndelegating() - { - // Given - var world = World; - var validatorKey = new PrivateKey(); - var delegatorKey = new PrivateKey(); - var validatorCash = GG * 10; - var validatorGold = GG * 100; - var delegatorGold = GG * 10; - var actionContext = new ActionContext { }; - var height = 1L; - world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); - world = EnsurePromotedValidator(world, validatorKey, validatorCash, height++); - world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, 10, height++); - world = EnsureBondedDelegator(world, validatorKey, validatorKey, validatorCash, height++); - - world = EnsureToMintAsset(world, delegatorKey, delegatorGold, height++); - world = EnsureBondedDelegator(world, delegatorKey, validatorKey, delegatorGold, height++); - world = EnsureUnjailedValidator(world, validatorKey, ref height); - - // When - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); - var expectedJailed = expectedDelegatee.Jailed; - - var undelegateValidator = new UndelegateValidator(validatorKey.Address, 10); - actionContext = new ActionContext - { - PreviousState = world, - Signer = delegatorKey.Address, - BlockIndex = height, - }; - world = undelegateValidator.Execute(actionContext); - - // Then - var actualRepository = new ValidatorRepository(world, actionContext); - var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); - var actualJailed = actualDelegatee.Jailed; - - Assert.False(actualJailed); - Assert.Equal(expectedJailed, actualJailed); - } - [Fact] public void Execute_JailsValidatorWhenUndelegationCausesLowDelegation() { @@ -374,61 +279,35 @@ private void ExecuteWithFixture(IUndelegateValidatorFixture fixture) var height = 1L; var validatorCash = fixture.ValidatorInfo.Cash; var validatorBalance = fixture.ValidatorInfo.Balance; - var delegatorKeys = fixture.DelegatorInfos.Select(i => i.Key).ToArray(); - var delegatorCashes = fixture.DelegatorInfos.Select(i => i.Cash).ToArray(); - var delegatorBalances = fixture.DelegatorInfos.Select(i => i.Balance).ToArray(); - var delegatorSubtractShares = fixture.DelegatorInfos.Select(i => i.SubtractShare).ToArray(); var actionContext = new ActionContext { }; world = EnsureToMintAsset(world, validatorKey, validatorBalance, height++); world = EnsurePromotedValidator(world, validatorKey, validatorCash, height++); - world = EnsureToMintAssets(world, delegatorKeys, delegatorBalances, height++); - world = EnsureBondedDelegators( - world, delegatorKeys, validatorKey, delegatorCashes, height++); // When var expectedRepository = new ValidatorRepository(world, new ActionContext()); var expectedValidator = expectedRepository.GetValidatorDelegatee(validatorKey.Address); var expectedValidatorBalance = validatorBalance - validatorCash; - var expectedDelegatorBalances = delegatorKeys - .Select(k => world.GetBalance(k.Address, GG)).ToArray(); - var expectedShares = delegatorCashes - .Select((c, i) => c.RawValue - delegatorSubtractShares[i]).ToArray(); - var expectedPower = expectedValidator.Power - delegatorSubtractShares.Aggregate( - BigInteger.Zero, (a, b) => a + b); - - for (var i = 0; i < delegatorKeys.Length; i++) + var expectedValidatorPower = expectedValidator.Power - fixture.ValidatorInfo.SubtractShare; + + var subtractShare = fixture.ValidatorInfo.SubtractShare; + var undelegateValidator = new UndelegateValidator( + validatorKey.Address, subtractShare); + actionContext = new ActionContext { - var subtractShare = fixture.DelegatorInfos[i].SubtractShare; - var undelegateValidator = new UndelegateValidator( - validatorKey.Address, subtractShare); - actionContext = new ActionContext - { - PreviousState = world, - Signer = delegatorKeys[i].Address, - BlockIndex = height++, - }; - world = undelegateValidator.Execute(actionContext); - } + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height++, + }; + world = undelegateValidator.Execute(actionContext); // Then var actualRepository = new ValidatorRepository(world, actionContext); var actualValidator = actualRepository.GetValidatorDelegatee(validatorKey.Address); - var actualValidatorBalance = world.GetBalance(validatorKey.Address, GG); - var actualDelegatorBalances = delegatorKeys - .Select(k => world.GetBalance(k.Address, GG)).ToArray(); - var actualPower = actualValidator.Power; - - for (var i = 0; i < delegatorKeys.Length; i++) - { - var actualBond = actualRepository.GetBond(actualValidator, delegatorKeys[i].Address); - Assert.Contains(delegatorKeys[i].Address, actualValidator.Delegators); - Assert.Equal(expectedShares[i], actualBond.Share); - Assert.Equal(expectedDelegatorBalances[i], actualDelegatorBalances[i]); - } + var actualValidatorBalance = world.GetBalance(validatorKey.Address, DelegationCurrency); + var actualValiatorPower = actualValidator.Power; Assert.Equal(expectedValidatorBalance, actualValidatorBalance); - Assert.Equal(expectedPower, actualPower); - Assert.Equal(expectedDelegatorBalances, actualDelegatorBalances); + Assert.Equal(expectedValidatorPower, actualValiatorPower); } private struct ValidatorInfo @@ -439,7 +318,7 @@ public ValidatorInfo() public ValidatorInfo(Random random) { - Balance = GetRandomGG(random); + Balance = GetRandomFAV(DelegationCurrency, random); Cash = GetRandomCash(random, Balance); SubtractShare = GetRandomCash(random, Cash).RawValue; if (SubtractShare == 0) @@ -450,31 +329,9 @@ public ValidatorInfo(Random random) public PrivateKey Key { get; set; } = new PrivateKey(); - public FungibleAssetValue Cash { get; set; } = GG * 10; + public FungibleAssetValue Cash { get; set; } = DelegationCurrency * 10; - public FungibleAssetValue Balance { get; set; } = GG * 100; - - public BigInteger SubtractShare { get; set; } = 100; - } - - private struct DelegatorInfo - { - public DelegatorInfo() - { - } - - public DelegatorInfo(Random random) - { - Balance = GetRandomGG(random); - Cash = GetRandomCash(random, Balance); - SubtractShare = GetRandomCash(random, Cash).RawValue; - } - - public PrivateKey Key { get; set; } = new PrivateKey(); - - public FungibleAssetValue Cash { get; set; } = GG * 10; - - public FungibleAssetValue Balance { get; set; } = GG * 100; + public FungibleAssetValue Balance { get; set; } = DelegationCurrency * 100; public BigInteger SubtractShare { get; set; } = 100; } @@ -482,8 +339,6 @@ public DelegatorInfo(Random random) private struct StaticFixture : IUndelegateValidatorFixture { public ValidatorInfo ValidatorInfo { get; set; } - - public DelegatorInfo[] DelegatorInfos { get; set; } } private class RandomFixture : IUndelegateValidatorFixture @@ -494,13 +349,8 @@ public RandomFixture(int randomSeed) { _random = new Random(randomSeed); ValidatorInfo = new ValidatorInfo(_random); - DelegatorInfos = Enumerable.Range(0, _random.Next(1, 10)) - .Select(_ => new DelegatorInfo(_random)) - .ToArray(); } public ValidatorInfo ValidatorInfo { get; } - - public DelegatorInfo[] DelegatorInfos { get; } } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs index 2247ba5364..6bf61fd62e 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs @@ -6,7 +6,6 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using Nekoyume.Action; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.ValidatorDelegation; -using Org.BouncyCastle.Bcpg.OpenPgp; using Xunit; public class UnjailValidatorTest : ValidatorDelegationTestBase @@ -28,7 +27,7 @@ public void Execute() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - var validatorGold = GG * 100; + var validatorGold = DelegationCurrency * 100; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); world = EnsureJailedValidator(world, validatorKey, ref height); @@ -79,8 +78,8 @@ public void Execute_OnNotJailedValidator_Throw() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 10, height++); // When var unjailValidator = new UnjailValidator(); @@ -102,8 +101,8 @@ public void Execute_TooEarly_Throw() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 10, height++); world = EnsureJailedValidator(world, validatorKey, ref height); // When @@ -127,8 +126,8 @@ public void Execute_OnTombstonedValidator_Throw() var world = World; var validatorKey = new PrivateKey(); var height = 1L; - world = EnsureToMintAsset(world, validatorKey, GG * 10, height++); - world = EnsurePromotedValidator(world, validatorKey, GG * 10, height++); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 10, height++); world = EnsureTombstonedValidator(world, validatorKey, height++); // When @@ -155,7 +154,7 @@ public void Execute_OnLowDelegatedValidator_Throw() var validatorGold = MinimumDelegation; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); - world = EnsureUnbondingDelegator(world, validatorKey, validatorKey, 10, height); + world = EnsureUnbondingValidator(world, validatorKey.Address, 10, height); // When var unjailValidator = new UnjailValidator(); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs index 17297db452..741ac87b41 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs @@ -29,7 +29,7 @@ public void Execute() const int length = 10; var world = World; var validatorKeys = CreateArray(length, _ => new PrivateKey()); - var validatorGolds = CreateArray(length, i => GG * Random.Shared.Next(1, length + 1)); + var validatorGolds = CreateArray(length, i => DelegationCurrency * Random.Shared.Next(1, length + 1)); var height = 1L; var actionContext = new ActionContext { }; world = EnsureToMintAssets(world, validatorKeys, validatorGolds, height++); @@ -65,9 +65,9 @@ public void Execute_ExcludesTombstonedValidator() const int length = 10; var world = World; var validatorKeys = CreateArray(length, _ => new PrivateKey()); - var validatorGolds = CreateArray(length, i => GG * 100); + var validatorGolds = CreateArray(length, i => DelegationCurrency * 100); var height = 1L; - var validatorGold = GG * 100; + var validatorGold = DelegationCurrency * 100; var actionContext = new ActionContext { }; world = EnsureToMintAssets(world, validatorKeys, validatorGolds, height++); world = EnsurePromotedValidators(world, validatorKeys, validatorGolds, height++); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 583b7a022b..af712216b0 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -4,7 +4,6 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Linq; using System.Numerics; using Libplanet.Action.State; @@ -15,20 +14,28 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using Libplanet.Types.Consensus; using Libplanet.Types.Evidence; using Nekoyume; +using Nekoyume.Action.Guild; using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.Model.Guild; using Nekoyume.Model.State; using Nekoyume.Module; +using Nekoyume.Module.Guild; +using Nekoyume.TypedAddress; using Nekoyume.ValidatorDelegation; +using Xunit; public class ValidatorDelegationTestBase { - protected static readonly Currency GG = Currencies.GuildGold; + protected static readonly Currency GoldCurrency = Currency.Uncapped("NCG", 2, null); + protected static readonly Currency DelegationCurrency = Currencies.GuildGold; + protected static readonly Currency RewardCurrency = Currencies.Mead; protected static readonly Currency Dollar = Currency.Uncapped("dollar", 2, null); + private static readonly int _maximumIntegerLength = 15; public ValidatorDelegationTestBase() { var world = new World(MockUtil.MockModernWorldState); - var goldCurrencyState = new GoldCurrencyState(GG); + var goldCurrencyState = new GoldCurrencyState(GoldCurrency); World = world .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); } @@ -40,7 +47,7 @@ public ValidatorDelegationTestBase() protected IWorld World { get; } - protected FungibleAssetValue MinimumDelegation { get; } = GG * 10; + protected FungibleAssetValue MinimumDelegation { get; } = DelegationCurrency * 10; protected static T[] CreateArray(int length, Func creator) => Enumerable.Range(0, length).Select(creator).ToArray(); @@ -126,6 +133,28 @@ protected static IWorld EnsurePromotedValidator( return promoteValidator.Execute(actionContext); } + protected static IWorld EnsureUnbondingValidator( + IWorld world, + Address validatorAddress, + BigInteger share, + long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } + + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight, + Signer = validatorAddress, + }; + var undelegateValidator = new UndelegateValidator( + validatorAddress, share); + return undelegateValidator.Execute(actionContext); + } + protected static IWorld ExecuteSlashValidator( IWorld world, PublicKey validatorKey, BlockCommit lastCommit, long blockHeight) { @@ -140,11 +169,10 @@ protected static IWorld ExecuteSlashValidator( return slashValidator.Execute(actionContext); } - protected static IWorld EnsureBondedDelegator( + protected static IWorld EnsureMakeGuild( IWorld world, - PrivateKey delegatorKey, - PrivateKey validatorKey, - FungibleAssetValue amount, + Address guildMasterAddress, + Address validatorAddress, long blockHeight) { if (blockHeight < 0) @@ -152,46 +180,50 @@ protected static IWorld EnsureBondedDelegator( throw new ArgumentOutOfRangeException(nameof(blockHeight)); } - var delegatorAddress = delegatorKey.Address; - var validatorAddress = validatorKey.Address; var actionContext = new ActionContext { PreviousState = world, BlockIndex = blockHeight, - Signer = delegatorAddress, + Signer = guildMasterAddress, }; - var delegatorValidator = new DelegateValidator( - validatorAddress, amount); - return delegatorValidator.Execute(actionContext); + var makeGuild = new MakeGuild(validatorAddress); + return makeGuild.Execute(actionContext); } - protected static IWorld EnsureBondedDelegators( + protected static IWorld EnsureJoinGuild( IWorld world, - PrivateKey[] delegatorKeys, - PrivateKey validatorKey, - FungibleAssetValue[] amounts, + Address guildParticipantAddress, + Address guildMasterAddress, + Address validatorAddress, long blockHeight) { - if (delegatorKeys.Length != amounts.Length) + if (blockHeight < 0) { - throw new ArgumentException( - "The length of delegatorPrivateKeys and amounts must be the same."); + throw new ArgumentOutOfRangeException(nameof(blockHeight)); } - for (var i = 0; i < delegatorKeys.Length; i++) + var repo = new GuildRepository(world, new ActionContext()); + var guildAddress = repo.GetJoinedGuild(new AgentAddress(guildMasterAddress)) + ?? throw new ArgumentException($"Guild master {guildMasterAddress} does not have guild"); + if (validatorAddress != repo.GetGuild(guildAddress).ValidatorAddress) { - world = EnsureBondedDelegator( - world, delegatorKeys[i], validatorKey, amounts[i], blockHeight); + throw new ArgumentException( + $"The guild of guild master does not belong to validator {validatorAddress}."); } - return world; + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight, + Signer = guildParticipantAddress, + }; + var joinGuild = new JoinGuild(guildAddress); + return joinGuild.Execute(actionContext); } - protected static IWorld EnsureUnbondingDelegator( + protected static IWorld EnsureQuitGuild( IWorld world, - PrivateKey delegatorKey, - PrivateKey validatorKey, - BigInteger share, + Address guildParticipantAddress, long blockHeight) { if (blockHeight < 0) @@ -199,16 +231,14 @@ protected static IWorld EnsureUnbondingDelegator( throw new ArgumentOutOfRangeException(nameof(blockHeight)); } - var delegatorAddress = delegatorKey.Address; - var validatorAddress = validatorKey.Address; + var delegatorAddress = guildParticipantAddress; var actionContext = new ActionContext { PreviousState = world, BlockIndex = blockHeight, Signer = delegatorAddress, }; - var undelegateValidator = new UndelegateValidator( - validatorAddress, share); + var undelegateValidator = new QuitGuild(); return undelegateValidator.Execute(actionContext); } @@ -550,30 +580,26 @@ protected static FungibleAssetValue CalculateCommunityFund(ImmutableArray return communityFund; } - protected static FungibleAssetValue GetRandomGG() => GetRandomGG(Random.Shared, 1, 100000); + protected static FungibleAssetValue GetRandomFAV(Currency currency) => GetRandomFAV(currency, Random.Shared); - protected static FungibleAssetValue GetRandomGG(Random random) - => GetRandomGG(random, 0.01m, 1000.0m); - - protected static FungibleAssetValue GetRandomGG(Random random, decimal min, decimal max) + protected static FungibleAssetValue GetRandomFAV(Currency currency, Random random) { - var minLong = (int)(min * 100); - var maxLong = (int)(max * 100); - var value = Math.Round(random.Next(minLong, maxLong) / 100.0, 2); - return FungibleAssetValue.Parse(GG, $"{value:R}"); + var decimalLength = random.Next(currency.DecimalPlaces); + var integerLength = random.Next(1, _maximumIntegerLength); + var decimalPart = Enumerable.Range(0, decimalLength) + .Aggregate(string.Empty, (s, i) => s + random.Next(10)); + var integerPart = Enumerable.Range(0, integerLength) + .Aggregate(string.Empty, (s, i) => s + (integerLength > 1 ? random.Next(10) : random.Next(1, 10))); + var isDecimalZero = decimalLength == 0 || decimalPart.All(c => c == '0'); + var text = isDecimalZero is false ? $"{integerPart}.{decimalPart}" : integerPart; + + return FungibleAssetValue.Parse(currency, text); } protected static FungibleAssetValue GetRandomCash(Random random, FungibleAssetValue fav) { - if (fav.RawValue >= long.MaxValue) - { - throw new ArgumentOutOfRangeException( - nameof(fav), "Fungible asset value is too large."); - } - - var num = random.NextInt64(1, (long)fav.RawValue); - var cash = FungibleAssetValue.FromRawValue(fav.Currency, num); - + var denominator = random.Next(100) + 1; + var cash = fav.DivRem(denominator, out var remainder); if (cash.Sign < 0 || cash > fav) { throw new InvalidOperationException("Invalid cash value."); diff --git a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs index 50715b1421..7cc0594050 100644 --- a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs +++ b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs @@ -8,7 +8,6 @@ using Libplanet.Types.Consensus; using Nekoyume.ValidatorDelegation; using Nekoyume.Module.ValidatorDelegation; -using Nekoyume.Module; using Libplanet.Types.Blocks; namespace Nekoyume.Action.ValidatorDelegation @@ -29,7 +28,7 @@ public override IWorld Execute(IActionContext context) { var world = context.PreviousState; var repository = new ValidatorRepository(world, context); - var rewardCurrency = world.GetGoldCurrency(); + var rewardCurrency = ValidatorDelegatee.ValidatorRewardCurrency; var proposerInfo = repository.GetProposerInfo(); if (context.LastCommit is BlockCommit lastCommit) diff --git a/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs index 3642ce4714..2d6b380139 100644 --- a/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs @@ -50,6 +50,14 @@ public override IWorld Execute(IActionContext context) { GasTracer.UseGas(1); + if (context.Signer != ValidatorDelegatee) + { + throw new InvalidAddressException( + $"{nameof(context.Signer)}({context.Signer}) is " + + $"not equal to {nameof(ValidatorDelegatee)}({ValidatorDelegatee})." + ); + } + var world = context.PreviousState; var repository = new ValidatorRepository(world, context); repository.DelegateValidator(ValidatorDelegatee, FAV); diff --git a/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs deleted file mode 100644 index 98870a12eb..0000000000 --- a/Lib9c/Action/ValidatorDelegation/RedelegateValidator.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Numerics; -using Bencodex.Types; -using Libplanet.Action.State; -using Libplanet.Action; -using Libplanet.Crypto; -using Nekoyume.Module.ValidatorDelegation; -using Nekoyume.ValidatorDelegation; - -namespace Nekoyume.Action.ValidatorDelegation -{ - [ActionType(TypeIdentifier)] - public sealed class RedelegateValidator : ActionBase - { - public const string TypeIdentifier = "redelegate_validator"; - - public RedelegateValidator() { } - - public RedelegateValidator( - Address srcValidatorDelegatee, Address dstValidatorDelegatee, BigInteger share) - { - SrcValidatorDelegatee = srcValidatorDelegatee; - DstValidatorDelegatee = dstValidatorDelegatee; - Share = share; - } - - public Address SrcValidatorDelegatee { get; private set; } - - public Address DstValidatorDelegatee { get; private set; } - - public BigInteger Share { get; private set; } - - public override IValue PlainValue => Dictionary.Empty - .Add("type_id", TypeIdentifier) - .Add("values", List.Empty - .Add(SrcValidatorDelegatee.Bencoded) - .Add(DstValidatorDelegatee.Bencoded) - .Add(Share)); - - 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(); - } - - SrcValidatorDelegatee = new Address(values[0]); - DstValidatorDelegatee = new Address(values[1]); - Share = (Integer)values[2]; - } - - public override IWorld Execute(IActionContext context) - { - GasTracer.UseGas(1); - - if (SrcValidatorDelegatee.Equals(DstValidatorDelegatee)) - { - throw new InvalidOperationException("Src and Dst cannot be the same."); - } - - var world = context.PreviousState; - var repository = new ValidatorRepository(world, context); - repository.RedelegateValidator(SrcValidatorDelegatee, DstValidatorDelegatee, Share); - - return repository.World; - } - } -} diff --git a/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs index 09e4ba172b..fc60dc06c8 100644 --- a/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs @@ -50,6 +50,14 @@ public override IWorld Execute(IActionContext context) { GasTracer.UseGas(1); + if (context.Signer != ValidatorDelegatee) + { + throw new InvalidAddressException( + $"{nameof(context.Signer)}({context.Signer}) is " + + $"not equal to {nameof(ValidatorDelegatee)}({ValidatorDelegatee})." + ); + } + var world = context.PreviousState; var repository = new ValidatorRepository(world, context); repository.UndelegateValidator(ValidatorDelegatee, Share); diff --git a/Lib9c/Model/Guild/GuildParticipant.cs b/Lib9c/Model/Guild/GuildParticipant.cs index a7439f7f66..91cad3de83 100644 --- a/Lib9c/Model/Guild/GuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildParticipant.cs @@ -3,7 +3,6 @@ using Bencodex.Types; using Nekoyume.Action; using Nekoyume.Delegation; -using Nekoyume.Model.Stake; using Nekoyume.TypedAddress; namespace Nekoyume.Model.Guild From b2707d3fefa0a8c38aaaa0adedd042d60a26e9fa Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 18 Oct 2024 15:21:59 +0900 Subject: [PATCH 102/206] test: Fix Action/Policy tests --- .Lib9c.Tests/Action/Guild/MakeGuildTest.cs | 35 ++++++------------ .Lib9c.Tests/Action/InitializeStatesTest.cs | 25 ++++++++----- .Lib9c.Tests/Policy/BlockPolicyTest.cs | 41 ++++++++++++--------- Lib9c/Action/Guild/MakeGuild.cs | 10 ++--- Lib9c/Action/InitializeStates.cs | 7 +++- Lib9c/Action/RewardGold.cs | 25 +++++++------ 6 files changed, 75 insertions(+), 68 deletions(-) diff --git a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs index 6d7ebb1556..5f6fc6bb73 100644 --- a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs @@ -43,38 +43,27 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Theory] - [MemberData(nameof(TestCases))] - public void Execute(AgentAddress guildMasterAddress, bool fail) + [Fact] + public void Execute() { IWorld world = World; var validatorPrivateKey = new PrivateKey(); + var guildMasterAddress = AddressUtil.CreateAgentAddress(); world = EnsureToMintAsset(world, validatorPrivateKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorPrivateKey.PublicKey); var action = new MakeGuild(validatorPrivateKey.Address); - if (fail) - { - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = world, - Signer = guildMasterAddress, - })); - } - else + world = action.Execute(new ActionContext { - world = action.Execute(new ActionContext - { - PreviousState = world, - Signer = guildMasterAddress, - }); + PreviousState = world, + Signer = guildMasterAddress, + }); - var repository = new GuildRepository(world, new ActionContext()); - var guildAddress = repository.GetJoinedGuild(guildMasterAddress); - Assert.NotNull(guildAddress); - Assert.True(repository.TryGetGuild(guildAddress.Value, out var guild)); - Assert.Equal(guildMasterAddress, guild.GuildMasterAddress); - } + var repository = new GuildRepository(world, new ActionContext()); + var guildAddress = repository.GetJoinedGuild(guildMasterAddress); + Assert.NotNull(guildAddress); + Assert.True(repository.TryGetGuild(guildAddress.Value, out var guild)); + Assert.Equal(guildMasterAddress, guild.GuildMasterAddress); } } } diff --git a/.Lib9c.Tests/Action/InitializeStatesTest.cs b/.Lib9c.Tests/Action/InitializeStatesTest.cs index e11e96a892..8f9898024e 100644 --- a/.Lib9c.Tests/Action/InitializeStatesTest.cs +++ b/.Lib9c.Tests/Action/InitializeStatesTest.cs @@ -44,7 +44,8 @@ public void Execute() var privateKey = new PrivateKey(); (ActivationKey activationKey, PendingActivationState pendingActivation) = ActivationKey.Create(privateKey, nonce); - var validatorSet = new ValidatorSet(new List { new (privateKey.PublicKey, 10) }); + var validatorSet = new ValidatorSet( + new List { new (privateKey.PublicKey, 1_000_000_000_000_000_000) }); var action = new InitializeStates( validatorSet: validatorSet, @@ -108,7 +109,8 @@ public void ExecuteWithAuthorizedMinersState() var privateKey = new PrivateKey(); (ActivationKey activationKey, PendingActivationState pendingActivation) = ActivationKey.Create(privateKey, nonce); - var validatorSet = new ValidatorSet(new List { new (privateKey.PublicKey, 10) }); + var validatorSet = new ValidatorSet( + new List { new (privateKey.PublicKey, 1_000_000_000_000_000_000) }); var action = new InitializeStates( validatorSet: validatorSet, @@ -167,7 +169,8 @@ public void ExecuteWithActivateAdminKey() (ActivationKey activationKey, PendingActivationState pendingActivation) = ActivationKey.Create(privateKey, nonce); var adminAddress = new Address("F9A15F870701268Bd7bBeA6502eB15F4997f32f9"); - var validatorSet = new ValidatorSet(new List { new (privateKey.PublicKey, 10) }); + var validatorSet = new ValidatorSet( + new List { new (privateKey.PublicKey, 1_000_000_000_000_000_000) }); var action = new InitializeStates( validatorSet: validatorSet, @@ -220,7 +223,8 @@ public void ExecuteWithCredits() "山田太郎", } ); - var validatorSet = new ValidatorSet(new List { new (minterKey.PublicKey, 10) }); + var validatorSet = new ValidatorSet( + new List { new (minterKey.PublicKey, 1_000_000_000_000_000_000) }); var action = new InitializeStates( validatorSet: validatorSet, @@ -269,7 +273,8 @@ public void ExecuteWithoutAdminState() var privateKey = new PrivateKey(); (ActivationKey activationKey, PendingActivationState pendingActivation) = ActivationKey.Create(privateKey, nonce); - var validatorSet = new ValidatorSet(new List { new (privateKey.PublicKey, 10) }); + var validatorSet = new ValidatorSet( + new List { new (privateKey.PublicKey, 1_000_000_000_000_000_000) }); var action = new InitializeStates( validatorSet: validatorSet, @@ -330,14 +335,15 @@ public void ExecuteWithoutInitialSupply() pendingActivationStates: Array.Empty() ); - // Cannot execute InitializeStates without initial supply, due to validator delegation. - Assert.Throws(() => action.Execute(new ActionContext() + var genesisState = action.Execute(new ActionContext() { BlockIndex = 0, Miner = default, Signer = minterKey.Address, PreviousState = new World(MockUtil.MockModernWorldState), - })); + }); + + Assert.Equal(0 * ncg, genesisState.GetBalance(GoldCurrencyState.Address, ncg)); } [Fact] @@ -355,7 +361,8 @@ public void ExecuteWithAssetMinters() #pragma warning restore CS0618 var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; var adminAddress = new Address("F9A15F870701268Bd7bBeA6502eB15F4997f32f9"); - var validatorSet = new ValidatorSet(new List { new (minterKey.PublicKey, 10) }); + var validatorSet = new ValidatorSet( + new List { new (minterKey.PublicKey, 1_000_000_000_000_000_000) }); var action = new InitializeStates( validatorSet: validatorSet, diff --git a/.Lib9c.Tests/Policy/BlockPolicyTest.cs b/.Lib9c.Tests/Policy/BlockPolicyTest.cs index afa9c7377d..af1f00ea98 100644 --- a/.Lib9c.Tests/Policy/BlockPolicyTest.cs +++ b/.Lib9c.Tests/Policy/BlockPolicyTest.cs @@ -28,20 +28,16 @@ namespace Lib9c.Tests using Nekoyume.Blockchain.Policy; using Nekoyume.Model; using Nekoyume.Model.State; + using Nekoyume.ValidatorDelegation; using Xunit; public class BlockPolicyTest { private readonly PrivateKey _privateKey; - private readonly Currency _currency; public BlockPolicyTest() { _privateKey = new PrivateKey(); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, _privateKey.Address); -#pragma warning restore CS0618 } [Fact] @@ -70,7 +66,8 @@ public void ValidateNextBlockTx_Mead() }, }; Block genesis = MakeGenesisBlock( - new ValidatorSet(new List { new (adminPrivateKey.PublicKey, 1000) }), + new ValidatorSet( + new List { new (adminPrivateKey.PublicKey, 10_000_000_000_000_000_000) }), adminAddress, ImmutableHashSet
.Empty, actionBases: new[] { mint, mint2 }, @@ -167,7 +164,8 @@ public void BlockCommitFromNonValidator() IBlockPolicy policy = blockPolicySource.GetPolicy(null, null, null, null); IStagePolicy stagePolicy = new VolatileStagePolicy(); Block genesis = MakeGenesisBlock( - new ValidatorSet(new List { new (adminPrivateKey.PublicKey, 1000) }), + new ValidatorSet( + new List { new (adminPrivateKey.PublicKey, 10_000_000_000_000_000_000) }), adminAddress, ImmutableHashSet.Create(adminAddress) ); @@ -229,7 +227,8 @@ public void MustNotIncludeBlockActionAtTransaction() maxTransactionsPerSignerPerBlockPolicy: null); IStagePolicy stagePolicy = new VolatileStagePolicy(); Block genesis = MakeGenesisBlock( - new ValidatorSet(new List { new (adminPrivateKey.PublicKey, 1000) }), + new ValidatorSet( + new List { new (adminPrivateKey.PublicKey, 10_000_000_000_000_000_000) }), adminAddress, ImmutableHashSet.Create(adminAddress), new AuthorizedMinersState( @@ -282,7 +281,8 @@ public void EarnMiningGoldWhenSuccessMining() maxTransactionsPerSignerPerBlockPolicy: null); IStagePolicy stagePolicy = new VolatileStagePolicy(); Block genesis = MakeGenesisBlock( - new ValidatorSet(new List { new (adminPrivateKey.PublicKey, 1000) }), + new ValidatorSet( + new List { new (adminPrivateKey.PublicKey, 10_000_000_000_000_000_000) }), adminAddress, ImmutableHashSet.Create(adminAddress), new AuthorizedMinersState( @@ -323,13 +323,14 @@ public void EarnMiningGoldWhenSuccessMining() // Since it's a block right after the Genesis, the reward is 0. blockChain.Append(block, commit); + var mintAmount = 10 * Currencies.Mead; var mint = new PrepareRewardAssets { RewardPoolAddress = adminAddress, Assets = new List - { - 10 * Currencies.Mead, - }, + { + mintAmount, + }, }; blockChain.MakeTransaction( @@ -346,12 +347,14 @@ public void EarnMiningGoldWhenSuccessMining() // Total 0.5 + 0.95 = 1.45 blockChain.Append(block, commit); + var rewardCurrency = ValidatorDelegatee.ValidatorRewardCurrency; var actualBalance = blockChain .GetNextWorldState() - .GetBalance(adminAddress, _currency); - var expectedBalance = new FungibleAssetValue(_currency, 1, 45); + .GetBalance(adminAddress, rewardCurrency); + var expectedBalance = mintAmount + new FungibleAssetValue(rewardCurrency, 1, 450000000000000000); Assert.Equal(expectedBalance, actualBalance); + // After claimed, mead have to be used? blockChain.MakeTransaction( adminPrivateKey, new ActionBase[] { new ClaimRewardValidator(adminAddress), } @@ -368,8 +371,8 @@ public void EarnMiningGoldWhenSuccessMining() actualBalance = blockChain .GetNextWorldState() - .GetBalance(adminAddress, _currency); - expectedBalance = new FungibleAssetValue(_currency, 20, 0); + .GetBalance(adminAddress, rewardCurrency); + expectedBalance = mintAmount + new FungibleAssetValue(rewardCurrency, 20, 0); Assert.Equal(expectedBalance, actualBalance); } @@ -389,7 +392,8 @@ public void ValidateNextBlockWithManyTransactions() IStagePolicy stagePolicy = new VolatileStagePolicy(); Block genesis = MakeGenesisBlock( - validators: new ValidatorSet(new List { new (adminPublicKey, 1000) }), + validators: new ValidatorSet( + new List { new (adminPublicKey, 10_000_000_000_000_000_000) }), adminPublicKey.Address, ImmutableHashSet
.Empty); @@ -521,7 +525,8 @@ public void ValidateNextBlockWithManyTransactionsPerSigner() .Default .Add(new SpannedSubPolicy(2, null, null, 5))); IStagePolicy stagePolicy = new VolatileStagePolicy(); - var validatorSet = new ValidatorSet(new List { new (adminPrivateKey.PublicKey, 1000) }); + var validatorSet = new ValidatorSet( + new List { new (adminPrivateKey.PublicKey, 10_000_000_000_000_000_000) }); Block genesis = MakeGenesisBlock( validatorSet, diff --git a/Lib9c/Action/Guild/MakeGuild.cs b/Lib9c/Action/Guild/MakeGuild.cs index b481aa97bc..cde0496359 100644 --- a/Lib9c/Action/Guild/MakeGuild.cs +++ b/Lib9c/Action/Guild/MakeGuild.cs @@ -58,11 +58,11 @@ public override IWorld Execute(IActionContext context) var validatorAddress = ValidatorAddress; // TODO: Remove this check when to deliver features to users. - if (context.Signer != GuildConfig.PlanetariumGuildOwner) - { - throw new InvalidOperationException( - $"This action is not allowed for {context.Signer}."); - } + // if (context.Signer != GuildConfig.PlanetariumGuildOwner) + // { + // throw new InvalidOperationException( + // $"This action is not allowed for {context.Signer}."); + // } repository.MakeGuild(guildAddress, validatorAddress); return repository.World; diff --git a/Lib9c/Action/InitializeStates.cs b/Lib9c/Action/InitializeStates.cs index b5b230490d..21bbd1b164 100644 --- a/Lib9c/Action/InitializeStates.cs +++ b/Lib9c/Action/InitializeStates.cs @@ -217,8 +217,11 @@ public override IWorld Execute(IActionContext context) validatorOperatorAddress, validatorOperatorAddress); repository.SetValidatorDelegatee(validatorDelegatee); - repository.TransferAsset( - GoldCurrencyState.Address, validatorDelegator.DelegationPoolAddress, delegationFAV); + repository.UpdateWorld( + repository.World.MintAsset( + repository.ActionContext, + validatorDelegator.DelegationPoolAddress, + delegationFAV)); validatorDelegator.Delegate(validatorDelegatee, delegationFAV, context.BlockIndex); states = repository.World; diff --git a/Lib9c/Action/RewardGold.cs b/Lib9c/Action/RewardGold.cs index 70e3a4b21f..97289da705 100644 --- a/Lib9c/Action/RewardGold.cs +++ b/Lib9c/Action/RewardGold.cs @@ -285,22 +285,25 @@ public IWorld MinerReward(IActionContext ctx, IWorld states) { // 마이닝 보상 // https://www.notion.so/planetarium/Mining-Reward-b7024ef463c24ebca40a2623027d497d - Currency currency = states.GetGoldCurrency(); + // Currency currency = states.GetGoldCurrency(); + Currency currency = Currencies.Mead; FungibleAssetValue defaultMiningReward = currency * 10; var countOfHalfLife = (int)Math.Pow(2, Convert.ToInt64((ctx.BlockIndex - 1) / 12614400)); FungibleAssetValue miningReward = defaultMiningReward.DivRem(countOfHalfLife, out FungibleAssetValue _); - var balance = states.GetBalance(GoldCurrencyState.Address, currency); - if (miningReward >= FungibleAssetValue.Parse(currency, "1.25") && balance >= miningReward) - { - states = states.TransferAsset( - ctx, - GoldCurrencyState.Address, - Addresses.RewardPool, - miningReward - ); - } + // var balance = states.GetBalance(GoldCurrencyState.Address, currency); + // if (miningReward >= FungibleAssetValue.Parse(currency, "1.25") && balance >= miningReward) + // { + // states = states.TransferAsset( + // ctx, + // GoldCurrencyState.Address, + // Addresses.RewardPool, + // miningReward + // ); + // } + + states = states.MintAsset(ctx, Addresses.RewardPool, miningReward); return states; } From dc243dfaad5030696f8f0569ea5b45f3a9a762a2 Mon Sep 17 00:00:00 2001 From: s2quake Date: Fri, 18 Oct 2024 15:42:09 +0900 Subject: [PATCH 103/206] fix: Fix an issue where the MakeGuild action does not execute properly --- .Lib9c.Tests/Action/Guild/MakeGuildTest.cs | 9 ++--- .../ValidatorDelegationTestBase.cs | 4 ++- ...GuildParticipantTest.Snapshot.received.txt | 34 ------------------- Lib9c/Action/Guild/MakeGuild.cs | 8 ++--- 4 files changed, 12 insertions(+), 43 deletions(-) delete mode 100644 .Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.received.txt diff --git a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs index 5f6fc6bb73..7003e5bd01 100644 --- a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs @@ -49,9 +49,10 @@ public void Execute() IWorld world = World; var validatorPrivateKey = new PrivateKey(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); + var guildAddress = AddressUtil.CreateGuildAddress(); world = EnsureToMintAsset(world, validatorPrivateKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorPrivateKey.PublicKey); - var action = new MakeGuild(validatorPrivateKey.Address); + var action = new MakeGuild(guildAddress, validatorPrivateKey.Address); world = action.Execute(new ActionContext { @@ -60,9 +61,9 @@ public void Execute() }); var repository = new GuildRepository(world, new ActionContext()); - var guildAddress = repository.GetJoinedGuild(guildMasterAddress); - Assert.NotNull(guildAddress); - Assert.True(repository.TryGetGuild(guildAddress.Value, out var guild)); + var guildParticipant = repository.GetGuildParticipant(guildMasterAddress); + var guild = repository.GetGuild(guildParticipant.GuildAddress); + Assert.Equal(guildAddress, guild.Address); Assert.Equal(guildMasterAddress, guild.GuildMasterAddress); } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index af712216b0..b9b41cb716 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System.Collections.Immutable; using System.Linq; using System.Numerics; +using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Mocks; @@ -186,7 +187,8 @@ protected static IWorld EnsureMakeGuild( BlockIndex = blockHeight, Signer = guildMasterAddress, }; - var makeGuild = new MakeGuild(validatorAddress); + var guildAddress = AddressUtil.CreateGuildAddress(); + var makeGuild = new MakeGuild(guildAddress, validatorAddress); return makeGuild.Execute(actionContext); } diff --git a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.received.txt b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.received.txt deleted file mode 100644 index 6f640eb219..0000000000 --- a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.received.txt +++ /dev/null @@ -1,34 +0,0 @@ -[ - { - EncodingLength: 21, - Kind: Text, - Value: guild_participant - }, - { - EncodingLength: 3, - Kind: Integer, - Value: 1 - }, - [ - 217, - 40, - 174, - 135, - 49, - 29, - 234, - 212, - 144, - 201, - 134, - 194, - 76, - 194, - 60, - 55, - 239, - 248, - 146, - 242 - ] -] \ No newline at end of file diff --git a/Lib9c/Action/Guild/MakeGuild.cs b/Lib9c/Action/Guild/MakeGuild.cs index cde0496359..279b4ef29a 100644 --- a/Lib9c/Action/Guild/MakeGuild.cs +++ b/Lib9c/Action/Guild/MakeGuild.cs @@ -3,7 +3,6 @@ using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; -using Nekoyume.Extensions; using Nekoyume.Model.Guild; using Nekoyume.Module.Guild; using Nekoyume.TypedAddress; @@ -19,16 +18,17 @@ public class MakeGuild : ActionBase public MakeGuild() { } - public MakeGuild(Address validatorAddress) + public MakeGuild(GuildAddress guildAddress, Address validatorAddress) { - + GuildAddress = guildAddress; + ValidatorAddress = validatorAddress; } public GuildAddress GuildAddress { get; private set; } public Address ValidatorAddress { get; private set; } - public bool IsNew{get; private set;} + public bool IsNew { get; private set; } public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) From c81c696daa01091de56a32f2f8197b5b82a02b32 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 18 Oct 2024 16:09:37 +0900 Subject: [PATCH 104/206] test: Use guild address for reward address --- .../ClaimRewardValidatorTest.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs index b3a87e2baa..a927dce7b0 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -8,6 +8,9 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.Model.Guild; +using Nekoyume.Module.Guild; +using Nekoyume.TypedAddress; using Nekoyume.ValidatorDelegation; using Xunit; @@ -244,8 +247,9 @@ var expectedProposerReward } // Then - var repository = new ValidatorRepository(world, actionContext); - var delegatee = repository.GetValidatorDelegatee(validatorKey.Address); + var validatorRepository = new ValidatorRepository(world, actionContext); + var guildRepository = new GuildRepository(world, actionContext); + var delegatee = validatorRepository.GetValidatorDelegatee(validatorKey.Address); var actualRemainReward = world.GetBalance(delegatee.RewardRemainderPoolAddress, RewardCurrency); var actualValidatorBalance = world.GetBalance(validatorKey.Address, DelegationCurrency); var actualValidatorReward = world.GetBalance(validatorKey.Address, RewardCurrency); @@ -253,7 +257,11 @@ var expectedProposerReward .Select(item => world.GetBalance(item.Address, DelegationCurrency)) .ToArray(); var actualDelegatorRewards = delegatorKeys - .Select(item => world.GetBalance(item.Address, RewardCurrency)) + .Select(item => world.GetBalance( + guildRepository.GetJoinedGuild( + new AgentAddress(item.Address)) + ?? throw new Exception($"Delegator {item.Address} does not joind to guild."), + RewardCurrency)) .ToArray(); Assert.Equal(expectedRemainReward, actualRemainReward); Assert.Equal(expectedValidatorBalance, actualValidatorBalance); From 71ea570f095f67ad339e0259918b1839edcf4ee2 Mon Sep 17 00:00:00 2001 From: s2quake Date: Fri, 18 Oct 2024 16:08:41 +0900 Subject: [PATCH 105/206] fix: Change address to pool address for minting --- .../ValidatorDelegation/DelegateValidatorTest.cs | 7 ++++--- .../ValidatorDelegation/PromoteValidatorTest.cs | 2 +- .../ReleaseValidatorUnbondingsTest.cs | 4 ++-- .../ValidatorDelegation/SlashValidatorTest.cs | 2 +- .../UndelegateValidatorTest.cs | 2 +- .../ValidatorDelegationTestBase.cs | 15 ++++++++++++--- Lib9c/ValidatorDelegation/ValidatorRepository.cs | 3 ++- 7 files changed, 23 insertions(+), 12 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index bbacce54b3..e91f85e535 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -8,6 +8,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using Libplanet.Types.Assets; using Nekoyume.Action; using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.Model.Stake; using Nekoyume.ValidatorDelegation; using Xunit; @@ -74,7 +75,7 @@ public void Execute() Assert.Equal(validatorGold.RawValue + delegatorGold.RawValue, bond.Share); Assert.Equal(validatorGold.RawValue + delegatorGold.RawValue, validator.Validator.Power); Assert.Equal(validator.Validator, Assert.Single(validatorList.Validators)); - Assert.Equal(DelegationCurrency * 80, world.GetBalance(validatorKey.Address, DelegationCurrency)); + Assert.Equal(DelegationCurrency * 80, GetBalance(world, validatorKey.Address)); } [Fact] @@ -240,7 +241,7 @@ private void ExecuteWithFixture(IDelegateValidatorFixture fixture) // Then var actualRepository = new ValidatorRepository(world, actionContext); var actualValidator = actualRepository.GetValidatorDelegatee(validatorKey.Address); - var actualValidatorBalance = world.GetBalance(validatorKey.Address, DelegationCurrency); + var actualValidatorBalance = GetBalance(world, validatorKey.Address); var actualPower = actualValidator.Power; var actualBond = actualRepository.GetBond(actualValidator, validatorKey.Address); @@ -258,7 +259,7 @@ public ValidatorInfo() public ValidatorInfo(Random random) { Balance = GetRandomFAV(DelegationCurrency, random); - Cash = GetRandomCash(random, Balance); + Cash = GetRandomCash(random, Balance, 99); CashToDelegate = GetRandomCash(random, Balance - Cash); } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs index 3d008cb8be..23aa0683cd 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs @@ -53,7 +53,7 @@ public void Execute() Assert.Equal(gold.RawValue, bond.Share); Assert.Equal(validator.Validator, Assert.Single(validatorList.Validators)); Assert.Equal(validator.Validator, Assert.Single(validatorList.GetBonded())); - Assert.Equal(DelegationCurrency * 90, world.GetBalance(validatorKey.Address, DelegationCurrency)); + Assert.Equal(DelegationCurrency * 90, GetBalance(world, validatorKey.Address)); Assert.Empty(validatorList.GetUnbonded()); } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs index d95ad3e77d..5f8ae913fd 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs @@ -42,7 +42,7 @@ public void Execute() var expectedUnbondingSet = expectedRepository.GetUnbondingSet(); var expectedReleaseCount = expectedUnbondingSet.UnbondingRefs.Count; var expectedDepositGold = expectedDelegatee.FAVFromShare(share); - var expectedBalance = world.GetBalance(validatorKey.Address, DelegationCurrency) + expectedDepositGold; + var expectedBalance = GetBalance(world, validatorKey.Address) + expectedDepositGold; var releaseValidatorUnbondings = new ReleaseValidatorUnbondings(validatorKey.Address); actionContext = new ActionContext @@ -55,7 +55,7 @@ public void Execute() // Then var actualRepository = new ValidatorRepository(world, actionContext); - var actualBalance = world.GetBalance(validatorKey.Address, DelegationCurrency); + var actualBalance = GetBalance(world, validatorKey.Address); var actualUnbondingSet = actualRepository.GetUnbondingSet(); var actualReleaseCount = actualUnbondingSet.UnbondingRefs.Count; diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs index 454eb36b5e..1671942d4c 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs @@ -92,7 +92,7 @@ public void Execute() world = slashValidator.Execute(actionContext); // Then - var balance = world.GetBalance(validatorKey.Address, DelegationCurrency); + var balance = GetBalance(world, validatorKey.Address); var actualRepository = new ValidatorRepository(world, actionContext); var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorKey.Address); var actualValidatorShare = actualRepository.GetBond(actualDelegatee, validatorKey.Address).Share; diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 745901ccf1..1ec9f52680 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -303,7 +303,7 @@ private void ExecuteWithFixture(IUndelegateValidatorFixture fixture) // Then var actualRepository = new ValidatorRepository(world, actionContext); var actualValidator = actualRepository.GetValidatorDelegatee(validatorKey.Address); - var actualValidatorBalance = world.GetBalance(validatorKey.Address, DelegationCurrency); + var actualValidatorBalance = GetBalance(world, validatorKey.Address); var actualValiatorPower = actualValidator.Power; Assert.Equal(expectedValidatorBalance, actualValidatorBalance); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index b9b41cb716..661ec08f2d 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -18,6 +18,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using Nekoyume.Action.Guild; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.Guild; +using Nekoyume.Model.Stake; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.Module.Guild; @@ -62,7 +63,8 @@ protected static IWorld EnsureToMintAsset( BlockIndex = blockHeight, }; var address = privateKey.Address; - return world.MintAsset(actionContext, address, amount); + var poolAddress = StakeState.DeriveAddress(address); + return world.MintAsset(actionContext, poolAddress, amount); } protected static IWorld EnsureToMintAssets( @@ -598,9 +600,10 @@ protected static FungibleAssetValue GetRandomFAV(Currency currency, Random rando return FungibleAssetValue.Parse(currency, text); } - protected static FungibleAssetValue GetRandomCash(Random random, FungibleAssetValue fav) + protected static FungibleAssetValue GetRandomCash(Random random, FungibleAssetValue fav, int maxDivisor = 100) { - var denominator = random.Next(100) + 1; + Assert.True(maxDivisor > 0 && maxDivisor <= 100); + var denominator = random.Next(maxDivisor) + 1; var cash = fav.DivRem(denominator, out var remainder); if (cash.Sign < 0 || cash > fav) { @@ -609,4 +612,10 @@ protected static FungibleAssetValue GetRandomCash(Random random, FungibleAssetVa return cash; } + + protected static FungibleAssetValue GetBalance(IWorld world, Address address) + { + var poolAddress = StakeState.DeriveAddress(address); + return world.GetBalance(poolAddress, DelegationCurrency); + } } diff --git a/Lib9c/ValidatorDelegation/ValidatorRepository.cs b/Lib9c/ValidatorDelegation/ValidatorRepository.cs index 164b3e5525..028aee0f8e 100644 --- a/Lib9c/ValidatorDelegation/ValidatorRepository.cs +++ b/Lib9c/ValidatorDelegation/ValidatorRepository.cs @@ -5,6 +5,7 @@ using Libplanet.Crypto; using Nekoyume.Action; using Nekoyume.Delegation; +using Nekoyume.Model.Stake; using System.Numerics; namespace Nekoyume.ValidatorDelegation @@ -57,7 +58,7 @@ public ValidatorDelegator GetValidatorDelegator(Address address, Address rewardA // TODO: delegationPoolAddress have to be changed after guild system is implemented. return new ValidatorDelegator( address, - address, + StakeState.DeriveAddress(address), rewardAddress, this); } From 42c5784d848314435177d3280ac06d0e283530e5 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 18 Oct 2024 19:32:24 +0900 Subject: [PATCH 106/206] test: Fix to indicate staking address on test --- .../Action/ValidatorDelegation/ClaimRewardValidatorTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs index a927dce7b0..10bf411d8b 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -9,6 +9,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using Libplanet.Types.Assets; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.Guild; +using Nekoyume.Model.Stake; using Nekoyume.Module.Guild; using Nekoyume.TypedAddress; using Nekoyume.ValidatorDelegation; @@ -251,7 +252,7 @@ var expectedProposerReward var guildRepository = new GuildRepository(world, actionContext); var delegatee = validatorRepository.GetValidatorDelegatee(validatorKey.Address); var actualRemainReward = world.GetBalance(delegatee.RewardRemainderPoolAddress, RewardCurrency); - var actualValidatorBalance = world.GetBalance(validatorKey.Address, DelegationCurrency); + var actualValidatorBalance = world.GetBalance(StakeState.DeriveAddress(validatorKey.Address), DelegationCurrency); var actualValidatorReward = world.GetBalance(validatorKey.Address, RewardCurrency); var actualDelegatorBalances = delegatorKeys .Select(item => world.GetBalance(item.Address, DelegationCurrency)) From 9699596f733cc5e9bfc7dc31f8462d403f07e3f9 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 18 Oct 2024 19:44:20 +0900 Subject: [PATCH 107/206] test: Fix remove guild test --- .Lib9c.Tests/Action/Guild/RemoveGuildTest.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs index 8ab47ee731..aa68fe383d 100644 --- a/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/RemoveGuildTest.cs @@ -10,6 +10,7 @@ namespace Lib9c.Tests.Action.Guild using Nekoyume.Action; using Nekoyume.Action.Guild; using Nekoyume.Model.Guild; + using Nekoyume.Model.Stake; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.Module.Guild; @@ -81,13 +82,15 @@ public void Execute_WhenDelegationExists_Throw() { var validatorKey = new PrivateKey(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); + var guildParticipantAddress = AddressUtil.CreateAgentAddress(); var guildAddress = AddressUtil.CreateGuildAddress(); IWorld world = World; world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorKey.PublicKey); - world = EnsureToMintAsset(world, guildMasterAddress, GG * 100); + world = EnsureToMintAsset(world, StakeState.DeriveAddress(guildMasterAddress), GG * 100); world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); + world = EnsureToJoinGuild(world, guildAddress, guildParticipantAddress); var actionContext = new ActionContext { From b88e1a0860df6c578036e7ccbf5b741466611872 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 18 Oct 2024 19:44:52 +0900 Subject: [PATCH 108/206] test: Comment out RewardGoldTest, since action is on changing --- .Lib9c.Tests/Action/RewardGoldTest.cs | 84 +++++++++++++-------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/.Lib9c.Tests/Action/RewardGoldTest.cs b/.Lib9c.Tests/Action/RewardGoldTest.cs index 4fdce90e2e..3bf008441e 100644 --- a/.Lib9c.Tests/Action/RewardGoldTest.cs +++ b/.Lib9c.Tests/Action/RewardGoldTest.cs @@ -449,48 +449,48 @@ public void GoldDistributedEachAccount() Assert.Equal(currency * 0, delta.GetBalance(address2, currency)); } - [Fact] - public void MiningReward() - { - Address miner = new Address("F9A15F870701268Bd7bBeA6502eB15F4997f32f9"); - Currency currency = _baseState.GetGoldCurrency(); - var ctx = new ActionContext() - { - BlockIndex = 0, - PreviousState = _baseState, - Miner = miner, - }; - - var action = new RewardGold(); - - void AssertMinerReward(int blockIndex, string expected) - { - ctx.BlockIndex = blockIndex; - IWorld delta = action.MinerReward(ctx, _baseState); - Assert.Equal(FungibleAssetValue.Parse(currency, expected), delta.GetBalance(Addresses.RewardPool, currency)); - } - - // Before halving (10 / 2^0 = 10) - AssertMinerReward(0, "10"); - AssertMinerReward(1, "10"); - AssertMinerReward(12614400, "10"); - - // First halving (10 / 2^1 = 5) - AssertMinerReward(12614401, "5"); - AssertMinerReward(25228800, "5"); - - // Second halving (10 / 2^2 = 2.5) - AssertMinerReward(25228801, "2.5"); - AssertMinerReward(37843200, "2.5"); - - // Third halving (10 / 2^3 = 1.25) - AssertMinerReward(37843201, "1.25"); - AssertMinerReward(50457600, "1.25"); - - // Rewardless era - AssertMinerReward(50457601, "0"); - } - + // Temporarily commented, since mining reward action is changing. + //[Fact] + //public void MiningReward() + //{ + // Address miner = new Address("F9A15F870701268Bd7bBeA6502eB15F4997f32f9"); + // Currency currency = _baseState.GetGoldCurrency(); + // var ctx = new ActionContext() + // { + // BlockIndex = 0, + // PreviousState = _baseState, + // Miner = miner, + // }; + + // var action = new RewardGold(); + + // void AssertMinerReward(int blockIndex, string expected) + // { + // ctx.BlockIndex = blockIndex; + // IWorld delta = action.MinerReward(ctx, _baseState); + // Assert.Equal(FungibleAssetValue.Parse(currency, expected), delta.GetBalance(Addresses.RewardPool, currency)); + // } + + // // Before halving (10 / 2^0 = 10) + // AssertMinerReward(0, "10"); + // AssertMinerReward(1, "10"); + // AssertMinerReward(12614400, "10"); + + // // First halving (10 / 2^1 = 5) + // AssertMinerReward(12614401, "5"); + // AssertMinerReward(25228800, "5"); + + // // Second halving (10 / 2^2 = 2.5) + // AssertMinerReward(25228801, "2.5"); + // AssertMinerReward(37843200, "2.5"); + + // // Third halving (10 / 2^3 = 1.25) + // AssertMinerReward(37843201, "1.25"); + // AssertMinerReward(50457600, "1.25"); + + // // Rewardless era + // AssertMinerReward(50457601, "0"); + //} [Theory] [InlineData(5, 4)] [InlineData(101, 100)] From bbd8ad5bd5b89f3b0bebb87f57ccbff108d0dcf3 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 18 Oct 2024 22:26:57 +0900 Subject: [PATCH 109/206] fix: Fix action typescript --- @planetarium/lib9c/src/actions/make_guild.ts | 21 +++++++++++++++++-- @planetarium/lib9c/tests/actions/fixtures.ts | 4 ++++ .../lib9c/tests/actions/make_guild.test.ts | 7 ++++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/@planetarium/lib9c/src/actions/make_guild.ts b/@planetarium/lib9c/src/actions/make_guild.ts index ca0ffd097e..e963fcb825 100644 --- a/@planetarium/lib9c/src/actions/make_guild.ts +++ b/@planetarium/lib9c/src/actions/make_guild.ts @@ -1,10 +1,27 @@ -import type { Value } from "@planetarium/bencodex"; +import type { Address } from "@planetarium/account"; +import { BencodexDictionary, type Value } from "@planetarium/bencodex"; import { PolymorphicAction } from "./common.js"; +export type MakeGuildArgs = { + validatorAddress: Address; +}; + export class MakeGuild extends PolymorphicAction { protected readonly type_id: string = "make_guild"; + private readonly validatorAddress: Address; + + constructor({ validatorAddress }: MakeGuildArgs) { + super(); + + this.validatorAddress = validatorAddress; + } + protected plain_value(): Value { - return null; + const validatorAddressKey = "va" as const; + + return new BencodexDictionary([ + [validatorAddressKey, this.validatorAddress.toBytes()], + ]); } } diff --git a/@planetarium/lib9c/tests/actions/fixtures.ts b/@planetarium/lib9c/tests/actions/fixtures.ts index 689399033d..d3622b7474 100644 --- a/@planetarium/lib9c/tests/actions/fixtures.ts +++ b/@planetarium/lib9c/tests/actions/fixtures.ts @@ -17,3 +17,7 @@ export const fungibleIdA = HashDigest.fromHex( export const patronAddress = Address.fromHex( "0xc64c7cBf29BF062acC26024D5b9D1648E8f8D2e1", ); + +export const validatorAddress = Address.fromHex( + "0xc2Ef377d9F526E4b480518863b94d79FCEABB2e1", +); diff --git a/@planetarium/lib9c/tests/actions/make_guild.test.ts b/@planetarium/lib9c/tests/actions/make_guild.test.ts index 90eaf2830e..ba071be146 100644 --- a/@planetarium/lib9c/tests/actions/make_guild.test.ts +++ b/@planetarium/lib9c/tests/actions/make_guild.test.ts @@ -1,7 +1,12 @@ import { describe } from "vitest"; import { MakeGuild } from "../../src/index.js"; import { runTests } from "./common.js"; +import { validatorAddress } from "./fixtures.js"; describe("MakeGuild", () => { - runTests("valid case", [new MakeGuild()]); + runTests("valid case", [ + new MakeGuild({ + validatorAddress: validatorAddress, + }), + ]); }); From bc4c5de231b5e51144b5a3070bbe36c2a21f0c3e Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 18 Oct 2024 22:46:10 +0900 Subject: [PATCH 110/206] fix: Fix Guild to be created without address --- .Lib9c.Tests/Action/Guild/MakeGuildTest.cs | 9 ++++----- .../ValidatorDelegation/ClaimRewardValidatorTest.cs | 4 +++- .../Action/ValidatorDelegation/SlashValidatorTest.cs | 4 +++- .../ValidatorDelegationTestBase.cs | 7 ++++--- Lib9c/Action/Guild/MakeGuild.cs | 12 +++++------- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs index 7003e5bd01..e770874209 100644 --- a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs @@ -49,10 +49,9 @@ public void Execute() IWorld world = World; var validatorPrivateKey = new PrivateKey(); var guildMasterAddress = AddressUtil.CreateAgentAddress(); - var guildAddress = AddressUtil.CreateGuildAddress(); world = EnsureToMintAsset(world, validatorPrivateKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorPrivateKey.PublicKey); - var action = new MakeGuild(guildAddress, validatorPrivateKey.Address); + var action = new MakeGuild(validatorPrivateKey.Address); world = action.Execute(new ActionContext { @@ -61,9 +60,9 @@ public void Execute() }); var repository = new GuildRepository(world, new ActionContext()); - var guildParticipant = repository.GetGuildParticipant(guildMasterAddress); - var guild = repository.GetGuild(guildParticipant.GuildAddress); - Assert.Equal(guildAddress, guild.Address); + var guildAddress = repository.GetJoinedGuild(guildMasterAddress); + Assert.NotNull(guildAddress); + var guild = repository.GetGuild(guildAddress.Value); Assert.Equal(guildMasterAddress, guild.GuildMasterAddress); } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs index 10bf411d8b..e68b0fa597 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -183,10 +183,12 @@ private void ExecuteWithFixture(IClaimRewardFixture fixture) var validatorBalance = fixture.ValidatorBalance; var validatorCash = fixture.ValidatorCash; var totalReward = fixture.TotalReward; + int seed = 0; world = EnsureToMintAsset(world, validatorKey, validatorBalance, height++); world = EnsurePromotedValidator(world, validatorKey, validatorCash, height++); world = EnsureToMintAssets(world, delegatorKeys, delegatorBalances, height++); - world = delegatorKeys.Aggregate(world, (w, d) => EnsureMakeGuild(w, d.Address, validatorKey.Address, height++)); + world = delegatorKeys.Aggregate(world, (w, d) => EnsureMakeGuild( + w, d.Address, validatorKey.Address, height++, seed++)); world = EnsureRewardAllocatedValidator(world, validatorKey, totalReward, ref height); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs index 1671942d4c..3173039718 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs @@ -40,10 +40,12 @@ public void Execute() var delegatorGolds = CreateArray(length, i => DelegationCurrency * Random.Shared.Next(10, 100)); var height = 1L; var actionContext = new ActionContext { }; + int seed = 0; world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 10, height++); world = EnsureToMintAssets(world, delegatorKeys, delegatorGolds, height++); - world = delegatorKeys.Aggregate(world, (w, d) => EnsureMakeGuild(w, d.Address, validatorKey.Address, height++)); + world = delegatorKeys.Aggregate(world, (w, d) => EnsureMakeGuild( + w, d.Address, validatorKey.Address, height++, seed++)); // When var expectedRepository = new ValidatorRepository(world, actionContext); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 661ec08f2d..e7a0fc99cc 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -176,7 +176,8 @@ protected static IWorld EnsureMakeGuild( IWorld world, Address guildMasterAddress, Address validatorAddress, - long blockHeight) + long blockHeight, + int seed) { if (blockHeight < 0) { @@ -188,9 +189,9 @@ protected static IWorld EnsureMakeGuild( PreviousState = world, BlockIndex = blockHeight, Signer = guildMasterAddress, + RandomSeed = seed, }; - var guildAddress = AddressUtil.CreateGuildAddress(); - var makeGuild = new MakeGuild(guildAddress, validatorAddress); + var makeGuild = new MakeGuild(validatorAddress); return makeGuild.Execute(actionContext); } diff --git a/Lib9c/Action/Guild/MakeGuild.cs b/Lib9c/Action/Guild/MakeGuild.cs index 279b4ef29a..8ab812df53 100644 --- a/Lib9c/Action/Guild/MakeGuild.cs +++ b/Lib9c/Action/Guild/MakeGuild.cs @@ -3,6 +3,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; +using Nekoyume.Extensions; using Nekoyume.Model.Guild; using Nekoyume.Module.Guild; using Nekoyume.TypedAddress; @@ -18,18 +19,13 @@ public class MakeGuild : ActionBase public MakeGuild() { } - public MakeGuild(GuildAddress guildAddress, Address validatorAddress) + public MakeGuild(Address validatorAddress) { - GuildAddress = guildAddress; ValidatorAddress = validatorAddress; } - public GuildAddress GuildAddress { get; private set; } - public Address ValidatorAddress { get; private set; } - public bool IsNew { get; private set; } - public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) .Add("values", Dictionary.Empty @@ -53,8 +49,10 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; + var random = context.GetRandom(); + var repository = new GuildRepository(world, context); - var guildAddress = GuildAddress; + var guildAddress = new GuildAddress(random.GenerateAddress()); var validatorAddress = ValidatorAddress; // TODO: Remove this check when to deliver features to users. From 699e8f10c58c1707f31e5df4279309c1988781f4 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 24 Oct 2024 01:02:40 +0900 Subject: [PATCH 111/206] 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 af1f00ea98..0e456d036d 100644 --- a/.Lib9c.Tests/Policy/BlockPolicyTest.cs +++ b/.Lib9c.Tests/Policy/BlockPolicyTest.cs @@ -357,7 +357,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 709374f5f90ef458ac408b8989517151727be96b Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 24 Oct 2024 12:26:46 +0900 Subject: [PATCH 112/206] 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 1085ddfd7d272e8cd36ec4d40170390d6dc4789c Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 25 Oct 2024 10:52:45 +0900 Subject: [PATCH 113/206] 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 f25fdb2f0f..ca836c6562 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("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 4264ffee31e384942b13933b8c6f5b8b1f6a682a Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 25 Oct 2024 10:53:04 +0900 Subject: [PATCH 114/206] 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 3e2a00174e8100d6c3e1ee4b634fde7a942f32a5 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 25 Oct 2024 11:37:04 +0900 Subject: [PATCH 115/206] 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); + } } } From a55d2fc9d9ceccd7e4f77390f23233bcd4e5f167 Mon Sep 17 00:00:00 2001 From: s2quake Date: Fri, 25 Oct 2024 15:52:41 +0900 Subject: [PATCH 116/206] refactor: Refactor RewardGold for mining --- Lib9c/Action/RewardGold.cs | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/Lib9c/Action/RewardGold.cs b/Lib9c/Action/RewardGold.cs index 97289da705..ad86f8d9b3 100644 --- a/Lib9c/Action/RewardGold.cs +++ b/Lib9c/Action/RewardGold.cs @@ -37,7 +37,6 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - GasTracer.UseGas(1); var states = context.PreviousState; states = TransferMead(context, states); states = GenesisGoldDistribution(context, states); @@ -283,27 +282,19 @@ public IWorld ResetChallengeCount(IActionContext ctx, IWorld states) public IWorld MinerReward(IActionContext ctx, IWorld states) { - // 마이닝 보상 - // https://www.notion.so/planetarium/Mining-Reward-b7024ef463c24ebca40a2623027d497d - // Currency currency = states.GetGoldCurrency(); Currency currency = Currencies.Mead; - FungibleAssetValue defaultMiningReward = currency * 10; - var countOfHalfLife = (int)Math.Pow(2, Convert.ToInt64((ctx.BlockIndex - 1) / 12614400)); - FungibleAssetValue miningReward = - defaultMiningReward.DivRem(countOfHalfLife, out FungibleAssetValue _); - - // var balance = states.GetBalance(GoldCurrencyState.Address, currency); - // if (miningReward >= FungibleAssetValue.Parse(currency, "1.25") && balance >= miningReward) - // { - // states = states.TransferAsset( - // ctx, - // GoldCurrencyState.Address, - // Addresses.RewardPool, - // miningReward - // ); - // } - - states = states.MintAsset(ctx, Addresses.RewardPool, miningReward); + var usedGas = states.GetBalance(Addresses.GasPool, currency); + var defaultReward = currency * 5; + var halfOfUsedGas = usedGas.DivRem(2).Quotient; + var gasToBurn = usedGas - halfOfUsedGas; + var miningReward = halfOfUsedGas + defaultReward; + states = states.MintAsset(ctx, Addresses.GasPool, defaultReward); + if (gasToBurn.Sign > 0) + { + states = states.BurnAsset(ctx, Addresses.GasPool, gasToBurn); + } + states = states.TransferAsset( + ctx, Addresses.GasPool, Addresses.RewardPool, miningReward); return states; } From 8b6f9d7971c5e8cbdace502abf9e5334591dfe10 Mon Sep 17 00:00:00 2001 From: s2quake Date: Mon, 28 Oct 2024 13:49:04 +0900 Subject: [PATCH 117/206] test: Test code for RewardGold --- .Lib9c.Tests/Policy/BlockPolicyTest.cs | 39 ++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/.Lib9c.Tests/Policy/BlockPolicyTest.cs b/.Lib9c.Tests/Policy/BlockPolicyTest.cs index 0e456d036d..3dd2acc2f8 100644 --- a/.Lib9c.Tests/Policy/BlockPolicyTest.cs +++ b/.Lib9c.Tests/Policy/BlockPolicyTest.cs @@ -262,7 +262,7 @@ public void MustNotIncludeBlockActionAtTransaction() } [Fact] - public void EarnMiningGoldWhenSuccessMining() + public void EarnMiningMeadWhenSuccessMining() { var adminPrivateKey = new PrivateKey(); var adminAddress = adminPrivateKey.Address; @@ -343,21 +343,27 @@ public void EarnMiningGoldWhenSuccessMining() block, blockChain.GetNextWorldState().GetValidatorSet(), new PrivateKey[] { adminPrivateKey }); - // First Reward : Proposer base reward 10 * 0.01, proposer bonus reward 10 * 0.04, Commission 9.5 * 0.1 - // Total 0.5 + 0.95 = 1.45 + // First Reward : Proposer base reward 5 * 0.01, proposer bonus reward 5 * 0.04, Commission 4.75 * 0.1 + // Total 10 + 0.05 + 0.2 + 0.475 = 10.725 blockChain.Append(block, commit); var rewardCurrency = ValidatorDelegatee.ValidatorRewardCurrency; var actualBalance = blockChain .GetNextWorldState() .GetBalance(adminAddress, rewardCurrency); - var expectedBalance = mintAmount + new FungibleAssetValue(rewardCurrency, 1, 450000000000000000); + var expectedBalance = mintAmount + new FungibleAssetValue(rewardCurrency, 0, 725000000000000000); Assert.Equal(expectedBalance, actualBalance); + var ssss = blockChain + .GetNextWorldState() + .GetBalance(Addresses.RewardPool, rewardCurrency); + // After claimed, mead have to be used? blockChain.MakeTransaction( adminPrivateKey, - new ActionBase[] { new ClaimRewardValidatorSelf(), } + new ActionBase[] { new ClaimRewardValidatorSelf(), }, + gasLimit: 1, + maxGasPrice: Currencies.Mead * 1 ); block = blockChain.ProposeBlock(adminPrivateKey, commit); @@ -367,12 +373,33 @@ public void EarnMiningGoldWhenSuccessMining() blockChain.GetNextWorldState().GetValidatorSet(), new PrivateKey[] { adminPrivateKey }); // First + Second Reward : Total reward of two blocks : 10 * 2 = 20 + // Base reward: 0.05 + 0.2 + 0.475 = 0.725 + // Total reward: 4.275 + 4.275 (two blocks) + // Used gas: 1 + // Total 10.725 + 0.725 + 4.275 + 4.275 - 1 = 19 blockChain.Append(block, commit); actualBalance = blockChain .GetNextWorldState() .GetBalance(adminAddress, rewardCurrency); - expectedBalance = mintAmount + new FungibleAssetValue(rewardCurrency, 20, 0); + expectedBalance = rewardCurrency * 19; + Assert.Equal(expectedBalance, actualBalance); + + block = blockChain.ProposeBlock(adminPrivateKey, commit); + power = blockChain.GetNextWorldState().GetValidatorSet().GetValidator(adminPrivateKey.PublicKey).Power; + commit = GenerateBlockCommit( + block, + blockChain.GetNextWorldState().GetValidatorSet(), + new PrivateKey[] { adminPrivateKey }); + // Mining reward: 5 + 1 / 2 = 5.5 + // Proposer base reward 5.5 * 0.01, proposer bonus reward 5.5 * 0.04, Commission (5.5 - 0.275) * 0.1 + // Base reward: (5.5 * 0.01 + 5.5 * 0.04) + (5.5 - 0.275) * 0.1 = 0.7975 + // Total 19 + 0.7975 = 19.7975 + blockChain.Append(block, commit); + actualBalance = blockChain + .GetNextWorldState() + .GetBalance(adminAddress, rewardCurrency); + expectedBalance = new FungibleAssetValue(rewardCurrency, 19, 797500000000000000); Assert.Equal(expectedBalance, actualBalance); } From 80138b882d1e2276ef4d1477a4564bedaebdbb4e Mon Sep 17 00:00:00 2001 From: s2quake Date: Fri, 25 Oct 2024 21:36:45 +0900 Subject: [PATCH 118/206] fix: Remove UseGas code from tx actions --- .../Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs | 2 -- Lib9c/Action/ValidatorDelegation/SlashValidator.cs | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs index 1ac8e7f721..9ac68cd66c 100644 --- a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -26,8 +26,6 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - GasTracer.UseGas(0L); - var world = context.PreviousState; var repository = new ValidatorRepository(world, context); var unbondingSet = repository.GetUnbondingSet(); diff --git a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs index 74ab18bd9c..9ec0001745 100644 --- a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs @@ -31,8 +31,6 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - GasTracer.UseGas(0L); - var world = context.PreviousState; var repository = new ValidatorRepository(world, context); @@ -68,7 +66,7 @@ public override IWorld Execute(IActionContext context) break; } } - + return repository.World; } } From 3258935f6180eb9bd39f8c0ca53bd0d5b8426790 Mon Sep 17 00:00:00 2001 From: s2quake Date: Fri, 25 Oct 2024 21:37:22 +0900 Subject: [PATCH 119/206] feat: Add tx actions for collecting used-gas --- Lib9c.Policy/Policy/BlockPolicySource.cs | 10 +++- Lib9c/Action/ValidatorDelegation/Mortgage.cs | 62 ++++++++++++++++++++ Lib9c/Action/ValidatorDelegation/Refund.cs | 50 ++++++++++++++++ Lib9c/Action/ValidatorDelegation/Reward.cs | 56 ++++++++++++++++++ Lib9c/Addresses.cs | 3 + 5 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 Lib9c/Action/ValidatorDelegation/Mortgage.cs create mode 100644 Lib9c/Action/ValidatorDelegation/Refund.cs create mode 100644 Lib9c/Action/ValidatorDelegation/Reward.cs diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index e4fa660d62..b7dd28b9bd 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -150,8 +150,12 @@ internal IBlockPolicy GetPolicy( new RewardGold(), new ReleaseValidatorUnbondings(), }.ToImmutableArray(), - beginTxActions: ImmutableArray.Empty, - endTxActions: ImmutableArray.Empty), + beginTxActions: new IAction[] { + new Mortgage(), + }.ToImmutableArray(), + endTxActions: new IAction[] { + new Reward(), new Refund(), + }.ToImmutableArray()), blockInterval: BlockInterval, validateNextBlockTx: validateNextBlockTx, validateNextBlock: validateNextBlock, @@ -169,7 +173,7 @@ internal IBlockPolicy GetPolicy( Transaction transaction) { // Avoid NRE when genesis block appended - long index = blockChain.Count > 0 ? blockChain.Tip.Index + 1: 0; + long index = blockChain.Count > 0 ? blockChain.Tip.Index + 1 : 0; if (transaction.Actions?.Count > 1) { diff --git a/Lib9c/Action/ValidatorDelegation/Mortgage.cs b/Lib9c/Action/ValidatorDelegation/Mortgage.cs new file mode 100644 index 0000000000..bd65d6e7d7 --- /dev/null +++ b/Lib9c/Action/ValidatorDelegation/Mortgage.cs @@ -0,0 +1,62 @@ +using Bencodex.Types; +using Lib9c; +using Libplanet.Action; +using Libplanet.Action.State; +using Libplanet.Types.Assets; + +namespace Nekoyume.Action.ValidatorDelegation +{ + /// + /// An action for mortgage gas fee for a transaction. + /// Should be executed at the beginning of the tx. + /// + public sealed class Mortgage : ActionBase + { + /// + /// Creates a new instance of . + /// + public Mortgage() + { + } + + /// + public override IValue PlainValue => new Bencodex.Types.Boolean(true); + + /// + public override void LoadPlainValue(IValue plainValue) + { + // Method intentionally left empty. + } + + /// + public override IWorld Execute(IActionContext context) + { + var state = context.PreviousState; + if (context.MaxGasPrice is not { Sign: > 0 } realGasPrice) + { + return state; + } + + var gasOwned = state.GetBalance(context.Signer, realGasPrice.Currency); + var gasRequired = realGasPrice * GasTracer.GasAvailable; + var gasToMortgage = gasOwned < gasRequired ? gasOwned : gasRequired; + if (gasOwned < gasRequired) + { + // var msg = + // $"The account {context.Signer}'s balance of {realGasPrice.Currency} is " + + // "insufficient to pay gas fee: " + + // $"{gasOwned} < {realGasPrice * gasLimit}."; + GasTracer.CancelTrace(); + // throw new InsufficientBalanceException(msg, context.Signer, gasOwned); + } + + if (gasToMortgage.Sign > 0) + { + return state.TransferAsset( + context, context.Signer, Addresses.MortgagePool, gasToMortgage); + } + + return state; + } + } +} diff --git a/Lib9c/Action/ValidatorDelegation/Refund.cs b/Lib9c/Action/ValidatorDelegation/Refund.cs new file mode 100644 index 0000000000..6ee1b8731a --- /dev/null +++ b/Lib9c/Action/ValidatorDelegation/Refund.cs @@ -0,0 +1,50 @@ +using Bencodex.Types; +using Lib9c; +using Libplanet.Action; +using Libplanet.Action.State; + +namespace Nekoyume.Action.ValidatorDelegation +{ + /// + /// An action for refund gas fee for a transaction. + /// Should be executed at the beginning of the tx. + /// + public sealed class Refund : ActionBase + { + /// + /// Creates a new instance of . + /// + public Refund() + { + } + + /// + public override IValue PlainValue => new Bencodex.Types.Boolean(true); + + /// + public override void LoadPlainValue(IValue plainValue) + { + // Method intentionally left empty. + } + + /// + public override IWorld Execute(IActionContext context) + { + var world = context.PreviousState; + if (context.MaxGasPrice is not { Sign: > 0 } realGasPrice) + { + return world; + } + + // Need to check if this matches GasTracer.GasAvailable? + var remaining = world.GetBalance(Addresses.MortgagePool, realGasPrice.Currency); + if (remaining.Sign <= 0) + { + return world; + } + + return world.TransferAsset( + context, Addresses.MortgagePool, context.Signer, remaining); + } + } +} diff --git a/Lib9c/Action/ValidatorDelegation/Reward.cs b/Lib9c/Action/ValidatorDelegation/Reward.cs new file mode 100644 index 0000000000..4d45e425a6 --- /dev/null +++ b/Lib9c/Action/ValidatorDelegation/Reward.cs @@ -0,0 +1,56 @@ +using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.State; + +namespace Nekoyume.Action.ValidatorDelegation +{ + /// + /// An action for reward for a transaction. + /// Should be executed at the beginning of the tx. + /// + public sealed class Reward : ActionBase + { + /// + /// Creates a new instance of . + /// + public Reward() + { + } + + /// + public override IValue PlainValue => new Bencodex.Types.Boolean(true); + + /// + public override void LoadPlainValue(IValue plainValue) + { + // Method intentionally left empty. + } + + /// + public override IWorld Execute(IActionContext context) + { + var world = context.PreviousState; + if (context.MaxGasPrice is not { Sign: > 0 } realGasPrice) + { + return world; + } + + if (GasTracer.GasUsed <= 0) + { + return world; + } + + var gasMortgaged = world.GetBalance(Addresses.MortgagePool, realGasPrice.Currency); + var gasUsedPrice = realGasPrice * GasTracer.GasUsed; + var gasToTransfer = gasMortgaged < gasUsedPrice ? gasMortgaged : gasUsedPrice; + + if (gasToTransfer.Sign <= 0) + { + return world; + } + + return world.TransferAsset( + context, Addresses.MortgagePool, Addresses.GasPool, gasToTransfer); + } + } +} diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index ca836c6562..7c1ddc6c5f 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -60,6 +60,9 @@ public static class Addresses public static readonly Address ExploreBoard = new("0000000000000000000000000000000000000102"); public static readonly Address ExplorerList = new("0000000000000000000000000000000000000103"); + public static readonly Address MortgagePool = new Address("0000000000000000000000000000000000100000"); + public static readonly Address GasPool = new Address("0000000000000000000000000000000000100001"); + #region Guild /// From 77c0ad72db05bb9a16e2d7d0b761a85402644e93 Mon Sep 17 00:00:00 2001 From: s2quake Date: Fri, 25 Oct 2024 21:37:53 +0900 Subject: [PATCH 120/206] test: Add test code for collecting gas --- .../Action/ValidatorDelegation/GasTest.cs | 181 ++++++++++++ .../GasWithTransferAssetTest.cs | 205 +++++++++++++ .../ValidatorDelegation/TxAcitonTestBase.cs | 275 ++++++++++++++++++ 3 files changed, 661 insertions(+) create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/GasTest.cs create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/GasWithTransferAssetTest.cs create mode 100644 .Lib9c.Tests/Action/ValidatorDelegation/TxAcitonTestBase.cs diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/GasTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/GasTest.cs new file mode 100644 index 0000000000..306f56640d --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/GasTest.cs @@ -0,0 +1,181 @@ +#nullable enable +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using System; +using System.Linq; +using Libplanet.Action; +using Libplanet.Crypto; +using Nekoyume.Action; +using Xunit; + +public class GasTest : TxAcitonTestBase +{ + [Theory] + [InlineData(0, 0, 4)] + [InlineData(1, 1, 4)] + [InlineData(4, 4, 4)] + public void Execute(long gasLimit, long gasConsumption, long gasOwned) + { + if (gasLimit < gasConsumption) + { + throw new ArgumentOutOfRangeException( + nameof(gasLimit), + $"{nameof(gasLimit)} must be greater than or equal to {nameof(gasConsumption)}."); + } + + if (gasOwned < gasConsumption) + { + throw new ArgumentOutOfRangeException( + nameof(gasOwned), + $"{nameof(gasOwned)} must be greater than or equal to {nameof(gasConsumption)}."); + } + + // Given + var signerKey = new PrivateKey(); + var signerMead = Mead * gasOwned; + EnsureToMintAsset(signerKey, signerMead); + + // When + var expectedMead = signerMead - Mead * gasConsumption; + var gasActions = new IAction[] + { + new GasAction { Consumption = gasConsumption }, + }; + + MakeTransaction( + signerKey, + gasActions, + maxGasPrice: Mead * 1, + gasLimit: gasLimit); + MoveToNextBlock(throwOnError: true); + + // Then + var actualMead = GetBalance(signerKey.Address, Mead); + Assert.Equal(expectedMead, actualMead); + } + + [Theory] + [InlineData(0, 4)] + [InlineData(1, 4)] + [InlineData(4, 4)] + public void Execute_Without_GasLimit_And_MaxGasPrice( + long gasConsumption, long gasOwned) + { + if (gasOwned < gasConsumption) + { + throw new ArgumentOutOfRangeException( + nameof(gasOwned), + $"{nameof(gasOwned)} must be greater than or equal to {nameof(gasConsumption)}."); + } + + // Given + var signerKey = new PrivateKey(); + var signerMead = Mead * gasOwned; + EnsureToMintAsset(signerKey, signerMead); + + // When + var expectedMead = signerMead; + var gasActions = new IAction[] + { + new GasAction { Consumption = gasConsumption }, + }; + + MakeTransaction(signerKey, gasActions); + MoveToNextBlock(throwOnError: true); + + // Then + var actualMead = GetBalance(signerKey.Address, Mead); + Assert.Equal(expectedMead, actualMead); + } + + [Theory] + [InlineData(1, 1, 0)] + [InlineData(4, 4, 0)] + [InlineData(4, 4, 1)] + public void Execute_InsufficientMead_Throw( + long gasLimit, long gasConsumption, long gasOwned) + { + if (gasLimit < gasConsumption) + { + throw new ArgumentOutOfRangeException( + nameof(gasLimit), + $"{nameof(gasLimit)} must be greater than or equal to {nameof(gasConsumption)}."); + } + + if (gasOwned >= gasConsumption) + { + throw new ArgumentOutOfRangeException( + nameof(gasOwned), + $"{nameof(gasOwned)} must be less than {nameof(gasConsumption)}."); + } + + // Given + var signerKey = new PrivateKey(); + var signerMead = Mead * gasOwned; + EnsureToMintAsset(signerKey, signerMead); + + // When + var expectedMead = Mead * 0; + var gasAction = new GasAction { Consumption = gasConsumption }; + MakeTransaction( + signerKey, + new ActionBase[] { gasAction, }, + maxGasPrice: Mead * 1, + gasLimit: gasLimit); + + // Then + var e = Assert.Throws(() => MoveToNextBlock(throwOnError: true)); + var innerExceptions = e.InnerExceptions + .Cast() + .ToArray(); + var actualMead = GetBalance(signerKey.Address, Mead); + Assert.Single(innerExceptions); + Assert.IsType(innerExceptions[0].Action); + Assert.Equal(expectedMead, actualMead); + } + + [Theory] + [InlineData(4, 5, 5)] + [InlineData(1, 5, 10)] + public void Execute_ExcceedGasLimit_Throw( + long gasLimit, long gasConsumption, long gasOwned) + { + if (gasLimit >= gasConsumption) + { + throw new ArgumentOutOfRangeException( + nameof(gasLimit), + $"{nameof(gasLimit)} must be less than {nameof(gasConsumption)}."); + } + + if (gasOwned < gasConsumption) + { + throw new ArgumentOutOfRangeException( + nameof(gasOwned), + $"{nameof(gasOwned)} must be greater than or equal to {nameof(gasConsumption)}."); + } + + // Given + var signerKey = new PrivateKey(); + var signerMead = Mead * gasOwned; + EnsureToMintAsset(signerKey, signerMead); + + // When + var expectedMead = signerMead - (Mead * gasLimit); + var gasAction = new GasAction { Consumption = gasConsumption }; + MakeTransaction( + signerKey, + new ActionBase[] { gasAction, }, + maxGasPrice: Mead * 1, + gasLimit: gasLimit); + + // Then + var e = Assert.Throws(() => MoveToNextBlock(throwOnError: true)); + var innerExceptions = e.InnerExceptions + .Cast() + .ToArray(); + var actualMead = GetBalance(signerKey.Address, Mead); + Assert.Single(innerExceptions); + Assert.Contains(innerExceptions, i => i.Action is GasAction); + Assert.Equal(expectedMead, actualMead); + } +} diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/GasWithTransferAssetTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/GasWithTransferAssetTest.cs new file mode 100644 index 0000000000..1815411ccd --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/GasWithTransferAssetTest.cs @@ -0,0 +1,205 @@ +#nullable enable +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using System; +using System.Linq; +using Libplanet.Action; +using Libplanet.Crypto; +using Nekoyume.Action; +using Xunit; + +public class GasWithTransferAssetTest : TxAcitonTestBase +{ + public const long GasConsumption = 4; + + [Theory] + [InlineData(4, 4)] + [InlineData(4, 5)] + [InlineData(4, 6)] + public void Execute(long gasLimit, long gasOwned) + { + if (gasLimit < GasConsumption) + { + throw new ArgumentOutOfRangeException( + nameof(gasLimit), + $"{nameof(gasLimit)} must be greater than or equal to {GasConsumption}."); + } + + if (gasOwned < GasConsumption) + { + throw new ArgumentOutOfRangeException( + nameof(gasOwned), + $"{nameof(gasOwned)} must be greater than or equal to {GasConsumption}."); + } + + // Given + var signerKey = new PrivateKey(); + var signerMead = Mead * gasOwned; + var recipientKey = new PrivateKey(); + EnsureToMintAsset(signerKey, signerMead); + EnsureToMintAsset(signerKey, NCG * 100); + + // When + var amount = NCG * 1; + var expectedNCG = NCG * 1; + var expectedMead = signerMead - Mead * GasConsumption; + var transferAsset = new TransferAsset( + signerKey.Address, recipientKey.Address, amount, memo: "test"); + MakeTransaction( + signerKey, + new ActionBase[] { transferAsset, }, + maxGasPrice: Mead * 1, + gasLimit: gasLimit); + + // ` + MoveToNextBlock(throwOnError: true); + var actualNCG = GetBalance(recipientKey.Address, NCG); + var actualMead = GetBalance(signerKey.Address, Mead); + Assert.Equal(expectedNCG, actualNCG); + Assert.Equal(expectedMead, actualMead); + } + + [Theory] + [InlineData(0, 4)] + [InlineData(1, 4)] + [InlineData(4, 4)] + public void Execute_Without_GasLimit_And_MaxGasPrice( + long gasConsumption, long gasOwned) + { + if (gasOwned < gasConsumption) + { + throw new ArgumentOutOfRangeException( + nameof(gasOwned), + $"{nameof(gasOwned)} must be greater than or equal to {nameof(gasConsumption)}."); + } + + // Given + var signerKey = new PrivateKey(); + var signerMead = Mead * gasOwned; + var recipientKey = new PrivateKey(); + EnsureToMintAsset(signerKey, signerMead); + EnsureToMintAsset(signerKey, NCG * 100); + + // When + var amount = NCG * 1; + var expectedNCG = NCG * 1; + var expectedMead = signerMead; + var transferAsset = new TransferAsset( + signerKey.Address, recipientKey.Address, amount, memo: "test"); + MakeTransaction( + signerKey, + new ActionBase[] { transferAsset, }); + + // Then + MoveToNextBlock(throwOnError: true); + var actualNCG = GetBalance(recipientKey.Address, NCG); + var actualMead = GetBalance(signerKey.Address, Mead); + Assert.Equal(expectedNCG, actualNCG); + Assert.Equal(expectedMead, actualMead); + } + + [Theory] + [InlineData(4, 0)] + [InlineData(5, 0)] + [InlineData(6, 1)] + public void Execute_InsufficientMead_Throw( + long gasLimit, long gasOwned) + { + if (gasLimit < GasConsumption) + { + throw new ArgumentOutOfRangeException( + nameof(gasLimit), + $"{nameof(gasLimit)} must be greater than or equal to {GasConsumption}."); + } + + if (gasOwned >= GasConsumption) + { + throw new ArgumentOutOfRangeException( + nameof(gasOwned), + $"{nameof(gasOwned)} must be less than {GasConsumption}."); + } + + // Given + var signerKey = new PrivateKey(); + var signerMead = Mead * gasOwned; + var recipientKey = new PrivateKey(); + EnsureToMintAsset(signerKey, signerMead); + EnsureToMintAsset(signerKey, NCG * 100); + + // When + var amount = NCG * 1; + var expectedMead = Mead * 0; + var expectedNCG = NCG * 0; + var transferAsset = new TransferAsset( + signerKey.Address, recipientKey.Address, amount, memo: "test"); + MakeTransaction( + signerKey, + new ActionBase[] { transferAsset, }, + maxGasPrice: Mead * 1, + gasLimit: gasLimit); + + // Then + var e = Assert.Throws(() => MoveToNextBlock(throwOnError: true)); + var innerExceptions = e.InnerExceptions + .Cast() + .ToArray(); + var actualNCG = GetBalance(recipientKey.Address, NCG); + var actualMead = GetBalance(signerKey.Address, Mead); + Assert.Single(innerExceptions); + Assert.Contains(innerExceptions, i => i.Action is TransferAsset); + Assert.Equal(expectedNCG, actualNCG); + Assert.Equal(expectedMead, actualMead); + } + + [Theory] + [InlineData(3, 5)] + [InlineData(1, 10)] + public void Execute_ExcceedGasLimit_Throw( + long gasLimit, long gasOwned) + { + if (gasLimit >= GasConsumption) + { + throw new ArgumentOutOfRangeException( + nameof(gasLimit), + $"{nameof(gasLimit)} must be less than {GasConsumption}."); + } + + if (gasOwned < GasConsumption) + { + throw new ArgumentOutOfRangeException( + nameof(gasOwned), + $"{nameof(gasOwned)} must be greater than or equal to {GasConsumption}."); + } + + // Given + var amount = NCG * 1; + var signerKey = new PrivateKey(); + var signerMead = Mead * gasOwned; + var recipientKey = new PrivateKey(); + EnsureToMintAsset(signerKey, signerMead); + EnsureToMintAsset(signerKey, NCG * 100); + + // When + var expectedMead = signerMead - (Mead * gasLimit); + var expectedNCG = NCG * 0; + var transferAsset = new TransferAsset( + signerKey.Address, recipientKey.Address, amount, memo: "test"); + MakeTransaction( + signerKey, + new ActionBase[] { transferAsset, }, + maxGasPrice: Mead * 1, + gasLimit: gasLimit); + + // Then + var e = Assert.Throws(() => MoveToNextBlock(throwOnError: true)); + var innerExceptions = e.InnerExceptions + .Cast() + .ToArray(); + var actualNCG = GetBalance(recipientKey.Address, NCG); + var actualMead = GetBalance(signerKey.Address, Mead); + Assert.Single(innerExceptions); + Assert.Contains(innerExceptions, i => i.Action is TransferAsset); + Assert.Equal(expectedNCG, actualNCG); + Assert.Equal(expectedMead, actualMead); + } +} diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/TxAcitonTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/TxAcitonTestBase.cs new file mode 100644 index 0000000000..04e63903b5 --- /dev/null +++ b/.Lib9c.Tests/Action/ValidatorDelegation/TxAcitonTestBase.cs @@ -0,0 +1,275 @@ +#nullable enable +namespace Lib9c.Tests.Action.ValidatorDelegation; + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Security.Cryptography; +using System.Threading; +using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.Loader; +using Libplanet.Action.State; +using Libplanet.Blockchain; +using Libplanet.Blockchain.Policies; +using Libplanet.Blockchain.Renderers; +using Libplanet.Common; +using Libplanet.Crypto; +using Libplanet.Store; +using Libplanet.Store.Trie; +using Libplanet.Types.Assets; +using Libplanet.Types.Blocks; +using Libplanet.Types.Consensus; +using Nekoyume; +using Nekoyume.Action; +using Nekoyume.Action.Loader; +using Nekoyume.Blockchain.Policy; +using Nekoyume.Model; +using Nekoyume.Model.State; + +public abstract class TxAcitonTestBase +{ + protected static readonly Currency Mead = Currencies.Mead; + protected static readonly Currency NCG = Currency.Legacy("NCG", 2, null); + private readonly PrivateKey _privateKey = new PrivateKey(); + private BlockCommit? _lastCommit; + + protected TxAcitonTestBase() + { + var validatorKey = new PrivateKey(); + + var blockPolicySource = new BlockPolicySource( + actionLoader: new GasActionLoader()); + var policy = blockPolicySource.GetPolicy( + maxTransactionsBytesPolicy: null!, + minTransactionsPerBlockPolicy: null!, + maxTransactionsPerBlockPolicy: null!, + maxTransactionsPerSignerPerBlockPolicy: null!); + var stagePolicy = new VolatileStagePolicy(); + var validator = new Validator(validatorKey.PublicKey, 10_000_000_000_000_000_000); + var genesis = MakeGenesisBlock( + new ValidatorSet(new List { validator })); + using var store = new MemoryStore(); + using var keyValueStore = new MemoryKeyValueStore(); + using var stateStore = new TrieStateStore(keyValueStore); + var actionEvaluator = new ActionEvaluator( + policy.PolicyActionsRegistry, + stateStore: stateStore, + actionTypeLoader: new GasActionLoader()); + var actionRenderer = new ActionRenderer(); + + var blockChain = BlockChain.Create( + policy, + stagePolicy, + store, + stateStore, + genesis, + actionEvaluator, + renderers: new[] { actionRenderer }); + + BlockChain = blockChain; + Renderer = actionRenderer; + ValidatorKey = validatorKey; + } + + protected BlockChain BlockChain { get; } + + protected ActionRenderer Renderer { get; } + + protected PrivateKey ValidatorKey { get; } + + protected void EnsureToMintAsset(PrivateKey privateKey, FungibleAssetValue fav) + { + var prepareRewardAssets = new PrepareRewardAssets + { + RewardPoolAddress = privateKey.Address, + Assets = new List + { + fav, + }, + }; + var actions = new ActionBase[] { prepareRewardAssets, }; + + Renderer.Reset(); + MakeTransaction(privateKey, actions); + MoveToNextBlock(); + Renderer.Wait(); + } + + protected void MoveToNextBlock(bool throwOnError = false) + { + var blockChain = BlockChain; + var lastCommit = _lastCommit; + var validatorKey = ValidatorKey; + var block = blockChain.ProposeBlock(validatorKey, lastCommit); + var worldState = blockChain.GetNextWorldState() + ?? throw new InvalidOperationException("Failed to get next world state"); + var validatorSet = worldState.GetValidatorSet(); + var blockCommit = GenerateBlockCommit( + block, validatorSet, new PrivateKey[] { validatorKey }); + + Renderer.Reset(); + blockChain.Append(block, blockCommit); + Renderer.Wait(); + if (throwOnError && Renderer.Exceptions.Any()) + { + throw new AggregateException(Renderer.Exceptions); + } + + _lastCommit = blockCommit; + } + + protected IWorldState GetNextWorldState() + { + var blockChain = BlockChain; + return blockChain.GetNextWorldState() + ?? throw new InvalidOperationException("Failed to get next world state"); + } + + protected void MakeTransaction( + PrivateKey privateKey, + IEnumerable actions, + FungibleAssetValue? maxGasPrice = null, + long? gasLimit = null, + DateTimeOffset? timestamp = null) + { + var blockChain = BlockChain; + blockChain.MakeTransaction( + privateKey, actions, maxGasPrice, gasLimit, timestamp); + } + + protected FungibleAssetValue GetBalance(Address address, Currency currency) + => GetNextWorldState().GetBalance(address, currency); + + private BlockCommit GenerateBlockCommit( + Block block, ValidatorSet validatorSet, IEnumerable validatorPrivateKeys) + { + return block.Index != 0 + ? new BlockCommit( + block.Index, + 0, + block.Hash, + validatorPrivateKeys.Select(k => new VoteMetadata( + block.Index, + 0, + block.Hash, + DateTimeOffset.UtcNow, + k.PublicKey, + validatorSet.GetValidator(k.PublicKey).Power, + VoteFlag.PreCommit).Sign(k)).ToImmutableArray()) + : throw new InvalidOperationException("Block index must be greater than 0"); + } + + private Block MakeGenesisBlock(ValidatorSet validators) + { + var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; + (ActivationKey _, PendingActivationState pendingActivation) = + ActivationKey.Create(_privateKey, nonce); + var pendingActivations = new PendingActivationState[] { pendingActivation }; + + var sheets = TableSheetsImporter.ImportSheets(); + return BlockHelper.ProposeGenesisBlock( + validators, + sheets, + new GoldDistribution[0], + pendingActivations); + } + + protected sealed class ActionRenderer : IActionRenderer + { + private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); + private List _exceptionList = new List(); + + public Exception[] Exceptions => _exceptionList.ToArray(); + + public void RenderAction(IValue action, ICommittedActionContext context, HashDigest nextState) + { + } + + public void RenderActionError(IValue action, ICommittedActionContext context, Exception exception) + { + _exceptionList.Add(exception); + } + + public void RenderBlock(Block oldTip, Block newTip) + { + _exceptionList.Clear(); + } + + public void RenderBlockEnd(Block oldTip, Block newTip) + { + _resetEvent.Set(); + } + + public void Reset() => _resetEvent.Reset(); + + public void Wait(int timeout) + { + if (!_resetEvent.WaitOne(timeout)) + { + throw new TimeoutException("Timeout"); + } + } + + public void Wait() => Wait(10000); + } + + [ActionType(TypeIdentifier)] + protected class GasAction : ActionBase + { + public const string TypeIdentifier = "gas_action"; + + public GasAction() + { + } + + public long Consumption { get; set; } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("consumption", new Integer(Consumption)); + + public override void LoadPlainValue(IValue plainValue) + { + if (plainValue is not Dictionary root || + !root.TryGetValue((Text)"consumption", out var rawValues) || + rawValues is not Integer value) + { + throw new InvalidCastException(); + } + + Consumption = (long)value.Value; + } + + public override IWorld Execute(IActionContext context) + { + GasTracer.UseGas(Consumption); + return context.PreviousState; + } + } + + protected class GasActionLoader : IActionLoader + { + private readonly NCActionLoader _actionLoader; + + public GasActionLoader() + { + _actionLoader = new NCActionLoader(); + } + + public IAction LoadAction(long index, IValue value) + { + if (value is Dictionary pv && + pv.TryGetValue((Text)"type_id", out IValue rawTypeId) && + rawTypeId is Text typeId && typeId == GasAction.TypeIdentifier) + { + var action = new GasAction(); + action.LoadPlainValue(pv); + return action; + } + + return _actionLoader.LoadAction(index, value); + } + } +} From dabfadf0cb4640b688753adbb26ce2276071ed37 Mon Sep 17 00:00:00 2001 From: s2quake Date: Mon, 28 Oct 2024 10:46:10 +0900 Subject: [PATCH 121/206] test: Fix a test failure for random delegation --- .../Action/ValidatorDelegation/AllocateRewardTest.cs | 1 + .../Action/ValidatorDelegation/DelegateValidatorTest.cs | 5 ++--- .../ValidatorDelegation/ValidatorDelegationTestBase.cs | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs index 6dd938eb44..432fd8f751 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -131,6 +131,7 @@ public void Execute_WithoutReward_Throw() [InlineData(0)] [InlineData(1181126949)] [InlineData(793705868)] + [InlineData(707058493)] public void Execute_Theory_WithStaticSeed(int randomSeed) { var fixture = new RandomFixture(randomSeed); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index e91f85e535..3a4790116c 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -2,13 +2,11 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System; using System.Collections.Generic; -using System.Linq; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Action; using Nekoyume.Action.ValidatorDelegation; -using Nekoyume.Model.Stake; using Nekoyume.ValidatorDelegation; using Xunit; @@ -97,6 +95,7 @@ public void Execute_Fact() [InlineData(0)] [InlineData(1181126949)] [InlineData(793705868)] + [InlineData(559431555)] public void Execute_Fact_WithStaticSeed(int randomSeed) { var fixture = new RandomFixture(randomSeed); @@ -259,7 +258,7 @@ public ValidatorInfo() public ValidatorInfo(Random random) { Balance = GetRandomFAV(DelegationCurrency, random); - Cash = GetRandomCash(random, Balance, 99); + Cash = GetRandomCash(random, Balance, minDivisor: 2); CashToDelegate = GetRandomCash(random, Balance - Cash); } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index e7a0fc99cc..69a2d49914 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -601,10 +601,12 @@ protected static FungibleAssetValue GetRandomFAV(Currency currency, Random rando return FungibleAssetValue.Parse(currency, text); } - protected static FungibleAssetValue GetRandomCash(Random random, FungibleAssetValue fav, int maxDivisor = 100) + protected static FungibleAssetValue GetRandomCash( + Random random, FungibleAssetValue fav, int minDivisor = 1, int maxDivisor = 100) { - Assert.True(maxDivisor > 0 && maxDivisor <= 100); - var denominator = random.Next(maxDivisor) + 1; + Assert.True(minDivisor > 0); + Assert.True(maxDivisor > minDivisor && maxDivisor <= 100); + var denominator = random.Next(minDivisor, maxDivisor + 1); var cash = fav.DivRem(denominator, out var remainder); if (cash.Sign < 0 || cash > fav) { From 7752d30be6b0149bac39e267bc2e4a60bf5f3650 Mon Sep 17 00:00:00 2001 From: s2quake Date: Mon, 28 Oct 2024 11:02:45 +0900 Subject: [PATCH 122/206] test: Fix a test failure for AllocateRewardTest --- .../Action/ValidatorDelegation/ValidatorDelegationTestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 69a2d49914..7ad175762b 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -594,7 +594,7 @@ protected static FungibleAssetValue GetRandomFAV(Currency currency, Random rando var decimalPart = Enumerable.Range(0, decimalLength) .Aggregate(string.Empty, (s, i) => s + random.Next(10)); var integerPart = Enumerable.Range(0, integerLength) - .Aggregate(string.Empty, (s, i) => s + (integerLength > 1 ? random.Next(10) : random.Next(1, 10))); + .Aggregate(string.Empty, (s, i) => s + (i != 0 ? random.Next(10) : random.Next(1, 10))); var isDecimalZero = decimalLength == 0 || decimalPart.All(c => c == '0'); var text = isDecimalZero is false ? $"{integerPart}.{decimalPart}" : integerPart; From b9f9c677d8c98d2031c08f1ff5ab0dc14dc23e9e Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 31 Oct 2024 00:31:40 +0900 Subject: [PATCH 123/206] feat: Add features for guild migration --- .Lib9c.Benchmarks/Actions/AutoJoinGuild.cs | 91 --------- .../Actions/MigratePledgeToGuild.cs | 113 ----------- .Lib9c.Plugin/PluginActionEvaluator.cs | 2 - .../Guild/Migration/GuildMigrationCtrlTest.cs | 114 ----------- .../Migration/MigratePledgeToGuildTest.cs | 192 ------------------ .../Tx/Begin/AutoJoinGuildTest.cs | 143 ------------- Lib9c.Policy/Policy/BlockPolicySource.cs | 15 +- Lib9c/Action/Guild/MakeGuild.cs | 10 +- .../Migration/Controls/GuildMigrationCtrl.cs | 60 ------ .../Guild/Migration/GuildMigrationConfig.cs | 8 + .../Migration/LegacyModels/LegacyGuild.cs | 61 ++++++ .../LegacyModels/LegacyGuildParticipant.cs | 61 ++++++ .../Guild/Migration/MigrateDelegation.cs | 102 ++++++++++ .../Migration/MigratePlanetariumGuild.cs | 69 +++++++ .../Guild/Migration/MigratePledgeToGuild.cs | 64 ------ .../ValidatorDelegation/PromoteValidator.cs | 8 + .../ValidatorDelegation/ValidatorConfig.cs | 10 + Lib9c/Addresses.cs | 6 + Lib9c/Model/Guild/GuildRejoinCooldown.cs | 50 +++++ Lib9c/Module/Guild/GuildParticipantModule.cs | 60 ++++-- Lib9c/PolicyAction/Tx/Begin/AutoJoinGuild.cs | 60 ------ 21 files changed, 429 insertions(+), 870 deletions(-) delete mode 100644 .Lib9c.Benchmarks/Actions/AutoJoinGuild.cs delete mode 100644 .Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs delete mode 100644 .Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs delete mode 100644 .Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs delete mode 100644 .Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs delete mode 100644 Lib9c/Action/Guild/Migration/Controls/GuildMigrationCtrl.cs create mode 100644 Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs create mode 100644 Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuild.cs create mode 100644 Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuildParticipant.cs create mode 100644 Lib9c/Action/Guild/Migration/MigrateDelegation.cs create mode 100644 Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs delete mode 100644 Lib9c/Action/Guild/Migration/MigratePledgeToGuild.cs create mode 100644 Lib9c/Action/ValidatorDelegation/ValidatorConfig.cs create mode 100644 Lib9c/Model/Guild/GuildRejoinCooldown.cs delete mode 100644 Lib9c/PolicyAction/Tx/Begin/AutoJoinGuild.cs diff --git a/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs b/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs deleted file mode 100644 index fd1b5a3ece..0000000000 --- a/.Lib9c.Benchmarks/Actions/AutoJoinGuild.cs +++ /dev/null @@ -1,91 +0,0 @@ -using BenchmarkDotNet.Attributes; -using Bencodex.Types; -using Lib9c.Tests.Action; -using Lib9c.Tests.Util; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Mocks; -using Nekoyume; -using Nekoyume.Action.Guild; -using Nekoyume.Extensions; -using Nekoyume.Model.Guild; -using Nekoyume.Module; -using Nekoyume.Module.Guild; -using Nekoyume.TypedAddress; - -namespace Lib9c.Benchmarks.Actions; - -public class AutoJoinGuild -{ - private AgentAddress signer = AddressUtil.CreateAgentAddress(); - private IWorld worldEmpty; - private IWorld worldWithPledge; - private IWorld worldWithPledgeAndGuild; - private IWorld worldAfterMigration; - - [GlobalSetup] - public void Setup() - { - worldEmpty = new World(MockUtil.MockModernWorldState); - worldWithPledge = worldEmpty - .SetLegacyState( - signer.GetPledgeAddress(), - new List(MeadConfig.PatronAddress.Bencoded, (Boolean)true, (Integer)4)); - - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var repository = new GuildRepository(worldWithPledge, new ActionContext()); - repository.MakeGuild(guildAddress, guildMasterAddress); - worldWithPledgeAndGuild = repository.World; - repository.JoinGuild(guildAddress, signer); - worldAfterMigration = repository.World; - } - - [Benchmark] - public void Execute_WithoutPledge() - { - var action = new Nekoyume.PolicyAction.Tx.Begin.AutoJoinGuild(); - action.Execute(new ActionContext - { - IsPolicyAction = true, - PreviousState = worldEmpty, - Signer = signer, - }); - } - - [Benchmark] - public void Execute_WithPledge_WithoutGuild() - { - var action = new Nekoyume.PolicyAction.Tx.Begin.AutoJoinGuild(); - action.Execute(new ActionContext - { - IsPolicyAction = true, - PreviousState = worldWithPledge, - Signer = signer, - }); - } - - [Benchmark] - public void Execute_WithPledge_WithGuild() - { - var action = new Nekoyume.PolicyAction.Tx.Begin.AutoJoinGuild(); - action.Execute(new ActionContext - { - IsPolicyAction = true, - PreviousState = worldWithPledgeAndGuild, - Signer = signer, - }); - } - - [Benchmark] - public void Execute_AfterMigration() - { - var action = new Nekoyume.PolicyAction.Tx.Begin.AutoJoinGuild(); - action.Execute(new ActionContext - { - IsPolicyAction = true, - PreviousState = worldAfterMigration, - Signer = signer, - }); - } -} diff --git a/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs b/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs deleted file mode 100644 index 5fe9343d25..0000000000 --- a/.Lib9c.Benchmarks/Actions/MigratePledgeToGuild.cs +++ /dev/null @@ -1,113 +0,0 @@ -using BenchmarkDotNet.Attributes; -using Bencodex.Types; -using Lib9c.Tests.Action; -using Lib9c.Tests.Util; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Mocks; -using Nekoyume; -using Nekoyume.Action.Guild; -using Nekoyume.Extensions; -using Nekoyume.Model.Guild; -using Nekoyume.Module; -using Nekoyume.Module.Guild; -using Nekoyume.TypedAddress; - -namespace Lib9c.Benchmarks.Actions; - -public class MigratePledgeToGuild -{ - private AgentAddress signer = AddressUtil.CreateAgentAddress(); - private AgentAddress target = AddressUtil.CreateAgentAddress(); - private IWorld worldEmpty; - private IWorld worldWithPledge; - private IWorld worldWithPledgeAndGuild; - private IWorld worldAfterMigration; - - [GlobalSetup] - public void Setup() - { - worldEmpty = new World(MockUtil.MockModernWorldState); - worldWithPledge = worldEmpty - .SetLegacyState( - target.GetPledgeAddress(), - new List(MeadConfig.PatronAddress.Bencoded, (Boolean)true, (Integer)4)); - - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var repository = new GuildRepository(worldWithPledge, new ActionContext()); - worldWithPledgeAndGuild = repository - .MakeGuild(guildAddress, guildMasterAddress).World; - worldAfterMigration = repository - .JoinGuild(guildAddress, signer).World; - } - - [Benchmark] - public void Execute_WithoutPledge() - { - var action = new Nekoyume.Action.Guild.Migration.MigratePledgeToGuild(target); - try - { - action.Execute(new ActionContext - { - IsPolicyAction = false, - PreviousState = worldEmpty, - Signer = signer, - }); - } - catch - { - // Do nothing. - } - } - - [Benchmark] - public void Execute_WithPledge_WithoutGuild() - { - var action = new Nekoyume.Action.Guild.Migration.MigratePledgeToGuild(target); - try - { - action.Execute(new ActionContext - { - IsPolicyAction = false, - PreviousState = worldWithPledge, - Signer = signer, - }); - } - catch - { - // Do nothing. - } - } - - [Benchmark] - public void Execute_WithPledge_WithGuild() - { - var action = new Nekoyume.Action.Guild.Migration.MigratePledgeToGuild(target); - action.Execute(new ActionContext - { - IsPolicyAction = false, - PreviousState = worldWithPledgeAndGuild, - Signer = signer, - }); - } - - [Benchmark] - public void Execute_AfterMigration() - { - var action = new Nekoyume.Action.Guild.Migration.MigratePledgeToGuild(target); - try - { - action.Execute(new ActionContext - { - IsPolicyAction = false, - PreviousState = worldAfterMigration, - Signer = signer, - }); - } - catch - { - // Do nothing. - } - } -} diff --git a/.Lib9c.Plugin/PluginActionEvaluator.cs b/.Lib9c.Plugin/PluginActionEvaluator.cs index a44ace7658..961d1cab36 100644 --- a/.Lib9c.Plugin/PluginActionEvaluator.cs +++ b/.Lib9c.Plugin/PluginActionEvaluator.cs @@ -7,8 +7,6 @@ using Libplanet.Store; using Nekoyume.Action; using Nekoyume.Action.Loader; -using Nekoyume.PolicyAction.Tx.Begin; - namespace Lib9c.Plugin { diff --git a/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs b/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs deleted file mode 100644 index 54b11496cc..0000000000 --- a/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs +++ /dev/null @@ -1,114 +0,0 @@ -namespace Lib9c.Tests.Action.Guild.Migration -{ - using Bencodex.Types; - using Lib9c.Tests.Util; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Mocks; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Action.Guild; - using Nekoyume.Action.Guild.Migration; - using Nekoyume.Action.Guild.Migration.Controls; - using Nekoyume.Extensions; - using Nekoyume.Model.Guild; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.Module.Guild; - using Nekoyume.TypedAddress; - using Xunit; - - public class GuildMigrationCtrlTest : GuildTestBase - { - [Fact] - public void MigratePlanetariumPledgeToGuild_When_WithUnapprovedPledgeContract() - { - var validatorKey = new PrivateKey(); - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var target = AddressUtil.CreateAgentAddress(); - var pledgeAddress = target.GetPledgeAddress(); - IWorld world = World; - world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); - world = EnsureToCreateValidator(world, validatorKey.PublicKey); - world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - - var repository = new GuildRepository(world, new ActionContext()); - repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( - MeadConfig.PatronAddress.Serialize(), - false.Serialize(), // Unapproved - RequestPledge.DefaultRefillMead.Serialize()))); - - Assert.Null(repository.GetJoinedGuild(target)); - Assert.Throws(() => - GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(repository, target)); - } - - [Fact] - public void MigratePlanetariumPledgeToGuild_When_WithPledgeContract() - { - var validatorKey = new PrivateKey(); - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var target = AddressUtil.CreateAgentAddress(); - var pledgeAddress = target.GetPledgeAddress(); - IWorld world = World; - world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); - world = EnsureToCreateValidator(world, validatorKey.PublicKey); - world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - - var repository = new GuildRepository(world, new ActionContext()); - repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( - MeadConfig.PatronAddress.Serialize(), - true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize()))); - - Assert.Null(repository.GetJoinedGuild(target)); - GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(repository, target); - - var joinedGuildAddress = Assert.IsType(repository.GetJoinedGuild(target)); - Assert.True(repository.TryGetGuild(joinedGuildAddress, out var guild)); - Assert.Equal(GuildConfig.PlanetariumGuildOwner, guild.GuildMasterAddress); - } - - [Fact] - public void MigratePlanetariumPledgeToGuild_When_WithoutPledgeContract() - { - var validatorKey = new PrivateKey(); - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var target = AddressUtil.CreateAgentAddress(); - IWorld world = World; - world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); - world = EnsureToCreateValidator(world, validatorKey.PublicKey); - world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - - var repository = new GuildRepository(world, new ActionContext()); - - Assert.Null(repository.GetJoinedGuild(target)); - Assert.Throws(() => - GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(repository, target)); - } - - [Fact] - public void MigratePlanetariumPledgeToGuild_When_WithoutGuildYet() - { - var target = AddressUtil.CreateAgentAddress(); - var pledgeAddress = target.GetPledgeAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var ncg = Currency.Uncapped("NCG", 2, null); - var goldCurrencyState = new GoldCurrencyState(ncg); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetLegacyState(pledgeAddress, new List( - MeadConfig.PatronAddress.Serialize(), - true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize())); - var repository = new GuildRepository(world, new ActionContext()); - Assert.Null(repository.GetJoinedGuild(target)); - Assert.Throws(() => - GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(repository, target)); - } - } -} diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs deleted file mode 100644 index d273846b88..0000000000 --- a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs +++ /dev/null @@ -1,192 +0,0 @@ -namespace Lib9c.Tests.Action.Guild.Migration -{ - using System; - using Bencodex.Types; - using Lib9c.Tests.Action; - using Lib9c.Tests.Model.Guild; - using Lib9c.Tests.Util; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Mocks; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Action.Guild; - using Nekoyume.Action.Guild.Migration; - using Nekoyume.Action.Loader; - using Nekoyume.Extensions; - using Nekoyume.Model.Guild; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.Module.Guild; - using Nekoyume.TypedAddress; - using Xunit; - - public class MigratePledgeToGuildTest : GuildTestBase - { - [Fact] - public void Serialization() - { - var agentAddress = AddressUtil.CreateAgentAddress(); - var action = new MigratePledgeToGuild(agentAddress); - var plainValue = action.PlainValue; - - var actionLoader = new NCActionLoader(); - var deserialized = - Assert.IsType(actionLoader.LoadAction(0, plainValue)); - - Assert.Equal(agentAddress, deserialized.Target); - } - - [Fact] - public void Execute_When_WithPledgeContract() - { - var validatorKey = new PrivateKey(); - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var target = AddressUtil.CreateAgentAddress(); - var caller = AddressUtil.CreateAgentAddress(); - var pledgeAddress = target.GetPledgeAddress(); - IWorld world = World; - world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); - world = EnsureToCreateValidator(world, validatorKey.PublicKey); - world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - - var repository = new GuildRepository(world, new ActionContext()); - repository.UpdateWorld(repository.World.SetLegacyState( - pledgeAddress, - new List( - MeadConfig.PatronAddress.Serialize(), - true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize()))); - - Assert.Null(repository.GetJoinedGuild(target)); - var action = new MigratePledgeToGuild(target); - - // Migrate by other. - IWorld newWorld = action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = caller, - }); - - var newRepository = new GuildRepository(newWorld, new ActionContext()); - var joinedGuildAddress = Assert.IsType(newRepository.GetJoinedGuild(target)); - Assert.True(newRepository.TryGetGuild(joinedGuildAddress, out var guild)); - Assert.Equal(GuildConfig.PlanetariumGuildOwner, guild.GuildMasterAddress); - - // Migrate by itself. - newWorld = action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = target, - }); - - newRepository.UpdateWorld(newWorld); - joinedGuildAddress = Assert.IsType(newRepository.GetJoinedGuild(target)); - Assert.True(newRepository.TryGetGuild(joinedGuildAddress, out guild)); - Assert.Equal(GuildConfig.PlanetariumGuildOwner, guild.GuildMasterAddress); - } - - [Fact] - public void Execute_When_WithUnapprovedPledgeContract() - { - var validatorKey = new PrivateKey(); - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var target = AddressUtil.CreateAgentAddress(); - var pledgeAddress = target.GetPledgeAddress(); - var caller = AddressUtil.CreateAgentAddress(); - IWorld world = World; - world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); - world = EnsureToCreateValidator(world, validatorKey.PublicKey); - world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - - var repository = new GuildRepository(world, new ActionContext()); - repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( - MeadConfig.PatronAddress.Serialize(), - false.Serialize(), - RequestPledge.DefaultRefillMead.Serialize()))); - - Assert.Null(repository.GetJoinedGuild(target)); - var action = new MigratePledgeToGuild(target); - - // Migrate by other. - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = caller, - })); - - // Migrate by itself. - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = target, - })); - } - - [Fact] - public void Execute_When_WithoutPledgeContract() - { - var validatorKey = new PrivateKey(); - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var target = AddressUtil.CreateAgentAddress(); - var caller = AddressUtil.CreateAgentAddress(); - IWorld world = World; - world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); - world = EnsureToCreateValidator(world, validatorKey.PublicKey); - world = EnsureToMakeGuild(world, guildAddress, guildMasterAddress, validatorKey.Address); - - var repository = new GuildRepository(world, new ActionContext()); - - Assert.Null(repository.GetJoinedGuild(target)); - var action = new MigratePledgeToGuild(target); - - // Migrate by other. - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = caller, - })); - - // Migrate by itself. - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = target, - })); - } - - [Fact] - public void Execute_When_WithoutGuildYet() - { - var target = AddressUtil.CreateAgentAddress(); - var caller = AddressUtil.CreateAgentAddress(); - var pledgeAddress = target.GetPledgeAddress(); - var world = new World(MockUtil.MockModernWorldState) - .SetLegacyState(pledgeAddress, new List( - MeadConfig.PatronAddress.Serialize(), - true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize())); - var repository = new GuildRepository(world, new ActionContext()); - Assert.Null(repository.GetJoinedGuild(target)); - var action = new MigratePledgeToGuild(target); - - // Migrate by other. - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = caller, - })); - - // Migrate by itself. - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = target, - })); - } - } -} diff --git a/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs b/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs deleted file mode 100644 index ead01b8df9..0000000000 --- a/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs +++ /dev/null @@ -1,143 +0,0 @@ -namespace Lib9c.Tests.PolicyAction.Tx.Begin -{ - using System; - using Bencodex.Types; - using Lib9c.Tests.Action; - using Lib9c.Tests.Util; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Mocks; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Action.Guild; - using Nekoyume.Extensions; - using Nekoyume.Model.Guild; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.Module.Guild; - using Nekoyume.Module.ValidatorDelegation; - using Nekoyume.PolicyAction.Tx.Begin; - using Nekoyume.TypedAddress; - using Nekoyume.ValidatorDelegation; - using Xunit; - - public class AutoJoinGuildTest - { - [Fact] - public void RunAsPolicyActionOnly() - { - Assert.Throws(() => new AutoJoinGuild().Execute( - new ActionContext - { - IsPolicyAction = false, - })); - } - - [Fact] - public void Execute_When_WithPledgeContract() - { - var validatorKey = new PrivateKey(); - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var agentAddress = AddressUtil.CreateAgentAddress(); - var pledgeAddress = agentAddress.GetPledgeAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var goldCurrencyState = new GoldCurrencyState(Currencies.GuildGold); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - world = world.MintAsset(new ActionContext(), validatorKey.Address, Currencies.GuildGold * 100); - var validatorRepository = new ValidatorRepository(world, new ActionContext - { - Signer = validatorKey.Address, - }); - validatorRepository.CreateValidatorDelegatee(validatorKey.PublicKey, 10); - world = validatorRepository.World; - - var repository = new GuildRepository(world, new ActionContext - { - Signer = guildMasterAddress, - }); - repository.MakeGuild(guildAddress, validatorKey.Address); - repository.UpdateWorld(repository.World.SetLegacyState(pledgeAddress, new List( - MeadConfig.PatronAddress.Serialize(), - true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize()))); - - Assert.Null(repository.GetJoinedGuild(agentAddress)); - var action = new AutoJoinGuild(); - world = action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = agentAddress, - IsPolicyAction = true, - }); - - repository.UpdateWorld(world); - var joinedGuildAddress = Assert.IsType(repository.GetJoinedGuild(agentAddress)); - Assert.True(repository.TryGetGuild(joinedGuildAddress, out var guild)); - Assert.Equal(GuildConfig.PlanetariumGuildOwner, guild.GuildMasterAddress); - } - - [Fact] - public void Execute_When_WithoutPledgeContract() - { - var validatorKey = new PrivateKey(); - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var agentAddress = AddressUtil.CreateAgentAddress(); - IWorld world = new World(MockUtil.MockModernWorldState); - var goldCurrencyState = new GoldCurrencyState(Currencies.GuildGold); - world = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - world = world.MintAsset(new ActionContext(), validatorKey.Address, Currencies.GuildGold * 100); - var validatorRepository = new ValidatorRepository(world, new ActionContext - { - Signer = validatorKey.Address, - }); - validatorRepository.CreateValidatorDelegatee(validatorKey.PublicKey, 10); - world = validatorRepository.World; - - var repository = new GuildRepository(world, new ActionContext()); - - Assert.Null(repository.GetJoinedGuild(agentAddress)); - var action = new AutoJoinGuild(); - world = action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = agentAddress, - IsPolicyAction = true, - }); - - repository.UpdateWorld(world); - Assert.Null(repository.GetJoinedGuild(agentAddress)); - } - - [Fact] - public void Execute_When_WithoutGuildYet() - { - var agentAddress = AddressUtil.CreateAgentAddress(); - var pledgeAddress = agentAddress.GetPledgeAddress(); - var world = new World(MockUtil.MockModernWorldState) - .SetLegacyState(pledgeAddress, new List( - MeadConfig.PatronAddress.Serialize(), - true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize())); - - var repository = new GuildRepository(world, new ActionContext()); - Assert.Null(repository.GetJoinedGuild(agentAddress)); - var action = new AutoJoinGuild(); - world = action.Execute(new ActionContext - { - PreviousState = repository.World, - Signer = agentAddress, - IsPolicyAction = true, - }); - - repository.UpdateWorld(world); - Assert.Null(repository.GetJoinedGuild(agentAddress)); - } - } -} diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index b7dd28b9bd..d7b5f5fff3 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -1,22 +1,15 @@ using System; using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; +using Lib9c; using Libplanet.Action; using Libplanet.Action.Loader; +using Libplanet.Action.State; using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; -using Nekoyume.Action; -using Nekoyume.Action.Loader; -using Nekoyume.Model; -using Nekoyume.Model.State; -using Lib9c; -using Libplanet.Action.State; -using Libplanet.Crypto; using Libplanet.Types.Blocks; using Libplanet.Types.Tx; -using Nekoyume.PolicyAction.Tx.Begin; +using Nekoyume.Action; +using Nekoyume.Action.Loader; using Nekoyume.Action.ValidatorDelegation; #if UNITY_EDITOR || UNITY_STANDALONE diff --git a/Lib9c/Action/Guild/MakeGuild.cs b/Lib9c/Action/Guild/MakeGuild.cs index 8ab812df53..7c490f73b0 100644 --- a/Lib9c/Action/Guild/MakeGuild.cs +++ b/Lib9c/Action/Guild/MakeGuild.cs @@ -56,11 +56,11 @@ public override IWorld Execute(IActionContext context) var validatorAddress = ValidatorAddress; // TODO: Remove this check when to deliver features to users. - // if (context.Signer != GuildConfig.PlanetariumGuildOwner) - // { - // throw new InvalidOperationException( - // $"This action is not allowed for {context.Signer}."); - // } + if (context.Signer != GuildConfig.PlanetariumGuildOwner) + { + throw new InvalidOperationException( + $"This action is not allowed for {context.Signer}."); + } repository.MakeGuild(guildAddress, validatorAddress); return repository.World; diff --git a/Lib9c/Action/Guild/Migration/Controls/GuildMigrationCtrl.cs b/Lib9c/Action/Guild/Migration/Controls/GuildMigrationCtrl.cs deleted file mode 100644 index f082e759f0..0000000000 --- a/Lib9c/Action/Guild/Migration/Controls/GuildMigrationCtrl.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Bencodex.Types; -using Nekoyume.Extensions; -using Nekoyume.Model.Guild; -using Nekoyume.Model.State; -using Nekoyume.Module; -using Nekoyume.Module.Guild; -using Nekoyume.TypedAddress; - -namespace Nekoyume.Action.Guild.Migration.Controls -{ - public static class GuildMigrationCtrl - { - /// - /// Migrate the pledge to the guild if the has contracted pledge - /// with Planetarium (). - /// - /// - /// - /// - /// Migration to guild from pledge failed. - public static void MigratePlanetariumPledgeToGuild(GuildRepository repository, AgentAddress target) - { - if (repository.GetJoinedGuild(GuildConfig.PlanetariumGuildOwner) is not - { } planetariumGuildAddress) - { - throw new GuildMigrationFailedException("Planetarium guild is not found."); - } - - if (!repository.TryGetGuild(planetariumGuildAddress, out var planetariumGuild)) - { - throw new GuildMigrationFailedException("Planetarium guild is not found."); - } - - if (planetariumGuild.GuildMasterAddress != GuildConfig.PlanetariumGuildOwner) - { - throw new GuildMigrationFailedException("Unexpected guild master."); - } - - if (repository.GetJoinedGuild(target) is not null) - { - throw new GuildMigrationFailedException("Already joined to other guild."); - } - - var pledgeAddress = target.GetPledgeAddress(); - - // Patron contract structure: - // [0] = PatronAddress - // [1] = IsApproved - // [2] = Mead amount to refill. - if (!repository.World.TryGetLegacyState(pledgeAddress, out List list) || list.Count < 3 || - list[0] is not Binary || list[0].ToAddress() != MeadConfig.PatronAddress || - list[1] is not Boolean approved || !approved) - { - throw new GuildMigrationFailedException("Unexpected pledge structure."); - } - - repository.JoinGuild(planetariumGuildAddress, target); - } - } -} diff --git a/Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs b/Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs new file mode 100644 index 0000000000..aa5aa3dcf0 --- /dev/null +++ b/Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs @@ -0,0 +1,8 @@ +namespace Nekoyume.Action.Guild.Migration +{ + // TODO: [GuildMigration] Remove this class when the migration is done. + public static class GuildMigrationConfig + { + public static readonly long MigrateDelegationHeight = 55555L; + } +} diff --git a/Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuild.cs b/Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuild.cs new file mode 100644 index 0000000000..b494b3d33e --- /dev/null +++ b/Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuild.cs @@ -0,0 +1,61 @@ +using System; +using Bencodex; +using Bencodex.Types; +using Nekoyume.TypedAddress; + +namespace Nekoyume.Action.Guild.Migration.LegacyModels +{ + // TODO: [GuildMigration] Remove this class when the migration is done. + public class LegacyGuild : IEquatable, IBencodable + { + private const string StateTypeName = "guild"; + private const long StateVersion = 1; + + public readonly AgentAddress GuildMasterAddress; + + public LegacyGuild(AgentAddress guildMasterAddress) + { + GuildMasterAddress = guildMasterAddress; + } + + public LegacyGuild(List list) : this(new AgentAddress(list[2])) + { + if (list[0] is not Text text || text != StateTypeName || list[1] is not Integer integer) + { + throw new InvalidCastException(); + } + + if (integer > StateVersion) + { + throw new FailedLoadStateException("Un-deserializable state."); + } + } + + public List Bencoded => List.Empty + .Add(StateTypeName) + .Add(StateVersion) + .Add(GuildMasterAddress.Bencoded); + + IValue IBencodable.Bencoded => Bencoded; + + public bool Equals(LegacyGuild other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return GuildMasterAddress.Equals(other.GuildMasterAddress); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((LegacyGuild)obj); + } + + public override int GetHashCode() + { + return GuildMasterAddress.GetHashCode(); + } + } +} diff --git a/Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuildParticipant.cs b/Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuildParticipant.cs new file mode 100644 index 0000000000..e69b6297aa --- /dev/null +++ b/Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuildParticipant.cs @@ -0,0 +1,61 @@ +using System; +using Bencodex; +using Bencodex.Types; +using Nekoyume.TypedAddress; + +namespace Nekoyume.Action.Guild.Migration.LegacyModels +{ + // TODO: [GuildMigration] Remove this class when the migration is done. + public class LegacyGuildParticipant : IBencodable, IEquatable + { + private const string StateTypeName = "guild_participant"; + private const long StateVersion = 1; + + public readonly GuildAddress GuildAddress; + + public LegacyGuildParticipant(GuildAddress guildAddress) + { + GuildAddress = guildAddress; + } + + public LegacyGuildParticipant(List list) : this(new GuildAddress(list[2])) + { + if (list[0] is not Text text || text != StateTypeName || list[1] is not Integer integer) + { + throw new InvalidCastException(); + } + + if (integer > StateVersion) + { + throw new FailedLoadStateException("Un-deserializable state."); + } + } + + public List Bencoded => List.Empty + .Add(StateTypeName) + .Add(StateVersion) + .Add(GuildAddress.Bencoded); + + IValue IBencodable.Bencoded => Bencoded; + + public bool Equals(LegacyGuildParticipant other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return GuildAddress.Equals(other.GuildAddress); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((LegacyGuildParticipant)obj); + } + + public override int GetHashCode() + { + return GuildAddress.GetHashCode(); + } + } +} diff --git a/Lib9c/Action/Guild/Migration/MigrateDelegation.cs b/Lib9c/Action/Guild/Migration/MigrateDelegation.cs new file mode 100644 index 0000000000..9ffce23042 --- /dev/null +++ b/Lib9c/Action/Guild/Migration/MigrateDelegation.cs @@ -0,0 +1,102 @@ +using System; +using Bencodex.Types; +using Lib9c; +using Libplanet.Action.State; +using Libplanet.Action; +using Nekoyume.Model.Guild; +using Nekoyume.TypedAddress; +using Nekoyume.Action.Guild.Migration.LegacyModels; +using Nekoyume.Module.Guild; +using Nekoyume.Model.Stake; +using Nekoyume.Model.State; +using Nekoyume.Module; + +namespace Nekoyume.Action.Guild.Migration +{ + // TODO: [GuildMigration] Remove this class when the migration is done. + /// + /// An action to migrate guild delegation. + /// + public class MigrateDelegation : ActionBase + { + public const string TypeIdentifier = "migrate_delegation"; + + private const string TargetKey = "t"; + + public AgentAddress Target { get; private set; } + + [Obsolete("Don't call in code.", error: false)] + public MigrateDelegation() + { + } + + public MigrateDelegation(AgentAddress target) + { + Target = target; + } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", Dictionary.Empty + .Add(TargetKey, Target.Bencoded)); + + public override void LoadPlainValue(IValue plainValue) + { + if (plainValue is not Dictionary root || + !root.TryGetValue((Text)"values", out var rawValues) || + rawValues is not Dictionary values || + !values.TryGetValue((Text)TargetKey, out var rawTarget) || + rawTarget is not Binary target) + { + throw new InvalidCastException(); + } + + Target = new AgentAddress(target); + } + + public override IWorld Execute(IActionContext context) + { + GasTracer.UseGas(1); + + var world = context.PreviousState; + + // Migrate stake state from v2 to v3 (Mint guild gold for staking) + var stakeStateAddr = LegacyStakeState.DeriveAddress(Target); + if (world.TryGetStakeState(Target, out var stakeState) + && stakeState.StateVersion == 2) + { + if (!StakeStateUtils.TryMigrateV2ToV3( + context, + world, + StakeState.DeriveAddress(Target), + stakeState, out var result)) + { + throw new InvalidOperationException( + "Failed to migrate stake state. Unexpected situation."); + } + + world = result.Value.world; + } + + // Migrate guild participant state from legacy to new + var value = world.GetAccountState(Addresses.GuildParticipant).GetState(Target) as List; + var legacyGuildParticipant = new LegacyGuildParticipant(value); + var repository = new GuildRepository(world, context); + var guildParticipant = new GuildParticipant( + Target, + legacyGuildParticipant.GuildAddress, + repository); + repository.SetGuildParticipant(guildParticipant); + + // Migrate delegation + var guild = repository.GetGuild(guildParticipant.GuildAddress); + var guildGold = repository.GetBalance(guildParticipant.DelegationPoolAddress, Currencies.GuildGold); + if (guildGold.RawValue > 0) + { + repository.Delegate(guildParticipant.Address, guildGold); + } + + return repository.World; + } + } +} diff --git a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs new file mode 100644 index 0000000000..5f87717295 --- /dev/null +++ b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs @@ -0,0 +1,69 @@ +using System; +using Bencodex.Types; +using Lib9c; +using Libplanet.Action.State; +using Libplanet.Action; +using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.Model.Guild; +using Nekoyume.Action.Guild.Migration.LegacyModels; + +namespace Nekoyume.Action.Guild.Migration +{ + // TODO: [GuildMigration] Remove this class when the migration is done. + /// + /// An action to migrate the planetarium guild. + /// + [ActionType(TypeIdentifier)] + public class MigratePlanetariumGuild : ActionBase + { + public const string TypeIdentifier = "migrate_planetarium_guild"; + + [Obsolete("Don't call in code.", error: false)] + public MigratePlanetariumGuild() + { + } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", Null.Value); + + public override void LoadPlainValue(IValue plainValue) + { + if (plainValue is not Dictionary root || + !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 GuildRepository(world, context); + + // Get Guild address + var guildMasterValue = world + .GetAccountState(Addresses.GuildParticipant) + .GetState(GuildConfig.PlanetariumGuildOwner) as List; + var guildAddress = new LegacyGuildParticipant(guildMasterValue).GuildAddress; + + // MigratePlanetariumGuild + var guildValue = world + .GetAccountState(Addresses.Guild) + .GetState(guildAddress) as List; + var legacyGuild = new LegacyGuild(guildValue); + var guild = new Model.Guild.Guild( + guildAddress, + legacyGuild.GuildMasterAddress, + ValidatorConfig.PlanetariumValidator, + Currencies.GuildGold, + repository); + repository.SetGuild(guild); + + return repository.World; + } + } +} diff --git a/Lib9c/Action/Guild/Migration/MigratePledgeToGuild.cs b/Lib9c/Action/Guild/Migration/MigratePledgeToGuild.cs deleted file mode 100644 index 4dace3ab1c..0000000000 --- a/Lib9c/Action/Guild/Migration/MigratePledgeToGuild.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using Bencodex.Types; -using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Action.Guild.Migration.Controls; -using Nekoyume.Model.Guild; -using Nekoyume.TypedAddress; - -namespace Nekoyume.Action.Guild.Migration -{ - /// - /// An action to migrate the pledge to the guild. - /// But it is only for accounts contracted pledge with Planetarium (). - /// - [ActionType(TypeIdentifier)] - public class MigratePledgeToGuild : ActionBase - { - public const string TypeIdentifier = "migrate_pledge_to_guild"; - - private const string TargetKey = "t"; - - public AgentAddress Target { get; private set; } - - [Obsolete("Don't call in code.", error: true)] - public MigratePledgeToGuild() - { - } - - public MigratePledgeToGuild(AgentAddress target) - { - Target = target; - } - - public override IValue PlainValue => Dictionary.Empty - .Add("type_id", TypeIdentifier) - .Add("values", Dictionary.Empty - .Add(TargetKey, Target.Bencoded)); - - public override void LoadPlainValue(IValue plainValue) - { - if (plainValue is not Dictionary root || - !root.TryGetValue((Text)"values", out var rawValues) || - rawValues is not Dictionary values || - !values.TryGetValue((Text)TargetKey, out var rawTarget) || - rawTarget is not Binary target) - { - throw new InvalidCastException(); - } - - Target = new AgentAddress(target); - } - - public override IWorld Execute(IActionContext context) - { - GasTracer.UseGas(1); - - var world = context.PreviousState; - var repository = new GuildRepository(world, context); - - GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(repository, Target); - return repository.World; - } - } -} diff --git a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs index 88f3f8776a..974b274740 100644 --- a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs @@ -7,6 +7,7 @@ using Libplanet.Types.Assets; using Nekoyume.Module.ValidatorDelegation; using Nekoyume.ValidatorDelegation; +using Org.BouncyCastle.Bcpg.Sig; namespace Nekoyume.Action.ValidatorDelegation { @@ -61,6 +62,13 @@ public override IWorld Execute(IActionContext context) { GasTracer.UseGas(1); + // TODO: Remove this check when to deliver features to users. + if (context.Signer != ValidatorConfig.PlanetariumValidator) + { + throw new InvalidOperationException( + $"This action is not allowed for {context.Signer}."); + } + var world = context.PreviousState; var repository = new ValidatorRepository(world, context); diff --git a/Lib9c/Action/ValidatorDelegation/ValidatorConfig.cs b/Lib9c/Action/ValidatorDelegation/ValidatorConfig.cs new file mode 100644 index 0000000000..ddd5c1d791 --- /dev/null +++ b/Lib9c/Action/ValidatorDelegation/ValidatorConfig.cs @@ -0,0 +1,10 @@ +using Libplanet.Crypto; + +namespace Nekoyume.Action.ValidatorDelegation +{ + public static class ValidatorConfig + { + public static readonly Address PlanetariumValidator = + new Address("0x8E1b572db70aB80bb02783A0D2c594A0edE6db28"); + } +} diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index 7c1ddc6c5f..de7b1d3b3d 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -85,6 +85,12 @@ public static class Addresses /// public static readonly Address GuildParticipant = new("0000000000000000000000000000000000000203"); + /// + /// An address of an account having + /// + public static readonly Address GuildRejoinCooldown = + new Address("0000000000000000000000000000000000000204"); + /// /// Build an of an , /// represented as `agentAddress` ↔ , indicates whether diff --git a/Lib9c/Model/Guild/GuildRejoinCooldown.cs b/Lib9c/Model/Guild/GuildRejoinCooldown.cs new file mode 100644 index 0000000000..c2cbbc4fbc --- /dev/null +++ b/Lib9c/Model/Guild/GuildRejoinCooldown.cs @@ -0,0 +1,50 @@ +#nullable enable +using System; +using Bencodex; +using Bencodex.Types; +using Nekoyume.TypedAddress; +using Nekoyume.ValidatorDelegation; + +namespace Nekoyume.Model.Guild +{ + public class GuildRejoinCooldown : IBencodable, IEquatable + { + public GuildRejoinCooldown(AgentAddress agentAddress, long quitHeight) + { + AgentAddress = agentAddress; + ReleaseHeight = quitHeight + ValidatorDelegatee.ValidatorUnbondingPeriod; + } + + public GuildRejoinCooldown(AgentAddress agentAddress, IValue bencoded) + : this(agentAddress, ((Integer)bencoded)) + { + } + + public GuildRejoinCooldown(AgentAddress agentAddress, Integer bencoded) + { + AgentAddress = agentAddress; + ReleaseHeight = bencoded; + } + + public AgentAddress AgentAddress { get; } + + public long ReleaseHeight { get; } + + public Integer Bencoded => new Integer(ReleaseHeight); + + IValue IBencodable.Bencoded => Bencoded; + + public long Cooldown(long currentHeight) + { + return Math.Max(0, ReleaseHeight - currentHeight); + } + + public bool Equals(GuildRejoinCooldown? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return AgentAddress.Equals(other.AgentAddress) + && ReleaseHeight == other.ReleaseHeight; + } + } +} diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index 3eec749118..c095bccf65 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -2,10 +2,11 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Numerics; +using Bencodex.Types; using Lib9c; using Libplanet.Types.Assets; +using Nekoyume.Action.Guild.Migration; using Nekoyume.Model.Guild; -using Nekoyume.Model.Stake; using Nekoyume.TypedAddress; using Nekoyume.ValidatorDelegation; @@ -27,6 +28,18 @@ public static GuildRepository JoinGuild( GuildAddress guildAddress, AgentAddress target) { + if (repository.TryGetGuildParticipant(target, out _)) + { + throw new ArgumentException("The signer already joined a guild."); + } + + if (repository.GetGuildRejoinCooldown(target) is { } cooldown + && cooldown.Cooldown(repository.ActionContext.BlockIndex) > 0L) + { + throw new InvalidOperationException( + $"The signer is in the rejoin cooldown period until block {cooldown.ReleaseHeight}"); + } + var guildParticipant = new GuildParticipant(target, guildAddress, repository); var guildGold = repository.GetBalance(guildParticipant.DelegationPoolAddress, Currencies.GuildGold); repository.SetGuildParticipant(guildParticipant); @@ -101,22 +114,11 @@ public static GuildRepository LeaveGuild( repository.Undelegate(agentAddress); } + repository.SetGuildRejoinCooldown(agentAddress, repository.ActionContext.BlockIndex); + return repository; } - // public static GuildRepository RawLeaveGuild(this GuildRepository repository, AgentAddress target) - // { - // if (!repository.TryGetGuildParticipant(target, out var guildParticipant)) - // { - // throw new InvalidOperationException("It may not join any guild."); - // } - - // repository.RemoveGuildParticipant(target); - // repository.DecreaseGuildMemberCount(guildParticipant.GuildAddress); - - // return repository; - // } - private static bool TryGetGuildParticipant( this GuildRepository repository, AgentAddress agentAddress, @@ -134,12 +136,17 @@ private static bool TryGetGuildParticipant( } } - private static GuildRepository Delegate( + // TODO: Hide this method as private after migration. + public static GuildRepository Delegate( this GuildRepository repository, AgentAddress guildParticipantAddress, FungibleAssetValue fav) { var height = repository.ActionContext.BlockIndex; + + // TODO: Remove below unnecessary height condition after migration. + height = Math.Max(height, GuildMigrationConfig.MigrateDelegationHeight); + var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); var guild = repository.GetGuild(guildParticipant.GuildAddress); guildParticipant.Delegate(guild, fav, height); @@ -200,5 +207,28 @@ private static GuildRepository ClaimReward( return repository; } + + private static GuildRepository SetGuildRejoinCooldown( + this GuildRepository repository, + AgentAddress guildParticipantAddress, + long height) + { + var guildRejoinCooldown = new GuildRejoinCooldown(guildParticipantAddress, height); + repository.UpdateWorld( + repository.World.SetAccount( + Addresses.GuildRejoinCooldown, + repository.World.GetAccount(Addresses.GuildRejoinCooldown) + .SetState(guildParticipantAddress, guildRejoinCooldown.Bencoded))); + return repository; + } + + private static GuildRejoinCooldown? GetGuildRejoinCooldown( + this GuildRepository repository, + AgentAddress guildParticipantAddress) + => repository.World + .GetAccount(Addresses.GuildRejoinCooldown) + .GetState(guildParticipantAddress) is Integer bencoded + ? new GuildRejoinCooldown(guildParticipantAddress, bencoded) + : null; } } diff --git a/Lib9c/PolicyAction/Tx/Begin/AutoJoinGuild.cs b/Lib9c/PolicyAction/Tx/Begin/AutoJoinGuild.cs deleted file mode 100644 index 0381f31805..0000000000 --- a/Lib9c/PolicyAction/Tx/Begin/AutoJoinGuild.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using Bencodex.Types; -using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Action; -using Nekoyume.Action.Guild.Migration; -using Nekoyume.Action.Guild.Migration.Controls; -using Nekoyume.Extensions; -using Nekoyume.Model.Guild; -using Serilog; - -namespace Nekoyume.PolicyAction.Tx.Begin -{ - /// - /// An action that automatically joins to Planetarium guild if it contracted pledge with Planetarium. - /// - public class AutoJoinGuild : ActionBase - { - public override IValue PlainValue => Null.Value; - - public override void LoadPlainValue(IValue plainValue) - { - throw new InvalidOperationException("Policy action shouldn't be serialized."); - } - - public override IWorld Execute(IActionContext context) - { - if (!context.IsPolicyAction) - { - throw new InvalidOperationException( - "This action must be called when it is a policy action."); - } - - var world = context.PreviousState; - var repository = new GuildRepository(world, context); - var signer = context.GetAgentAddress(); - - try - { - GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(repository, signer); - } - catch (GuildMigrationFailedException guildMigrationFailedException) - { - Log.ForContext() - .Debug( - "Migration from pledge to guild failed but it just skips. {Message}", - guildMigrationFailedException.Message); - } - catch (Exception e) - { - Log.ForContext() - .Error( - "Unexpected exception but it skips. You should debug this situation. {Message}", - e.Message); - } - - return repository.World; - } - } -} From 13ef1ae370dfa515c7904961cfeb76c3ce2f598f Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 31 Oct 2024 15:47:10 +0900 Subject: [PATCH 124/206] test: Add migration tests --- .../Guild/Migration/MigrateDelegationTest.cs | 107 ++++++++++++++++++ .../Migration/MigratePlanetariumGuildTest.cs | 60 ++++++++++ 2 files changed, 167 insertions(+) create mode 100644 .Lib9c.Tests/Action/Guild/Migration/MigrateDelegationTest.cs create mode 100644 .Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigrateDelegationTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigrateDelegationTest.cs new file mode 100644 index 0000000000..b7e46ae0a3 --- /dev/null +++ b/.Lib9c.Tests/Action/Guild/Migration/MigrateDelegationTest.cs @@ -0,0 +1,107 @@ +namespace Lib9c.Tests.Action.Guild.Migration +{ + using System; + using System.Linq; + using System.Numerics; + using Bencodex.Types; + using Lib9c.Tests.Util; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Nekoyume; + using Nekoyume.Action; + using Nekoyume.Action.Guild; + using Nekoyume.Action.Guild.Migration; + using Nekoyume.Action.Guild.Migration.LegacyModels; + using Nekoyume.Extensions; + using Nekoyume.Model.Guild; + using Nekoyume.TypedAddress; + using Xunit; + + public class MigrateDelegationTest : GuildTestBase + { + [Fact] + public void Execute() + { + var guildAddress = AddressUtil.CreateGuildAddress(); + var world = EnsureLegacyPlanetariumGuild(World, guildAddress); + var guildMemberCount = 10; + var migratedGuildMemberCount = 5; + var guildMemberAddresses = Enumerable.Range(0, guildMemberCount).Select( + _ => AddressUtil.CreateAgentAddress()).ToList(); + for (var i = 0; i < guildMemberCount; i++) + { + world = EnsureJoinLegacyPlanetariumGuild(world, guildMemberAddresses[i]); + } + + world = EnsureMigratedPlanetariumGuild(world); + + for (var i = 0; i < migratedGuildMemberCount; i++) + { + var action = new MigrateDelegation(guildMemberAddresses[i]); + var actionContext = new ActionContext + { + PreviousState = world, + Signer = new PrivateKey().Address, + }; + world = action.Execute(actionContext); + } + + var repo = new GuildRepository(world, new ActionContext()); + var guild = repo.GetGuild(guildAddress); + + for (var i = 0; i < migratedGuildMemberCount; i++) + { + repo.GetGuildParticipant(guildMemberAddresses[i]); + } + + for (var i = migratedGuildMemberCount; i < guildMemberCount; i++) + { + Assert.Throws(() => repo.GetGuildParticipant(guildMemberAddresses[i])); + } + } + + private static IWorld EnsureLegacyPlanetariumGuild(IWorld world, GuildAddress guildAddress) + { + var legacyPlanetariumGuild = new LegacyGuild(GuildConfig.PlanetariumGuildOwner); + var legacyPlanetariumGuildParticipant = new LegacyGuildParticipant(guildAddress); + return world + .MutateAccount( + Addresses.Guild, + account => account.SetState(guildAddress, legacyPlanetariumGuild.Bencoded)) + .MutateAccount( + Addresses.GuildParticipant, + account => account.SetState(GuildConfig.PlanetariumGuildOwner, legacyPlanetariumGuildParticipant.Bencoded)); + } + + private static IWorld EnsureJoinLegacyPlanetariumGuild(IWorld world, AgentAddress guildParticipantAddress) + { + var planetariumGuildAddress + = new LegacyGuildParticipant( + world.GetAccount(Addresses.GuildParticipant).GetState(GuildConfig.PlanetariumGuildOwner) as List).GuildAddress; + var legacyParticipant = new LegacyGuildParticipant(planetariumGuildAddress); + + return world + .MutateAccount(Addresses.GuildParticipant, account => account.SetState(guildParticipantAddress, legacyParticipant.Bencoded)) + .MutateAccount( + Addresses.GuildMemberCounter, + account => + { + BigInteger count = account.GetState(planetariumGuildAddress) switch + { + Integer i => i.Value, + null => 0, + _ => throw new InvalidCastException(), + }; + + return account.SetState(planetariumGuildAddress, (Integer)(count + 1)); + }); + } + + private static IWorld EnsureMigratedPlanetariumGuild(IWorld world) + => new MigratePlanetariumGuild().Execute(new ActionContext + { + PreviousState = world, + Signer = new PrivateKey().Address, + }); + } +} diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs new file mode 100644 index 0000000000..8d150d629b --- /dev/null +++ b/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs @@ -0,0 +1,60 @@ +namespace Lib9c.Tests.Action.Guild.Migration +{ + using Lib9c.Tests.Util; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Nekoyume; + using Nekoyume.Action; + using Nekoyume.Action.Guild; + using Nekoyume.Action.Guild.Migration; + using Nekoyume.Action.Guild.Migration.LegacyModels; + using Nekoyume.Extensions; + using Nekoyume.Model.Guild; + using Nekoyume.TypedAddress; + using Xunit; + + public class MigratePlanetariumGuildTest : GuildTestBase + { + [Fact] + public void Execute() + { + var guildAddress = AddressUtil.CreateGuildAddress(); + var world = EnsureLegacyPlanetariumGuild(World, guildAddress); + + var action = new MigratePlanetariumGuild(); + var actionContext = new ActionContext + { + PreviousState = world, + Signer = new PrivateKey().Address, + }; + world = action.Execute(actionContext); + + var repo = new GuildRepository(world, new ActionContext()); + + var guild = repo.GetGuild(guildAddress); + Assert.Throws(() => repo.GetGuildParticipant(GuildConfig.PlanetariumGuildOwner)); + } + + private static IWorld EnsureLegacyPlanetariumGuild(IWorld world, GuildAddress guildAddress) + { + var legacyPlanetariumGuild = new LegacyGuild(GuildConfig.PlanetariumGuildOwner); + var legacyPlanetariumGuildParticipant = new LegacyGuildParticipant(guildAddress); + var guildAccount = world.GetAccount(Addresses.Guild); + var guildParticipantAccount = world.GetAccount(Addresses.GuildParticipant); + return world + .MutateAccount( + Addresses.Guild, + account => account.SetState(guildAddress, legacyPlanetariumGuild.Bencoded)) + .MutateAccount( + Addresses.GuildParticipant, + account => account.SetState(GuildConfig.PlanetariumGuildOwner, legacyPlanetariumGuildParticipant.Bencoded)); + } + + private static IWorld EnsureMigratedPlanetariumGuild(IWorld world) + => new MigratePlanetariumGuild().Execute(new ActionContext + { + PreviousState = world, + Signer = new PrivateKey().Address, + }); + } +} From b1d5e9fa58109e2d2cb7171e7ce3cf932c8c1fc2 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 1 Nov 2024 01:46:45 +0900 Subject: [PATCH 125/206] feat: Prepare unbonding for migration --- .../MigratePlanetariumGuildUnbond.cs | 56 +++++++++++++++++++ Lib9c/Delegation/DelegateeMetadata.cs | 10 +++- .../ValidatorDelegation/ValidatorDelegatee.cs | 3 +- 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs diff --git a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs new file mode 100644 index 0000000000..8a3302cbee --- /dev/null +++ b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs @@ -0,0 +1,56 @@ +using System; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Nekoyume.Model.Guild; + +namespace Nekoyume.Action.Guild.Migration +{ + // TODO: [GuildMigration] Remove this class when the migration is done. + // Enable this action on PoS release. + /// + /// An action to migrate the planetarium guild. + /// + // [ActionType(TypeIdentifier)] + public class MigratePlanetariumGuildUnbond : ActionBase + { + public const string TypeIdentifier = "migrate_planetarium_guild_unbond"; + + [Obsolete("Don't call in code.", error: false)] + public MigratePlanetariumGuildUnbond() + { + } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", Null.Value); + + public override void LoadPlainValue(IValue plainValue) + { + if (plainValue is not Dictionary root || + !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 GuildRepository(world, context); + + var guildAddress = repository.GetGuildParticipant(GuildConfig.PlanetariumGuildOwner).GuildAddress; + var guild = repository.GetGuild(guildAddress); + + // TODO: [GuildMigration] Replace below height when determined. + guild.Metadata.UpdateUnbondingPeriod(0L); + + repository.SetGuild(guild); + + return repository.World; + } + } +} diff --git a/Lib9c/Delegation/DelegateeMetadata.cs b/Lib9c/Delegation/DelegateeMetadata.cs index 971d20c366..916f979bf5 100644 --- a/Lib9c/Delegation/DelegateeMetadata.cs +++ b/Lib9c/Delegation/DelegateeMetadata.cs @@ -165,7 +165,7 @@ public Address Address public Address SlashedPoolAddress { get; } - public long UnbondingPeriod { get; } + public long UnbondingPeriod { get; private set; } public int MaxUnbondLockInEntries { get; } @@ -295,5 +295,13 @@ public virtual bool Equals(IDelegateeMetadata? other) public override int GetHashCode() => DelegateeAddress.GetHashCode(); + + // TODO: [GuildMigration] Remove this method when the migration is done. + // Remove private setter for UnbondingPeriod. + public void UpdateUnbondingPeriod(long unbondingPeriod) + { + UnbondingPeriod = unbondingPeriod; + } + } } diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index 050930b724..36b9fb6302 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -89,7 +89,8 @@ public ValidatorDelegatee( public static Currency ValidatorRewardCurrency => Currencies.Mead; - public static long ValidatorUnbondingPeriod => 10L; + // TODO: [MigrateGuild] Change unbonding period after migration. + public static long ValidatorUnbondingPeriod => 0L; public static int ValidatorMaxUnbondLockInEntries => 10; From 466cd1283d4b5f2eaa722393c160dcfa957ebc84 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 1 Nov 2024 01:48:29 +0900 Subject: [PATCH 126/206] test: Update benchmark and migration tests --- .../Actions/MigrateDelegation.cs | 128 ++++++++++++++++++ .../Guild/Migration/MigrateDelegationTest.cs | 17 ++- .../Migration/MigratePlanetariumGuildTest.cs | 1 + 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 .Lib9c.Benchmarks/Actions/MigrateDelegation.cs diff --git a/.Lib9c.Benchmarks/Actions/MigrateDelegation.cs b/.Lib9c.Benchmarks/Actions/MigrateDelegation.cs new file mode 100644 index 0000000000..01610e9d14 --- /dev/null +++ b/.Lib9c.Benchmarks/Actions/MigrateDelegation.cs @@ -0,0 +1,128 @@ +using BenchmarkDotNet.Attributes; +using Bencodex.Types; +using Lib9c.Tests.Action; +using Lib9c.Tests.Util; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Mocks; +using Nekoyume; +using Nekoyume.Action.Guild; +using Nekoyume.Action.Guild.Migration; +using Nekoyume.Action.Guild.Migration.LegacyModels; +using Nekoyume.Extensions; +using Nekoyume.TypedAddress; + +namespace Lib9c.Benchmarks.Actions; + +public class MigrateDelegation +{ + private GuildAddress planetariumGuild = AddressUtil.CreateGuildAddress(); + private AgentAddress target = AddressUtil.CreateAgentAddress(); + private AgentAddress signer = AddressUtil.CreateAgentAddress(); + private IWorld worldEmpty; + private IWorld worldBeforeGuildMigration; + private IWorld worldBeforeParticipantMigration; + private IWorld worldAfterMigration; + + [GlobalSetup] + public void Setup() + { + worldEmpty = new World(MockUtil.MockModernWorldState); + var legacyPlanetariumGuild = new LegacyGuild(GuildConfig.PlanetariumGuildOwner); + var legacyPlanetariumGuildParticipant = new LegacyGuildParticipant(planetariumGuild); + worldBeforeGuildMigration = worldEmpty + .MutateAccount( + Addresses.Guild, + account => account.SetState(planetariumGuild, legacyPlanetariumGuild.Bencoded)) + .MutateAccount( + Addresses.GuildParticipant, + account => account.SetState(GuildConfig.PlanetariumGuildOwner, legacyPlanetariumGuildParticipant.Bencoded)) + .MutateAccount( + Addresses.GuildParticipant, + account => account.SetState(target, legacyPlanetariumGuildParticipant.Bencoded)) + .MutateAccount( + Addresses.GuildMemberCounter, + account => account.SetState(planetariumGuild, (Integer)2)); + worldBeforeParticipantMigration = new MigratePlanetariumGuild().Execute(new ActionContext + { + PreviousState = worldBeforeGuildMigration, + Signer = new PrivateKey().Address, + }); + } + + [Benchmark] + public void Execute_Empty() + { + var action = new Nekoyume.Action.Guild.Migration.MigrateDelegation(target); + try + { + action.Execute(new ActionContext + { + IsPolicyAction = false, + PreviousState = worldEmpty, + Signer = signer, + }); + } + catch + { + // Do nothing. + } + } + + [Benchmark] + public void Execute_Before_Guild_Migration() + { + var action = new Nekoyume.Action.Guild.Migration.MigrateDelegation(target); + try + { + action.Execute(new ActionContext + { + IsPolicyAction = false, + PreviousState = worldBeforeGuildMigration, + Signer = signer, + }); + } + catch + { + // Do nothing. + } + } + + [Benchmark] + public void Execute_Before_Participant_Migration() + { + var action = new Nekoyume.Action.Guild.Migration.MigrateDelegation(target); + try + { + action.Execute(new ActionContext + { + IsPolicyAction = false, + PreviousState = worldBeforeParticipantMigration, + Signer = signer, + }); + } + catch + { + // Do nothing. + } + } + + [Benchmark] + public void Execute_After_Migration() + { + var action = new Nekoyume.Action.Guild.Migration.MigrateDelegation(target); + try + { + action.Execute(new ActionContext + { + IsPolicyAction = false, + PreviousState = worldAfterMigration, + Signer = signer, + }); + } + catch + { + // Do nothing. + } + } +} diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigrateDelegationTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigrateDelegationTest.cs index b7e46ae0a3..aa42c37100 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/MigrateDelegationTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/MigrateDelegationTest.cs @@ -17,6 +17,7 @@ namespace Lib9c.Tests.Action.Guild.Migration using Nekoyume.TypedAddress; using Xunit; + // TODO: Remove this test class after the migration is completed. public class MigrateDelegationTest : GuildTestBase { [Fact] @@ -64,13 +65,27 @@ private static IWorld EnsureLegacyPlanetariumGuild(IWorld world, GuildAddress gu { var legacyPlanetariumGuild = new LegacyGuild(GuildConfig.PlanetariumGuildOwner); var legacyPlanetariumGuildParticipant = new LegacyGuildParticipant(guildAddress); + return world .MutateAccount( Addresses.Guild, account => account.SetState(guildAddress, legacyPlanetariumGuild.Bencoded)) .MutateAccount( Addresses.GuildParticipant, - account => account.SetState(GuildConfig.PlanetariumGuildOwner, legacyPlanetariumGuildParticipant.Bencoded)); + account => account.SetState(GuildConfig.PlanetariumGuildOwner, legacyPlanetariumGuildParticipant.Bencoded)) + .MutateAccount( + Addresses.GuildMemberCounter, + account => + { + BigInteger count = account.GetState(guildAddress) switch + { + Integer i => i.Value, + null => 0, + _ => throw new InvalidCastException(), + }; + + return account.SetState(guildAddress, (Integer)(count + 1)); + }); } private static IWorld EnsureJoinLegacyPlanetariumGuild(IWorld world, AgentAddress guildParticipantAddress) diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs index 8d150d629b..82a8a1444f 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs @@ -13,6 +13,7 @@ namespace Lib9c.Tests.Action.Guild.Migration using Nekoyume.TypedAddress; using Xunit; + // TODO: Remove this test class after the migration is completed. public class MigratePlanetariumGuildTest : GuildTestBase { [Fact] From 43fb94dc8867b675f187afa520bbea8800744003 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 1 Nov 2024 01:49:21 +0900 Subject: [PATCH 127/206] test: Temporarily ignore failing tests because of migration --- .../Action/Guild/ClaimRewardGuildTest.cs | 4 ++-- .Lib9c.Tests/Action/Guild/MakeGuildTest.cs | 2 +- .../ValidatorDelegation/AllocateRewardTest.cs | 10 +++++----- .../ClaimRewardValidatorTest.cs | 10 +++++----- .../Action/ValidatorDelegation/ConstantTest.cs | 2 +- .../DelegateValidatorTest.cs | 14 +++++++------- .../PromoteValidatorTest.cs | 8 ++++---- .../ReleaseValidatorUnbondingsTest.cs | 4 ++-- .../SetValidatorCommissionTest.cs | 12 ++++++------ .../ValidatorDelegation/SlashValidatorTest.cs | 6 +++--- .../UndelegateValidatorTest.cs | 18 +++++++++--------- .../ValidatorDelegation/UnjailValidatorTest.cs | 10 +++++----- .../UpdateValidatorsTest.cs | 4 ++-- 13 files changed, 52 insertions(+), 52 deletions(-) diff --git a/.Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs b/.Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs index a8ee2f06db..df741fd800 100644 --- a/.Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs @@ -21,7 +21,7 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Fact] + [Fact(Skip = "Allow after bond on actual block height")] public void Execute() { // Given @@ -78,7 +78,7 @@ public void Execute() } } - [Fact] + [Fact(Skip = "Allow after bond on actual block height")] public void SkipDuplicatedClaim() { // Given diff --git a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs index e770874209..2a69503535 100644 --- a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs @@ -43,7 +43,7 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute() { IWorld world = World; diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs index 432fd8f751..2bbf44ce76 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -65,7 +65,7 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute() { var fixture = new StaticFixture @@ -83,7 +83,7 @@ public void Execute() ExecuteWithFixture(fixture); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [InlineData(1, 1000)] [InlineData(33, 33)] [InlineData(33, 33.33)] @@ -109,7 +109,7 @@ public void Execute_Theory(int validatorCount, double totalReward) ExecuteWithFixture(fixture); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_WithoutReward_Throw() { var fixture = new StaticFixture @@ -127,7 +127,7 @@ public void Execute_WithoutReward_Throw() Assert.Throws(() => ExecuteWithFixture(fixture)); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [InlineData(0)] [InlineData(1181126949)] [InlineData(793705868)] @@ -138,7 +138,7 @@ public void Execute_Theory_WithStaticSeed(int randomSeed) ExecuteWithFixture(fixture); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [MemberData(nameof(RandomSeeds))] public void Execute_Theory_WithRandomSeed(int randomSeed) { diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs index dcca4a3add..1939653765 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -60,7 +60,7 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute() { // Given @@ -92,7 +92,7 @@ public void Execute() Assert.Equal(expectedBalance, actualBalance); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [InlineData(33.33)] [InlineData(11.11)] [InlineData(10)] @@ -131,7 +131,7 @@ public void Execute_Theory_OneDelegator(decimal totalReward) ExecuteWithFixture(fixture); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [InlineData(0.1)] [InlineData(1)] [InlineData(3)] @@ -190,7 +190,7 @@ public void Execute_Theory_TwoDelegators(decimal totalReward) ExecuteWithFixture(fixture); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [InlineData(0)] [InlineData(123)] [InlineData(34352535)] @@ -200,7 +200,7 @@ public void Execute_Theory_WithStaticSeed(int randomSeed) ExecuteWithFixture(fixture); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [MemberData(nameof(RandomSeeds))] public void Execute_Theory_WithRandomSeed(int randomSeed) { diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ConstantTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ConstantTest.cs index 95039f7586..f03a1a7b93 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ConstantTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ConstantTest.cs @@ -6,7 +6,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; public class ConstantTest { - [Fact] + [Fact(Skip = "Allow after positive unbonding period")] public void StaticPropertyTest() { Assert.True(ValidatorDelegatee.ValidatorUnbondingPeriod > 0); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index 3a4790116c..2aa9715aae 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -41,7 +41,7 @@ public void Serialization() Assert.Equal(gold, deserialized.FAV); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute() { // Given @@ -76,7 +76,7 @@ public void Execute() Assert.Equal(DelegationCurrency * 80, GetBalance(world, validatorKey.Address)); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_Fact() { var fixture = new StaticFixture @@ -91,7 +91,7 @@ public void Execute_Fact() ExecuteWithFixture(fixture); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [InlineData(0)] [InlineData(1181126949)] [InlineData(793705868)] @@ -102,7 +102,7 @@ public void Execute_Fact_WithStaticSeed(int randomSeed) ExecuteWithFixture(fixture); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [MemberData(nameof(RandomSeeds))] public void Execute_Fact_WithRandomSeed(int randomSeed) { @@ -110,7 +110,7 @@ public void Execute_Fact_WithRandomSeed(int randomSeed) ExecuteWithFixture(fixture); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_WithInvalidCurrency_Throw() { // Given @@ -136,7 +136,7 @@ public void Execute_WithInvalidCurrency_Throw() Assert.Throws(() => delegateValidator.Execute(actionContext)); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_WithInsufficientBalance_Throw() { // Given @@ -183,7 +183,7 @@ public void Execute_ToInvalidValidator_Throw() Assert.Throws(() => delegateValidator.Execute(actionContext)); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_ToTombstonedValidator_Throw() { // Given diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs index 23aa0683cd..2763fce43b 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs @@ -23,7 +23,7 @@ public void Serialization() Assert.Equal(gold, deserialized.FAV); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute() { // Given @@ -57,7 +57,7 @@ public void Execute() Assert.Empty(validatorList.GetUnbonded()); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_ToInvalidValidator_Throw() { // Given @@ -106,7 +106,7 @@ public void Execute_WithInvalidCurrency_Throw() () => promoteValidator.Execute(actionContext)); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_WithInsufficientBalance_Throw() { // Given @@ -129,7 +129,7 @@ public void Execute_WithInsufficientBalance_Throw() () => promoteValidator.Execute(actionContext)); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_PromotedValidator_Throw() { // Given diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs index 5f8ae913fd..ff7c6098da 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs @@ -20,7 +20,7 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute() { // Given @@ -65,7 +65,7 @@ public void Execute() Assert.Equal(expectedReleaseCount - 1, actualReleaseCount); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_ThereIsNoUnbonding_AtEarlyHeight() { // Given diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs index 6886fa952c..163ebe54fa 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs @@ -60,7 +60,7 @@ public void Serialization() Assert.Equal(commissionPercentage, deserialized.CommissionPercentage); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute() { // Given @@ -90,7 +90,7 @@ public void Execute() Assert.Equal(11, actualPercentage); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [InlineData(9, 10)] [InlineData(9, 8)] [InlineData(0, 1)] @@ -127,7 +127,7 @@ public void Execute_Theory(int oldCommissionPercentage, int newCommissionPercent Assert.Equal(newCommissionPercentage, actualPercentage); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [MemberData(nameof(RandomInvalidCommisionPercentage))] public void Execute_Theory_WithValueGreaterThanMaximum_Throw(int commissionPercentage) { @@ -156,7 +156,7 @@ public void Execute_Theory_WithValueGreaterThanMaximum_Throw(int commissionPerce () => setValidatorCommission.Execute(actionContext)); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [InlineData(-1)] [InlineData(-2)] public void Execute_Theory_WithNegative_Throw(int commissionPercentage) @@ -186,7 +186,7 @@ public void Execute_Theory_WithNegative_Throw(int commissionPercentage) () => setValidatorCommission.Execute(actionContext)); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [MemberData(nameof(InvalidCommisionPercentageCooldown))] public void Execute_Theory_WithInvalidValue_Throw(int cooldown) { @@ -214,7 +214,7 @@ public void Execute_Theory_WithInvalidValue_Throw(int cooldown) () => setValidatorCommission.Execute(actionContext)); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [MemberData(nameof(ValidCommisionPercentagePeriod))] public void Execute_Theory_WitValue(int period) { diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs index 3173039718..4c53a112f7 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs @@ -28,7 +28,7 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute() { // Given @@ -132,7 +132,7 @@ public void Execute_ToNotPromotedValidator_Throw() Assert.Throws(() => slashValidator.Execute(actionContext)); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_ByAbstain() { // Given @@ -170,7 +170,7 @@ public void Execute_ByAbstain() Assert.False(delegatee.Tombstoned); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_ToJailedValidator_ThenNothingHappens() { // Given diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 1ec9f52680..422b685417 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -43,7 +43,7 @@ public void Serialization() Assert.Equal(share, deserialized.Share); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute() { // Given @@ -82,7 +82,7 @@ public void Execute() Assert.Equal(BigInteger.Zero, actualBond.Share); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_Theory() { var fixture = new StaticFixture @@ -98,7 +98,7 @@ public void Execute_Theory() ExecuteWithFixture(fixture); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [InlineData(0)] [InlineData(1181126949)] [InlineData(793705868)] @@ -109,7 +109,7 @@ public void Execute_Theory_WithStaticSeed(int randomSeed) ExecuteWithFixture(fixture); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [MemberData(nameof(RandomSeeds))] public void Execute_Theory_WithRandomSeed(int randomSeed) { @@ -117,7 +117,7 @@ public void Execute_Theory_WithRandomSeed(int randomSeed) ExecuteWithFixture(fixture); } - [Fact] + [Fact(Skip ="Allow after Planetarium validator restriction")] public void Execute_FromInvalidValidtor_Throw() { // Given @@ -141,7 +141,7 @@ public void Execute_FromInvalidValidtor_Throw() () => undelegateValidator.Execute(actionContext)); } - [Theory] + [Theory(Skip = "Allow after Planetarium validator restriction")] [InlineData(0)] [InlineData(-1)] public void Execute_WithNotPositiveShare_Throw(long share) @@ -168,7 +168,7 @@ public void Execute_WithNotPositiveShare_Throw(long share) () => undelegateValidator.Execute(actionContext)); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_FromJailedValidator() { // Given @@ -201,7 +201,7 @@ public void Execute_FromJailedValidator() Assert.Equal(expectedBond.Share - 10, actualBond.Share); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_FromTombstonedValidator() { // Given @@ -234,7 +234,7 @@ public void Execute_FromTombstonedValidator() Assert.Equal(expectedBond.Share - 10, actualBond.Share); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_JailsValidatorWhenUndelegationCausesLowDelegation() { // Given diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs index 6bf61fd62e..d61882c9ec 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs @@ -20,7 +20,7 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute() { // Given @@ -71,7 +71,7 @@ public void Execute_OnNotPromotedValidator_Throw() Assert.Throws(() => unjailValidator.Execute(actionContext)); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_OnNotJailedValidator_Throw() { // Given @@ -94,7 +94,7 @@ public void Execute_OnNotJailedValidator_Throw() Assert.Throws(() => unjailValidator.Execute(actionContext)); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_TooEarly_Throw() { // Given @@ -119,7 +119,7 @@ public void Execute_TooEarly_Throw() () => unjailValidator.Execute(actionContext)); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_OnTombstonedValidator_Throw() { // Given @@ -144,7 +144,7 @@ public void Execute_OnTombstonedValidator_Throw() () => unjailValidator.Execute(actionContext)); } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_OnLowDelegatedValidator_Throw() { // Given diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs index 741ac87b41..32e6119542 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs @@ -22,7 +22,7 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Fact] + [Fact(Skip ="Allow after Planetarium validator restriction")] public void Execute() { // Given @@ -58,7 +58,7 @@ public void Execute() } } - [Fact] + [Fact(Skip = "Allow after Planetarium validator restriction")] public void Execute_ExcludesTombstonedValidator() { // Given From 6e7e225cf05fabe40eb7ce8bedcd51e809e6e581 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 1 Nov 2024 11:22:02 +0900 Subject: [PATCH 128/206] fix: Fix GQL for migration --- ...edge_to_guild.ts => migrate_delegation.ts} | 8 +++---- .../src/actions/migrate_planetarium_guild.ts | 10 +++++++++ @planetarium/lib9c/src/index.ts | 12 +++++++---- .../tests/actions/migrate_delegation.test.ts | 12 +++++++++++ .../actions/migrate_planetarium_guild.test.ts | 7 +++++++ .../actions/migrate_pledge_to_guild.test.ts | 21 ------------------- .../Guild/Migration/MigrateDelegation.cs | 1 + 7 files changed, 42 insertions(+), 29 deletions(-) rename @planetarium/lib9c/src/actions/{migrate_pledge_to_guild.ts => migrate_delegation.ts} (65%) create mode 100644 @planetarium/lib9c/src/actions/migrate_planetarium_guild.ts create mode 100644 @planetarium/lib9c/tests/actions/migrate_delegation.test.ts create mode 100644 @planetarium/lib9c/tests/actions/migrate_planetarium_guild.test.ts delete mode 100644 @planetarium/lib9c/tests/actions/migrate_pledge_to_guild.test.ts diff --git a/@planetarium/lib9c/src/actions/migrate_pledge_to_guild.ts b/@planetarium/lib9c/src/actions/migrate_delegation.ts similarity index 65% rename from @planetarium/lib9c/src/actions/migrate_pledge_to_guild.ts rename to @planetarium/lib9c/src/actions/migrate_delegation.ts index 5185cd04cc..4eec55ee3b 100644 --- a/@planetarium/lib9c/src/actions/migrate_pledge_to_guild.ts +++ b/@planetarium/lib9c/src/actions/migrate_delegation.ts @@ -2,16 +2,16 @@ import type { Address } from "@planetarium/account"; import { BencodexDictionary, type Value } from "@planetarium/bencodex"; import { PolymorphicAction } from "./common.js"; -export type MigratePledgeToGuildArgs = { +export type MigrateDelegationArgs = { target: Address; }; -export class MigratePledgeToGuild extends PolymorphicAction { - protected readonly type_id: string = "migrate_pledge_to_guild"; +export class MigrateDelegation extends PolymorphicAction { + protected readonly type_id: string = "migrate_delegation"; private readonly target: Address; - constructor({ target }: MigratePledgeToGuildArgs) { + constructor({ target }: MigrateDelegationArgs) { super(); this.target = target; diff --git a/@planetarium/lib9c/src/actions/migrate_planetarium_guild.ts b/@planetarium/lib9c/src/actions/migrate_planetarium_guild.ts new file mode 100644 index 0000000000..f8a772c07e --- /dev/null +++ b/@planetarium/lib9c/src/actions/migrate_planetarium_guild.ts @@ -0,0 +1,10 @@ +import type { Value } from "@planetarium/bencodex"; +import { PolymorphicAction } from "./common.js"; + +export class MigratePlanetariumGuild extends PolymorphicAction { + protected readonly type_id: string = "migrate_planetarium_guild"; + + protected plain_value(): Value { + return null; + } +} diff --git a/@planetarium/lib9c/src/index.ts b/@planetarium/lib9c/src/index.ts index bf11110b53..80c336a447 100644 --- a/@planetarium/lib9c/src/index.ts +++ b/@planetarium/lib9c/src/index.ts @@ -53,7 +53,11 @@ export { type ApprovePledgeArgs, } from "./actions/approve_pledge.js"; export { - MigratePledgeToGuild, - type MigratePledgeToGuildArgs, -} from "./actions/migrate_pledge_to_guild.js"; -export { MakeGuild } from "./actions/make_guild.js"; + MakeGuild, + type MakeGuildArgs, +} from "./actions/make_guild.js"; +export { MigratePlanetariumGuild } from "./actions/migrate_planetarium_guild.js"; +export { + MigrateDelegation, + type MigrateDelegationArgs, +} from "./actions/migrate_delegation.js"; diff --git a/@planetarium/lib9c/tests/actions/migrate_delegation.test.ts b/@planetarium/lib9c/tests/actions/migrate_delegation.test.ts new file mode 100644 index 0000000000..49ab47e9bb --- /dev/null +++ b/@planetarium/lib9c/tests/actions/migrate_delegation.test.ts @@ -0,0 +1,12 @@ +import { describe } from "vitest"; +import { MigrateDelegation } from "../../src/index.js"; +import { runTests } from "./common.js"; +import { agentAddress } from "./fixtures.js"; + +describe("MigrateDelegation", () => { + runTests("valid case", [ + new MigrateDelegation({ + target: agentAddress, + }), + ]); +}); diff --git a/@planetarium/lib9c/tests/actions/migrate_planetarium_guild.test.ts b/@planetarium/lib9c/tests/actions/migrate_planetarium_guild.test.ts new file mode 100644 index 0000000000..67d0e243cb --- /dev/null +++ b/@planetarium/lib9c/tests/actions/migrate_planetarium_guild.test.ts @@ -0,0 +1,7 @@ +import { describe } from "vitest"; +import { MigratePlanetariumGuild } from "../../src/index.js"; +import { runTests } from "./common.js"; + +describe("MigratePlanetariumGuild", () => { + runTests("valid case", [new MigratePlanetariumGuild()]); +}); diff --git a/@planetarium/lib9c/tests/actions/migrate_pledge_to_guild.test.ts b/@planetarium/lib9c/tests/actions/migrate_pledge_to_guild.test.ts deleted file mode 100644 index 7a9bd383da..0000000000 --- a/@planetarium/lib9c/tests/actions/migrate_pledge_to_guild.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { describe } from "vitest"; -import { MigratePledgeToGuild } from "../../src/index.js"; -import { runTests } from "./common.js"; -import { agentAddress } from "./fixtures.js"; - -describe("MigratePledgeToGuild", () => { - describe("odin", () => { - runTests("valid case", [ - new MigratePledgeToGuild({ - target: agentAddress, - }), - ]); - }); - describe("heimdall", () => { - runTests("valid case", [ - new MigratePledgeToGuild({ - target: agentAddress, - }), - ]); - }); -}); diff --git a/Lib9c/Action/Guild/Migration/MigrateDelegation.cs b/Lib9c/Action/Guild/Migration/MigrateDelegation.cs index 9ffce23042..3e4102ffbe 100644 --- a/Lib9c/Action/Guild/Migration/MigrateDelegation.cs +++ b/Lib9c/Action/Guild/Migration/MigrateDelegation.cs @@ -17,6 +17,7 @@ namespace Nekoyume.Action.Guild.Migration /// /// An action to migrate guild delegation. /// + [ActionType(TypeIdentifier)] public class MigrateDelegation : ActionBase { public const string TypeIdentifier = "migrate_delegation"; From 1ce26ebfe031a98d29d33fa2d462bc0705ea5a28 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 1 Nov 2024 15:45:47 +0900 Subject: [PATCH 129/206] fix: Build fix from rebase --- .Lib9c.Tests/Action/ApprovePledgeTest.cs | 22 +++++++++++++++---- .Lib9c.Tests/Action/GrindingTest.cs | 5 +++-- .../Action/MigrateMonsterCollectionTest.cs | 2 +- Lib9c/Action/ApprovePledge.cs | 7 ++++-- .../CustomEquipmentCraft.cs | 2 +- Lib9c/Action/UnlockCombinationSlot.cs | 2 +- 6 files changed, 29 insertions(+), 11 deletions(-) diff --git a/.Lib9c.Tests/Action/ApprovePledgeTest.cs b/.Lib9c.Tests/Action/ApprovePledgeTest.cs index a2b9179a29..a3b4d9caa3 100644 --- a/.Lib9c.Tests/Action/ApprovePledgeTest.cs +++ b/.Lib9c.Tests/Action/ApprovePledgeTest.cs @@ -6,9 +6,11 @@ namespace Lib9c.Tests.Action using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Mocks; + using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; using Nekoyume.Action.Guild; + using Nekoyume.Model.Guild; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.Module.Guild; @@ -45,7 +47,8 @@ public void Execute(int mead) Assert.Equal(patron, contract[0].ToAddress()); Assert.True(contract[1].ToBoolean()); Assert.Equal(mead, contract[2].ToInteger()); - Assert.Null(nextState.GetJoinedGuild(new AgentAddress(address))); + Assert.Null(new GuildRepository(nextState, new ActionContext()) + .GetJoinedGuild(new AgentAddress(address))); } [Theory] @@ -58,11 +61,21 @@ public void Execute_JoinGuild(int mead) var contractAddress = address.Derive(nameof(RequestPledge)); var guildAddress = AddressUtil.CreateGuildAddress(); IWorld states = new World(MockUtil.MockModernWorldState) + .SetLegacyState( + Addresses.GoldCurrency, + new GoldCurrencyState(Currency.Legacy("NCG", 2, null)).Serialize()) .SetLegacyState( contractAddress, List.Empty.Add(patron.Serialize()).Add(false.Serialize()).Add(mead.Serialize()) - ) - .MakeGuild(guildAddress, GuildConfig.PlanetariumGuildOwner); + ); + + states = new GuildRepository( + states, + new ActionContext + { + Signer = GuildConfig.PlanetariumGuildOwner, + }) + .MakeGuild(guildAddress, GuildConfig.PlanetariumGuildOwner).World; var action = new ApprovePledge { @@ -78,7 +91,8 @@ public void Execute_JoinGuild(int mead) Assert.Equal(patron, contract[0].ToAddress()); Assert.True(contract[1].ToBoolean()); Assert.Equal(mead, contract[2].ToInteger()); - var joinedGuildAddress = nextState.GetJoinedGuild(new AgentAddress(address)); + var joinedGuildAddress = new GuildRepository(nextState, new ActionContext()) + .GetJoinedGuild(new AgentAddress(address)); Assert.NotNull(joinedGuildAddress); Assert.Equal(guildAddress, joinedGuildAddress); } diff --git a/.Lib9c.Tests/Action/GrindingTest.cs b/.Lib9c.Tests/Action/GrindingTest.cs index 11a941d080..6e0fb6d6b9 100644 --- a/.Lib9c.Tests/Action/GrindingTest.cs +++ b/.Lib9c.Tests/Action/GrindingTest.cs @@ -13,6 +13,7 @@ namespace Lib9c.Tests.Action using Nekoyume.Action; using Nekoyume.Model.Item; using Nekoyume.Model.Mail; + using Nekoyume.Model.Stake; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.TableData; @@ -138,8 +139,8 @@ public void Execute_Success_With_StakeState( Assert.Equal(0 * _crystalCurrency, state.GetBalance(_avatarAddress, _crystalCurrency)); // StakeState; - var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); - var stakeState = new StakeState(stakeStateAddress, 1); + var stakeStateAddress = LegacyStakeState.DeriveAddress(_agentAddress); + var stakeState = new LegacyStakeState(stakeStateAddress, 1); var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows .FirstOrDefault(r => r.Level == monsterCollectLevel)?.RequiredGold ?? 0; diff --git a/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs b/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs index e892851b08..6c8b8d7788 100644 --- a/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs +++ b/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs @@ -72,7 +72,7 @@ public void Execute_ThrowsIfAlreadyStakeStateExists() Address stakeStateAddress = LegacyStakeState.DeriveAddress(_signer); var states = _state.SetLegacyState( stakeStateAddress, new LegacyStakeState(stakeStateAddress, 0).SerializeV2()) - .SetLegacyState(monsterCollectionAddress, monsterCollectionState.SerializeV2()); + .SetLegacyState(monsterCollectionAddress, monsterCollectionState.Serialize()); MigrateMonsterCollection action = new MigrateMonsterCollection(_avatarAddress); Assert.Throws(() => action.Execute(new ActionContext { diff --git a/Lib9c/Action/ApprovePledge.cs b/Lib9c/Action/ApprovePledge.cs index 7d69303d97..d25271b59e 100644 --- a/Lib9c/Action/ApprovePledge.cs +++ b/Lib9c/Action/ApprovePledge.cs @@ -4,6 +4,7 @@ using Libplanet.Crypto; using Nekoyume.Action.Guild; using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.Module.Guild; @@ -49,9 +50,11 @@ public override IWorld Execute(IActionContext context) throw new AlreadyContractedException($"{signer} already contracted."); } - if (PatronAddress == MeadConfig.PatronAddress && states.GetJoinedGuild(GuildConfig.PlanetariumGuildOwner) is { } guildAddress) + var repository = new GuildRepository(states, context); + if (PatronAddress == MeadConfig.PatronAddress + && repository.GetJoinedGuild(GuildConfig.PlanetariumGuildOwner) is { } guildAddress) { - states = states.JoinGuild(guildAddress, context.GetAgentAddress()); + states = repository.JoinGuild(guildAddress, context.GetAgentAddress()).World; } return states.SetLegacyState( diff --git a/Lib9c/Action/CustomEquipmentCraft/CustomEquipmentCraft.cs b/Lib9c/Action/CustomEquipmentCraft/CustomEquipmentCraft.cs index a52ea9d7e6..9ca9d6e823 100644 --- a/Lib9c/Action/CustomEquipmentCraft/CustomEquipmentCraft.cs +++ b/Lib9c/Action/CustomEquipmentCraft/CustomEquipmentCraft.cs @@ -70,7 +70,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { - context.UseGas(1); + GasTracer.UseGas(1); var states = context.PreviousState; // Validate duplicated slot indices in action diff --git a/Lib9c/Action/UnlockCombinationSlot.cs b/Lib9c/Action/UnlockCombinationSlot.cs index 030c70a470..aa54d055f9 100644 --- a/Lib9c/Action/UnlockCombinationSlot.cs +++ b/Lib9c/Action/UnlockCombinationSlot.cs @@ -45,7 +45,7 @@ protected override void LoadPlainValueInternal(IImmutableDictionary Date: Fri, 1 Nov 2024 16:34:32 +0900 Subject: [PATCH 130/206] chore: Prohibit MakeGuild from empty validator --- Lib9c/Module/Guild/GuildModule.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Lib9c/Module/Guild/GuildModule.cs b/Lib9c/Module/Guild/GuildModule.cs index 3bbfd053eb..5d46d499d4 100644 --- a/Lib9c/Module/Guild/GuildModule.cs +++ b/Lib9c/Module/Guild/GuildModule.cs @@ -9,6 +9,7 @@ using Nekoyume.TypedAddress; using Libplanet.Crypto; using Nekoyume.ValidatorDelegation; +using Nekoyume.Module.ValidatorDelegation; namespace Nekoyume.Module.Guild { @@ -50,6 +51,12 @@ public static GuildRepository MakeGuild( throw new InvalidOperationException("Duplicated guild address. Please retry."); } + var validatorRepository = new ValidatorRepository(repository.World, repository.ActionContext); + if (!validatorRepository.TryGetValidatorDelegatee(validatorAddress, out _)) + { + throw new InvalidOperationException("The validator does not exist."); + } + var guild = new Model.Guild.Guild( guildAddress, signer, validatorAddress, repository.World.GetGoldCurrency(), repository); repository.SetGuild(guild); From 6271364370f9d95931606efb2132dfdbfa2c80fc Mon Sep 17 00:00:00 2001 From: s2quake Date: Fri, 1 Nov 2024 19:38:13 +0900 Subject: [PATCH 131/206] feat: Add InitializeValidator action for test --- Lib9c/Action/InitializeValidator.cs | 106 ++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 Lib9c/Action/InitializeValidator.cs diff --git a/Lib9c/Action/InitializeValidator.cs b/Lib9c/Action/InitializeValidator.cs new file mode 100644 index 0000000000..381cabb68a --- /dev/null +++ b/Lib9c/Action/InitializeValidator.cs @@ -0,0 +1,106 @@ +using System; +using System.Linq; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Types.Assets; +using Libplanet.Types.Consensus; +using Nekoyume; +using Nekoyume.Action; +using Nekoyume.Model.State; +using Nekoyume.Module; +using Nekoyume.Module.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; + +namespace Nekoyume.Action +{ + [ActionType(TypeIdentifier)] + public sealed class InitializeValidator : ActionBase + { + public const string TypeIdentifier = "initialize_validator"; + + public InitializeValidator( + ValidatorSet validatorSet, + Currency goldCurrency) + { + Validators = validatorSet.Validators.ToArray(); + GoldCurrency = goldCurrency; + } + + public InitializeValidator() + { + } + + public Validator[] Validators { get; set; } + + public Currency GoldCurrency { get; set; } + + public override IValue PlainValue + => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("validator_set", new List(Validators.Select(item => item.Bencoded))) + .Add("gold_currency", GoldCurrency.Serialize()); + + public override void LoadPlainValue(IValue plainValue) + { + if (plainValue is not Dictionary dict || + dict["validator_set"] is not List list || + dict["gold_currency"] is not Dictionary currencyDict) + { + throw new InvalidCastException("Invalid types"); + } + + Validators = list.Select(item => new Validator((Dictionary)item)).ToArray(); + GoldCurrency = new Currency(currencyDict); + } + + public override IWorld Execute(IActionContext context) + { + var world = context.PreviousState; + + var goldCurrency = GoldCurrency; + var currencyState = new GoldCurrencyState(goldCurrency); + world = world + .SetLegacyState(GoldCurrencyState.Address, currencyState.Serialize()) + .SetLegacyState(Addresses.GoldDistribution, new List().Serialize()); + + if (currencyState.InitialSupply > 0) + { + world = world.MintAsset( + context, + GoldCurrencyState.Address, + currencyState.Currency * currencyState.InitialSupply); + } + + var repository = new ValidatorRepository(world, context); + var validators = Validators; + foreach (var validator in validators) + { + var validatorDelegatee = new ValidatorDelegatee( + validator.OperatorAddress, + validator.PublicKey, + ValidatorDelegatee.DefaultCommissionPercentage, + context.BlockIndex, + repository); + var delegationFAV = FungibleAssetValue.FromRawValue( + validatorDelegatee.DelegationCurrency, validator.Power); + var validatorOperatorAddress = validator.OperatorAddress; + var validatorDelegator = repository.GetValidatorDelegator( + validatorOperatorAddress, validatorOperatorAddress); + + repository.SetValidatorDelegatee(validatorDelegatee); + repository.UpdateWorld( + repository.World.MintAsset( + repository.ActionContext, + validatorDelegator.DelegationPoolAddress, + delegationFAV)); + validatorDelegator.Delegate(validatorDelegatee, delegationFAV, context.BlockIndex); + } + + repository.SetAbstainHistory(new()); + world = repository.World; + + return world; + } + } +} From 979b4abf78a6c5437d71db5e2b59baa3879dddc8 Mon Sep 17 00:00:00 2001 From: Jeesu Choi Date: Fri, 1 Nov 2024 14:21:36 +0900 Subject: [PATCH 132/206] Merge pull request #2961 from s2quake/refactor/stake-action Refactor Stake action for PoS --- .../ClaimAdventureBossRewardTest.cs | 31 ++ .../AdventureBoss/SweepAdventureBossTest.cs | 5 + .../Action/AdventureBoss/UnlockFloorTest.cs | 5 + .../Action/AdventureBoss/WantedTest.cs | 6 + .../Scenario/StakeAndClaimScenarioTest.cs | 9 +- .Lib9c.Tests/Action/StakeTest.cs | 301 +++++------------- .../ValidatorDelegation/AllocateRewardTest.cs | 8 +- .../ValidatorDelegationTestBase.cs | 4 +- .Lib9c.Tests/Util/DelegationUtil.cs | 120 +++++++ Lib9c/Action/Stake.cs | 80 +++-- .../ReleaseValidatorUnbondings.cs | 54 +++- Lib9c/Model/Stake/Contract.cs | 1 + Lib9c/Model/Stake/StakeState.cs | 1 + Lib9c/Module/Guild/GuildParticipantModule.cs | 2 +- 14 files changed, 351 insertions(+), 276 deletions(-) create mode 100644 .Lib9c.Tests/Util/DelegationUtil.cs diff --git a/.Lib9c.Tests/Action/AdventureBoss/ClaimAdventureBossRewardTest.cs b/.Lib9c.Tests/Action/AdventureBoss/ClaimAdventureBossRewardTest.cs index 598b36b047..6135bbc564 100644 --- a/.Lib9c.Tests/Action/AdventureBoss/ClaimAdventureBossRewardTest.cs +++ b/.Lib9c.Tests/Action/AdventureBoss/ClaimAdventureBossRewardTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action.AdventureBoss using System.Collections.Generic; using System.Linq; using System.Numerics; + using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Mocks; @@ -386,6 +387,10 @@ AdventureBossGameData.ClaimableReward expectedReward state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } + var validatorKey = new PrivateKey(); + state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); + + state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey, 0L); state = Stake(state, TesterAddress); // Wanted @@ -404,6 +409,7 @@ AdventureBossGameData.ClaimableReward expectedReward if (anotherWanted) { + state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey, 0L); state = Stake(state, WantedAddress); state = new Wanted { @@ -471,6 +477,11 @@ public void WantedMultipleSeason() state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } + var validatorKey = new PrivateKey(); + state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey, 0L); + state = Stake(state, TesterAddress); state = Stake(state, WantedAddress); @@ -564,6 +575,10 @@ FungibleAssetValue expectedRemainingNcg state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } + var validatorKey = new PrivateKey(); + state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey, 0L); + state = Stake(state, WantedAddress); // Wanted @@ -737,6 +752,12 @@ public void ExploreMultipleSeason() state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } + var validatorKey = new PrivateKey(); + state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, ExplorerAddress, validatorKey, 0L); + state = Stake(state, TesterAddress); state = Stake(state, WantedAddress); state = Stake(state, ExplorerAddress); @@ -865,6 +886,10 @@ public void AllReward() state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } + var validatorKey = new PrivateKey(); + state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey, 0L); + state = Stake(state, TesterAddress); // Wanted @@ -934,6 +959,12 @@ Type exc state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } + var validatorKey = new PrivateKey(); + state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, ExplorerAddress, validatorKey, 0L); + state = Stake(state, TesterAddress); state = Stake(state, WantedAddress); state = Stake(state, ExplorerAddress); diff --git a/.Lib9c.Tests/Action/AdventureBoss/SweepAdventureBossTest.cs b/.Lib9c.Tests/Action/AdventureBoss/SweepAdventureBossTest.cs index 93bf24913d..29838195f0 100644 --- a/.Lib9c.Tests/Action/AdventureBoss/SweepAdventureBossTest.cs +++ b/.Lib9c.Tests/Action/AdventureBoss/SweepAdventureBossTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action.AdventureBoss using System.Collections.Generic; using System.Linq; using System.Numerics; + using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Mocks; @@ -130,6 +131,10 @@ public void Execute( state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } + var validatorKey = new PrivateKey(); + state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey, 0L); + state = Stake(state, WantedAddress); var sheets = state.GetSheets(sheetTypes: new[] { diff --git a/.Lib9c.Tests/Action/AdventureBoss/UnlockFloorTest.cs b/.Lib9c.Tests/Action/AdventureBoss/UnlockFloorTest.cs index 80b2f201b1..01cf0885bf 100644 --- a/.Lib9c.Tests/Action/AdventureBoss/UnlockFloorTest.cs +++ b/.Lib9c.Tests/Action/AdventureBoss/UnlockFloorTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action.AdventureBoss using System.Collections.Generic; using System.Linq; using System.Numerics; + using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Mocks; @@ -108,6 +109,10 @@ public void Execute( state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } + var validatorKey = new PrivateKey(); + state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey, 0L); + state = Stake(state, WantedAddress); var materialSheet = state.GetSheet(); var goldenDust = diff --git a/.Lib9c.Tests/Action/AdventureBoss/WantedTest.cs b/.Lib9c.Tests/Action/AdventureBoss/WantedTest.cs index 60528d50c6..e0c9704a64 100644 --- a/.Lib9c.Tests/Action/AdventureBoss/WantedTest.cs +++ b/.Lib9c.Tests/Action/AdventureBoss/WantedTest.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Action.AdventureBoss using System.Collections.Generic; using System.Linq; using System.Numerics; + using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Mocks; @@ -14,6 +15,7 @@ namespace Lib9c.Tests.Action.AdventureBoss using Nekoyume.Exceptions; using Nekoyume.Helper; using Nekoyume.Model.AdventureBoss; + using Nekoyume.Model.Guild; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.TableData; @@ -574,6 +576,10 @@ private IWorld Stake(IWorld world, long amount = 0) amount = stakeRegularRewardSheet[requiredStakingLevel].RequiredGold; } + var validatorKey = new PrivateKey(); + world = DelegationUtil.EnsureValidatorPromotionReady(world, validatorKey, 0L); + world = DelegationUtil.MakeGuild(world, AgentAddress, validatorKey, 0L); + var action = new Stake(new BigInteger(amount)); return action.Execute(new ActionContext { diff --git a/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs index bb8b1761b5..aeba853b03 100644 --- a/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs @@ -15,6 +15,7 @@ namespace Lib9c.Tests.Action.Scenario using Nekoyume.Module; using Nekoyume.TableData; using Nekoyume.TableData.Stake; + using Nekoyume.ValidatorDelegation; using Serilog; using Xunit; using Xunit.Abstractions; @@ -108,8 +109,14 @@ public void Test() LegacyStakeState.RewardInterval, LegacyStakeState.LockupInterval); + var validatorKey = new PrivateKey(); + state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, _agentAddr, validatorKey, 0L); + // Withdraw stake via stake3. - state = Stake3(state, _agentAddr, 0, stake2BlockIndex + LegacyStakeState.LockupInterval + 1); + state = Stake3(state, _agentAddr, 0, stake2BlockIndex); + + state = DelegationUtil.EnsureStakeReleased(state, stake2BlockIndex + ValidatorDelegatee.ValidatorUnbondingPeriod); // Stake 50 NCG via stake3 before patching. const long firstStake3BlockIndex = stake2BlockIndex + LegacyStakeState.LockupInterval + 1; diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index 6ceba5fd62..3c9c486f87 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -11,11 +11,15 @@ namespace Lib9c.Tests.Action using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; + using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Exceptions; + using Nekoyume.Model.Guild; using Nekoyume.Model.Stake; using Nekoyume.Model.State; using Nekoyume.Module; + using Nekoyume.Module.ValidatorDelegation; using Nekoyume.TableData.Stake; + using Nekoyume.ValidatorDelegation; using Serilog; using Xunit; using Xunit.Abstractions; @@ -284,249 +288,103 @@ public void Execute_Throw_StakeExistingClaimableException_With_StakeStateV2( previousAmount)); } - [Theory] - // NOTE: minimum required_gold of StakeRegularRewardSheetFixtures.V2 is 50. - // NOTE: LockupInterval of StakePolicySheetFixtures.V2 is 201,600. - [InlineData(0, 50 + 1, 201_600 - 1, 50)] - [InlineData( - long.MaxValue - 201_600, - 50 + 1, - long.MaxValue - 1, - 50)] - public void - Execute_Throw_RequiredBlockIndexException_Via_Reduced_Amount_When_Lucked_Up_With_StakeState( - long previousStartedBlockIndex, - long previousAmount, - long blockIndex, - long reducedAmount) - { - var stakeStateAddr = LegacyStakeState.DeriveAddress(_agentAddr); - var stakeState = new LegacyStakeState( - address: stakeStateAddr, - startedBlockIndex: previousStartedBlockIndex); - Assert.False(stakeState.IsCancellable(blockIndex)); - stakeState.Claim(blockIndex); - Assert.False(stakeState.IsClaimable(blockIndex)); - var previousState = _initialState - .MintAsset( - new ActionContext { Signer = Addresses.Admin }, - stakeStateAddr, - _ncg * previousAmount) - .SetLegacyState(stakeStateAddr, stakeState.Serialize()); - Assert.Throws(() => - Execute( - blockIndex, - previousState, - new TestRandom(), - _agentAddr, - reducedAmount)); - } - - [Theory] - // NOTE: minimum required_gold of StakeRegularRewardSheetFixtures.V2 is 50. - // NOTE: LockupInterval of StakePolicySheetFixtures.V2 is 201,600. - [InlineData(0, 50 + 1, 201_600 - 1, 50)] - [InlineData( - long.MaxValue - 201_600, - 50 + 1, - long.MaxValue - 1, - 50)] - public void - Execute_Throw_RequiredBlockIndexException_Via_Reduced_Amount_When_Locked_Up_With_StakeStateV2( - long previousStartedBlockIndex, - long previousAmount, - long blockIndex, - long reducedAmount) - { - var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); - var stakeStateV2 = new StakeState( - contract: new Contract(_stakePolicySheet), - startedBlockIndex: previousStartedBlockIndex, - receivedBlockIndex: blockIndex); - var previousState = _initialState - .MintAsset( - new ActionContext { Signer = Addresses.Admin }, - stakeStateAddr, - _ncg * previousAmount) - .SetLegacyState(stakeStateAddr, stakeStateV2.Serialize()); - Assert.Throws(() => - Execute( - blockIndex, - previousState, - new TestRandom(), - _agentAddr, - reducedAmount)); - } - [Theory] // NOTE: minimum required_gold of StakeRegularRewardSheetFixtures.V2 is 50. [InlineData(50)] [InlineData(long.MaxValue)] public void Execute_Success_When_Staking_State_Null(long amount) { - var previousState = _initialState.MintAsset( + var world = _initialState.MintAsset( new ActionContext { Signer = Addresses.Admin }, _agentAddr, _ncg * amount); + var height = 0L; + + var validatorKey = new PrivateKey(); + world = DelegationUtil.EnsureValidatorPromotionReady(world, validatorKey, height++); + world = DelegationUtil.MakeGuild(world, _agentAddr, validatorKey.Address, height++); + Execute( 0, - previousState, + world, new TestRandom(), _agentAddr, amount); + + world = DelegationUtil.EnsureStakeReleased( + world, height + ValidatorDelegatee.ValidatorUnbondingPeriod); } [Theory] - // NOTE: minimum required_gold of StakeRegularRewardSheetFixtures.V2 is 50. - // NOTE: non claimable, locked up, same amount. - [InlineData(0, 50, 0, 50)] - [InlineData(0, 50, LegacyStakeState.LockupInterval - 1, 50)] - [InlineData(0, long.MaxValue, 0, long.MaxValue)] - [InlineData(0, long.MaxValue, LegacyStakeState.LockupInterval - 1, long.MaxValue)] - // NOTE: non claimable, locked up, increased amount. - [InlineData(0, 50, 0, 500)] - [InlineData(0, 50, LegacyStakeState.LockupInterval - 1, 500)] - // NOTE: non claimable, unlocked, same amount. - [InlineData(0, 50, LegacyStakeState.LockupInterval, 50)] - [InlineData(0, long.MaxValue, LegacyStakeState.LockupInterval, long.MaxValue)] - // NOTE: non claimable, unlocked, increased amount. - [InlineData(0, 50, LegacyStakeState.LockupInterval, 500)] - // NOTE: non claimable, unlocked, decreased amount. - [InlineData(0, 50, LegacyStakeState.LockupInterval, 0)] - [InlineData(0, long.MaxValue, LegacyStakeState.LockupInterval, 50)] - public void Execute_Success_When_Exist_StakeState( - long previousStartedBlockIndex, + // NOTE: non + [InlineData(50, 50)] + [InlineData(long.MaxValue, long.MaxValue)] + // NOTE: delegate + [InlineData(0, 500)] + [InlineData(50, 100)] + [InlineData(0, long.MaxValue)] + // NOTE: undelegate + [InlineData(50, 0)] + [InlineData(long.MaxValue, 0)] + [InlineData(long.MaxValue, 500)] + public void Execute_Success_When_Exist_StakeStateV3( long previousAmount, - long blockIndex, long amount) { - var stakeStateAddr = LegacyStakeState.DeriveAddress(_agentAddr); - var stakeState = new LegacyStakeState( - address: stakeStateAddr, - startedBlockIndex: previousStartedBlockIndex); - stakeState.Claim(blockIndex); - var previousState = _initialState - .MintAsset( - new ActionContext(), - _agentAddr, - _ncg * Math.Max(previousAmount, amount)) - .TransferAsset( - new ActionContext(), - _agentAddr, - stakeStateAddr, - _ncg * previousAmount) - .SetLegacyState(stakeStateAddr, stakeState.Serialize()); - var nextState = Execute( - blockIndex, - previousState, - new TestRandom(), - _agentAddr, - amount); + var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); + var stakeStateV2 = new StakeState( + contract: new Contract(_stakePolicySheet), + startedBlockIndex: 0L, + stateVersion: 3); + var world = _initialState; + var height = 0L; - if (amount == 0) + var validatorKey = new PrivateKey(); + world = DelegationUtil.EnsureValidatorPromotionReady(world, validatorKey, height); + world = DelegationUtil.MakeGuild(world, _agentAddr, validatorKey.Address, height); + if (previousAmount > 0) { - return; + var ncgToStake = _ncg * previousAmount; + var gg = FungibleAssetValue.Parse(Currencies.GuildGold, ncgToStake.GetQuantityString(true)); + world = DelegationUtil.MintGuildGold(world, _agentAddr, gg, height); + world = world.MintAsset(new ActionContext(), _agentAddr, ncgToStake); + world = world.TransferAsset( + new ActionContext(), _agentAddr, stakeStateAddr, ncgToStake); + + var guildRepository = new GuildRepository(world, new ActionContext { Signer = _agentAddr }); + var guildParticipant = guildRepository.GetGuildParticipant(_agentAddr); + var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); + guildParticipant.Delegate(guild, gg, height++); + world = guildRepository.World; } - Assert.True(nextState.TryGetStakeState(_agentAddr, out StakeState newStakeState)); - Assert.Equal(3, newStakeState.StateVersion); - } + world = world.SetLegacyState(stakeStateAddr, stakeStateV2.Serialize()); + + if (amount - previousAmount > 0) + { + var ncgToStake = _ncg * (amount - previousAmount); + world = world.MintAsset(new ActionContext(), _agentAddr, ncgToStake); + } - [Theory] - // NOTE: minimum required_gold of StakeRegularRewardSheetFixtures.V2 is 50. - // NOTE: LockupInterval of StakePolicySheetFixtures.V2 is 201,600. - // NOTE: non claimable, locked up, same amount. - [InlineData(0, 50, 0, 50)] - [InlineData(0, 50, 201_599, 50)] - [InlineData(0, long.MaxValue, 0, long.MaxValue)] - [InlineData(0, long.MaxValue, 201_599, long.MaxValue)] - // NOTE: non claimable, locked up, increased amount. - [InlineData(0, 50, 0, 500)] - [InlineData(0, 50, 201_599, 500)] - // NOTE: non claimable, unlocked, same amount. - [InlineData(0, 50, 201_600, 50)] - [InlineData(0, long.MaxValue, 201_600, long.MaxValue)] - // NOTE: non claimable, unlocked, increased amount. - [InlineData(0, 50, 201_600, 500)] - // NOTE: non claimable, unlocked, decreased amount. - [InlineData(0, 50, 201_600, 0)] - [InlineData(0, long.MaxValue, LegacyStakeState.LockupInterval, 50)] - public void Execute_Success_When_Exist_StakeStateV2( - long previousStartedBlockIndex, - long previousAmount, - long blockIndex, - long amount) - { - var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); - var stakeStateV2 = new StakeState( - contract: new Contract(_stakePolicySheet), - startedBlockIndex: previousStartedBlockIndex, - receivedBlockIndex: blockIndex, - stateVersion: 2); - var previousState = _initialState - .MintAsset( - new ActionContext(), - _agentAddr, - _ncg * Math.Max(previousAmount, amount)) - .TransferAsset( - new ActionContext(), - _agentAddr, - stakeStateAddr, - _ncg * previousAmount) - .SetLegacyState(stakeStateAddr, stakeStateV2.Serialize()); var nextState = Execute( - blockIndex, - previousState, + height, + world, new TestRandom(), _agentAddr, amount); - if (amount == 0) + if (amount > 0) { - return; + Assert.True(nextState.TryGetStakeState(_agentAddr, out StakeState stakeState)); + Assert.Equal(3, stakeState.StateVersion); } - Assert.True(nextState.TryGetStakeState(_agentAddr, out StakeState stakeState)); - Assert.Equal(3, stakeState.StateVersion); - } - - [Fact] - public void Execute_Success_When_Exist_StakeStateV3() - { - var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); - var stakeStateV2 = new StakeState( - contract: new Contract(_stakePolicySheet), - startedBlockIndex: 0, - receivedBlockIndex: 0, - stateVersion: 3); + world = DelegationUtil.EnsureStakeReleased( + nextState, height + LegacyStakeState.LockupInterval); - var previousState = _initialState - .MintAsset( - new ActionContext(), - _agentAddr, - _ncg * 100) - .TransferAsset( - new ActionContext(), - _agentAddr, - stakeStateAddr, - _ncg * 50) - .MintAsset(new ActionContext(), stakeStateAddr, Currencies.GuildGold * 50) - .SetLegacyState(stakeStateAddr, stakeStateV2.Serialize()); - - var action = new Stake(100); - var nextState = action.Execute(new ActionContext - { - BlockIndex = 0, - Signer = _agentAddr, - PreviousState = previousState, - }); - - Assert.Equal( - Currencies.GuildGold * 100, - nextState.GetBalance(stakeStateAddr, Currencies.GuildGold)); - Assert.Equal( - _ncg * 100, - nextState.GetBalance(stakeStateAddr, _ncg)); + var expectedBalance = _ncg * Math.Max(0, previousAmount - amount); + var actualBalance = world.GetBalance(_agentAddr, _ncg); + Assert.Equal(expectedBalance, actualBalance); } private IWorld Execute( @@ -536,11 +394,6 @@ private IWorld Execute( Address signer, long amount) { - var previousBalance = previousState.GetBalance(signer, _ncg); - var previousStakeBalance = previousState.GetBalance( - LegacyStakeState.DeriveAddress(signer), - _ncg); - var previousTotalBalance = previousBalance + previousStakeBalance; var action = new Stake(amount); var nextState = action.Execute(new ActionContext { @@ -550,18 +403,15 @@ private IWorld Execute( Signer = signer, }); - var stakeStateAddress = LegacyStakeState.DeriveAddress(signer); + var guildRepository = new GuildRepository(nextState, new ActionContext()); + var guildParticipant = guildRepository.GetGuildParticipant(signer); + var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); + var bond = guildRepository.GetBond(guild, signer); var amountNCG = _ncg * amount; - var nextBalance = nextState.GetBalance(signer, _ncg); - var nextStakeBalance = nextState.GetBalance( - stakeStateAddress, - _ncg); - Assert.Equal(previousTotalBalance - amountNCG, nextBalance); - Assert.Equal(amountNCG, nextStakeBalance); - Assert.Equal( - Currencies.GuildGold * amount, - nextState.GetBalance(stakeStateAddress, Currencies.GuildGold)); + var expectedGG = DelegationUtil.GetGuildCoinFromNCG(amountNCG); + var expectedShare = guild.ShareFromFAV(expectedGG); + Assert.Equal(expectedShare, bond.Share); if (amount == 0) { @@ -584,9 +434,6 @@ private IWorld Execute( stakeStateV2.Contract.LockupInterval); Assert.Equal(blockIndex, stakeStateV2.StartedBlockIndex); Assert.Equal(0, stakeStateV2.ReceivedBlockIndex); - Assert.Equal( - blockIndex + stakeStateV2.Contract.LockupInterval, - stakeStateV2.CancellableBlockIndex); Assert.Equal(blockIndex, stakeStateV2.ClaimedBlockIndex); Assert.Equal( blockIndex + stakeStateV2.Contract.RewardInterval, diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs index 2bbf44ce76..0d2c306134 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -132,6 +132,7 @@ public void Execute_WithoutReward_Throw() [InlineData(1181126949)] [InlineData(793705868)] [InlineData(707058493)] + [InlineData(37149681)] public void Execute_Theory_WithStaticSeed(int randomSeed) { var fixture = new RandomFixture(randomSeed); @@ -301,12 +302,13 @@ public RandomFixture(int randomSeed) ValidatorsInfos = CreateArray(_random.Next(1, 200), i => { var balance = GetRandomFAV(DelegationCurrency, _random); - var flag = _random.Next() % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null; + var cash = GetRandomCash(_random, balance); + var flag = i == 0 || _random.Next() % 2 == 0 ? VoteFlag.PreCommit : VoteFlag.Null; return new ValidatorInfo { Key = new PrivateKey(), - Balance = balance, - Cash = GetRandomCash(_random, balance), + Balance = balance < MinimumDelegation ? MinimumDelegation : balance, + Cash = cash < MinimumDelegation ? MinimumDelegation : cash, VoteFlag = flag, }; }); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 7ad175762b..ad40712287 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -45,12 +45,12 @@ public ValidatorDelegationTestBase() protected static BlockHash EmptyBlockHash { get; } = new BlockHash(CreateArray(BlockHash.Size, _ => (byte)0x01)); + protected static FungibleAssetValue MinimumDelegation { get; } = DelegationCurrency * 10; + protected PrivateKey AdminKey { get; } = new PrivateKey(); protected IWorld World { get; } - protected FungibleAssetValue MinimumDelegation { get; } = DelegationCurrency * 10; - protected static T[] CreateArray(int length, Func creator) => Enumerable.Range(0, length).Select(creator).ToArray(); diff --git a/.Lib9c.Tests/Util/DelegationUtil.cs b/.Lib9c.Tests/Util/DelegationUtil.cs new file mode 100644 index 0000000000..f50d44fa4e --- /dev/null +++ b/.Lib9c.Tests/Util/DelegationUtil.cs @@ -0,0 +1,120 @@ +namespace Lib9c.Tests.Util +{ + using System; + using Lib9c.Tests.Action; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Types.Assets; + using Nekoyume.Action.Guild; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.Model.Stake; + using Nekoyume.TableData.Stake; + + public static class DelegationUtil + { + public static IWorld MintGuildGold( + IWorld world, PrivateKey privateKey, FungibleAssetValue amount, long blockHeight) + => MintGuildGold(world, privateKey.Address, amount, blockHeight); + + public static IWorld MintGuildGold( + IWorld world, Address address, FungibleAssetValue amount, long blockHeight) + { + if (!amount.Currency.Equals(Currencies.GuildGold)) + { + throw new ArgumentException( + $"The currency of the amount must be {Currencies.GuildGold}.", + nameof(amount) + ); + } + + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight, + }; + var poolAddress = StakeState.DeriveAddress(address); + return world.MintAsset(actionContext, poolAddress, amount); + } + + public static IWorld PromoteValidator( + IWorld world, + PrivateKey validatorKey, + FungibleAssetValue amount, + long blockHeight) + { + var validatorPublicKey = validatorKey.PublicKey; + var promoteValidator = new PromoteValidator(validatorPublicKey, amount); + + var actionContext = new ActionContext + { + PreviousState = world, + Signer = validatorPublicKey.Address, + BlockIndex = blockHeight, + }; + return promoteValidator.Execute(actionContext); + } + + public static IWorld MakeGuild( + IWorld world, Address guildMasterAddress, PrivateKey validatorKey, long blockHeight) + => MakeGuild(world, guildMasterAddress, validatorKey.Address, blockHeight); + + public static IWorld MakeGuild( + IWorld world, Address guildMasterAddress, Address validatorAddress, long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } + + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight, + Signer = guildMasterAddress, + RandomSeed = Random.Shared.Next(), + }; + var makeGuild = new MakeGuild(validatorAddress); + return makeGuild.Execute(actionContext); + } + + public static FungibleAssetValue GetGuildCoinFromNCG(FungibleAssetValue balance) + { + return FungibleAssetValue.Parse(Currencies.GuildGold, balance.GetQuantityString(true)); + } + + public static IWorld EnsureValidatorPromotionReady( + IWorld world, PrivateKey validatorKey, long blockHeight) + { + world = MintGuildGold(world, validatorKey, Currencies.GuildGold * 10, blockHeight); + world = PromoteValidator(world, validatorKey, Currencies.GuildGold * 10, blockHeight); + return world; + } + + public static IWorld EnsureGuildParticipentIsStaked( + IWorld world, + Address agentAddress, + FungibleAssetValue ncg, + StakePolicySheet stakePolicySheet, + long blockHeight) + { + return world; + } + + public static IWorld EnsureStakeReleased( + IWorld world, long blockHeight) + { + if (blockHeight < 0) + { + throw new ArgumentOutOfRangeException(nameof(blockHeight)); + } + + var actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight, + }; + var releaseValidatorUnbondings = new ReleaseValidatorUnbondings(); + return releaseValidatorUnbondings.Execute(actionContext); + } + } +} diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index 7733be8048..3304ca563e 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -10,11 +10,17 @@ using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Exceptions; +using Nekoyume.Extensions; +using Nekoyume.Model.Guild; using Nekoyume.Model.Stake; using Nekoyume.Model.State; using Nekoyume.Module; +using Nekoyume.Module.Guild; +using Nekoyume.Module.ValidatorDelegation; using Nekoyume.TableData; using Nekoyume.TableData.Stake; +using Nekoyume.ValidatorDelegation; +using Org.BouncyCastle.Asn1.Esf; using Serilog; using static Lib9c.SerializeKeys; @@ -124,7 +130,7 @@ public override IWorld Execute(IActionContext context) context, states, stakeStateAddress, - stakedBalance: null, + stakedBalance: currency * 0, targetStakeBalance, latestStakeContract); Log.Debug( @@ -140,16 +146,6 @@ public override IWorld Execute(IActionContext context) throw new StakeExistingClaimableException(); } - // NOTE: When the staking state is locked up. - if (stakeStateV2.CancellableBlockIndex > context.BlockIndex) - { - // NOTE: Cannot re-contract with less balance. - if (targetStakeBalance < stakedBalance) - { - throw new RequiredBlockIndexException(); - } - } - if (stakeStateV2.StateVersion == 2) { if (!StakeStateUtils.TryMigrateV2ToV3(context, states, stakeStateAddress, stakeStateV2, out var result)) @@ -160,13 +156,6 @@ public override IWorld Execute(IActionContext context) states = result.Value.world; } - // NOTE: Withdraw staking. - if (Amount == 0) - { - return Refund(context, states, stakeStateAddress, context.Signer, stakedBalance) - .RemoveLegacyState(stakeStateAddress); - } - // NOTE: Contract a new staking. states = ContractNewStake( context, @@ -186,36 +175,45 @@ private static IWorld ContractNewStake( IActionContext context, IWorld state, Address stakeStateAddr, - FungibleAssetValue? stakedBalance, + FungibleAssetValue stakedBalance, FungibleAssetValue targetStakeBalance, Contract latestStakeContract) { - var newStakeState = new StakeState(latestStakeContract, context.BlockIndex); + var stakeStateValue = new StakeState(latestStakeContract, context.BlockIndex).Serialize(); + var additionalBalance = targetStakeBalance - stakedBalance; + var height = context.BlockIndex; - if (stakedBalance.HasValue) + if (additionalBalance.Sign > 0) { - state = Refund(context, state, stakeStateAddr, context.Signer, stakedBalance.Value); + var gg = GetGuildCoinFromNCG(additionalBalance); + var guildRepository = new GuildRepository(state, context); + var guildParticipant = guildRepository.GetGuildParticipant(context.Signer); + var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); + guildRepository.UpdateWorld( + state.TransferAsset(context, context.Signer, stakeStateAddr, additionalBalance) + .MintAsset(context, stakeStateAddr, gg)); + + guildParticipant.Delegate(guild, gg, height); + state = guildRepository.World; + } + else if (additionalBalance.Sign < 0) + { + var gg = GetGuildCoinFromNCG(-additionalBalance); + var guildRepository = new GuildRepository(state, context); + var guildParticipant = guildRepository.GetGuildParticipant(context.Signer); + var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); + var share = guild.ShareFromFAV(gg); + guildParticipant.Undelegate(guild, share, height); + state = guildRepository.World; + if ((stakedBalance + additionalBalance).Sign == 0) + { + return state.MutateAccount( + ReservedAddresses.LegacyAccount, + state => state.RemoveState(stakeStateAddr)); + } } - return state - .TransferAsset(context, context.Signer, stakeStateAddr, targetStakeBalance) - .MintAsset(context, stakeStateAddr, GetGuildCoinFromNCG(targetStakeBalance)) - .SetLegacyState(stakeStateAddr, newStakeState.Serialize()); - } - - private static IWorld Refund( - IActionContext context, IWorld world, Address stakeStateAddress, Address signer, - FungibleAssetValue stakedBalance) - { - return world.BurnAsset( - context, - stakeStateAddress, - GetGuildCoinFromNCG(stakedBalance) - ).TransferAsset( - context, - stakeStateAddress, - signer, - stakedBalance); + return state.SetLegacyState(stakeStateAddr, stakeStateValue); } private static FungibleAssetValue GetGuildCoinFromNCG(FungibleAssetValue balance) diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs index 9ac68cd66c..edc5aac653 100644 --- a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -7,6 +7,13 @@ using System; using System.Linq; using System.Collections.Immutable; +using Libplanet.Types.Assets; +using System.Numerics; +using Nekoyume.Model.Guild; +using Nekoyume.Module.Guild; +using Nekoyume.TypedAddress; +using Nekoyume.Module; +using Nekoyume.Model.Stake; namespace Nekoyume.Action.ValidatorDelegation { @@ -38,7 +45,11 @@ public override IWorld Execute(IActionContext context) switch (unbonding) { case UnbondLockIn unbondLockIn: - repository.SetUnbondLockIn(unbondLockIn); + { + repository.SetUnbondLockIn(unbondLockIn); + repository.UpdateWorld( + Unstake(repository.World, context, unbondLockIn.DelegatorAddress)); + } break; case RebondGrace rebondGrace: repository.SetRebondGrace(rebondGrace); @@ -52,5 +63,46 @@ public override IWorld Execute(IActionContext context) return repository.World; } + + private IWorld Unstake(IWorld world, IActionContext context, Address address) + { + var agentAddress = new AgentAddress(address); + var guildRepository = new GuildRepository(world, context); + if (guildRepository.TryGetGuildParticipant(agentAddress, out var guildParticipant)) + { + var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); + var goldCurrency = world.GetGoldCurrency(); + var stakeStateAddress = guildParticipant.DelegationPoolAddress; + var gg = world.GetBalance(stakeStateAddress, guild.DelegationCurrency); + if (gg.Sign > 0) + { + var (ncg, _) = ConvertToGoldCurrency(gg, goldCurrency); + world = world.BurnAsset(context, stakeStateAddress, gg); + world = world.TransferAsset( + context, stakeStateAddress, address, ncg); + } + } + + return world; + } + + private static (FungibleAssetValue Gold, FungibleAssetValue Remainder) + ConvertToGoldCurrency(FungibleAssetValue fav, Currency targetCurrency) + { + var sourceCurrency = fav.Currency; + if (targetCurrency.DecimalPlaces < sourceCurrency.DecimalPlaces) + { + var d = BigInteger.Pow(10, sourceCurrency.DecimalPlaces - targetCurrency.DecimalPlaces); + var value = FungibleAssetValue.FromRawValue(targetCurrency, fav.RawValue / d); + var fav2 = FungibleAssetValue.FromRawValue(sourceCurrency, value.RawValue * d); + return (value, fav - fav2); + } + else + { + var d = BigInteger.Pow(10, targetCurrency.DecimalPlaces - sourceCurrency.DecimalPlaces); + var value = FungibleAssetValue.FromRawValue(targetCurrency, fav.RawValue * d); + return (value, targetCurrency * 0); + } + } } } diff --git a/Lib9c/Model/Stake/Contract.cs b/Lib9c/Model/Stake/Contract.cs index 738ed66b0b..6a5464712b 100644 --- a/Lib9c/Model/Stake/Contract.cs +++ b/Lib9c/Model/Stake/Contract.cs @@ -18,6 +18,7 @@ public const string StakeRegularRewardSheetPrefix public string StakeRegularFixedRewardSheetTableName { get; } public string StakeRegularRewardSheetTableName { get; } public long RewardInterval { get; } + [Obsolete("Not used because of guild system")] public long LockupInterval { get; } public Contract(StakePolicySheet stakePolicySheet) : this( diff --git a/Lib9c/Model/Stake/StakeState.cs b/Lib9c/Model/Stake/StakeState.cs index 6ac1e628c7..261983e225 100644 --- a/Lib9c/Model/Stake/StakeState.cs +++ b/Lib9c/Model/Stake/StakeState.cs @@ -18,6 +18,7 @@ public static Address DeriveAddress(Address address) => public readonly long StartedBlockIndex; public readonly long ReceivedBlockIndex; + [Obsolete("Not used because of guild system")] public long CancellableBlockIndex => StartedBlockIndex + Contract.LockupInterval; diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index c095bccf65..a228243853 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -119,7 +119,7 @@ public static GuildRepository LeaveGuild( return repository; } - private static bool TryGetGuildParticipant( + public static bool TryGetGuildParticipant( this GuildRepository repository, AgentAddress agentAddress, [NotNullWhen(true)] out Model.Guild.GuildParticipant? guildParticipant) From f670c6d62cae66748d69d477f9be87a982c66ddb Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sat, 2 Nov 2024 15:50:40 +0900 Subject: [PATCH 133/206] chore: Minor changes for migration --- Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs | 2 +- Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs | 2 +- Lib9c/Action/ValidatorDelegation/PromoteValidator.cs | 2 +- Lib9c/Action/ValidatorDelegation/ValidatorConfig.cs | 5 ++++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs b/Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs index aa5aa3dcf0..dc1ecbfb03 100644 --- a/Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs +++ b/Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs @@ -3,6 +3,6 @@ namespace Nekoyume.Action.Guild.Migration // TODO: [GuildMigration] Remove this class when the migration is done. public static class GuildMigrationConfig { - public static readonly long MigrateDelegationHeight = 55555L; + public static readonly long MigrateDelegationHeight = 12302304L; } } diff --git a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs index 5f87717295..9d46a667fe 100644 --- a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs +++ b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs @@ -58,7 +58,7 @@ public override IWorld Execute(IActionContext context) var guild = new Model.Guild.Guild( guildAddress, legacyGuild.GuildMasterAddress, - ValidatorConfig.PlanetariumValidator, + ValidatorConfig.PlanetariumValidatorAddress, Currencies.GuildGold, repository); repository.SetGuild(guild); diff --git a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs index 974b274740..fb51387212 100644 --- a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs @@ -63,7 +63,7 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); // TODO: Remove this check when to deliver features to users. - if (context.Signer != ValidatorConfig.PlanetariumValidator) + if (context.Signer != ValidatorConfig.PlanetariumValidatorAddress) { throw new InvalidOperationException( $"This action is not allowed for {context.Signer}."); diff --git a/Lib9c/Action/ValidatorDelegation/ValidatorConfig.cs b/Lib9c/Action/ValidatorDelegation/ValidatorConfig.cs index ddd5c1d791..985f631c29 100644 --- a/Lib9c/Action/ValidatorDelegation/ValidatorConfig.cs +++ b/Lib9c/Action/ValidatorDelegation/ValidatorConfig.cs @@ -4,7 +4,10 @@ namespace Nekoyume.Action.ValidatorDelegation { public static class ValidatorConfig { - public static readonly Address PlanetariumValidator = + public static readonly Address PlanetariumValidatorAddress = new Address("0x8E1b572db70aB80bb02783A0D2c594A0edE6db28"); + + public static readonly PublicKey PlanetariumValidatorPublicKey = + PublicKey.FromHex("03a0f95711564d10c60ba1889d068c26cb8e5fcd5211d5aeb8810e133d629aa306"); } } From f4b622d0007a8957f041659e1091ea932fdf58e3 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 4 Nov 2024 14:00:34 +0900 Subject: [PATCH 134/206] chore: Minor fixes for migration phase --- Lib9c/Action/Guild/MakeGuild.cs | 21 ++++++--- Lib9c/Action/Stake.cs | 46 +++++++++++++------ .../ValidatorDelegation/PromoteValidator.cs | 12 +++-- Lib9c/Delegation/Bond.cs | 15 +++--- 4 files changed, 62 insertions(+), 32 deletions(-) diff --git a/Lib9c/Action/Guild/MakeGuild.cs b/Lib9c/Action/Guild/MakeGuild.cs index 7c490f73b0..a5fac12d97 100644 --- a/Lib9c/Action/Guild/MakeGuild.cs +++ b/Lib9c/Action/Guild/MakeGuild.cs @@ -44,7 +44,21 @@ rawValues is not Dictionary values || ValidatorAddress = new Address(rawValidatorAddress); } + // TODO: Replace this with ExecutePublic when to deliver features to users. public override IWorld Execute(IActionContext context) + { + var world = ExecutePublic(context); + + if (context.Signer != GuildConfig.PlanetariumGuildOwner) + { + throw new InvalidOperationException( + $"This action is not allowed for {context.Signer}."); + } + + return world; + } + + public IWorld ExecutePublic(IActionContext context) { GasTracer.UseGas(1); @@ -55,13 +69,6 @@ public override IWorld Execute(IActionContext context) var guildAddress = new GuildAddress(random.GenerateAddress()); var validatorAddress = ValidatorAddress; - // TODO: Remove this check when to deliver features to users. - if (context.Signer != GuildConfig.PlanetariumGuildOwner) - { - throw new InvalidOperationException( - $"This action is not allowed for {context.Signer}."); - } - repository.MakeGuild(guildAddress, validatorAddress); return repository.World; } diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index 3304ca563e..cbff5cb2c2 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -186,25 +186,41 @@ private static IWorld ContractNewStake( if (additionalBalance.Sign > 0) { var gg = GetGuildCoinFromNCG(additionalBalance); - var guildRepository = new GuildRepository(state, context); - var guildParticipant = guildRepository.GetGuildParticipant(context.Signer); - var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); - guildRepository.UpdateWorld( - state.TransferAsset(context, context.Signer, stakeStateAddr, additionalBalance) - .MintAsset(context, stakeStateAddr, gg)); - - guildParticipant.Delegate(guild, gg, height); - state = guildRepository.World; + state = state + .TransferAsset(context, context.Signer, stakeStateAddr, additionalBalance) + .MintAsset(context, stakeStateAddr, gg); + + try + { + var guildRepository = new GuildRepository(state, context); + var guildParticipant = guildRepository.GetGuildParticipant(context.Signer); + var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); + guildParticipant.Delegate(guild, gg, height); + state = guildRepository.World; + } + catch (FailedLoadStateException) + { + } + } else if (additionalBalance.Sign < 0) { var gg = GetGuildCoinFromNCG(-additionalBalance); - var guildRepository = new GuildRepository(state, context); - var guildParticipant = guildRepository.GetGuildParticipant(context.Signer); - var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); - var share = guild.ShareFromFAV(gg); - guildParticipant.Undelegate(guild, share, height); - state = guildRepository.World; + + try + { + var guildRepository = new GuildRepository(state, context); + var guildParticipant = guildRepository.GetGuildParticipant(context.Signer); + var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); + var share = guild.ShareFromFAV(gg); + guildParticipant.Undelegate(guild, share, height); + state = guildRepository.World; + } + catch (FailedLoadStateException) + { + // TODO: Create self unbonding + } + if ((stakedBalance + additionalBalance).Sign == 0) { return state.MutateAccount( diff --git a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs index fb51387212..9ab759d2c3 100644 --- a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs @@ -58,17 +58,23 @@ public override void LoadPlainValue(IValue plainValue) CommissionPercentage = (Integer)values[2]; } + // TODO: Remove this with ExecutePublic when to deliver features to users. public override IWorld Execute(IActionContext context) { - GasTracer.UseGas(1); - - // TODO: Remove this check when to deliver features to users. + var world = ExecutePublic(context); if (context.Signer != ValidatorConfig.PlanetariumValidatorAddress) { throw new InvalidOperationException( $"This action is not allowed for {context.Signer}."); } + return world; + } + + public IWorld ExecutePublic(IActionContext context) + { + GasTracer.UseGas(1); + var world = context.PreviousState; var repository = new ValidatorRepository(world, context); diff --git a/Lib9c/Delegation/Bond.cs b/Lib9c/Delegation/Bond.cs index 3f208dc5e0..e0d057d4a2 100644 --- a/Lib9c/Delegation/Bond.cs +++ b/Lib9c/Delegation/Bond.cs @@ -107,13 +107,14 @@ internal Bond SubtractShare(BigInteger share) internal Bond UpdateLastDistributeHeight(long height) { - if (LastDistributeHeight.HasValue && LastDistributeHeight >= height) - { - throw new ArgumentOutOfRangeException( - nameof(height), - height, - "height must be greater than the last distribute height."); - } + // TODO: [GuildMigration] Revive below check after migration + // if (LastDistributeHeight.HasValue && LastDistributeHeight >= height) + // { + // throw new ArgumentOutOfRangeException( + // nameof(height), + // height, + // "height must be greater than the last distribute height."); + // } return new Bond(Address, Share, height); } From f8fd2d67f5218c3ea74f34c9a073498aa82ddc35 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 4 Nov 2024 14:01:15 +0900 Subject: [PATCH 135/206] test: Update tests --- .../ClaimAdventureBossRewardTest.cs | 36 +++++++++---------- .../AdventureBoss/SweepAdventureBossTest.cs | 4 +-- .../Action/AdventureBoss/UnlockFloorTest.cs | 4 +-- .../Action/AdventureBoss/WantedTest.cs | 4 +-- .Lib9c.Tests/Action/ApprovePledgeTest.cs | 6 +++- .Lib9c.Tests/Action/Guild/JoinGuildTest.cs | 9 ++--- .Lib9c.Tests/Action/Guild/MakeGuildTest.cs | 4 +-- .../Scenario/StakeAndClaimScenarioTest.cs | 4 +-- .Lib9c.Tests/Action/StakeTest.cs | 4 +-- .../ValidatorDelegation/AllocateRewardTest.cs | 10 +++--- .../ClaimRewardValidatorTest.cs | 10 +++--- .../DelegateValidatorTest.cs | 14 ++++---- .../PromoteValidatorTest.cs | 18 +++++----- .../ReleaseValidatorUnbondingsTest.cs | 4 +-- .../SetValidatorCommissionTest.cs | 12 +++---- .../ValidatorDelegation/SlashValidatorTest.cs | 6 ++-- .../UndelegateValidatorTest.cs | 16 ++++----- .../UnjailValidatorTest.cs | 10 +++--- .../UpdateValidatorsTest.cs | 2 +- .../ValidatorDelegationTestBase.cs | 4 +-- .Lib9c.Tests/Util/DelegationUtil.cs | 21 ++++------- 21 files changed, 99 insertions(+), 103 deletions(-) diff --git a/.Lib9c.Tests/Action/AdventureBoss/ClaimAdventureBossRewardTest.cs b/.Lib9c.Tests/Action/AdventureBoss/ClaimAdventureBossRewardTest.cs index 6135bbc564..c72d70bb43 100644 --- a/.Lib9c.Tests/Action/AdventureBoss/ClaimAdventureBossRewardTest.cs +++ b/.Lib9c.Tests/Action/AdventureBoss/ClaimAdventureBossRewardTest.cs @@ -387,10 +387,10 @@ AdventureBossGameData.ClaimableReward expectedReward state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } - var validatorKey = new PrivateKey(); + var validatorKey = new PrivateKey().PublicKey; state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); - state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey.Address, 0L); state = Stake(state, TesterAddress); // Wanted @@ -409,7 +409,7 @@ AdventureBossGameData.ClaimableReward expectedReward if (anotherWanted) { - state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey.Address, 0L); state = Stake(state, WantedAddress); state = new Wanted { @@ -477,10 +477,10 @@ public void WantedMultipleSeason() state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } - var validatorKey = new PrivateKey(); + var validatorKey = new PrivateKey().PublicKey; state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); - state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey, 0L); - state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey.Address, 0L); + state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey.Address, 0L); state = Stake(state, TesterAddress); state = Stake(state, WantedAddress); @@ -575,9 +575,9 @@ FungibleAssetValue expectedRemainingNcg state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } - var validatorKey = new PrivateKey(); + var validatorKey = new PrivateKey().PublicKey; state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); - state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey.Address, 0L); state = Stake(state, WantedAddress); @@ -752,11 +752,11 @@ public void ExploreMultipleSeason() state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } - var validatorKey = new PrivateKey(); + var validatorKey = new PrivateKey().PublicKey; state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); - state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey, 0L); - state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey, 0L); - state = DelegationUtil.MakeGuild(state, ExplorerAddress, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey.Address, 0L); + state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey.Address, 0L); + state = DelegationUtil.MakeGuild(state, ExplorerAddress, validatorKey.Address, 0L); state = Stake(state, TesterAddress); state = Stake(state, WantedAddress); @@ -886,9 +886,9 @@ public void AllReward() state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } - var validatorKey = new PrivateKey(); + var validatorKey = new PrivateKey().PublicKey; state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); - state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey.Address, 0L); state = Stake(state, TesterAddress); @@ -959,11 +959,11 @@ Type exc state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } - var validatorKey = new PrivateKey(); + var validatorKey = new PrivateKey().PublicKey; state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); - state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey, 0L); - state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey, 0L); - state = DelegationUtil.MakeGuild(state, ExplorerAddress, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, TesterAddress, validatorKey.Address, 0L); + state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey.Address, 0L); + state = DelegationUtil.MakeGuild(state, ExplorerAddress, validatorKey.Address, 0L); state = Stake(state, TesterAddress); state = Stake(state, WantedAddress); diff --git a/.Lib9c.Tests/Action/AdventureBoss/SweepAdventureBossTest.cs b/.Lib9c.Tests/Action/AdventureBoss/SweepAdventureBossTest.cs index 29838195f0..0f6c24f74d 100644 --- a/.Lib9c.Tests/Action/AdventureBoss/SweepAdventureBossTest.cs +++ b/.Lib9c.Tests/Action/AdventureBoss/SweepAdventureBossTest.cs @@ -131,9 +131,9 @@ public void Execute( state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } - var validatorKey = new PrivateKey(); + var validatorKey = new PrivateKey().PublicKey; state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); - state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey.Address, 0L); state = Stake(state, WantedAddress); var sheets = state.GetSheets(sheetTypes: new[] diff --git a/.Lib9c.Tests/Action/AdventureBoss/UnlockFloorTest.cs b/.Lib9c.Tests/Action/AdventureBoss/UnlockFloorTest.cs index 01cf0885bf..1a74798c0a 100644 --- a/.Lib9c.Tests/Action/AdventureBoss/UnlockFloorTest.cs +++ b/.Lib9c.Tests/Action/AdventureBoss/UnlockFloorTest.cs @@ -109,9 +109,9 @@ public void Execute( state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } - var validatorKey = new PrivateKey(); + var validatorKey = new PrivateKey().PublicKey; state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); - state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, WantedAddress, validatorKey.Address, 0L); state = Stake(state, WantedAddress); var materialSheet = state.GetSheet(); diff --git a/.Lib9c.Tests/Action/AdventureBoss/WantedTest.cs b/.Lib9c.Tests/Action/AdventureBoss/WantedTest.cs index e0c9704a64..29fc64af70 100644 --- a/.Lib9c.Tests/Action/AdventureBoss/WantedTest.cs +++ b/.Lib9c.Tests/Action/AdventureBoss/WantedTest.cs @@ -576,9 +576,9 @@ private IWorld Stake(IWorld world, long amount = 0) amount = stakeRegularRewardSheet[requiredStakingLevel].RequiredGold; } - var validatorKey = new PrivateKey(); + var validatorKey = new PrivateKey().PublicKey; world = DelegationUtil.EnsureValidatorPromotionReady(world, validatorKey, 0L); - world = DelegationUtil.MakeGuild(world, AgentAddress, validatorKey, 0L); + world = DelegationUtil.MakeGuild(world, AgentAddress, validatorKey.Address, 0L); var action = new Stake(new BigInteger(amount)); return action.Execute(new ActionContext diff --git a/.Lib9c.Tests/Action/ApprovePledgeTest.cs b/.Lib9c.Tests/Action/ApprovePledgeTest.cs index a3b4d9caa3..7f93df5d73 100644 --- a/.Lib9c.Tests/Action/ApprovePledgeTest.cs +++ b/.Lib9c.Tests/Action/ApprovePledgeTest.cs @@ -10,6 +10,7 @@ namespace Lib9c.Tests.Action using Nekoyume; using Nekoyume.Action; using Nekoyume.Action.Guild; + using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.Guild; using Nekoyume.Model.State; using Nekoyume.Module; @@ -69,13 +70,16 @@ public void Execute_JoinGuild(int mead) List.Empty.Add(patron.Serialize()).Add(false.Serialize()).Add(mead.Serialize()) ); + states = DelegationUtil.EnsureValidatorPromotionReady( + states, ValidatorConfig.PlanetariumValidatorPublicKey, 0L); + states = new GuildRepository( states, new ActionContext { Signer = GuildConfig.PlanetariumGuildOwner, }) - .MakeGuild(guildAddress, GuildConfig.PlanetariumGuildOwner).World; + .MakeGuild(guildAddress, ValidatorConfig.PlanetariumValidatorAddress).World; var action = new ApprovePledge { diff --git a/.Lib9c.Tests/Action/Guild/JoinGuildTest.cs b/.Lib9c.Tests/Action/Guild/JoinGuildTest.cs index df981b1a74..1a1f5fe1c8 100644 --- a/.Lib9c.Tests/Action/Guild/JoinGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/JoinGuildTest.cs @@ -34,14 +34,15 @@ public void Execute() IWorld world = World; world = EnsureToMintAsset(world, validatorKey.Address, GG * 100); world = EnsureToCreateValidator(world, validatorKey.PublicKey); + world = DelegationUtil.MakeGuild(world, guildMasterAddress, validatorKey.Address, 0L); - var repository = new GuildRepository(world, new ActionContext + world = new JoinGuild(guildAddress).Execute(new ActionContext { - Signer = guildMasterAddress, + PreviousState = world, + Signer = agentAddress, }); - repository.MakeGuild(guildAddress, guildMasterAddress); - repository.JoinGuild(guildAddress, agentAddress); + var repository = new GuildRepository(world, new ActionContext { }); var guildParticipant = repository.GetGuildParticipant(agentAddress); Assert.Equal(agentAddress, guildParticipant.Address); diff --git a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs index 2a69503535..65e648a03e 100644 --- a/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/MakeGuildTest.cs @@ -43,7 +43,7 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute() { IWorld world = World; @@ -53,7 +53,7 @@ public void Execute() world = EnsureToCreateValidator(world, validatorPrivateKey.PublicKey); var action = new MakeGuild(validatorPrivateKey.Address); - world = action.Execute(new ActionContext + world = action.ExecutePublic(new ActionContext { PreviousState = world, Signer = guildMasterAddress, diff --git a/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs index aeba853b03..a42ca83217 100644 --- a/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs @@ -109,9 +109,9 @@ public void Test() LegacyStakeState.RewardInterval, LegacyStakeState.LockupInterval); - var validatorKey = new PrivateKey(); + var validatorKey = new PrivateKey().PublicKey; state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); - state = DelegationUtil.MakeGuild(state, _agentAddr, validatorKey, 0L); + state = DelegationUtil.MakeGuild(state, _agentAddr, validatorKey.Address, 0L); // Withdraw stake via stake3. state = Stake3(state, _agentAddr, 0, stake2BlockIndex); diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index 3c9c486f87..427e04290f 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -300,7 +300,7 @@ public void Execute_Success_When_Staking_State_Null(long amount) _ncg * amount); var height = 0L; - var validatorKey = new PrivateKey(); + var validatorKey = new PrivateKey().PublicKey; world = DelegationUtil.EnsureValidatorPromotionReady(world, validatorKey, height++); world = DelegationUtil.MakeGuild(world, _agentAddr, validatorKey.Address, height++); @@ -339,7 +339,7 @@ public void Execute_Success_When_Exist_StakeStateV3( var world = _initialState; var height = 0L; - var validatorKey = new PrivateKey(); + var validatorKey = new PrivateKey().PublicKey; world = DelegationUtil.EnsureValidatorPromotionReady(world, validatorKey, height); world = DelegationUtil.MakeGuild(world, _agentAddr, validatorKey.Address, height); if (previousAmount > 0) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs index 0d2c306134..c0850d9438 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -65,7 +65,7 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute() { var fixture = new StaticFixture @@ -83,7 +83,7 @@ public void Execute() ExecuteWithFixture(fixture); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [InlineData(1, 1000)] [InlineData(33, 33)] [InlineData(33, 33.33)] @@ -109,7 +109,7 @@ public void Execute_Theory(int validatorCount, double totalReward) ExecuteWithFixture(fixture); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_WithoutReward_Throw() { var fixture = new StaticFixture @@ -127,7 +127,7 @@ public void Execute_WithoutReward_Throw() Assert.Throws(() => ExecuteWithFixture(fixture)); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [InlineData(0)] [InlineData(1181126949)] [InlineData(793705868)] @@ -139,7 +139,7 @@ public void Execute_Theory_WithStaticSeed(int randomSeed) ExecuteWithFixture(fixture); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [MemberData(nameof(RandomSeeds))] public void Execute_Theory_WithRandomSeed(int randomSeed) { diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs index 1939653765..dcca4a3add 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs @@ -60,7 +60,7 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute() { // Given @@ -92,7 +92,7 @@ public void Execute() Assert.Equal(expectedBalance, actualBalance); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [InlineData(33.33)] [InlineData(11.11)] [InlineData(10)] @@ -131,7 +131,7 @@ public void Execute_Theory_OneDelegator(decimal totalReward) ExecuteWithFixture(fixture); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [InlineData(0.1)] [InlineData(1)] [InlineData(3)] @@ -190,7 +190,7 @@ public void Execute_Theory_TwoDelegators(decimal totalReward) ExecuteWithFixture(fixture); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [InlineData(0)] [InlineData(123)] [InlineData(34352535)] @@ -200,7 +200,7 @@ public void Execute_Theory_WithStaticSeed(int randomSeed) ExecuteWithFixture(fixture); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [MemberData(nameof(RandomSeeds))] public void Execute_Theory_WithRandomSeed(int randomSeed) { diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index 2aa9715aae..3a4790116c 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -41,7 +41,7 @@ public void Serialization() Assert.Equal(gold, deserialized.FAV); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute() { // Given @@ -76,7 +76,7 @@ public void Execute() Assert.Equal(DelegationCurrency * 80, GetBalance(world, validatorKey.Address)); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_Fact() { var fixture = new StaticFixture @@ -91,7 +91,7 @@ public void Execute_Fact() ExecuteWithFixture(fixture); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [InlineData(0)] [InlineData(1181126949)] [InlineData(793705868)] @@ -102,7 +102,7 @@ public void Execute_Fact_WithStaticSeed(int randomSeed) ExecuteWithFixture(fixture); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [MemberData(nameof(RandomSeeds))] public void Execute_Fact_WithRandomSeed(int randomSeed) { @@ -110,7 +110,7 @@ public void Execute_Fact_WithRandomSeed(int randomSeed) ExecuteWithFixture(fixture); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_WithInvalidCurrency_Throw() { // Given @@ -136,7 +136,7 @@ public void Execute_WithInvalidCurrency_Throw() Assert.Throws(() => delegateValidator.Execute(actionContext)); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_WithInsufficientBalance_Throw() { // Given @@ -183,7 +183,7 @@ public void Execute_ToInvalidValidator_Throw() Assert.Throws(() => delegateValidator.Execute(actionContext)); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_ToTombstonedValidator_Throw() { // Given diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs index 2763fce43b..e2baacc850 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs @@ -23,7 +23,7 @@ public void Serialization() Assert.Equal(gold, deserialized.FAV); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute() { // Given @@ -41,7 +41,7 @@ public void Execute() BlockIndex = height++, }; var promoteValidator = new PromoteValidator(validatorKey.PublicKey, gold); - world = promoteValidator.Execute(actionContext); + world = promoteValidator.ExecutePublic(actionContext); // Then var repository = new ValidatorRepository(world, actionContext); @@ -57,7 +57,7 @@ public void Execute() Assert.Empty(validatorList.GetUnbonded()); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_ToInvalidValidator_Throw() { // Given @@ -79,7 +79,7 @@ public void Execute_ToInvalidValidator_Throw() // Then Assert.Throws( - () => promoteValidator.Execute(actionContext)); + () => promoteValidator.ExecutePublic(actionContext)); } [Fact] @@ -103,10 +103,10 @@ public void Execute_WithInvalidCurrency_Throw() // Then Assert.Throws( - () => promoteValidator.Execute(actionContext)); + () => promoteValidator.ExecutePublic(actionContext)); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_WithInsufficientBalance_Throw() { // Given @@ -126,10 +126,10 @@ public void Execute_WithInsufficientBalance_Throw() // Then Assert.Throws( - () => promoteValidator.Execute(actionContext)); + () => promoteValidator.ExecutePublic(actionContext)); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_PromotedValidator_Throw() { // Given @@ -151,6 +151,6 @@ public void Execute_PromotedValidator_Throw() // Then Assert.Throws( - () => promoteValidator.Execute(actionContext)); + () => promoteValidator.ExecutePublic(actionContext)); } } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs index ff7c6098da..80d7125485 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs @@ -20,7 +20,7 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute() { // Given @@ -65,7 +65,7 @@ public void Execute() Assert.Equal(expectedReleaseCount - 1, actualReleaseCount); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact(Skip = "Skip due to zero unbonding period before migration")] public void Execute_ThereIsNoUnbonding_AtEarlyHeight() { // Given diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs index 163ebe54fa..6886fa952c 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SetValidatorCommissionTest.cs @@ -60,7 +60,7 @@ public void Serialization() Assert.Equal(commissionPercentage, deserialized.CommissionPercentage); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute() { // Given @@ -90,7 +90,7 @@ public void Execute() Assert.Equal(11, actualPercentage); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [InlineData(9, 10)] [InlineData(9, 8)] [InlineData(0, 1)] @@ -127,7 +127,7 @@ public void Execute_Theory(int oldCommissionPercentage, int newCommissionPercent Assert.Equal(newCommissionPercentage, actualPercentage); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [MemberData(nameof(RandomInvalidCommisionPercentage))] public void Execute_Theory_WithValueGreaterThanMaximum_Throw(int commissionPercentage) { @@ -156,7 +156,7 @@ public void Execute_Theory_WithValueGreaterThanMaximum_Throw(int commissionPerce () => setValidatorCommission.Execute(actionContext)); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [InlineData(-1)] [InlineData(-2)] public void Execute_Theory_WithNegative_Throw(int commissionPercentage) @@ -186,7 +186,7 @@ public void Execute_Theory_WithNegative_Throw(int commissionPercentage) () => setValidatorCommission.Execute(actionContext)); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [MemberData(nameof(InvalidCommisionPercentageCooldown))] public void Execute_Theory_WithInvalidValue_Throw(int cooldown) { @@ -214,7 +214,7 @@ public void Execute_Theory_WithInvalidValue_Throw(int cooldown) () => setValidatorCommission.Execute(actionContext)); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [MemberData(nameof(ValidCommisionPercentagePeriod))] public void Execute_Theory_WitValue(int period) { diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs index 4c53a112f7..3173039718 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs @@ -28,7 +28,7 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute() { // Given @@ -132,7 +132,7 @@ public void Execute_ToNotPromotedValidator_Throw() Assert.Throws(() => slashValidator.Execute(actionContext)); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_ByAbstain() { // Given @@ -170,7 +170,7 @@ public void Execute_ByAbstain() Assert.False(delegatee.Tombstoned); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_ToJailedValidator_ThenNothingHappens() { // Given diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 422b685417..71e7840aca 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -43,7 +43,7 @@ public void Serialization() Assert.Equal(share, deserialized.Share); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute() { // Given @@ -82,7 +82,7 @@ public void Execute() Assert.Equal(BigInteger.Zero, actualBond.Share); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_Theory() { var fixture = new StaticFixture @@ -98,7 +98,7 @@ public void Execute_Theory() ExecuteWithFixture(fixture); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [InlineData(0)] [InlineData(1181126949)] [InlineData(793705868)] @@ -109,7 +109,7 @@ public void Execute_Theory_WithStaticSeed(int randomSeed) ExecuteWithFixture(fixture); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [MemberData(nameof(RandomSeeds))] public void Execute_Theory_WithRandomSeed(int randomSeed) { @@ -141,7 +141,7 @@ public void Execute_FromInvalidValidtor_Throw() () => undelegateValidator.Execute(actionContext)); } - [Theory(Skip = "Allow after Planetarium validator restriction")] + [Theory] [InlineData(0)] [InlineData(-1)] public void Execute_WithNotPositiveShare_Throw(long share) @@ -168,7 +168,7 @@ public void Execute_WithNotPositiveShare_Throw(long share) () => undelegateValidator.Execute(actionContext)); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_FromJailedValidator() { // Given @@ -201,7 +201,7 @@ public void Execute_FromJailedValidator() Assert.Equal(expectedBond.Share - 10, actualBond.Share); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_FromTombstonedValidator() { // Given @@ -234,7 +234,7 @@ public void Execute_FromTombstonedValidator() Assert.Equal(expectedBond.Share - 10, actualBond.Share); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_JailsValidatorWhenUndelegationCausesLowDelegation() { // Given diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs index d61882c9ec..6bf61fd62e 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UnjailValidatorTest.cs @@ -20,7 +20,7 @@ public void Serialization() deserialized.LoadPlainValue(plainValue); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute() { // Given @@ -71,7 +71,7 @@ public void Execute_OnNotPromotedValidator_Throw() Assert.Throws(() => unjailValidator.Execute(actionContext)); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_OnNotJailedValidator_Throw() { // Given @@ -94,7 +94,7 @@ public void Execute_OnNotJailedValidator_Throw() Assert.Throws(() => unjailValidator.Execute(actionContext)); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_TooEarly_Throw() { // Given @@ -119,7 +119,7 @@ public void Execute_TooEarly_Throw() () => unjailValidator.Execute(actionContext)); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_OnTombstonedValidator_Throw() { // Given @@ -144,7 +144,7 @@ public void Execute_OnTombstonedValidator_Throw() () => unjailValidator.Execute(actionContext)); } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_OnLowDelegatedValidator_Throw() { // Given diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs index 32e6119542..c9a8de5a0f 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs @@ -58,7 +58,7 @@ public void Execute() } } - [Fact(Skip = "Allow after Planetarium validator restriction")] + [Fact] public void Execute_ExcludesTombstonedValidator() { // Given diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index ad40712287..2a6ba8aa24 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -133,7 +133,7 @@ protected static IWorld EnsurePromotedValidator( Signer = validatorPublicKey.Address, BlockIndex = blockHeight, }; - return promoteValidator.Execute(actionContext); + return promoteValidator.ExecutePublic(actionContext); } protected static IWorld EnsureUnbondingValidator( @@ -192,7 +192,7 @@ protected static IWorld EnsureMakeGuild( RandomSeed = seed, }; var makeGuild = new MakeGuild(validatorAddress); - return makeGuild.Execute(actionContext); + return makeGuild.ExecutePublic(actionContext); } protected static IWorld EnsureJoinGuild( diff --git a/.Lib9c.Tests/Util/DelegationUtil.cs b/.Lib9c.Tests/Util/DelegationUtil.cs index f50d44fa4e..5a6e2c41e8 100644 --- a/.Lib9c.Tests/Util/DelegationUtil.cs +++ b/.Lib9c.Tests/Util/DelegationUtil.cs @@ -12,10 +12,6 @@ namespace Lib9c.Tests.Util public static class DelegationUtil { - public static IWorld MintGuildGold( - IWorld world, PrivateKey privateKey, FungibleAssetValue amount, long blockHeight) - => MintGuildGold(world, privateKey.Address, amount, blockHeight); - public static IWorld MintGuildGold( IWorld world, Address address, FungibleAssetValue amount, long blockHeight) { @@ -38,11 +34,10 @@ public static IWorld MintGuildGold( public static IWorld PromoteValidator( IWorld world, - PrivateKey validatorKey, + PublicKey validatorPublicKey, FungibleAssetValue amount, long blockHeight) { - var validatorPublicKey = validatorKey.PublicKey; var promoteValidator = new PromoteValidator(validatorPublicKey, amount); var actionContext = new ActionContext @@ -51,13 +46,9 @@ public static IWorld PromoteValidator( Signer = validatorPublicKey.Address, BlockIndex = blockHeight, }; - return promoteValidator.Execute(actionContext); + return promoteValidator.ExecutePublic(actionContext); } - public static IWorld MakeGuild( - IWorld world, Address guildMasterAddress, PrivateKey validatorKey, long blockHeight) - => MakeGuild(world, guildMasterAddress, validatorKey.Address, blockHeight); - public static IWorld MakeGuild( IWorld world, Address guildMasterAddress, Address validatorAddress, long blockHeight) { @@ -74,7 +65,7 @@ public static IWorld MakeGuild( RandomSeed = Random.Shared.Next(), }; var makeGuild = new MakeGuild(validatorAddress); - return makeGuild.Execute(actionContext); + return makeGuild.ExecutePublic(actionContext); } public static FungibleAssetValue GetGuildCoinFromNCG(FungibleAssetValue balance) @@ -83,10 +74,10 @@ public static FungibleAssetValue GetGuildCoinFromNCG(FungibleAssetValue balance) } public static IWorld EnsureValidatorPromotionReady( - IWorld world, PrivateKey validatorKey, long blockHeight) + IWorld world, PublicKey validatorPublicKey, long blockHeight) { - world = MintGuildGold(world, validatorKey, Currencies.GuildGold * 10, blockHeight); - world = PromoteValidator(world, validatorKey, Currencies.GuildGold * 10, blockHeight); + world = MintGuildGold(world, validatorPublicKey.Address, Currencies.GuildGold * 10, blockHeight); + world = PromoteValidator(world, validatorPublicKey, Currencies.GuildGold * 10, blockHeight); return world; } From c28e7453592c7c43012f6825875a6bef2623d724 Mon Sep 17 00:00:00 2001 From: s2quake Date: Tue, 5 Nov 2024 10:50:44 +0900 Subject: [PATCH 136/206] refactor: Delete InitializeValidator --- Lib9c/Action/InitializeValidator.cs | 106 ---------------------------- 1 file changed, 106 deletions(-) delete mode 100644 Lib9c/Action/InitializeValidator.cs diff --git a/Lib9c/Action/InitializeValidator.cs b/Lib9c/Action/InitializeValidator.cs deleted file mode 100644 index 381cabb68a..0000000000 --- a/Lib9c/Action/InitializeValidator.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Linq; -using Bencodex.Types; -using Libplanet.Action.State; -using Libplanet.Action; -using Libplanet.Types.Assets; -using Libplanet.Types.Consensus; -using Nekoyume; -using Nekoyume.Action; -using Nekoyume.Model.State; -using Nekoyume.Module; -using Nekoyume.Module.ValidatorDelegation; -using Nekoyume.ValidatorDelegation; - -namespace Nekoyume.Action -{ - [ActionType(TypeIdentifier)] - public sealed class InitializeValidator : ActionBase - { - public const string TypeIdentifier = "initialize_validator"; - - public InitializeValidator( - ValidatorSet validatorSet, - Currency goldCurrency) - { - Validators = validatorSet.Validators.ToArray(); - GoldCurrency = goldCurrency; - } - - public InitializeValidator() - { - } - - public Validator[] Validators { get; set; } - - public Currency GoldCurrency { get; set; } - - public override IValue PlainValue - => Dictionary.Empty - .Add("type_id", TypeIdentifier) - .Add("validator_set", new List(Validators.Select(item => item.Bencoded))) - .Add("gold_currency", GoldCurrency.Serialize()); - - public override void LoadPlainValue(IValue plainValue) - { - if (plainValue is not Dictionary dict || - dict["validator_set"] is not List list || - dict["gold_currency"] is not Dictionary currencyDict) - { - throw new InvalidCastException("Invalid types"); - } - - Validators = list.Select(item => new Validator((Dictionary)item)).ToArray(); - GoldCurrency = new Currency(currencyDict); - } - - public override IWorld Execute(IActionContext context) - { - var world = context.PreviousState; - - var goldCurrency = GoldCurrency; - var currencyState = new GoldCurrencyState(goldCurrency); - world = world - .SetLegacyState(GoldCurrencyState.Address, currencyState.Serialize()) - .SetLegacyState(Addresses.GoldDistribution, new List().Serialize()); - - if (currencyState.InitialSupply > 0) - { - world = world.MintAsset( - context, - GoldCurrencyState.Address, - currencyState.Currency * currencyState.InitialSupply); - } - - var repository = new ValidatorRepository(world, context); - var validators = Validators; - foreach (var validator in validators) - { - var validatorDelegatee = new ValidatorDelegatee( - validator.OperatorAddress, - validator.PublicKey, - ValidatorDelegatee.DefaultCommissionPercentage, - context.BlockIndex, - repository); - var delegationFAV = FungibleAssetValue.FromRawValue( - validatorDelegatee.DelegationCurrency, validator.Power); - var validatorOperatorAddress = validator.OperatorAddress; - var validatorDelegator = repository.GetValidatorDelegator( - validatorOperatorAddress, validatorOperatorAddress); - - repository.SetValidatorDelegatee(validatorDelegatee); - repository.UpdateWorld( - repository.World.MintAsset( - repository.ActionContext, - validatorDelegator.DelegationPoolAddress, - delegationFAV)); - validatorDelegator.Delegate(validatorDelegatee, delegationFAV, context.BlockIndex); - } - - repository.SetAbstainHistory(new()); - world = repository.World; - - return world; - } - } -} From 2a817b4dcf3ea4c5d5b47cd68a5fcf108e017214 Mon Sep 17 00:00:00 2001 From: s2quake Date: Tue, 5 Nov 2024 13:12:17 +0900 Subject: [PATCH 137/206] refactor: Enable unstaking without a guild --- Lib9c/Action/Stake.cs | 36 ++++++++++++------- .../ReleaseValidatorUnbondings.cs | 15 +++++++- Lib9c/Addresses.cs | 6 ++++ Lib9c/Delegation/UnbondLockIn.cs | 25 +++++++++---- 4 files changed, 62 insertions(+), 20 deletions(-) diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index cbff5cb2c2..81fa4587f6 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -9,6 +9,7 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; +using Nekoyume.Delegation; using Nekoyume.Exceptions; using Nekoyume.Extensions; using Nekoyume.Model.Guild; @@ -16,11 +17,10 @@ using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.Module.Guild; -using Nekoyume.Module.ValidatorDelegation; using Nekoyume.TableData; using Nekoyume.TableData.Stake; +using Nekoyume.TypedAddress; using Nekoyume.ValidatorDelegation; -using Org.BouncyCastle.Asn1.Esf; using Serilog; using static Lib9c.SerializeKeys; @@ -182,6 +182,7 @@ private static IWorld ContractNewStake( var stakeStateValue = new StakeState(latestStakeContract, context.BlockIndex).Serialize(); var additionalBalance = targetStakeBalance - stakedBalance; var height = context.BlockIndex; + var agentAddress = new AgentAddress(context.Signer); if (additionalBalance.Sign > 0) { @@ -190,35 +191,46 @@ private static IWorld ContractNewStake( .TransferAsset(context, context.Signer, stakeStateAddr, additionalBalance) .MintAsset(context, stakeStateAddr, gg); - try + var guildRepository = new GuildRepository(state, context); + if (guildRepository.TryGetGuildParticipant(agentAddress, out var guildParticipant)) { - var guildRepository = new GuildRepository(state, context); - var guildParticipant = guildRepository.GetGuildParticipant(context.Signer); var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); guildParticipant.Delegate(guild, gg, height); state = guildRepository.World; } - catch (FailedLoadStateException) + else { + state = state + .TransferAsset(context, stakeStateAddr, Addresses.NonValidatorDelegatee, gg); } - + } else if (additionalBalance.Sign < 0) { var gg = GetGuildCoinFromNCG(-additionalBalance); - try + var guildRepository = new GuildRepository(state, context); + if (guildRepository.TryGetGuildParticipant(agentAddress, out var guildParticipant)) { - var guildRepository = new GuildRepository(state, context); - var guildParticipant = guildRepository.GetGuildParticipant(context.Signer); var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); var share = guild.ShareFromFAV(gg); guildParticipant.Undelegate(guild, share, height); state = guildRepository.World; } - catch (FailedLoadStateException) + else { - // TODO: Create self unbonding + var delegateeAddress = Addresses.NonValidatorDelegatee; + var delegatorAddress = context.Signer; + var repository = new ValidatorRepository(state, context); + var unbondLockInAddress = DelegationAddress.UnbondLockInAddress(delegateeAddress, delegatorAddress); + var unbondLockIn = new UnbondLockIn( + unbondLockInAddress, 1, delegateeAddress, delegatorAddress, null); + unbondLockIn = unbondLockIn.LockIn( + gg, height, height + ValidatorDelegatee.ValidatorUnbondingPeriod); + repository.SetUnbondLockIn(unbondLockIn); + repository.SetUnbondingSet( + repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); + state = repository.World; } if ((stakedBalance + additionalBalance).Sign == 0) diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs index edc5aac653..33ebd4ac82 100644 --- a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -14,6 +14,7 @@ using Nekoyume.TypedAddress; using Nekoyume.Module; using Nekoyume.Model.Stake; +using Lib9c; namespace Nekoyume.Action.ValidatorDelegation { @@ -68,10 +69,10 @@ private IWorld Unstake(IWorld world, IActionContext context, Address address) { var agentAddress = new AgentAddress(address); var guildRepository = new GuildRepository(world, context); + var goldCurrency = world.GetGoldCurrency(); if (guildRepository.TryGetGuildParticipant(agentAddress, out var guildParticipant)) { var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); - var goldCurrency = world.GetGoldCurrency(); var stakeStateAddress = guildParticipant.DelegationPoolAddress; var gg = world.GetBalance(stakeStateAddress, guild.DelegationCurrency); if (gg.Sign > 0) @@ -82,6 +83,18 @@ private IWorld Unstake(IWorld world, IActionContext context, Address address) context, stakeStateAddress, address, ncg); } } + else + { + var stakeStateAddress = StakeState.DeriveAddress(address); + var gg = world.GetBalance(stakeStateAddress, Currencies.GuildGold); + if (gg.Sign > 0) + { + var (ncg, _) = ConvertToGoldCurrency(gg, goldCurrency); + world = world.BurnAsset(context, stakeStateAddress, gg); + world = world.TransferAsset( + context, stakeStateAddress, address, ncg); + } + } return world; } diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index de7b1d3b3d..5843e119f4 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -226,6 +226,12 @@ public static readonly Address RewardPool public static readonly Address CommunityPool = new Address("0000000000000000000000000000000000000312"); + /// + /// An address for non validator. + /// + public static readonly Address NonValidatorDelegatee + = new Address("0000000000000000000000000000000000000313"); + #endregion public static Address GetSheetAddress() where T : ISheet => GetSheetAddress(typeof(T).Name); diff --git a/Lib9c/Delegation/UnbondLockIn.cs b/Lib9c/Delegation/UnbondLockIn.cs index 8ce441b322..dd86120b0d 100644 --- a/Lib9c/Delegation/UnbondLockIn.cs +++ b/Lib9c/Delegation/UnbondLockIn.cs @@ -8,6 +8,7 @@ using Bencodex.Types; using Libplanet.Crypto; using Libplanet.Types.Assets; +using Nekoyume.Model.Stake; namespace Nekoyume.Delegation { @@ -160,7 +161,6 @@ public UnbondLockIn Release(long height) ? releasingFAV.Value + entriesFAV : entriesFAV; updatedEntries = updatedEntries.Remove(expireHeight); - } else { @@ -170,12 +170,23 @@ public UnbondLockIn Release(long height) if (releasingFAV.HasValue) { - var delegateeMetadata = _repository!.GetDelegateeMetadata(DelegateeAddress); - var delegatorMetadata = _repository.GetDelegatorMetadata(DelegatorAddress); - _repository!.TransferAsset( - delegateeMetadata.DelegationPoolAddress, - delegatorMetadata.DelegationPoolAddress, - releasingFAV.Value); + if (DelegateeAddress != Addresses.NonValidatorDelegatee) + { + var delegateeMetadata = _repository!.GetDelegateeMetadata(DelegateeAddress); + var delegatorMetadata = _repository.GetDelegatorMetadata(DelegatorAddress); + _repository!.TransferAsset( + delegateeMetadata.DelegationPoolAddress, + delegatorMetadata.DelegationPoolAddress, + releasingFAV.Value); + } + else + { + var stakeStateAddress = StakeState.DeriveAddress(DelegatorAddress); + _repository!.TransferAsset( + Addresses.NonValidatorDelegatee, + stakeStateAddress, + releasingFAV.Value); + } } return UpdateEntries(updatedEntries); From 7c967f649597faf7dc67920b22b7de34b5b81e4b Mon Sep 17 00:00:00 2001 From: s2quake Date: Tue, 5 Nov 2024 13:13:07 +0900 Subject: [PATCH 138/206] test: Add test code for unstaking without a guild --- .Lib9c.Tests/Action/StakeTest.cs | 86 +++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 8 deletions(-) diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index 427e04290f..839d4566e8 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -17,8 +17,10 @@ namespace Lib9c.Tests.Action using Nekoyume.Model.Stake; using Nekoyume.Model.State; using Nekoyume.Module; + using Nekoyume.Module.Guild; using Nekoyume.Module.ValidatorDelegation; using Nekoyume.TableData.Stake; + using Nekoyume.TypedAddress; using Nekoyume.ValidatorDelegation; using Serilog; using Xunit; @@ -325,6 +327,7 @@ public void Execute_Success_When_Staking_State_Null(long amount) [InlineData(0, long.MaxValue)] // NOTE: undelegate [InlineData(50, 0)] + [InlineData(75, 50)] [InlineData(long.MaxValue, 0)] [InlineData(long.MaxValue, 500)] public void Execute_Success_When_Exist_StakeStateV3( @@ -387,6 +390,72 @@ public void Execute_Success_When_Exist_StakeStateV3( Assert.Equal(expectedBalance, actualBalance); } + [Theory] + // NOTE: non + [InlineData(50, 50)] + [InlineData(long.MaxValue, long.MaxValue)] + // NOTE: delegate + [InlineData(0, 500)] + [InlineData(50, 100)] + [InlineData(0, long.MaxValue)] + // NOTE: undelegate + [InlineData(50, 0)] + [InlineData(75, 50)] + [InlineData(long.MaxValue, 0)] + [InlineData(long.MaxValue, 500)] + public void Execute_Success_When_Exist_StakeStateV3_Without_Guild( + long previousAmount, + long amount) + { + var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); + var stakeStateV2 = new StakeState( + contract: new Contract(_stakePolicySheet), + startedBlockIndex: 0L, + stateVersion: 3); + var world = _initialState; + var height = 0L; + + if (previousAmount > 0) + { + var ncgToStake = _ncg * previousAmount; + var gg = FungibleAssetValue.Parse(Currencies.GuildGold, ncgToStake.GetQuantityString(true)); + world = DelegationUtil.MintGuildGold(world, _agentAddr, gg, height); + world = world.MintAsset(new ActionContext(), _agentAddr, ncgToStake); + world = world.TransferAsset( + new ActionContext(), _agentAddr, stakeStateAddr, ncgToStake); + world = world.TransferAsset( + new ActionContext(), stakeStateAddr, Addresses.NonValidatorDelegatee, gg); + } + + world = world.SetLegacyState(stakeStateAddr, stakeStateV2.Serialize()); + + if (amount - previousAmount > 0) + { + var ncgToStake = _ncg * (amount - previousAmount); + world = world.MintAsset(new ActionContext(), _agentAddr, ncgToStake); + } + + var nextState = Execute( + height, + world, + new TestRandom(), + _agentAddr, + amount); + + if (amount > 0) + { + Assert.True(nextState.TryGetStakeState(_agentAddr, out StakeState stakeState)); + Assert.Equal(3, stakeState.StateVersion); + } + + world = DelegationUtil.EnsureStakeReleased( + nextState, height + LegacyStakeState.LockupInterval); + + var expectedBalance = _ncg * Math.Max(0, previousAmount - amount); + var actualBalance = world.GetBalance(_agentAddr, _ncg); + Assert.Equal(expectedBalance, actualBalance); + } + private IWorld Execute( long blockIndex, IWorld previousState, @@ -404,14 +473,15 @@ private IWorld Execute( }); var guildRepository = new GuildRepository(nextState, new ActionContext()); - var guildParticipant = guildRepository.GetGuildParticipant(signer); - var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); - - var bond = guildRepository.GetBond(guild, signer); - var amountNCG = _ncg * amount; - var expectedGG = DelegationUtil.GetGuildCoinFromNCG(amountNCG); - var expectedShare = guild.ShareFromFAV(expectedGG); - Assert.Equal(expectedShare, bond.Share); + if (guildRepository.TryGetGuildParticipant(new AgentAddress(signer), out var guildParticipant)) + { + var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); + var bond = guildRepository.GetBond(guild, signer); + var amountNCG = _ncg * amount; + var expectedGG = DelegationUtil.GetGuildCoinFromNCG(amountNCG); + var expectedShare = guild.ShareFromFAV(expectedGG); + Assert.Equal(expectedShare, bond.Share); + } if (amount == 0) { From c20a707e4ff867b2d36b9a8cdf065f038dcf13e8 Mon Sep 17 00:00:00 2001 From: s2quake Date: Tue, 5 Nov 2024 13:42:26 +0900 Subject: [PATCH 139/206] fix: Disable unstaking when a given address is validator --- .../ReleaseValidatorUnbondings.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs index 33ebd4ac82..1eadf94c46 100644 --- a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -83,7 +83,7 @@ private IWorld Unstake(IWorld world, IActionContext context, Address address) context, stakeStateAddress, address, ncg); } } - else + else if (!IsValidator(world, context, address)) { var stakeStateAddress = StakeState.DeriveAddress(address); var gg = world.GetBalance(stakeStateAddress, Currencies.GuildGold); @@ -99,6 +99,20 @@ private IWorld Unstake(IWorld world, IActionContext context, Address address) return world; } + private static bool IsValidator(IWorld world, IActionContext context, Address address) + { + var repository = new ValidatorRepository(world, context); + try + { + repository.GetValidatorDelegatee(address); + return true; + } + catch (FailedLoadStateException) + { + return false; + } + } + private static (FungibleAssetValue Gold, FungibleAssetValue Remainder) ConvertToGoldCurrency(FungibleAssetValue fav, Currency targetCurrency) { From afb94731a485a1d7fbfa2a94031144f29480e30e Mon Sep 17 00:00:00 2001 From: s2quake Date: Wed, 6 Nov 2024 16:57:10 +0900 Subject: [PATCH 140/206] fix: Change maxEntries to 2 in UnbondLockIn --- Lib9c/Action/Stake.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index 81fa4587f6..abc91ba218 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -224,7 +224,7 @@ private static IWorld ContractNewStake( var repository = new ValidatorRepository(state, context); var unbondLockInAddress = DelegationAddress.UnbondLockInAddress(delegateeAddress, delegatorAddress); var unbondLockIn = new UnbondLockIn( - unbondLockInAddress, 1, delegateeAddress, delegatorAddress, null); + unbondLockInAddress, 2, delegateeAddress, delegatorAddress, null); unbondLockIn = unbondLockIn.LockIn( gg, height, height + ValidatorDelegatee.ValidatorUnbondingPeriod); repository.SetUnbondLockIn(unbondLockIn); From 563d231a338017d219e77c85eed360f510b4ab05 Mon Sep 17 00:00:00 2001 From: Jeesu Choi Date: Wed, 6 Nov 2024 18:05:33 +0900 Subject: [PATCH 141/206] Update Lib9c/Action/Stake.cs Co-authored-by: ilgyu --- Lib9c/Action/Stake.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index abc91ba218..3077a8a39b 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -222,7 +222,7 @@ private static IWorld ContractNewStake( var delegateeAddress = Addresses.NonValidatorDelegatee; var delegatorAddress = context.Signer; var repository = new ValidatorRepository(state, context); - var unbondLockInAddress = DelegationAddress.UnbondLockInAddress(delegateeAddress, delegatorAddress); + var unbondLockInAddress = DelegationAddress.UnbondLockInAddress(delegateeAddress, repository.DelegateeAccountAddress, delegatorAddress); var unbondLockIn = new UnbondLockIn( unbondLockInAddress, 2, delegateeAddress, delegatorAddress, null); unbondLockIn = unbondLockIn.LockIn( From 831105c3e24e24717a10bbca801a1f2ee59f633b Mon Sep 17 00:00:00 2001 From: Jeesu Choi Date: Thu, 7 Nov 2024 12:07:17 +0900 Subject: [PATCH 142/206] Update Lib9c/Action/Stake.cs Co-authored-by: ilgyu --- Lib9c/Action/Stake.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index 3077a8a39b..008f4beb37 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -224,7 +224,7 @@ private static IWorld ContractNewStake( var repository = new ValidatorRepository(state, context); var unbondLockInAddress = DelegationAddress.UnbondLockInAddress(delegateeAddress, repository.DelegateeAccountAddress, delegatorAddress); var unbondLockIn = new UnbondLockIn( - unbondLockInAddress, 2, delegateeAddress, delegatorAddress, null); + unbondLockInAddress, ValidatorDelegatee.ValidatorMaxUnbondLockInEntries, delegateeAddress, delegatorAddress, null); unbondLockIn = unbondLockIn.LockIn( gg, height, height + ValidatorDelegatee.ValidatorUnbondingPeriod); repository.SetUnbondLockIn(unbondLockIn); From 23c8831dbe34171cbcfaf6a83210a52be184a5fe Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sun, 10 Nov 2024 12:19:36 +0900 Subject: [PATCH 143/206] feat: Enable multiple reward currencies --- Lib9c/Delegation/CurrencyComparer.cs | 29 +++++ Lib9c/Delegation/Delegatee.cs | 157 +++++++++++------------ Lib9c/Delegation/DelegateeMetadata.cs | 18 +-- Lib9c/Delegation/IDelegatee.cs | 3 +- Lib9c/Delegation/IDelegateeMetadata.cs | 3 +- Lib9c/Delegation/LumpSumRewardsRecord.cs | 77 +++++++++-- 6 files changed, 179 insertions(+), 108 deletions(-) create mode 100644 Lib9c/Delegation/CurrencyComparer.cs diff --git a/Lib9c/Delegation/CurrencyComparer.cs b/Lib9c/Delegation/CurrencyComparer.cs new file mode 100644 index 0000000000..8f560b75d4 --- /dev/null +++ b/Lib9c/Delegation/CurrencyComparer.cs @@ -0,0 +1,29 @@ +#nullable enable +using System.Collections.Generic; +using Libplanet.Types.Assets; + +namespace Nekoyume.Delegation +{ + internal class CurrencyComparer : IComparer + { + public int Compare(Currency x, Currency y) + => ByteArrayCompare(x.Hash.ToByteArray(), y.Hash.ToByteArray()); + + private static int ByteArrayCompare(byte[] x, byte[] y) + { + for (int i = 0; i < x.Length; i++) + { + if (x[i] < y[i]) + { + return -1; + } + else if (x[i] > y[i]) + { + return 1; + } + } + + return 0; + } + } +} diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index 410f84794e..ed36dcf4bf 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Numerics; using Bencodex.Types; using Libplanet.Crypto; @@ -17,7 +18,7 @@ public Delegatee( Address address, Address accountAddress, Currency delegationCurrency, - Currency rewardCurrency, + IEnumerable rewardCurrencies, Address delegationPoolAddress, Address rewardPoolAddress, Address rewardRemainderPoolAddress, @@ -31,7 +32,7 @@ public Delegatee( address, accountAddress, delegationCurrency, - rewardCurrency, + rewardCurrencies, delegationPoolAddress, rewardPoolAddress, rewardRemainderPoolAddress, @@ -74,7 +75,7 @@ private Delegatee(DelegateeMetadata metadata, IDelegationRepository repository) public Currency DelegationCurrency => Metadata.DelegationCurrency; - public Currency RewardCurrency => Metadata.RewardCurrency; + public ImmutableHashSet RewardCurrencies => Metadata.RewardCurrencies; public Address DelegationPoolAddress => Metadata.DelegationPoolAddress; @@ -243,54 +244,20 @@ public void DistributeReward(T delegator, long height) if (!share.IsZero && bond.LastDistributeHeight.HasValue) { - IEnumerable lumpSumRewardsRecords = - GetLumpSumRewardsRecords(bond.LastDistributeHeight); + IEnumerable lumpSumRewardsRecords + = GetLumpSumRewardsRecords(bond.LastDistributeHeight); - long? linkedStartHeight = null; foreach (LumpSumRewardsRecord record in lumpSumRewardsRecords) { - if (!(record.StartHeight is long startHeight)) - { - throw new ArgumentException("lump sum reward record wasn't started."); - } - - if (linkedStartHeight is long startHeightFromHigher - && startHeightFromHigher != startHeight) - { - throw new ArgumentException("Fetched wrong lump sum reward record."); - } - if (!record.Delegators.Contains(delegator.Address)) { continue; } - FungibleAssetValue reward = record.RewardsDuringPeriod(share); - if (reward.Sign > 0) - { - Repository.TransferAsset(record.Address, delegator.RewardAddress, reward); - } - + TransferReward(delegator, share, record); LumpSumRewardsRecord newRecord = record.RemoveDelegator(delegator.Address); - - if (newRecord.Delegators.IsEmpty) - { - FungibleAssetValue remainder = Repository.GetBalance(newRecord.Address, RewardCurrency); - - if (remainder.Sign > 0) - { - Repository.TransferAsset(newRecord.Address, RewardRemainderPoolAddress, remainder); - } - } - + TransferRemainders(newRecord); Repository.SetLumpSumRewardsRecord(newRecord); - - linkedStartHeight = newRecord.LastStartHeight; - - if (linkedStartHeight == -1) - { - break; - } } } @@ -307,21 +274,24 @@ void IDelegatee.DistributeReward(IDelegator delegator, long height) public void CollectRewards(long height) { - FungibleAssetValue rewards = Repository.GetBalance(RewardPoolAddress, RewardCurrency); + var rewards = RewardCurrencies.Select(c => Repository.GetBalance(RewardPoolAddress, c)); LumpSumRewardsRecord record = Repository.GetCurrentLumpSumRewardsRecord(this) ?? new LumpSumRewardsRecord( CurrentLumpSumRewardsRecordAddress(), height, TotalShares, Delegators, - RewardCurrency); + RewardCurrencies); record = record.AddLumpSumRewards(rewards); - if (rewards.Sign > 0) + foreach (var rewardsEach in rewards) { - Repository.TransferAsset(RewardPoolAddress, record.Address, rewards); + if (rewardsEach.Sign > 0) + { + Repository.TransferAsset(RewardPoolAddress, record.Address, rewardsEach); + } } - + Repository.SetLumpSumRewardsRecord(record); } @@ -383,6 +353,23 @@ public void AddUnbondingRef(UnbondingRef reference) public void RemoveUnbondingRef(UnbondingRef reference) => Metadata.RemoveUnbondingRef(reference); + public ImmutableDictionary CalculateReward( + BigInteger share, + IEnumerable lumpSumRewardsRecords) + { + ImmutableDictionary reward + = RewardCurrencies.ToImmutableDictionary(c => c, c => c * 0); + + foreach (LumpSumRewardsRecord record in lumpSumRewardsRecords) + { + var rewardDuringPeriod = record.RewardsDuringPeriod(share); + reward = rewardDuringPeriod.Aggregate(reward, (acc, pair) + => acc.SetItem(pair.Key, acc[pair.Key] + pair.Value)); + } + + return reward; + } + private void StartNewRewardPeriod(long height) { LumpSumRewardsRecord? currentRecord = Repository.GetCurrentLumpSumRewardsRecord(this); @@ -397,7 +384,7 @@ private void StartNewRewardPeriod(long height) currentRecord.StartHeight, TotalShares, Delegators, - RewardCurrency, + RewardCurrencies, currentRecord.LastStartHeight); Repository.SetLumpSumRewardsRecord(currentRecord); @@ -405,12 +392,16 @@ private void StartNewRewardPeriod(long height) } Address archiveAddress = LumpSumRewardsRecordAddress(lastRecord.StartHeight); - FungibleAssetValue reward = Repository.GetBalance(lastRecord.Address, RewardCurrency); - if (reward.Sign > 0) + + foreach (var rewardCurrency in RewardCurrencies) { - Repository.TransferAsset(lastRecord.Address, archiveAddress, reward); + FungibleAssetValue reward = Repository.GetBalance(lastRecord.Address, rewardCurrency); + if (reward.Sign > 0) + { + Repository.TransferAsset(lastRecord.Address, archiveAddress, reward); + } } - + lastRecord = lastRecord.MoveAddress(archiveAddress); Repository.SetLumpSumRewardsRecord(lastRecord); } @@ -420,44 +411,12 @@ private void StartNewRewardPeriod(long height) height, TotalShares, Delegators, - RewardCurrency, + RewardCurrencies, lastStartHeight); Repository.SetLumpSumRewardsRecord(newRecord); } - private FungibleAssetValue CalculateReward( - BigInteger share, - IEnumerable lumpSumRewardsRecords) - { - FungibleAssetValue reward = RewardCurrency * 0; - long? linkedStartHeight = null; - - foreach (LumpSumRewardsRecord record in lumpSumRewardsRecords) - { - if (!(record.StartHeight is long startHeight)) - { - throw new ArgumentException("lump sum reward record wasn't started."); - } - - if (linkedStartHeight is long startHeightFromHigher - && startHeightFromHigher != startHeight) - { - throw new ArgumentException("lump sum reward record was started."); - } - - reward += record.RewardsDuringPeriod(share); - linkedStartHeight = record.LastStartHeight; - - if (linkedStartHeight == -1) - { - break; - } - } - - return reward; - } - private List GetLumpSumRewardsRecords(long? lastRewardHeight) { List records = new(); @@ -483,5 +442,35 @@ record = Repository.GetLumpSumRewardsRecord(this, lastStartHeight) return records; } + + private void TransferReward(T delegator, BigInteger share, LumpSumRewardsRecord record) + { + ImmutableDictionary reward = record.RewardsDuringPeriod(share); + foreach (var rewardEach in reward) + { + if (rewardEach.Value.Sign > 0) + { + Repository.TransferAsset(record.Address, delegator.RewardAddress, rewardEach.Value); + } + } + } + + private void TransferRemainders(LumpSumRewardsRecord record) + { + if (!record.Delegators.IsEmpty) + { + return; + } + + foreach (var rewardCurrency in RewardCurrencies) + { + FungibleAssetValue remainder = Repository.GetBalance(record.Address, rewardCurrency); + + if (remainder.Sign > 0) + { + Repository.TransferAsset(record.Address, RewardRemainderPoolAddress, remainder); + } + } + } } } diff --git a/Lib9c/Delegation/DelegateeMetadata.cs b/Lib9c/Delegation/DelegateeMetadata.cs index 916f979bf5..1f93f0e86d 100644 --- a/Lib9c/Delegation/DelegateeMetadata.cs +++ b/Lib9c/Delegation/DelegateeMetadata.cs @@ -14,12 +14,13 @@ namespace Nekoyume.Delegation public class DelegateeMetadata : IDelegateeMetadata { private Address? _address; + private readonly IComparer _currencyComparer = new CurrencyComparer(); public DelegateeMetadata( Address delegateeAddress, Address delegateeAccountAddress, Currency delegationCurrency, - Currency rewardCurrency, + IEnumerable rewardCurrencies, Address delegationPoolAddress, Address rewardPoolAddress, Address rewardRemainderPoolAddress, @@ -31,7 +32,7 @@ public DelegateeMetadata( delegateeAddress, delegateeAccountAddress, delegationCurrency, - rewardCurrency, + rewardCurrencies, delegationPoolAddress, rewardPoolAddress, rewardRemainderPoolAddress, @@ -65,7 +66,7 @@ public DelegateeMetadata( address, accountAddress, new Currency(bencoded[0]), - new Currency(bencoded[1]), + ((List)bencoded[1]).Select(v => new Currency(v)), new Address(bencoded[2]), new Address(bencoded[3]), new Address(bencoded[4]), @@ -87,7 +88,7 @@ private DelegateeMetadata( Address delegateeAddress, Address delegateeAccountAddress, Currency delegationCurrency, - Currency rewardCurrency, + IEnumerable rewardCurrencies, Address delegationPoolAddress, Address rewardPoolAddress, Address rewardRemainderPoolAddress, @@ -127,7 +128,7 @@ private DelegateeMetadata( DelegateeAddress = delegateeAddress; DelegateeAccountAddress = delegateeAccountAddress; DelegationCurrency = delegationCurrency; - RewardCurrency = rewardCurrency; + RewardCurrencies = rewardCurrencies.ToImmutableHashSet(); DelegationPoolAddress = delegationPoolAddress; RewardPoolAddress = rewardPoolAddress; RewardRemainderPoolAddress = rewardRemainderPoolAddress; @@ -155,7 +156,7 @@ public Address Address public Currency DelegationCurrency { get; } - public Currency RewardCurrency { get; } + public ImmutableHashSet RewardCurrencies { get; } public Address DelegationPoolAddress { get; } @@ -185,9 +186,10 @@ public Address Address public ImmutableSortedSet UnbondingRefs { get; private set; } + // TODO : Better serialization public List Bencoded => List.Empty .Add(DelegationCurrency.Serialize()) - .Add(RewardCurrency.Serialize()) + .Add(new List(RewardCurrencies.OrderBy(c => c, _currencyComparer).Select(c => c.Serialize()))) .Add(DelegationPoolAddress.Bencoded) .Add(RewardPoolAddress.Bencoded) .Add(RewardRemainderPoolAddress.Bencoded) @@ -280,7 +282,7 @@ public virtual bool Equals(IDelegateeMetadata? other) && DelegateeAddress.Equals(delegatee.DelegateeAddress) && DelegateeAccountAddress.Equals(delegatee.DelegateeAccountAddress) && DelegationCurrency.Equals(delegatee.DelegationCurrency) - && RewardCurrency.Equals(delegatee.RewardCurrency) + && RewardCurrencies.SequenceEqual(delegatee.RewardCurrencies) && DelegationPoolAddress.Equals(delegatee.DelegationPoolAddress) && RewardPoolAddress.Equals(delegatee.RewardPoolAddress) && RewardRemainderPoolAddress.Equals(delegatee.RewardRemainderPoolAddress) diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index ce820e8c12..d39f05cb82 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Immutable; using System.Numerics; -using Bencodex; using Libplanet.Crypto; using Libplanet.Types.Assets; @@ -16,7 +15,7 @@ public interface IDelegatee Currency DelegationCurrency { get; } - Currency RewardCurrency { get; } + ImmutableHashSet RewardCurrencies { get; } Address DelegationPoolAddress { get; } diff --git a/Lib9c/Delegation/IDelegateeMetadata.cs b/Lib9c/Delegation/IDelegateeMetadata.cs index 678f7cc6f3..52ecf054d7 100644 --- a/Lib9c/Delegation/IDelegateeMetadata.cs +++ b/Lib9c/Delegation/IDelegateeMetadata.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Numerics; using Bencodex; @@ -18,7 +19,7 @@ public interface IDelegateeMetadata : IBencodable Currency DelegationCurrency { get; } - Currency RewardCurrency { get; } + ImmutableHashSet RewardCurrencies { get; } Address DelegationPoolAddress { get; } diff --git a/Lib9c/Delegation/LumpSumRewardsRecord.cs b/Lib9c/Delegation/LumpSumRewardsRecord.cs index fabde59293..7c927edfd4 100644 --- a/Lib9c/Delegation/LumpSumRewardsRecord.cs +++ b/Lib9c/Delegation/LumpSumRewardsRecord.cs @@ -1,24 +1,33 @@ #nullable enable using System; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Numerics; using Bencodex.Types; using Bencodex; using Libplanet.Crypto; using Libplanet.Types.Assets; -using System.Collections.Immutable; namespace Nekoyume.Delegation { public class LumpSumRewardsRecord : IBencodable, IEquatable { + private readonly IComparer _currencyComparer = new CurrencyComparer(); + public LumpSumRewardsRecord( Address address, long startHeight, BigInteger totalShares, ImmutableSortedSet
delegators, - Currency currency) - : this(address, startHeight, totalShares, delegators, currency, null) + IEnumerable currencies) + : this( + address, + startHeight, + totalShares, + delegators, + currencies, + null) { } @@ -27,9 +36,15 @@ public LumpSumRewardsRecord( long startHeight, BigInteger totalShares, ImmutableSortedSet
delegators, - Currency currency, + IEnumerable currencies, long? lastStartHeight) - : this(address, startHeight, totalShares, delegators, currency * 0, lastStartHeight) + : this( + address, + startHeight, + totalShares, + delegators, + currencies.Select(c => c * 0), + lastStartHeight) { } @@ -38,14 +53,20 @@ public LumpSumRewardsRecord( long startHeight, BigInteger totalShares, ImmutableSortedSet
delegators, - FungibleAssetValue lumpSumRewards, + IEnumerable lumpSumRewards, long? lastStartHeight) { Address = address; StartHeight = startHeight; TotalShares = totalShares; Delegators = delegators; - LumpSumRewards = lumpSumRewards; + + if (!lumpSumRewards.Select(f => f.Currency).All(new HashSet().Add)) + { + throw new ArgumentException("Duplicated currency in lump sum rewards."); + } + + LumpSumRewards = lumpSumRewards.ToImmutableDictionary(f => f.Currency, f => f); LastStartHeight = lastStartHeight; } @@ -60,18 +81,34 @@ public LumpSumRewardsRecord(Address address, List bencoded) (Integer)bencoded[0], (Integer)bencoded[1], ((List)bencoded[2]).Select(a => new Address(a)).ToImmutableSortedSet(), - new FungibleAssetValue(bencoded[3]), + ((List)bencoded[3]).Select(v => new FungibleAssetValue(v)), (Integer?)bencoded.ElementAtOrDefault(4)) { } + private LumpSumRewardsRecord( + Address address, + long startHeight, + BigInteger totalShares, + ImmutableSortedSet
delegators, + ImmutableDictionary lumpSumRewards, + long? lastStartHeight) + { + Address = address; + StartHeight = startHeight; + TotalShares = totalShares; + Delegators = delegators; + LumpSumRewards = lumpSumRewards; + LastStartHeight = lastStartHeight; + } + public Address Address { get; } public long StartHeight { get; } public BigInteger TotalShares { get; } - public FungibleAssetValue LumpSumRewards { get; } + public ImmutableDictionary LumpSumRewards { get; } public ImmutableSortedSet
Delegators { get; } @@ -85,7 +122,9 @@ public List Bencoded .Add(StartHeight) .Add(TotalShares) .Add(new List(Delegators.Select(a => a.Bencoded))) - .Add(LumpSumRewards.Serialize()); + .Add(new List(LumpSumRewards + .OrderBy(r => r.Key, _currencyComparer) + .Select(r => r.Value.Serialize()))); return LastStartHeight is long lastStartHeight ? bencoded.Add(lastStartHeight) @@ -104,13 +143,18 @@ public LumpSumRewardsRecord MoveAddress(Address address) LumpSumRewards, LastStartHeight); + public LumpSumRewardsRecord AddLumpSumRewards(IEnumerable rewards) + => rewards.Aggregate(this, (accum, next) => AddLumpSumRewards(next)); + public LumpSumRewardsRecord AddLumpSumRewards(FungibleAssetValue rewards) => new LumpSumRewardsRecord( Address, StartHeight, TotalShares, Delegators, - LumpSumRewards + rewards, + LumpSumRewards.TryGetValue(rewards.Currency, out var cumulative) + ? LumpSumRewards.SetItem(rewards.Currency, cumulative + rewards) + : throw new ArgumentException($"Invalid reward currency: {rewards.Currency}"), LastStartHeight); public LumpSumRewardsRecord RemoveDelegator(Address delegator) @@ -122,8 +166,15 @@ public LumpSumRewardsRecord RemoveDelegator(Address delegator) LumpSumRewards, LastStartHeight); - public FungibleAssetValue RewardsDuringPeriod(BigInteger share) - => (LumpSumRewards * share).DivRem(TotalShares).Quotient; + public ImmutableDictionary RewardsDuringPeriod(BigInteger share) + => LumpSumRewards.Keys.Select(k => RewardsDuringPeriod(share, k)) + .ToImmutableDictionary(f => f.Currency); + + public FungibleAssetValue RewardsDuringPeriod(BigInteger share, Currency currency) + => LumpSumRewards.TryGetValue(currency, out var reward) + ? (reward * share).DivRem(TotalShares).Quotient + : throw new ArgumentException($"Invalid reward currency: {currency}"); + public override bool Equals(object? obj) => obj is LumpSumRewardsRecord other && Equals(other); From 2fdb9a9c0c3165d50f68d2d52135a0e6d38fe7b4 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Sun, 10 Nov 2024 14:24:14 +0900 Subject: [PATCH 144/206] feat: Concretize (De)Activation of Validator --- .../ValidatorDelegation/AllocateRewardTest.cs | 8 ++-- .../PromoteValidatorTest.cs | 4 +- .../UpdateValidatorsTest.cs | 6 +-- .../ValidatorDelegation/UpdateValidators.cs | 20 +++++++- Lib9c/Delegation/DelegateeMetadata.cs | 2 +- Lib9c/ValidatorDelegation/ProposerInfo.cs | 3 +- .../ValidatorDelegation/ValidatorDelegatee.cs | 46 ++++++++++++++----- Lib9c/ValidatorDelegation/ValidatorList.cs | 6 +-- 8 files changed, 68 insertions(+), 27 deletions(-) diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs index c0850d9438..6a7f41c4ad 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs @@ -39,7 +39,7 @@ PrivateKey GetProposerKey(List validators) return ValidatorsInfos .Where(item => item.VoteFlag == VoteFlag.PreCommit) .Where(item => validators.Any(v => v.PublicKey.Equals(item.Key.PublicKey))) - .Take(ValidatorList.MaxBondedSetSize) + .Take(ValidatorList.MaxActiveSetSize) .First() .Key; } @@ -191,12 +191,12 @@ private void ExecuteWithFixture(IAllocateRewardFixture fixture) world = EnsurePromotedValidators(world, validatorKeys, validatorCashes, height++); world = world.MintAsset(actionContext, Addresses.RewardPool, totalReward); var repository = new ValidatorRepository(world, actionContext); - var bondedSet = repository.GetValidatorList().GetBonded(); - var proposerKey = fixture.GetProposerKey(bondedSet); + var activeSet = repository.GetValidatorList().ActiveSet(); + var proposerKey = fixture.GetProposerKey(activeSet); world = EnsureProposer(world, proposerKey, height++); // Calculate expected values for comparison with actual values. - var votes = CreateVotes(validatorInfos, bondedSet, height - 1); + var votes = CreateVotes(validatorInfos, activeSet, height - 1); var expectedProposerReward = CalculatePropserReward(totalReward) + CalculateBonusPropserReward(votes, totalReward); var expectedValidatorsReward = totalReward - expectedProposerReward; diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs index e2baacc850..9c99515b23 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/PromoteValidatorTest.cs @@ -52,9 +52,9 @@ public void Execute() Assert.Equal(validatorKey.Address, Assert.Single(validator.Delegators)); Assert.Equal(gold.RawValue, bond.Share); Assert.Equal(validator.Validator, Assert.Single(validatorList.Validators)); - Assert.Equal(validator.Validator, Assert.Single(validatorList.GetBonded())); + Assert.Equal(validator.Validator, Assert.Single(validatorList.ActiveSet())); Assert.Equal(DelegationCurrency * 90, GetBalance(world, validatorKey.Address)); - Assert.Empty(validatorList.GetUnbonded()); + Assert.Empty(validatorList.InActiveSet()); } [Fact] diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs index c9a8de5a0f..54741e28f9 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UpdateValidatorsTest.cs @@ -38,7 +38,7 @@ public void Execute() // When var expectedRepository = new ValidatorRepository(world, actionContext); var expectedValidators = expectedRepository.GetValidatorList() - .GetBonded().OrderBy(item => item.OperatorAddress).ToList(); + .ActiveSet().OrderBy(item => item.OperatorAddress).ToList(); actionContext = new ActionContext { BlockIndex = height, @@ -75,7 +75,7 @@ public void Execute_ExcludesTombstonedValidator() // When var expectedRepository = new ValidatorRepository(world, actionContext); var expectedValidators = expectedRepository.GetValidatorList() - .GetBonded().OrderBy(item => item.OperatorAddress).ToList(); + .ActiveSet().OrderBy(item => item.OperatorAddress).ToList(); var updateValidators = new UpdateValidators(); world = EnsureTombstonedValidator(world, validatorKeys[0], height); actionContext = new ActionContext @@ -89,7 +89,7 @@ public void Execute_ExcludesTombstonedValidator() // Then var actualRepository = new ValidatorRepository(world, actionContext); var actualValidators = actualRepository.GetValidatorList() - .GetBonded().OrderBy(item => item.OperatorAddress).ToList(); + .ActiveSet().OrderBy(item => item.OperatorAddress).ToList(); var tombstonedValidator = actualRepository.GetValidatorDelegatee(validatorKeys[0].Address); Assert.True(tombstonedValidator.Tombstoned); diff --git a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs index 6595be1e52..e6e55e17fc 100644 --- a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs +++ b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs @@ -1,3 +1,4 @@ +using System.Linq; using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Action; @@ -19,10 +20,25 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { var world = context.PreviousState; + var prevValidators = world.GetValidatorSet().Validators; var repository = new ValidatorRepository(world, context); - var validators = repository.GetValidatorList(); + var validators = repository.GetValidatorList().ActiveSet(); - return world.SetValidatorSet(new ValidatorSet(validators.GetBonded())); + foreach (var deactivated in prevValidators.Except(validators)) + { + var validatorDelegatee = repository.GetValidatorDelegatee(deactivated.OperatorAddress); + validatorDelegatee.Deactivate(); + repository.SetValidatorDelegatee(validatorDelegatee); + } + + foreach (var activated in validators.Except(prevValidators)) + { + var validatorDelegatee = repository.GetValidatorDelegatee(activated.OperatorAddress); + validatorDelegatee.Activate(); + repository.SetValidatorDelegatee(validatorDelegatee); + } + + return repository.World.SetValidatorSet(new ValidatorSet(validators)); } } } diff --git a/Lib9c/Delegation/DelegateeMetadata.cs b/Lib9c/Delegation/DelegateeMetadata.cs index 1f93f0e86d..69527635cb 100644 --- a/Lib9c/Delegation/DelegateeMetadata.cs +++ b/Lib9c/Delegation/DelegateeMetadata.cs @@ -158,7 +158,7 @@ public Address Address public ImmutableHashSet RewardCurrencies { get; } - public Address DelegationPoolAddress { get; } + public Address DelegationPoolAddress { get; internal set; } public Address RewardPoolAddress { get; } diff --git a/Lib9c/ValidatorDelegation/ProposerInfo.cs b/Lib9c/ValidatorDelegation/ProposerInfo.cs index 6139730c4a..0f791c6312 100644 --- a/Lib9c/ValidatorDelegation/ProposerInfo.cs +++ b/Lib9c/ValidatorDelegation/ProposerInfo.cs @@ -1,7 +1,8 @@ +#nullable enable +using System.Collections.Immutable; using Bencodex.Types; using Libplanet.Crypto; using Nekoyume.Model.State; -using System.Collections.Immutable; namespace Nekoyume.ValidatorDelegation { diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index 36b9fb6302..bb119ad2d3 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Numerics; using Bencodex; @@ -21,13 +22,14 @@ public ValidatorDelegatee( PublicKey publicKey, BigInteger commissionPercentage, long creationHeight, + IEnumerable rewardCurrencies, ValidatorRepository repository) : base( address: address, accountAddress: repository.DelegateeAccountAddress, delegationCurrency: ValidatorDelegationCurrency, - rewardCurrency: ValidatorRewardCurrency, - delegationPoolAddress: UnbondedPoolAddress, + rewardCurrencies: rewardCurrencies, + delegationPoolAddress: InactiveDelegationPoolAddress, rewardPoolAddress: DelegationAddress.RewardPoolAddress(address, repository.DelegateeAccountAddress), rewardRemainderPoolAddress: Addresses.CommunityPool, slashedPoolAddress: Addresses.CommunityPool, @@ -49,7 +51,7 @@ public ValidatorDelegatee( } PublicKey = publicKey; - IsBonded = false; + IsActive = false; CommissionPercentage = commissionPercentage; CommissionPercentageLastUpdateHeight = creationHeight; DelegationChanged += OnDelegationChanged; @@ -77,7 +79,7 @@ public ValidatorDelegatee( repository: repository) { PublicKey = new PublicKey(((Binary)bencoded[0]).ByteArray); - IsBonded = (Bencodex.Types.Boolean)bencoded[1]; + IsActive = (Bencodex.Types.Boolean)bencoded[1]; CommissionPercentage = (Integer)bencoded[2]; CommissionPercentageLastUpdateHeight = (Integer)bencoded[3]; DelegationChanged += OnDelegationChanged; @@ -116,7 +118,7 @@ public ValidatorDelegatee( public List Bencoded => List.Empty .Add(PublicKey.Format(true)) - .Add(IsBonded) + .Add(IsActive) .Add(CommissionPercentage) .Add(CommissionPercentageLastUpdateHeight); @@ -124,7 +126,7 @@ public ValidatorDelegatee( public PublicKey PublicKey { get; } - public bool IsBonded { get; private set; } + public bool IsActive { get; private set; } public BigInteger Power => TotalDelegated.RawValue; @@ -197,6 +199,28 @@ public void SetCommissionPercentage(BigInteger percentage, long height) base.Unjail(height); } + public void Activate() + { + ValidatorRepository repository = (ValidatorRepository)Repository; + IsActive = true; + Metadata.DelegationPoolAddress = ActiveDelegationPoolAddress; + repository.TransferAsset( + InactiveDelegationPoolAddress, + ActiveDelegationPoolAddress, + repository.GetBalance(InactiveDelegationPoolAddress, DelegationCurrency)); + } + + public void Deactivate() + { + ValidatorRepository repository = (ValidatorRepository)Repository; + IsActive = false; + Metadata.DelegationPoolAddress = InactiveDelegationPoolAddress; + repository.TransferAsset( + ActiveDelegationPoolAddress, + InactiveDelegationPoolAddress, + repository.GetBalance(ActiveDelegationPoolAddress, DelegationCurrency)); + } + public void OnDelegationChanged(object? sender, long height) { ValidatorRepository repository = (ValidatorRepository)Repository; @@ -238,7 +262,7 @@ public bool Equals(ValidatorDelegatee? other) => other is ValidatorDelegatee validatorDelegatee && Metadata.Equals(validatorDelegatee.Metadata) && PublicKey.Equals(validatorDelegatee.PublicKey) - && IsBonded == validatorDelegatee.IsBonded + && IsActive == validatorDelegatee.IsActive && CommissionPercentage == validatorDelegatee.CommissionPercentage && CommissionPercentageLastUpdateHeight == validatorDelegatee.CommissionPercentageLastUpdateHeight; @@ -251,14 +275,14 @@ public override bool Equals(object? obj) public override int GetHashCode() => HashCode.Combine(Address, AccountAddress); - public static Address BondedPoolAddress => new Address( + public static Address ActiveDelegationPoolAddress => new Address( ImmutableArray.Create( 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42)); + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41)); - public static Address UnbondedPoolAddress => new Address( + public static Address InactiveDelegationPoolAddress => new Address( ImmutableArray.Create( 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55)); + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49)); } } diff --git a/Lib9c/ValidatorDelegation/ValidatorList.cs b/Lib9c/ValidatorDelegation/ValidatorList.cs index cea8741dd6..b0ca670d3b 100644 --- a/Lib9c/ValidatorDelegation/ValidatorList.cs +++ b/Lib9c/ValidatorDelegation/ValidatorList.cs @@ -42,15 +42,15 @@ private ValidatorList(ImmutableList validators) public ImmutableList Validators { get; } - public static int MaxBondedSetSize => 100; + public static int MaxActiveSetSize => 100; public List Bencoded => new List(Validators.Select(v => v.Bencoded)); IValue IBencodable.Bencoded => Bencoded; - public List GetBonded() => Validators.Take(MaxBondedSetSize).ToList(); + public List ActiveSet() => Validators.Take(MaxActiveSetSize).ToList(); - public List GetUnbonded() => Validators.Skip(MaxBondedSetSize).ToList(); + public List InActiveSet() => Validators.Skip(MaxActiveSetSize).ToList(); public ValidatorList SetValidator(Validator validator) => RemoveValidator(validator.PublicKey).AddValidator(validator); From ef62c9aab6c919923b271fe853796b2f2bd1a1ef Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 11 Nov 2024 03:09:26 +0900 Subject: [PATCH 145/206] feat: Reconstruct delegation relation --- Lib9c.Policy/Policy/BlockPolicySource.cs | 1 + ...laimRewardGuild.cs => ClaimGuildReward.cs} | 28 +-- Lib9c/Action/Guild/ClaimReward.cs | 47 +++++ .../Migration/MigratePlanetariumGuild.cs | 1 - .../MigratePlanetariumGuildUnbond.cs | 20 +- Lib9c/Action/InitializeStates.cs | 3 +- Lib9c/Action/Stake.cs | 3 +- .../AllocateGuildReward.cs | 171 +++++++++++++++ .../ValidatorDelegation/AllocateReward.cs | 197 +++++++++++------- .../ClaimRewardValidator.cs | 60 ------ ...torSelf.cs => ClaimValidatorRewardSelf.cs} | 8 +- .../ValidatorDelegation/PromoteValidator.cs | 10 +- .../ReleaseValidatorUnbondings.cs | 2 +- .../ValidatorDelegation/SlashValidator.cs | 17 +- .../ValidatorDelegation/UpdateValidators.cs | 11 + Lib9c/Addresses.cs | 4 +- Lib9c/Delegation/Delegatee.cs | 12 +- Lib9c/Model/Guild/Guild.cs | 34 +-- Lib9c/Model/Guild/GuildParticipant.cs | 94 ++++----- Lib9c/Model/Guild/GuildRepository.cs | 46 ++-- Lib9c/Model/Guild/GuildValidatorDelegator.cs | 36 ---- Lib9c/Model/Guild/GuildValidatorRepository.cs | 74 ------- ... ValidatorDelegateeForGuildParticipant.cs} | 32 ++- Lib9c/Module/Guild/GuildModule.cs | 21 +- Lib9c/Module/Guild/GuildParticipantModule.cs | 23 +- .../Guild/ValidatorDelegateeForGuildModule.cs | 60 ++++++ .../ValidatorDelegateeModule.cs | 9 +- .../ValidatorDelegatorModule.cs | 17 +- .../ValidatorDelegation/ValidatorDelegatee.cs | 11 +- .../ValidatorDelegation/ValidatorDelegator.cs | 6 +- .../ValidatorRepository.cs | 14 +- 31 files changed, 622 insertions(+), 450 deletions(-) rename Lib9c/Action/Guild/{ClaimRewardGuild.cs => ClaimGuildReward.cs} (53%) create mode 100644 Lib9c/Action/Guild/ClaimReward.cs create mode 100644 Lib9c/Action/ValidatorDelegation/AllocateGuildReward.cs delete mode 100644 Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs rename Lib9c/Action/ValidatorDelegation/{ClaimRewardValidatorSelf.cs => ClaimValidatorRewardSelf.cs} (85%) delete mode 100644 Lib9c/Model/Guild/GuildValidatorDelegator.cs delete mode 100644 Lib9c/Model/Guild/GuildValidatorRepository.cs rename Lib9c/Model/Guild/{GuildValidatorDelegatee.cs => ValidatorDelegateeForGuildParticipant.cs} (54%) create mode 100644 Lib9c/Module/Guild/ValidatorDelegateeForGuildModule.cs diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index d7b5f5fff3..5ab6802df1 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -135,6 +135,7 @@ internal IBlockPolicy GetPolicy( policyActionsRegistry: new PolicyActionsRegistry( beginBlockActions: new IAction[] { new SlashValidator(), + new AllocateGuildReward(), new AllocateReward(), }.ToImmutableArray(), endBlockActions: new IAction[] { diff --git a/Lib9c/Action/Guild/ClaimRewardGuild.cs b/Lib9c/Action/Guild/ClaimGuildReward.cs similarity index 53% rename from Lib9c/Action/Guild/ClaimRewardGuild.cs rename to Lib9c/Action/Guild/ClaimGuildReward.cs index c311051d36..a28df7d15b 100644 --- a/Lib9c/Action/Guild/ClaimRewardGuild.cs +++ b/Lib9c/Action/Guild/ClaimGuildReward.cs @@ -3,17 +3,16 @@ using Libplanet.Action.State; using Libplanet.Action; using Nekoyume.Model.Guild; -using Nekoyume.Module.Guild; -using Nekoyume.TypedAddress; +using Nekoyume.ValidatorDelegation; -namespace Nekoyume.Action.ValidatorDelegation +namespace Nekoyume.Action.Guild { [ActionType(TypeIdentifier)] - public sealed class ClaimRewardGuild : ActionBase + public sealed class ClaimGuildReward : ActionBase { - public const string TypeIdentifier = "claim_reward_guild"; + public const string TypeIdentifier = "claim_guild_reward"; - public ClaimRewardGuild() { } + public ClaimGuildReward() { } public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) @@ -27,7 +26,7 @@ public override void LoadPlainValue(IValue plainValue) rawValues is not Null) { throw new InvalidCastException(); - } + }; } public override IWorld Execute(IActionContext context) @@ -38,17 +37,18 @@ public override IWorld Execute(IActionContext context) var guildRepository = new GuildRepository(world, context); var guildParticipant = guildRepository.GetGuildParticipant(context.Signer); - if (!(guildRepository.GetJoinedGuild(new AgentAddress(context.Signer)) - is GuildAddress guildAddress)) + var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); + if (context.Signer != guild.GuildMasterAddress) { - throw new InvalidOperationException("Signer does not joind guild."); + throw new InvalidOperationException("Signer is not a guild master."); } - var guild = guildRepository.GetGuild(guildAddress); - - guildParticipant.ClaimReward(guild, context.BlockIndex); + var repository = new ValidatorRepository(guildRepository.World, guildRepository.ActionContext); + var validatorDelegatee = repository.GetValidatorDelegatee(guild.ValidatorAddress); + var validatorDelegator = repository.GetValidatorDelegator(guild.Address); + validatorDelegator.ClaimReward(validatorDelegatee, context.BlockIndex); - return guildRepository.World; + return repository.World; } } } diff --git a/Lib9c/Action/Guild/ClaimReward.cs b/Lib9c/Action/Guild/ClaimReward.cs new file mode 100644 index 0000000000..afa4ef6de5 --- /dev/null +++ b/Lib9c/Action/Guild/ClaimReward.cs @@ -0,0 +1,47 @@ +using System; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Nekoyume.Model.Guild; + +namespace Nekoyume.Action.Guild +{ + [ActionType(TypeIdentifier)] + public sealed class ClaimReward : ActionBase + { + public const string TypeIdentifier = "claim_reward"; + + public ClaimReward() { } + + 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 GuildRepository(world, context); + + var guildParticipant = repository.GetGuildParticipant(context.Signer); + var guild = repository.GetGuild(guildParticipant.GuildAddress); + var validatorDelegateeForGuildParticipant + = repository.GetValidatorDelegateeForGuildParticipant(guild.ValidatorAddress); + guildParticipant.ClaimReward(validatorDelegateeForGuildParticipant, context.BlockIndex); + + return repository.World; + } + } +} diff --git a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs index 9d46a667fe..deca751572 100644 --- a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs +++ b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs @@ -59,7 +59,6 @@ public override IWorld Execute(IActionContext context) guildAddress, legacyGuild.GuildMasterAddress, ValidatorConfig.PlanetariumValidatorAddress, - Currencies.GuildGold, repository); repository.SetGuild(guild); diff --git a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs index 8a3302cbee..a3fb35c65a 100644 --- a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs +++ b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs @@ -3,6 +3,8 @@ using Libplanet.Action.State; using Libplanet.Action; using Nekoyume.Model.Guild; +using Nekoyume.Model.State; +using Nekoyume.ValidatorDelegation; namespace Nekoyume.Action.Guild.Migration { @@ -40,15 +42,23 @@ public override IWorld Execute(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; - var repository = new GuildRepository(world, context); + var guildRepository = new GuildRepository(world, context); - var guildAddress = repository.GetGuildParticipant(GuildConfig.PlanetariumGuildOwner).GuildAddress; - var guild = repository.GetGuild(guildAddress); + var guildAddress = guildRepository.GetGuildParticipant(GuildConfig.PlanetariumGuildOwner).GuildAddress; + var guild = guildRepository.GetGuild(guildAddress); + var validatorDelegateeForGuildParticipant + = guildRepository.GetValidatorDelegateeForGuildParticipant(guild.ValidatorAddress); // TODO: [GuildMigration] Replace below height when determined. - guild.Metadata.UpdateUnbondingPeriod(0L); + validatorDelegateeForGuildParticipant.Metadata.UpdateUnbondingPeriod(LegacyStakeState.LockupInterval); + guildRepository.SetValidatorDelegateeForGuildParticipant(validatorDelegateeForGuildParticipant); - repository.SetGuild(guild); + var repository = new ValidatorRepository(guildRepository); + var validatorDelegatee = repository.GetValidatorDelegatee(guild.ValidatorAddress); + + // TODO: [GuildMigration] Replace below height when determined. + validatorDelegatee.Metadata.UpdateUnbondingPeriod(LegacyStakeState.LockupInterval); + repository.SetValidatorDelegatee(validatorDelegatee); return repository.World; } diff --git a/Lib9c/Action/InitializeStates.cs b/Lib9c/Action/InitializeStates.cs index 21bbd1b164..065313961e 100644 --- a/Lib9c/Action/InitializeStates.cs +++ b/Lib9c/Action/InitializeStates.cs @@ -209,12 +209,13 @@ public override IWorld Execute(IActionContext context) validator.PublicKey, ValidatorDelegatee.DefaultCommissionPercentage, context.BlockIndex, + new Currency[] { currencyState.Currency }, repository); var delegationFAV = FungibleAssetValue.FromRawValue( validatorDelegatee.DelegationCurrency, validator.Power); var validatorOperatorAddress = validator.OperatorAddress; var validatorDelegator = repository.GetValidatorDelegator( - validatorOperatorAddress, validatorOperatorAddress); + validatorOperatorAddress); repository.SetValidatorDelegatee(validatorDelegatee); repository.UpdateWorld( diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index 008f4beb37..9f2abd3ec4 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -213,7 +213,8 @@ private static IWorld ContractNewStake( if (guildRepository.TryGetGuildParticipant(agentAddress, out var guildParticipant)) { var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); - var share = guild.ShareFromFAV(gg); + var validatorDelegateeForGuildParticipant = guildRepository.GetValidatorDelegateeForGuildParticipant(guild.ValidatorAddress); + var share = validatorDelegateeForGuildParticipant.ShareFromFAV(gg); guildParticipant.Undelegate(guild, share, height); state = guildRepository.World; } diff --git a/Lib9c/Action/ValidatorDelegation/AllocateGuildReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateGuildReward.cs new file mode 100644 index 0000000000..219fde53d0 --- /dev/null +++ b/Lib9c/Action/ValidatorDelegation/AllocateGuildReward.cs @@ -0,0 +1,171 @@ +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Types.Assets; +using Libplanet.Types.Consensus; +using Nekoyume.ValidatorDelegation; +using Nekoyume.Module.ValidatorDelegation; +using Libplanet.Types.Blocks; +using Lib9c; + +namespace Nekoyume.Action.ValidatorDelegation +{ + public sealed class AllocateGuildReward : ActionBase + { + public AllocateGuildReward() + { + } + + public override IValue PlainValue => Null.Value; + + public override void LoadPlainValue(IValue plainValue) + { + } + + public override IWorld Execute(IActionContext context) + { + var world = context.PreviousState; + var repository = new ValidatorRepository(world, context); + var rewardCurrency = Currencies.Mead; + var proposerInfo = repository.GetProposerInfo(); + + if (context.LastCommit is BlockCommit lastCommit) + { + var validatorSetPower = lastCommit.Votes.Aggregate( + BigInteger.Zero, + (total, next) + => total + (next.ValidatorPower ?? BigInteger.Zero)); + + DistributeProposerReward( + repository, + rewardCurrency, + proposerInfo, + validatorSetPower, + lastCommit.Votes); + + DistributeValidatorReward( + repository, + rewardCurrency, + validatorSetPower, + lastCommit.Votes); + } + + var communityFund = repository.GetBalance(Addresses.RewardPool, rewardCurrency); + + if (communityFund.Sign > 0) + { + repository.TransferAsset( + Addresses.RewardPool, + Addresses.CommunityPool, + communityFund); + } + + return repository.World; + } + + private static void DistributeProposerReward( + ValidatorRepository repository, + Currency rewardCurrency, + ProposerInfo proposerInfo, + BigInteger validatorSetPower, + IEnumerable lastVotes) + { + FungibleAssetValue blockReward = repository.GetBalance( + Addresses.RewardPool, rewardCurrency); + + if (proposerInfo.BlockIndex != repository.ActionContext.BlockIndex - 1) + { + return; + } + + if (blockReward.Sign <= 0) + { + return; + } + + BigInteger votePowerNumerator + = lastVotes.Aggregate( + BigInteger.Zero, + (total, next) + => total + + (next.Flag == VoteFlag.PreCommit + ? next.ValidatorPower ?? BigInteger.Zero + : BigInteger.Zero)); + + BigInteger votePowerDenominator = validatorSetPower; + + if (votePowerDenominator == BigInteger.Zero) + { + return; + } + + var baseProposerReward + = (blockReward * ValidatorDelegatee.BaseProposerRewardPercentage) + .DivRem(100).Quotient; + var bonusProposerReward + = (blockReward * votePowerNumerator * ValidatorDelegatee.BonusProposerRewardPercentage) + .DivRem(votePowerDenominator * 100).Quotient; + FungibleAssetValue proposerReward = baseProposerReward + bonusProposerReward; + + if (proposerReward.Sign > 0) + { + repository.TransferAsset( + Addresses.RewardPool, + proposerInfo.Proposer, + proposerReward); + } + } + + private static void DistributeValidatorReward( + ValidatorRepository repository, + Currency rewardCurrency, + BigInteger validatorSetPower, + IEnumerable lastVotes) + { + long blockHeight = repository.ActionContext.BlockIndex; + + FungibleAssetValue rewardToAllocate + = repository.GetBalance(Addresses.RewardPool, rewardCurrency); + + if (rewardToAllocate.Sign <= 0) + { + return; + } + + if (validatorSetPower == BigInteger.Zero) + { + return; + } + + foreach (Vote vote in lastVotes) + { + if (vote.Flag == VoteFlag.Null || vote.Flag == VoteFlag.Unknown) + { + continue; + } + + if (!repository.TryGetValidatorDelegatee( + vote.ValidatorPublicKey.Address, out var validatorDelegatee)) + { + continue; + } + + BigInteger validatorPower = vote.ValidatorPower ?? BigInteger.Zero; + if (validatorPower == BigInteger.Zero) + { + continue; + } + + validatorDelegatee.AllocateReward( + rewardToAllocate, + validatorPower, + validatorSetPower, + Addresses.RewardPool, + blockHeight); + } + } + } +} diff --git a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs index 7cc0594050..5e3ff452b4 100644 --- a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs +++ b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs @@ -6,9 +6,12 @@ using Libplanet.Action; using Libplanet.Types.Assets; using Libplanet.Types.Consensus; +using Libplanet.Types.Blocks; +using Nekoyume.Module; +using Nekoyume.Model.Guild; +using Nekoyume.Module.Guild; using Nekoyume.ValidatorDelegation; using Nekoyume.Module.ValidatorDelegation; -using Libplanet.Types.Blocks; namespace Nekoyume.Action.ValidatorDelegation { @@ -27,122 +30,101 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { var world = context.PreviousState; - var repository = new ValidatorRepository(world, context); - var rewardCurrency = ValidatorDelegatee.ValidatorRewardCurrency; - var proposerInfo = repository.GetProposerInfo(); + var rewardCurrency = world.GetGoldCurrency(); + var repository = new GuildRepository(world, context); if (context.LastCommit is BlockCommit lastCommit) { - DistributeProposerReward( - repository, - context, - rewardCurrency, - proposerInfo, - lastCommit.Votes); - - DistributeValidatorReward( - repository, - context, - rewardCurrency, - lastCommit.Votes); - } - - var communityFund = repository.GetBalance(Addresses.RewardPool, rewardCurrency); + var validatorSetPower = lastCommit.Votes.Aggregate( + BigInteger.Zero, + (total, next) + => total + (next.ValidatorPower ?? BigInteger.Zero)); - if (communityFund.Sign > 0) - { - repository.TransferAsset( - Addresses.RewardPool, - Addresses.CommunityPool, - communityFund); + DistributeValidator(repository, rewardCurrency, validatorSetPower, lastCommit.Votes); + var validatorRepository = new ValidatorRepository(repository.World, context); + DistributeGuild(validatorRepository, rewardCurrency, validatorSetPower, lastCommit.Votes); + repository.UpdateWorld(validatorRepository.World); + DistributeGuildParticipant(repository, rewardCurrency, validatorSetPower, lastCommit.Votes); } return repository.World; } - internal static void DistributeProposerReward( - ValidatorRepository repository, - IActionContext ctx, + private static void DistributeValidator( + GuildRepository repository, Currency rewardCurrency, - ProposerInfo proposerInfo, + BigInteger validatorSetPower, IEnumerable lastVotes) { - FungibleAssetValue blockReward = repository.GetBalance( - Addresses.RewardPool, rewardCurrency); + FungibleAssetValue reward + = repository.GetBalance(Addresses.RewardPool, rewardCurrency); - if (proposerInfo.BlockIndex != ctx.BlockIndex - 1) + if (reward.Sign <= 0) { return; } - if (blockReward.Sign <= 0) + if (validatorSetPower == BigInteger.Zero) { return; } - BigInteger votePowerNumerator - = lastVotes.Aggregate( - BigInteger.Zero, - (total, next) - => total + - (next.Flag == VoteFlag.PreCommit - ? next.ValidatorPower ?? BigInteger.Zero - : BigInteger.Zero)); - - BigInteger votePowerDenominator - = lastVotes.Aggregate( - BigInteger.Zero, - (total, next) - => total + (next.ValidatorPower ?? BigInteger.Zero)); + var validatorReward = reward.DivRem(10).Quotient; - if (votePowerDenominator == BigInteger.Zero) + foreach (Vote vote in lastVotes) { - return; - } + if (vote.Flag == VoteFlag.Null || vote.Flag == VoteFlag.Unknown) + { + continue; + } - var baseProposerReward - = (blockReward * ValidatorDelegatee.BaseProposerRewardPercentage) - .DivRem(100).Quotient; - var bonusProposerReward - = (blockReward * votePowerNumerator * ValidatorDelegatee.BonusProposerRewardPercentage) - .DivRem(votePowerDenominator * 100).Quotient; - FungibleAssetValue proposerReward = baseProposerReward + bonusProposerReward; + BigInteger validatorPower = vote.ValidatorPower ?? BigInteger.Zero; + if (validatorPower == BigInteger.Zero) + { + continue; + } - if (proposerReward.Sign > 0) - { - repository.TransferAsset( - Addresses.RewardPool, - proposerInfo.Proposer, - proposerReward); + var validatorAddress = vote.ValidatorPublicKey.Address; + if (!repository.TryGetValidatorDelegateeForGuild( + validatorAddress, out var validatorDelegatee)) + { + continue; + } + + + FungibleAssetValue rewardEach + = (validatorReward * validatorPower).DivRem(validatorSetPower).Quotient; + + if (rewardEach.Sign > 0) + { + repository.TransferAsset(Addresses.RewardPool, validatorAddress, rewardEach); + } } } - internal static void DistributeValidatorReward( + private static void DistributeGuild( ValidatorRepository repository, - IActionContext ctx, Currency rewardCurrency, + BigInteger validatorSetPower, IEnumerable lastVotes) { - long blockHeight = ctx.BlockIndex; + long blockHeight = repository.ActionContext.BlockIndex; - FungibleAssetValue rewardToAllocate + FungibleAssetValue reward = repository.GetBalance(Addresses.RewardPool, rewardCurrency); - if (rewardToAllocate.Sign <= 0) + if (reward.Sign <= 0) { return; } - BigInteger validatorSetPower - = lastVotes.Aggregate( - BigInteger.Zero, - (total, next) => total + (next.ValidatorPower ?? BigInteger.Zero)); - if (validatorSetPower == BigInteger.Zero) { return; } + var guildReward = reward.DivRem(10).Quotient; + foreach (Vote vote in lastVotes) { if (vote.Flag == VoteFlag.Null || vote.Flag == VoteFlag.Unknown) @@ -150,25 +132,78 @@ BigInteger validatorSetPower continue; } + BigInteger validatorPower = vote.ValidatorPower ?? BigInteger.Zero; + if (validatorPower == BigInteger.Zero) + { + continue; + } + + var validatorAddress = vote.ValidatorPublicKey.Address; if (!repository.TryGetValidatorDelegatee( - vote.ValidatorPublicKey.Address, out var validatorDelegatee)) + validatorAddress, out var validatorDelegatee)) { continue; } - BigInteger validatorPower = vote.ValidatorPower ?? BigInteger.Zero; + FungibleAssetValue rewardEach + = (guildReward * validatorPower).DivRem(validatorSetPower).Quotient; + + if (rewardEach.Sign > 0) + { + repository.TransferAsset( + Addresses.RewardPool, validatorDelegatee.RewardPoolAddress, rewardEach); + validatorDelegatee.CollectRewards(blockHeight); + } + } + } + + private static void DistributeGuildParticipant( + GuildRepository repository, + Currency rewardCurrency, + BigInteger validatorSetPower, + IEnumerable lastVotes) + { + FungibleAssetValue reward + = repository.GetBalance(Addresses.RewardPool, rewardCurrency); + + if (reward.Sign <= 0) + { + return; + } + + if (validatorSetPower == BigInteger.Zero) + { + return; + } + + foreach (Vote vote in lastVotes) + { + if (vote.Flag == VoteFlag.Null || vote.Flag == VoteFlag.Unknown) + { + continue; + } + + BigInteger validatorPower = vote.ValidatorPower ?? BigInteger.Zero; if (validatorPower == BigInteger.Zero) { continue; } - validatorDelegatee.AllocateReward( - rewardToAllocate, - validatorPower, - validatorSetPower, - Addresses.RewardPool, - blockHeight); + var validatorAddress = vote.ValidatorPublicKey.Address; + if (!repository.TryGetValidatorDelegateeForGuild( + validatorAddress, out var validatorDelegatee)) + { + continue; + } + + FungibleAssetValue rewardEach + = (reward * validatorPower).DivRem(validatorSetPower).Quotient; + + if (rewardEach.Sign > 0) + { + repository.TransferAsset(Addresses.RewardPool, validatorDelegatee.RewardPoolAddress, rewardEach); + } } } } diff --git a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs b/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs deleted file mode 100644 index 79f81d0efd..0000000000 --- a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidator.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using Bencodex.Types; -using Libplanet.Action.State; -using Libplanet.Action; -using Nekoyume.Model.Guild; -using Nekoyume.Module.Guild; -using Nekoyume.TypedAddress; - -namespace Nekoyume.Action.ValidatorDelegation -{ - [ActionType(TypeIdentifier)] - public sealed class ClaimRewardValidator : ActionBase - { - public const string TypeIdentifier = "claim_reward_validator"; - - public ClaimRewardValidator() { } - - 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 guildRepository = new GuildRepository(world, context); - - 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(guild.ValidatorAddress); - guildValidatorDelegator.ClaimReward(guildValidatorDelegatee, context.BlockIndex); - - return guildValidatorRepository.World; - } - } -} diff --git a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidatorSelf.cs b/Lib9c/Action/ValidatorDelegation/ClaimValidatorRewardSelf.cs similarity index 85% rename from Lib9c/Action/ValidatorDelegation/ClaimRewardValidatorSelf.cs rename to Lib9c/Action/ValidatorDelegation/ClaimValidatorRewardSelf.cs index 57a0e0c7dc..f01cbf25b0 100644 --- a/Lib9c/Action/ValidatorDelegation/ClaimRewardValidatorSelf.cs +++ b/Lib9c/Action/ValidatorDelegation/ClaimValidatorRewardSelf.cs @@ -8,11 +8,11 @@ namespace Nekoyume.Action.ValidatorDelegation { [ActionType(TypeIdentifier)] - public sealed class ClaimRewardValidatorSelf : ActionBase + public sealed class ClaimValidatorRewardSelf : ActionBase { - public const string TypeIdentifier = "claim_reward_validator_self"; + public const string TypeIdentifier = "claim_validator_reward_self"; - public ClaimRewardValidatorSelf() { } + public ClaimValidatorRewardSelf() { } public Address ValidatorDelegatee { get; private set; } @@ -38,7 +38,7 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; var repository = new ValidatorRepository(world, context); var validatorDelegatee = repository.GetValidatorDelegatee(context.Signer); - var validatorDelegator = repository.GetValidatorDelegator(context.Signer, context.Signer); + var validatorDelegator = repository.GetValidatorDelegator(context.Signer); validatorDelegator.ClaimReward(validatorDelegatee, context.BlockIndex); return repository.World; diff --git a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs index 9ab759d2c3..2a6987aa7b 100644 --- a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs @@ -7,7 +7,8 @@ using Libplanet.Types.Assets; using Nekoyume.Module.ValidatorDelegation; using Nekoyume.ValidatorDelegation; -using Org.BouncyCastle.Bcpg.Sig; +using Nekoyume.Model.Guild; +using Nekoyume.Module.Guild; namespace Nekoyume.Action.ValidatorDelegation { @@ -76,12 +77,15 @@ public IWorld ExecutePublic(IActionContext context) GasTracer.UseGas(1); var world = context.PreviousState; - var repository = new ValidatorRepository(world, context); + var repository = new ValidatorRepository(world, context); repository.CreateValidatorDelegatee(PublicKey, CommissionPercentage); repository.DelegateValidator(context.Signer, FAV); - return repository.World; + var guildRepository = new GuildRepository(repository.World, context); + guildRepository.CreateValidatorDelegateeForGuildParticipant(); + + return guildRepository.World; } } } diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs index 1eadf94c46..5554e9b132 100644 --- a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -74,7 +74,7 @@ private IWorld Unstake(IWorld world, IActionContext context, Address address) { var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); var stakeStateAddress = guildParticipant.DelegationPoolAddress; - var gg = world.GetBalance(stakeStateAddress, guild.DelegationCurrency); + var gg = world.GetBalance(stakeStateAddress, ValidatorDelegatee.ValidatorDelegationCurrency); if (gg.Sign > 0) { var (ncg, _) = ConvertToGoldCurrency(gg, goldCurrency); diff --git a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs index 9ec0001745..b620c608d0 100644 --- a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs @@ -1,13 +1,14 @@ using System; +using System.Linq; using System.Numerics; using Bencodex.Types; -using Libplanet.Action.State; using Libplanet.Action; +using Libplanet.Action.State; using Libplanet.Types.Consensus; using Libplanet.Types.Evidence; -using Nekoyume.ValidatorDelegation; -using System.Linq; +using Nekoyume.Model.Guild; using Nekoyume.Module.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; namespace Nekoyume.Action.ValidatorDelegation { @@ -46,6 +47,11 @@ public override IWorld Execute(IActionContext context) var validatorDelegatee = repository.GetValidatorDelegatee(abstain.Address); validatorDelegatee.Slash(LivenessSlashFactor, context.BlockIndex, context.BlockIndex); validatorDelegatee.Jail(context.BlockIndex + AbstainJailTime); + + var guildRepository = new GuildRepository(repository.World, repository.ActionContext); + var validatorDelegateeForGuildParticipant = guildRepository.GetValidatorDelegateeForGuildParticipant(abstain.Address); + validatorDelegateeForGuildParticipant.Slash(LivenessSlashFactor, context.BlockIndex, context.BlockIndex); + repository.UpdateWorld(guildRepository.World); } foreach (var evidence in context.Evidence) @@ -61,6 +67,11 @@ public override IWorld Execute(IActionContext context) var validatorDelegatee = repository.GetValidatorDelegatee(e.TargetAddress); validatorDelegatee.Slash(DuplicateVoteSlashFactor, e.Height, context.BlockIndex); validatorDelegatee.Tombstone(); + + var guildRepository = new GuildRepository(repository.World, repository.ActionContext); + var validatorDelegateeForGuildParticipant = guildRepository.GetValidatorDelegateeForGuildParticipant(e.TargetAddress); + validatorDelegateeForGuildParticipant.Slash(DuplicateVoteSlashFactor, e.Height, context.BlockIndex); + repository.UpdateWorld(guildRepository.World); break; default: break; diff --git a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs index e6e55e17fc..0fb6cd5ddc 100644 --- a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs +++ b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs @@ -4,6 +4,7 @@ using Libplanet.Action; using Libplanet.Types.Consensus; using Nekoyume.ValidatorDelegation; +using Nekoyume.Model.Guild; namespace Nekoyume.Action.ValidatorDelegation { @@ -29,6 +30,11 @@ public override IWorld Execute(IActionContext context) var validatorDelegatee = repository.GetValidatorDelegatee(deactivated.OperatorAddress); validatorDelegatee.Deactivate(); repository.SetValidatorDelegatee(validatorDelegatee); + var guildRepository = new GuildRepository(repository.World, repository.ActionContext); + var validatorDelegateeForGuildParticipant = guildRepository.GetValidatorDelegateeForGuildParticipant(deactivated.OperatorAddress); + validatorDelegateeForGuildParticipant.Deactivate(); + guildRepository.SetValidatorDelegateeForGuildParticipant(validatorDelegateeForGuildParticipant); + repository.UpdateWorld(guildRepository.World); } foreach (var activated in validators.Except(prevValidators)) @@ -36,6 +42,11 @@ public override IWorld Execute(IActionContext context) var validatorDelegatee = repository.GetValidatorDelegatee(activated.OperatorAddress); validatorDelegatee.Activate(); repository.SetValidatorDelegatee(validatorDelegatee); + var guildRepository = new GuildRepository(repository.World, repository.ActionContext); + var validatorDelegateeForGuildParticipant = guildRepository.GetValidatorDelegateeForGuildParticipant(activated.OperatorAddress); + validatorDelegateeForGuildParticipant.Activate(); + guildRepository.SetValidatorDelegateeForGuildParticipant(validatorDelegateeForGuildParticipant); + repository.UpdateWorld(guildRepository.World); } return repository.World.SetValidatorSet(new ValidatorSet(validators)); diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index 5843e119f4..28a140d574 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -105,8 +105,8 @@ public static Address GetGuildBanAccountAddress(Address guildAddress) => /// /// An address of an account having . - /// - public static readonly Address GuildMetadata + ///
+ public static readonly Address ValidatorDelegateeForGuildParticipantMetadata = new Address("0000000000000000000000000000000000000210"); /// diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index ed36dcf4bf..f72ccdf791 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -339,7 +339,17 @@ public void Slash(BigInteger slashFactor, long infractionHeight, long height) } } - Repository.TransferAsset(DelegationPoolAddress, SlashedPoolAddress, slashed); + var delegationBalance = Repository.GetBalance(DelegationPoolAddress, DelegationCurrency); + if (delegationBalance < slashed) + { + slashed = delegationBalance; + } + + if (slashed > DelegationCurrency * 0) + { + Repository.TransferAsset(DelegationPoolAddress, SlashedPoolAddress, slashed); + } + Repository.SetDelegateeMetadata(Metadata); DelegationChanged?.Invoke(this, height); } diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index 781bc34a6f..5120e90ba8 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -1,16 +1,14 @@ +#nullable enable using System; using Bencodex; using Bencodex.Types; -using Lib9c; using Libplanet.Crypto; -using Libplanet.Types.Assets; using Nekoyume.Action; -using Nekoyume.Delegation; using Nekoyume.TypedAddress; namespace Nekoyume.Model.Guild { - public class Guild : Delegatee, IBencodable, IEquatable + public class Guild : IBencodable, IEquatable { private const string StateTypeName = "guild"; private const long StateVersion = 1; @@ -23,31 +21,17 @@ public Guild( GuildAddress address, AgentAddress guildMasterAddress, Address validatorAddress, - Currency rewardCurrency, GuildRepository repository) - : base( - address: address, - accountAddress: repository.DelegateeAccountAddress, - delegationCurrency: Currencies.GuildGold, - rewardCurrency: rewardCurrency, - delegationPoolAddress: DelegationAddress.DelegationPoolAddress(address, repository.DelegateeAccountAddress), - rewardPoolAddress: DelegationAddress.RewardPoolAddress(address, repository.DelegateeAccountAddress), - rewardRemainderPoolAddress: Addresses.CommunityPool, - slashedPoolAddress: Addresses.CommunityPool, - unbondingPeriod: 0L, - maxUnbondLockInEntries: 0, - maxRebondGraceEntries: 0, - repository: repository) { - ValidatorAddress = validatorAddress; + Address = address; GuildMasterAddress = guildMasterAddress; + ValidatorAddress = validatorAddress; } public Guild( GuildAddress address, IValue bencoded, GuildRepository repository) - : base(address: address, repository: repository) { if (bencoded is not List list) { @@ -64,11 +48,12 @@ public Guild( throw new FailedLoadStateException("Un-deserializable state."); } + Address = address; GuildMasterAddress = new AgentAddress(list[2]); ValidatorAddress = new AgentAddress(list[3]); } - public new GuildAddress Address => new GuildAddress(base.Address); + public GuildAddress Address { get; } public List Bencoded => List.Empty .Add(StateTypeName) @@ -78,17 +63,16 @@ public Guild( IValue IBencodable.Bencoded => Bencoded; - public bool Equals(Guild other) + public bool Equals(Guild? other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Address.Equals(other.Address) && GuildMasterAddress.Equals(other.GuildMasterAddress) - && ValidatorAddress.Equals(other.ValidatorAddress) - && Metadata.Equals(other.Metadata); + && ValidatorAddress.Equals(other.ValidatorAddress); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; diff --git a/Lib9c/Model/Guild/GuildParticipant.cs b/Lib9c/Model/Guild/GuildParticipant.cs index dfbd21488a..490106c280 100644 --- a/Lib9c/Model/Guild/GuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildParticipant.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Numerics; using Bencodex; @@ -11,7 +12,10 @@ namespace Nekoyume.Model.Guild { - public class GuildParticipant : Delegator, IBencodable, IEquatable + // It Does not inherit from `Delegator`, since `Validator` related functionalities + // will be moved to lower level library. + public class GuildParticipant + : Delegator, IBencodable, IEquatable { private const string StateTypeName = "guild_participant"; private const long StateVersion = 1; @@ -24,7 +28,7 @@ public GuildParticipant( GuildRepository repository) : base( address: address, - accountAddress: Addresses.GuildParticipant, + accountAddress: repository.DelegatorAccountAddress, delegationPoolAddress: StakeState.DeriveAddress(address), rewardAddress: address, repository: repository) @@ -65,31 +69,31 @@ public GuildParticipant( IValue IBencodable.Bencoded => Bencoded; - public override void Delegate(Guild delegatee, FungibleAssetValue fav, long height) + public void Delegate(Guild guild, FungibleAssetValue fav, long height) { + var repository = (GuildRepository)Repository; + if (fav.Sign <= 0) { throw new ArgumentOutOfRangeException( nameof(fav), fav, "Fungible asset value must be positive."); } - 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); + var validatorDelegateeForGuildParticipant = repository.GetValidatorDelegateeForGuildParticipant(guild.ValidatorAddress); + base.Delegate(validatorDelegateeForGuildParticipant, fav, height); 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); + var validatorDelegatee = validatorRepository.GetValidatorDelegatee(guild.ValidatorAddress); + var validatorDelegator = validatorRepository.GetValidatorDelegator(guild.Address); + validatorDelegatee.Bond(validatorDelegator, fav, height); + Repository.UpdateWorld(validatorRepository.World); } - public override void Undelegate(Guild delegatee, BigInteger share, long height) + public void Undelegate(Guild guild, BigInteger share, long height) { + var repository = (GuildRepository)Repository; + if (share.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -102,24 +106,22 @@ public override void Undelegate(Guild delegatee, BigInteger share, long height) nameof(height), height, "Height must be positive."); } - FungibleAssetValue fav = delegatee.Unbond(this, 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); + var validatorDelegateeForGuildParticipant = repository.GetValidatorDelegateeForGuildParticipant(guild.ValidatorAddress); + base.Undelegate(validatorDelegateeForGuildParticipant, share, height); 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); + var validatorDelegatee = validatorRepository.GetValidatorDelegatee(guild.ValidatorAddress); + var validatorDelegator = validatorRepository.GetValidatorDelegator(guild.Address); + validatorDelegatee.Unbond(validatorDelegator, share, height); + Repository.UpdateWorld(validatorRepository.World); } - public override void Redelegate( - Guild srcDelegatee, Guild dstDelegatee, BigInteger share, long height) + public void Redelegate( + Guild srcGuild, Guild dstGuild, BigInteger share, long height) { + var repository = (GuildRepository)Repository; + if (share.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -132,45 +134,35 @@ public override void Redelegate( nameof(height), height, "Height must be positive."); } - FungibleAssetValue fav = srcDelegatee.Unbond(this, share, height); - dstDelegatee.Bond(this, fav, height); - - var guildValidatorRepository = new GuildValidatorRepository( - Repository.World, Repository.ActionContext); - 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); - - 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); + var srcValidatorDelegateeForGuildParticipant = repository.GetValidatorDelegateeForGuildParticipant(srcGuild.ValidatorAddress); + var dstValidatorDelegateeForGuildParticipant = repository.GetValidatorDelegateeForGuildParticipant(dstGuild.ValidatorAddress); + base.Redelegate(srcValidatorDelegateeForGuildParticipant, dstValidatorDelegateeForGuildParticipant, share, height); + + var validatorRepository = new ValidatorRepository(Repository.World, Repository.ActionContext); + var srcValidatorDelegatee = validatorRepository.GetValidatorDelegatee(srcGuild.ValidatorAddress); + var srcValidatorDelegator = validatorRepository.GetValidatorDelegator(srcGuild.Address); + var fav = srcValidatorDelegatee.Unbond(srcValidatorDelegator, share, height); + var dstValidatorDelegatee = validatorRepository.GetValidatorDelegatee(dstGuild.ValidatorAddress); + var dstValidatorDelegator = validatorRepository.GetValidatorDelegator(dstGuild.Address); dstValidatorDelegatee.Bond(dstValidatorDelegator, fav, height); + Repository.UpdateWorld(validatorRepository.World); } - public bool Equals(GuildParticipant other) + public bool Equals(GuildParticipant? other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Address.Equals(other.Address) - && GuildAddress.Equals(other.GuildAddress) - && Metadata.Equals(other.Metadata); + && GuildAddress.Equals(other.GuildAddress); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; - return Equals((Guild)obj); + return Equals((GuildParticipant)obj); } public override int GetHashCode() diff --git a/Lib9c/Model/Guild/GuildRepository.cs b/Lib9c/Model/Guild/GuildRepository.cs index f8ebc11009..c61836adaa 100644 --- a/Lib9c/Model/Guild/GuildRepository.cs +++ b/Lib9c/Model/Guild/GuildRepository.cs @@ -10,13 +10,18 @@ namespace Nekoyume.Model.Guild { public class GuildRepository : DelegationRepository { + public GuildRepository(IDelegationRepository repository) + : this(repository.World, repository.ActionContext) + { + } + public GuildRepository(IWorld world, IActionContext actionContext) : base( world: world, actionContext: actionContext, - delegateeAccountAddress: Addresses.Guild, + delegateeAccountAddress: Addresses.ValidatorDelegateeForGuildParticipantMetadata, delegatorAccountAddress: Addresses.GuildParticipant, - delegateeMetadataAccountAddress: Addresses.GuildMetadata, + delegateeMetadataAccountAddress: Addresses.ValidatorDelegateeForGuildParticipantMetadata, delegatorMetadataAccountAddress: Addresses.GuildParticipantMetadata, bondAccountAddress: Addresses.GuildBond, unbondLockInAccountAddress: Addresses.GuildUnbondLockIn, @@ -26,17 +31,15 @@ public GuildRepository(IWorld world, IActionContext actionContext) { } - public Guild GetGuild(Address address) + public ValidatorDelegateeForGuildParticipant GetValidatorDelegateeForGuildParticipant(Address address) => delegateeAccount.GetState(address) is IValue bencoded - ? new Guild( - new GuildAddress(address), - bencoded, + ? new ValidatorDelegateeForGuildParticipant( + address, this) - : throw new FailedLoadStateException("Guild does not exist."); + : throw new FailedLoadStateException("Validator delegatee for guild participant does not exist."); public override IDelegatee GetDelegatee(Address address) - => GetGuild(address); - + => GetValidatorDelegateeForGuildParticipant(address); public GuildParticipant GetGuildParticipant(Address address) => delegatorAccount.GetState(address) is IValue bencoded @@ -44,20 +47,18 @@ public GuildParticipant GetGuildParticipant(Address address) new AgentAddress(address), bencoded, this) - : throw new FailedLoadStateException("Delegator does not exist."); + : throw new FailedLoadStateException("Guild participant does not exist."); public override IDelegator GetDelegator(Address address) => GetGuildParticipant(address); - public void SetGuild(Guild guild) + public void SetValidatorDelegateeForGuildParticipant(ValidatorDelegateeForGuildParticipant validatorDelegateeForGuild) { - delegateeAccount = delegateeAccount.SetState( - guild.Address, guild.Bencoded); - SetDelegateeMetadata(guild.Metadata); + SetDelegateeMetadata(validatorDelegateeForGuild.Metadata); } public override void SetDelegatee(IDelegatee delegatee) - => SetGuild(delegatee as Guild); + => SetValidatorDelegateeForGuildParticipant(delegatee as ValidatorDelegateeForGuildParticipant); public void SetGuildParticipant(GuildParticipant guildParticipant) { @@ -65,7 +66,6 @@ public void SetGuildParticipant(GuildParticipant guildParticipant) guildParticipant.Address, guildParticipant.Bencoded); SetDelegatorMetadata(guildParticipant.Metadata); } - public override void SetDelegator(IDelegator delegator) => SetGuildParticipant(delegator as GuildParticipant); @@ -73,5 +73,19 @@ public void RemoveGuildParticipant(Address guildParticipantAddress) { delegatorAccount = delegatorAccount.RemoveState(guildParticipantAddress); } + + public Guild GetGuild(Address address) + => delegateeAccount.GetState(address) is IValue bencoded + ? new Guild( + new GuildAddress(address), + bencoded, + this) + : throw new FailedLoadStateException("Guild does not exist."); + + public void SetGuild(Guild guild) + { + delegateeAccount = delegateeAccount.SetState( + guild.Address, guild.Bencoded); + } } } diff --git a/Lib9c/Model/Guild/GuildValidatorDelegator.cs b/Lib9c/Model/Guild/GuildValidatorDelegator.cs deleted file mode 100644 index d6c9731237..0000000000 --- a/Lib9c/Model/Guild/GuildValidatorDelegator.cs +++ /dev/null @@ -1,36 +0,0 @@ -#nullable enable -using System; -using Libplanet.Crypto; -using Nekoyume.Delegation; - -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 deleted file mode 100644 index 30dfbb541d..0000000000 --- a/Lib9c/Model/Guild/GuildValidatorRepository.cs +++ /dev/null @@ -1,74 +0,0 @@ -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.ValidatorDelegatee, - delegatorAccountAddress: Addresses.ValidatorDelegator, - delegateeMetadataAccountAddress: Addresses.GuildMetadata, - delegatorMetadataAccountAddress: Addresses.ValidatorDelegatorMetadata, - bondAccountAddress: Addresses.ValidatorBond, - unbondLockInAccountAddress: Addresses.ValidatorUnbondLockIn, - rebondGraceAccountAddress: Addresses.ValidatorRebondGrace, - unbondingSetAccountAddress: Addresses.ValidatorUnbondingSet, - lumpSumRewardRecordAccountAddress: Addresses.ValidatorLumpSumRewardsRecord) - { - } - - 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); - } -} diff --git a/Lib9c/Model/Guild/GuildValidatorDelegatee.cs b/Lib9c/Model/Guild/ValidatorDelegateeForGuildParticipant.cs similarity index 54% rename from Lib9c/Model/Guild/GuildValidatorDelegatee.cs rename to Lib9c/Model/Guild/ValidatorDelegateeForGuildParticipant.cs index 1c372e115f..306442ffb9 100644 --- a/Lib9c/Model/Guild/GuildValidatorDelegatee.cs +++ b/Lib9c/Model/Guild/ValidatorDelegateeForGuildParticipant.cs @@ -1,24 +1,26 @@ #nullable enable using System; +using System.Collections.Generic; using Libplanet.Crypto; +using Libplanet.Types.Assets; using Nekoyume.Delegation; using Nekoyume.ValidatorDelegation; namespace Nekoyume.Model.Guild { - public class GuildValidatorDelegatee - : Delegatee, IEquatable + public class ValidatorDelegateeForGuildParticipant + : Delegatee, IEquatable { - public GuildValidatorDelegatee( + public ValidatorDelegateeForGuildParticipant( Address address, - Address delegationPoolAddress, - GuildValidatorRepository repository) + IEnumerable rewardCurrencies, + GuildRepository repository) : base( address: address, accountAddress: repository.DelegateeAccountAddress, delegationCurrency: ValidatorDelegatee.ValidatorDelegationCurrency, - rewardCurrency: ValidatorDelegatee.ValidatorRewardCurrency, - delegationPoolAddress: ValidatorDelegatee.UnbondedPoolAddress, + rewardCurrencies: rewardCurrencies, + delegationPoolAddress: ValidatorDelegatee.InactiveDelegationPoolAddress, rewardPoolAddress: DelegationAddress.RewardPoolAddress(address, repository.DelegateeAccountAddress), rewardRemainderPoolAddress: Addresses.CommunityPool, slashedPoolAddress: Addresses.CommunityPool, @@ -29,16 +31,26 @@ public GuildValidatorDelegatee( { } - public GuildValidatorDelegatee( + public ValidatorDelegateeForGuildParticipant( Address address, - GuildValidatorRepository repository) + GuildRepository repository) : base( address: address, repository: repository) { } - public bool Equals(GuildValidatorDelegatee? other) + public void Activate() + { + Metadata.DelegationPoolAddress = ValidatorDelegatee.ActiveDelegationPoolAddress; + } + + public void Deactivate() + { + Metadata.DelegationPoolAddress = ValidatorDelegatee.InactiveDelegationPoolAddress; + } + + public bool Equals(ValidatorDelegateeForGuildParticipant? other) => Metadata.Equals(other?.Metadata); } } diff --git a/Lib9c/Module/Guild/GuildModule.cs b/Lib9c/Module/Guild/GuildModule.cs index 5d46d499d4..3b57b1d238 100644 --- a/Lib9c/Module/Guild/GuildModule.cs +++ b/Lib9c/Module/Guild/GuildModule.cs @@ -1,15 +1,14 @@ #nullable enable using System; using System.Diagnostics.CodeAnalysis; -using Lib9c; -using Libplanet.Action.State; using Libplanet.Action; +using Libplanet.Action.State; +using Libplanet.Crypto; using Nekoyume.Extensions; using Nekoyume.Model.Guild; +using Nekoyume.Module.ValidatorDelegation; using Nekoyume.TypedAddress; -using Libplanet.Crypto; using Nekoyume.ValidatorDelegation; -using Nekoyume.Module.ValidatorDelegation; namespace Nekoyume.Module.Guild { @@ -57,8 +56,7 @@ public static GuildRepository MakeGuild( throw new InvalidOperationException("The validator does not exist."); } - var guild = new Model.Guild.Guild( - guildAddress, signer, validatorAddress, repository.World.GetGoldCurrency(), repository); + var guild = new Model.Guild.Guild(guildAddress, signer, validatorAddress, repository); repository.SetGuild(guild); repository.JoinGuild(guildAddress, signer); @@ -106,16 +104,5 @@ public static GuildRepository RemoveGuild( return repository; } - - public static GuildRepository CollectRewardGuild( - this GuildRepository repository, - GuildAddress guildAddress) - { - var guild = repository.GetGuild(guildAddress); - guild.CollectRewards(repository.ActionContext.BlockIndex); - repository.SetGuild(guild); - - return repository; - } } } diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index a228243853..86c7b0d2aa 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -161,7 +161,10 @@ private static GuildRepository Undelegate( var height = repository.ActionContext.BlockIndex; var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); var guild = repository.GetGuild(guildParticipant.GuildAddress); - var share = repository.GetBond(guild, guildParticipantAddress).Share; + var share = repository.GetBond( + new ValidatorRepository(repository.World, repository.ActionContext) + .GetValidatorDelegatee(guild.ValidatorAddress), + guildParticipantAddress).Share; guildParticipant.Undelegate(guild, share, height); return repository; @@ -188,26 +191,16 @@ public static GuildRepository Redelegate( var height = repository.ActionContext.BlockIndex; var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); var guild = repository.GetGuild(guildParticipant.GuildAddress); - var share = repository.GetBond(guild, guildParticipantAddress).Share; + var share = repository.GetBond( + new ValidatorRepository(repository.World, repository.ActionContext) + .GetValidatorDelegatee(guild.ValidatorAddress), + guildParticipantAddress).Share; var dstGuild = repository.GetGuild(dstGuildAddress); guildParticipant.Redelegate(guild, dstGuild, share, height); return repository; } - private static GuildRepository ClaimReward( - this GuildRepository repository, - AgentAddress guildParticipantAddress, - GuildAddress guildAddress, - long height) - { - var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); - var guild = repository.GetGuild(guildAddress); - guildParticipant.ClaimReward(guild, height); - - return repository; - } - private static GuildRepository SetGuildRejoinCooldown( this GuildRepository repository, AgentAddress guildParticipantAddress, diff --git a/Lib9c/Module/Guild/ValidatorDelegateeForGuildModule.cs b/Lib9c/Module/Guild/ValidatorDelegateeForGuildModule.cs new file mode 100644 index 0000000000..b727b1dab9 --- /dev/null +++ b/Lib9c/Module/Guild/ValidatorDelegateeForGuildModule.cs @@ -0,0 +1,60 @@ +#nullable enable +using System; +using System.Diagnostics.CodeAnalysis; +using Lib9c; +using Libplanet.Crypto; +using Libplanet.Types.Assets; +using Nekoyume.Model.Guild; +using Nekoyume.Module.ValidatorDelegation; +using Nekoyume.ValidatorDelegation; + + +namespace Nekoyume.Module.Guild +{ + public static class ValidatorDelegateeForGuildModule + { + public static bool TryGetValidatorDelegateeForGuild( + this GuildRepository repository, + Address address, + [NotNullWhen(true)] out ValidatorDelegateeForGuildParticipant? validatorDelegateeForGuildParticipant) + { + try + { + validatorDelegateeForGuildParticipant = repository.GetValidatorDelegateeForGuildParticipant(address); + return true; + } + catch + { + validatorDelegateeForGuildParticipant = null; + return false; + } + } + + public static GuildRepository CreateValidatorDelegateeForGuildParticipant( + this GuildRepository repository) + { + var context = repository.ActionContext; + var signer = context.Signer; + + if (repository.TryGetValidatorDelegateeForGuild(context.Signer, out _)) + { + throw new InvalidOperationException("The signer already has a validator delegatee for guild."); + } + + var validatorRepository = new ValidatorRepository(repository.World, repository.ActionContext); + if (!validatorRepository.TryGetValidatorDelegatee(context.Signer, out _)) + { + throw new InvalidOperationException("The signer does not have a validator delegatee."); + } + + var validatorDelegateeForGuildParticipant = new ValidatorDelegateeForGuildParticipant( + signer, + new Currency[] { repository.World.GetGoldCurrency() }, + repository); + + repository.SetValidatorDelegateeForGuildParticipant(validatorDelegateeForGuildParticipant); + + return repository; + } + } +} diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs index 558772159e..19c1299f2b 100644 --- a/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs @@ -2,7 +2,9 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Numerics; +using Lib9c; using Libplanet.Crypto; +using Libplanet.Types.Assets; using Nekoyume.ValidatorDelegation; namespace Nekoyume.Module.ValidatorDelegation @@ -43,7 +45,12 @@ public static ValidatorRepository CreateValidatorDelegatee( } var validatorDelegatee = new ValidatorDelegatee( - signer, publicKey, commissionPercentage, context.BlockIndex, repository); + signer, + publicKey, + commissionPercentage, + context.BlockIndex, + new Currency[] { repository.World.GetGoldCurrency(), Currencies.Mead }, + repository); repository.SetValidatorDelegatee(validatorDelegatee); diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs index c221130384..85ac432962 100644 --- a/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs @@ -24,8 +24,7 @@ public static ValidatorRepository DelegateValidator( { var context = repository.ActionContext; var delegatorAddress = context.Signer; - var validatorDelegator = repository.GetValidatorDelegator( - delegatorAddress, rewardAddress); + var validatorDelegator = repository.GetValidatorDelegator(delegatorAddress); var validatorDelegatee = repository.GetValidatorDelegatee(validatorAddress); validatorDelegator.Delegate(validatorDelegatee, fav, context.BlockIndex); @@ -46,8 +45,7 @@ public static ValidatorRepository UndelegateValidator( { var context = repository.ActionContext; var delegatorAddress = context.Signer; - var validatorDelegator = repository.GetValidatorDelegator( - delegatorAddress, rewardAddress); + var validatorDelegator = repository.GetValidatorDelegator(delegatorAddress); var validatorDelegatee = repository.GetValidatorDelegatee(validatorAddress); validatorDelegator.Undelegate(validatorDelegatee, share, context.BlockIndex); @@ -70,8 +68,7 @@ public static ValidatorRepository RedelegateValidator( { var context = repository.ActionContext; var delegatorAddress = context.Signer; - var validatorDelegator = repository.GetValidatorDelegator( - delegatorAddress, rewardAddress); + var validatorDelegator = repository.GetValidatorDelegator(delegatorAddress); var srcValidatorDelegatee = repository.GetValidatorDelegatee(srcValidatorAddress); var dstValidatorDelegatee = repository.GetValidatorDelegatee(dstValidatorAddress); validatorDelegator.Redelegate(srcValidatorDelegatee, dstValidatorDelegatee, share, context.BlockIndex); @@ -91,8 +88,7 @@ public static ValidatorRepository ClaimRewardValidator( { var context = repository.ActionContext; var delegatorAddress = context.Signer; - var validatorDelegator = repository.GetValidatorDelegator( - delegatorAddress, rewardAddress); + var validatorDelegator = repository.GetValidatorDelegator(delegatorAddress); var validatorDelegatee = repository.GetValidatorDelegatee(address); validatorDelegator.ClaimReward(validatorDelegatee, context.BlockIndex); @@ -103,14 +99,11 @@ public static ValidatorRepository ClaimRewardValidator( public static bool TryGetValidatorDelegator( this ValidatorRepository repository, Address address, - Address rewardAddress, [NotNullWhen(true)] out ValidatorDelegator? validatorDelegator) { try { - validatorDelegator = repository.GetValidatorDelegator( - address, - rewardAddress); + validatorDelegator = repository.GetValidatorDelegator(address); return true; } catch diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index bb119ad2d3..bf4513a069 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -16,7 +16,6 @@ namespace Nekoyume.ValidatorDelegation public sealed class ValidatorDelegatee : Delegatee, IEquatable, IBencodable { - // TODO: After guild-PoS implemented, delegation currency have to be changed into guild gold. public ValidatorDelegatee( Address address, PublicKey publicKey, @@ -89,14 +88,12 @@ public ValidatorDelegatee( public static Currency ValidatorDelegationCurrency => Currencies.GuildGold; - public static Currency ValidatorRewardCurrency => Currencies.Mead; - // TODO: [MigrateGuild] Change unbonding period after migration. public static long ValidatorUnbondingPeriod => 0L; - public static int ValidatorMaxUnbondLockInEntries => 10; + public static int ValidatorMaxUnbondLockInEntries => 2; - public static int ValidatorMaxRebondGraceEntries => 10; + public static int ValidatorMaxRebondGraceEntries => 2; public static BigInteger BaseProposerRewardPercentage => 1; @@ -207,7 +204,7 @@ public void Activate() repository.TransferAsset( InactiveDelegationPoolAddress, ActiveDelegationPoolAddress, - repository.GetBalance(InactiveDelegationPoolAddress, DelegationCurrency)); + TotalDelegated); } public void Deactivate() @@ -218,7 +215,7 @@ public void Deactivate() repository.TransferAsset( ActiveDelegationPoolAddress, InactiveDelegationPoolAddress, - repository.GetBalance(ActiveDelegationPoolAddress, DelegationCurrency)); + TotalDelegated); } public void OnDelegationChanged(object? sender, long height) diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegator.cs b/Lib9c/ValidatorDelegation/ValidatorDelegator.cs index b3f045b1b2..206df3bf6d 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegator.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegator.cs @@ -5,18 +5,18 @@ namespace Nekoyume.ValidatorDelegation { - public sealed class ValidatorDelegator : Delegator, IEquatable + public sealed class ValidatorDelegator + : Delegator, IEquatable { public ValidatorDelegator( Address address, Address delegationPoolAddress, - Address rewardAddress, ValidatorRepository repository) : base( address: address, accountAddress: repository.DelegatorAccountAddress, delegationPoolAddress: delegationPoolAddress, - rewardAddress: rewardAddress, + rewardAddress: address, repository: repository) { } diff --git a/Lib9c/ValidatorDelegation/ValidatorRepository.cs b/Lib9c/ValidatorDelegation/ValidatorRepository.cs index 028aee0f8e..2526bede6d 100644 --- a/Lib9c/ValidatorDelegation/ValidatorRepository.cs +++ b/Lib9c/ValidatorDelegation/ValidatorRepository.cs @@ -1,12 +1,11 @@ #nullable enable +using System.Numerics; using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; using Nekoyume.Action; using Nekoyume.Delegation; -using Nekoyume.Model.Stake; -using System.Numerics; namespace Nekoyume.ValidatorDelegation { @@ -16,6 +15,11 @@ public sealed class ValidatorRepository : DelegationRepository private IAccount _validatorList; + public ValidatorRepository(IDelegationRepository repository) + : this(repository.World, repository.ActionContext) + { + } + public ValidatorRepository(IWorld world, IActionContext actionContext) : base( world, @@ -47,7 +51,7 @@ public ValidatorDelegatee GetValidatorDelegatee(Address address) public override IDelegatee GetDelegatee(Address address) => GetValidatorDelegatee(address); - public ValidatorDelegator GetValidatorDelegator(Address address, Address rewardAddress) + public ValidatorDelegator GetValidatorDelegator(Address address) { try { @@ -55,11 +59,9 @@ public ValidatorDelegator GetValidatorDelegator(Address address, Address rewardA } catch (FailedLoadStateException) { - // TODO: delegationPoolAddress have to be changed after guild system is implemented. return new ValidatorDelegator( address, - StakeState.DeriveAddress(address), - rewardAddress, + address, this); } } From 69dc482dcbf4fd94f27bed04b73cf7cdb161eb59 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 13 Nov 2024 17:31:12 +0900 Subject: [PATCH 146/206] refactor: Refactor and fix some bugs --- Lib9c/Action/Guild/ClaimGuildReward.cs | 2 +- Lib9c/Action/Guild/ClaimReward.cs | 6 +- .../MigratePlanetariumGuildUnbond.cs | 4 +- Lib9c/Action/InitializeStates.cs | 44 ++++---- Lib9c/Action/Stake.cs | 6 +- .../ValidatorDelegation/AllocateReward.cs | 34 +++++- .../ClaimValidatorRewardSelf.cs | 8 +- .../ValidatorDelegation/DelegateValidator.cs | 34 +++--- .../ValidatorDelegation/PromoteValidator.cs | 18 ++- .../ReleaseValidatorUnbondings.cs | 2 +- .../ValidatorDelegation/SlashValidator.cs | 4 +- .../UndelegateValidator.cs | 34 +++--- .../ValidatorDelegation/UpdateValidators.cs | 8 +- Lib9c/Addresses.cs | 4 +- Lib9c/Delegation/LumpSumRewardsRecord.cs | 19 ++-- Lib9c/Model/Guild/Guild.cs | 26 ++++- ...rGuildParticipant.cs => GuildDelegatee.cs} | 10 +- Lib9c/Model/Guild/GuildDelegator.cs | 34 ++++++ Lib9c/Model/Guild/GuildParticipant.cs | 70 +++++++----- Lib9c/Model/Guild/GuildRepository.cs | 105 ++++++++++++------ ...GuildModule.cs => GuildDelegateeModule.cs} | 29 +++-- .../ValidatorDelegateeModule.cs | 14 +-- .../ValidatorDelegatorModule.cs | 89 --------------- .../ValidatorRepository.cs | 12 +- 24 files changed, 329 insertions(+), 287 deletions(-) rename Lib9c/Model/Guild/{ValidatorDelegateeForGuildParticipant.cs => GuildDelegatee.cs} (83%) create mode 100644 Lib9c/Model/Guild/GuildDelegator.cs rename Lib9c/Module/Guild/{ValidatorDelegateeForGuildModule.cs => GuildDelegateeModule.cs} (54%) diff --git a/Lib9c/Action/Guild/ClaimGuildReward.cs b/Lib9c/Action/Guild/ClaimGuildReward.cs index a28df7d15b..7a05ee74fc 100644 --- a/Lib9c/Action/Guild/ClaimGuildReward.cs +++ b/Lib9c/Action/Guild/ClaimGuildReward.cs @@ -43,7 +43,7 @@ public override IWorld Execute(IActionContext context) throw new InvalidOperationException("Signer is not a guild master."); } - var repository = new ValidatorRepository(guildRepository.World, guildRepository.ActionContext); + var repository = new ValidatorRepository(guildRepository); var validatorDelegatee = repository.GetValidatorDelegatee(guild.ValidatorAddress); var validatorDelegator = repository.GetValidatorDelegator(guild.Address); validatorDelegator.ClaimReward(validatorDelegatee, context.BlockIndex); diff --git a/Lib9c/Action/Guild/ClaimReward.cs b/Lib9c/Action/Guild/ClaimReward.cs index afa4ef6de5..51e45fa034 100644 --- a/Lib9c/Action/Guild/ClaimReward.cs +++ b/Lib9c/Action/Guild/ClaimReward.cs @@ -37,9 +37,9 @@ public override IWorld Execute(IActionContext context) var guildParticipant = repository.GetGuildParticipant(context.Signer); var guild = repository.GetGuild(guildParticipant.GuildAddress); - var validatorDelegateeForGuildParticipant - = repository.GetValidatorDelegateeForGuildParticipant(guild.ValidatorAddress); - guildParticipant.ClaimReward(validatorDelegateeForGuildParticipant, context.BlockIndex); + var guildDelegatee = repository.GetGuildDelegatee(guild.ValidatorAddress); + var guildDelegator = repository.GetGuildDelegator(context.Signer); + guildDelegator.ClaimReward(guildDelegatee, context.BlockIndex); return repository.World; } diff --git a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs index a3fb35c65a..d5824692b4 100644 --- a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs +++ b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs @@ -47,11 +47,11 @@ public override IWorld Execute(IActionContext context) var guildAddress = guildRepository.GetGuildParticipant(GuildConfig.PlanetariumGuildOwner).GuildAddress; var guild = guildRepository.GetGuild(guildAddress); var validatorDelegateeForGuildParticipant - = guildRepository.GetValidatorDelegateeForGuildParticipant(guild.ValidatorAddress); + = guildRepository.GetGuildDelegatee(guild.ValidatorAddress); // TODO: [GuildMigration] Replace below height when determined. validatorDelegateeForGuildParticipant.Metadata.UpdateUnbondingPeriod(LegacyStakeState.LockupInterval); - guildRepository.SetValidatorDelegateeForGuildParticipant(validatorDelegateeForGuildParticipant); + guildRepository.SetGuildDelgatee(validatorDelegateeForGuildParticipant); var repository = new ValidatorRepository(guildRepository); var validatorDelegatee = repository.GetValidatorDelegatee(guild.ValidatorAddress); diff --git a/Lib9c/Action/InitializeStates.cs b/Lib9c/Action/InitializeStates.cs index 065313961e..f60f467983 100644 --- a/Lib9c/Action/InitializeStates.cs +++ b/Lib9c/Action/InitializeStates.cs @@ -1,7 +1,7 @@ -using System.Linq; using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using Bencodex.Types; using Lib9c.Abstractions; using Libplanet.Action; @@ -10,10 +10,12 @@ using Libplanet.Types.Assets; using Libplanet.Types.Consensus; using Nekoyume.Model.State; +using Nekoyume.Model.Guild; +using Nekoyume.Model.Stake; using Nekoyume.Module; +using Nekoyume.Module.Guild; +using Nekoyume.Module.ValidatorDelegation; using Nekoyume.ValidatorDelegation; -using Nekoyume.Action.Guild; -using Lib9c; namespace Nekoyume.Action { @@ -200,32 +202,24 @@ public override IWorld Execute(IActionContext context) ); } - var repository = new ValidatorRepository(states, context); var validatorSet = new ValidatorSet(ValidatorSet); foreach (var validator in validatorSet.Validators) { - var validatorDelegatee = new ValidatorDelegatee( - validator.OperatorAddress, - validator.PublicKey, - ValidatorDelegatee.DefaultCommissionPercentage, - context.BlockIndex, - new Currency[] { currencyState.Currency }, - repository); var delegationFAV = FungibleAssetValue.FromRawValue( - validatorDelegatee.DelegationCurrency, validator.Power); - var validatorOperatorAddress = validator.OperatorAddress; - var validatorDelegator = repository.GetValidatorDelegator( - validatorOperatorAddress); - - repository.SetValidatorDelegatee(validatorDelegatee); - repository.UpdateWorld( - repository.World.MintAsset( - repository.ActionContext, - validatorDelegator.DelegationPoolAddress, - delegationFAV)); - validatorDelegator.Delegate(validatorDelegatee, delegationFAV, context.BlockIndex); - - states = repository.World; + ValidatorDelegatee.ValidatorDelegationCurrency, validator.Power); + states = states.MintAsset(ctx, StakeState.DeriveAddress(validator.OperatorAddress), delegationFAV); + + var validatorRepository = new ValidatorRepository(states, ctx); + var validatorDelegatee = validatorRepository.CreateValidatorDelegatee( + validator.PublicKey, ValidatorDelegatee.DefaultCommissionPercentage); + var validatorDelegator = validatorRepository.GetValidatorDelegator(validator.OperatorAddress); + validatorDelegatee.Bond(validatorDelegator, delegationFAV, context.BlockIndex); + + var guildRepository = new GuildRepository(validatorRepository); + var guildDelegatee = guildRepository.CreateGuildDelegatee(validator.OperatorAddress); + var guildDelegator = guildRepository.GetGuildDelegator(validator.OperatorAddress); + guildDelegator.Delegate(guildDelegatee, delegationFAV, context.BlockIndex); + states = guildRepository.World; } return states; diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index 9f2abd3ec4..2c488fc5d0 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -213,8 +213,8 @@ private static IWorld ContractNewStake( if (guildRepository.TryGetGuildParticipant(agentAddress, out var guildParticipant)) { var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); - var validatorDelegateeForGuildParticipant = guildRepository.GetValidatorDelegateeForGuildParticipant(guild.ValidatorAddress); - var share = validatorDelegateeForGuildParticipant.ShareFromFAV(gg); + var guildDelegatee = guildRepository.GetGuildDelegatee(guild.ValidatorAddress); + var share = guildDelegatee.ShareFromFAV(gg); guildParticipant.Undelegate(guild, share, height); state = guildRepository.World; } @@ -222,7 +222,7 @@ private static IWorld ContractNewStake( { var delegateeAddress = Addresses.NonValidatorDelegatee; var delegatorAddress = context.Signer; - var repository = new ValidatorRepository(state, context); + var repository = new GuildRepository(state, context); var unbondLockInAddress = DelegationAddress.UnbondLockInAddress(delegateeAddress, repository.DelegateeAccountAddress, delegatorAddress); var unbondLockIn = new UnbondLockIn( unbondLockInAddress, ValidatorDelegatee.ValidatorMaxUnbondLockInEntries, delegateeAddress, delegatorAddress, null); diff --git a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs index 5e3ff452b4..f82721b06a 100644 --- a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs +++ b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs @@ -47,6 +47,16 @@ public override IWorld Execute(IActionContext context) DistributeGuildParticipant(repository, rewardCurrency, validatorSetPower, lastCommit.Votes); } + var communityFund = repository.GetBalance(Addresses.RewardPool, rewardCurrency); + + if (communityFund.Sign > 0) + { + repository.TransferAsset( + Addresses.RewardPool, + Addresses.CommunityPool, + communityFund); + } + return repository.World; } @@ -70,7 +80,7 @@ FungibleAssetValue reward } var validatorReward = reward.DivRem(10).Quotient; - + var distributed = rewardCurrency * 0; foreach (Vote vote in lastVotes) { if (vote.Flag == VoteFlag.Null || vote.Flag == VoteFlag.Unknown) @@ -85,7 +95,7 @@ FungibleAssetValue reward } var validatorAddress = vote.ValidatorPublicKey.Address; - if (!repository.TryGetValidatorDelegateeForGuild( + if (!repository.TryGetGuildDelegatee( validatorAddress, out var validatorDelegatee)) { continue; @@ -98,8 +108,15 @@ FungibleAssetValue rewardEach if (rewardEach.Sign > 0) { repository.TransferAsset(Addresses.RewardPool, validatorAddress, rewardEach); + distributed += rewardEach; } } + + var remainder = validatorReward - distributed; + if (remainder.Sign > 0) + { + repository.TransferAsset(Addresses.RewardPool, Addresses.CommunityPool, remainder); + } } private static void DistributeGuild( @@ -124,6 +141,7 @@ FungibleAssetValue reward } var guildReward = reward.DivRem(10).Quotient; + var distributed = rewardCurrency * 0; foreach (Vote vote in lastVotes) { @@ -154,8 +172,15 @@ FungibleAssetValue rewardEach repository.TransferAsset( Addresses.RewardPool, validatorDelegatee.RewardPoolAddress, rewardEach); validatorDelegatee.CollectRewards(blockHeight); + distributed += rewardEach; } } + + var remainder = guildReward - distributed; + if (remainder.Sign > 0) + { + repository.TransferAsset(Addresses.RewardPool, Addresses.CommunityPool, remainder); + } } private static void DistributeGuildParticipant( @@ -164,6 +189,8 @@ private static void DistributeGuildParticipant( BigInteger validatorSetPower, IEnumerable lastVotes) { + long blockHeight = repository.ActionContext.BlockIndex; + FungibleAssetValue reward = repository.GetBalance(Addresses.RewardPool, rewardCurrency); @@ -191,7 +218,7 @@ FungibleAssetValue reward } var validatorAddress = vote.ValidatorPublicKey.Address; - if (!repository.TryGetValidatorDelegateeForGuild( + if (!repository.TryGetGuildDelegatee( validatorAddress, out var validatorDelegatee)) { continue; @@ -203,6 +230,7 @@ FungibleAssetValue rewardEach if (rewardEach.Sign > 0) { repository.TransferAsset(Addresses.RewardPool, validatorDelegatee.RewardPoolAddress, rewardEach); + validatorDelegatee.CollectRewards(blockHeight); } } } diff --git a/Lib9c/Action/ValidatorDelegation/ClaimValidatorRewardSelf.cs b/Lib9c/Action/ValidatorDelegation/ClaimValidatorRewardSelf.cs index f01cbf25b0..412e86f6c5 100644 --- a/Lib9c/Action/ValidatorDelegation/ClaimValidatorRewardSelf.cs +++ b/Lib9c/Action/ValidatorDelegation/ClaimValidatorRewardSelf.cs @@ -4,6 +4,7 @@ using Libplanet.Action; using Libplanet.Crypto; using Nekoyume.ValidatorDelegation; +using Nekoyume.Model.Guild; namespace Nekoyume.Action.ValidatorDelegation { @@ -41,7 +42,12 @@ public override IWorld Execute(IActionContext context) var validatorDelegator = repository.GetValidatorDelegator(context.Signer); validatorDelegator.ClaimReward(validatorDelegatee, context.BlockIndex); - return repository.World; + var guildRepository = new GuildRepository(repository); + var guildDelegatee = guildRepository.GetGuildDelegatee(context.Signer); + var guildDelegator = guildRepository.GetGuildDelegator(context.Signer); + guildDelegator.ClaimReward(guildDelegatee, context.BlockIndex); + + return guildRepository.World; } } } diff --git a/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs index 2d6b380139..fbfcad76ed 100644 --- a/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/DelegateValidator.cs @@ -2,10 +2,9 @@ using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Action; -using Libplanet.Crypto; using Libplanet.Types.Assets; -using Nekoyume.Module.ValidatorDelegation; using Nekoyume.ValidatorDelegation; +using Nekoyume.Model.Guild; namespace Nekoyume.Action.ValidatorDelegation { @@ -16,20 +15,16 @@ public sealed class DelegateValidator : ActionBase public DelegateValidator() { } - public DelegateValidator(Address validatorDelegatee, FungibleAssetValue fav) + public DelegateValidator(FungibleAssetValue fav) { - ValidatorDelegatee = validatorDelegatee; FAV = fav; } - public Address ValidatorDelegatee { get; private set; } - public FungibleAssetValue FAV { get; private set; } public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) .Add("values", List.Empty - .Add(ValidatorDelegatee.Bencoded) .Add(FAV.Serialize())); public override void LoadPlainValue(IValue plainValue) @@ -42,27 +37,30 @@ public override void LoadPlainValue(IValue plainValue) throw new InvalidCastException(); } - ValidatorDelegatee = new Address(values[0]); - FAV = new FungibleAssetValue(values[1]); + FAV = new FungibleAssetValue(values[0]); } public override IWorld Execute(IActionContext context) { GasTracer.UseGas(1); - if (context.Signer != ValidatorDelegatee) + if (FAV.Sign <= 0) { - throw new InvalidAddressException( - $"{nameof(context.Signer)}({context.Signer}) is " + - $"not equal to {nameof(ValidatorDelegatee)}({ValidatorDelegatee})." - ); + throw new ArgumentOutOfRangeException( + nameof(FAV), FAV, "Fungible asset value must be positive."); } - var world = context.PreviousState; - var repository = new ValidatorRepository(world, context); - repository.DelegateValidator(ValidatorDelegatee, FAV); + var guildRepository = new GuildRepository(context.PreviousState, context); + var guildDelegatee = guildRepository.GetGuildDelegatee(context.Signer); + var guildDelegator = guildRepository.GetGuildDelegator(context.Signer); + guildDelegator.Delegate(guildDelegatee, FAV, context.BlockIndex); + + var validatorRepository = new ValidatorRepository(guildRepository); + var validatorDelegatee = validatorRepository.GetValidatorDelegatee(context.Signer); + var validatorDelegator = validatorRepository.GetValidatorDelegator(context.Signer); + validatorDelegatee.Bond(validatorDelegator, FAV, context.BlockIndex); - return repository.World; + return validatorRepository.World; } } } diff --git a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs index 2a6987aa7b..7c20d82482 100644 --- a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs @@ -78,12 +78,20 @@ public IWorld ExecutePublic(IActionContext context) var world = context.PreviousState; - var repository = new ValidatorRepository(world, context); - repository.CreateValidatorDelegatee(PublicKey, CommissionPercentage); - repository.DelegateValidator(context.Signer, FAV); + if (!PublicKey.Address.Equals(context.Signer)) + { + throw new ArgumentException("The public key does not match the signer."); + } - var guildRepository = new GuildRepository(repository.World, context); - guildRepository.CreateValidatorDelegateeForGuildParticipant(); + var repository = new ValidatorRepository(world, context); + var validatorDelegatee = repository.CreateValidatorDelegatee(PublicKey, CommissionPercentage); + var validatorDelegator = repository.GetValidatorDelegator(context.Signer); + validatorDelegatee.Bond(validatorDelegator, FAV, context.BlockIndex); + + var guildRepository = new GuildRepository(repository); + var guildDelegatee = guildRepository.CreateGuildDelegatee(context.Signer); + var guildDelegator = guildRepository.GetGuildDelegator(context.Signer); + guildDelegator.Delegate(guildDelegatee, FAV, context.BlockIndex); return guildRepository.World; } diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs index 5554e9b132..9f1375cd8e 100644 --- a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -35,7 +35,7 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { var world = context.PreviousState; - var repository = new ValidatorRepository(world, context); + var repository = new GuildRepository(world, context); var unbondingSet = repository.GetUnbondingSet(); var unbondings = unbondingSet.UnbondingsToRelease(context.BlockIndex); diff --git a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs index b620c608d0..400b9375aa 100644 --- a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs @@ -49,7 +49,7 @@ public override IWorld Execute(IActionContext context) validatorDelegatee.Jail(context.BlockIndex + AbstainJailTime); var guildRepository = new GuildRepository(repository.World, repository.ActionContext); - var validatorDelegateeForGuildParticipant = guildRepository.GetValidatorDelegateeForGuildParticipant(abstain.Address); + var validatorDelegateeForGuildParticipant = guildRepository.GetGuildDelegatee(abstain.Address); validatorDelegateeForGuildParticipant.Slash(LivenessSlashFactor, context.BlockIndex, context.BlockIndex); repository.UpdateWorld(guildRepository.World); } @@ -69,7 +69,7 @@ public override IWorld Execute(IActionContext context) validatorDelegatee.Tombstone(); var guildRepository = new GuildRepository(repository.World, repository.ActionContext); - var validatorDelegateeForGuildParticipant = guildRepository.GetValidatorDelegateeForGuildParticipant(e.TargetAddress); + var validatorDelegateeForGuildParticipant = guildRepository.GetGuildDelegatee(e.TargetAddress); validatorDelegateeForGuildParticipant.Slash(DuplicateVoteSlashFactor, e.Height, context.BlockIndex); repository.UpdateWorld(guildRepository.World); break; diff --git a/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs b/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs index fc60dc06c8..06a9898954 100644 --- a/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/UndelegateValidator.cs @@ -3,9 +3,8 @@ using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Action; -using Libplanet.Crypto; -using Nekoyume.Module.ValidatorDelegation; using Nekoyume.ValidatorDelegation; +using Nekoyume.Model.Guild; namespace Nekoyume.Action.ValidatorDelegation { @@ -16,20 +15,16 @@ public sealed class UndelegateValidator : ActionBase public UndelegateValidator() { } - public UndelegateValidator(Address validatorDelegatee, BigInteger share) + public UndelegateValidator(BigInteger share) { - ValidatorDelegatee = validatorDelegatee; Share = share; } - public Address ValidatorDelegatee { get; private set; } - public BigInteger Share { get; private set; } public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) .Add("values", List.Empty - .Add(ValidatorDelegatee.Bencoded) .Add(Share)); public override void LoadPlainValue(IValue plainValue) @@ -42,27 +37,30 @@ public override void LoadPlainValue(IValue plainValue) throw new InvalidCastException(); } - ValidatorDelegatee = new Address(values[0]); - Share = (Integer)values[1]; + Share = (Integer)values[0]; } public override IWorld Execute(IActionContext context) { GasTracer.UseGas(1); - if (context.Signer != ValidatorDelegatee) + if (Share.Sign <= 0) { - throw new InvalidAddressException( - $"{nameof(context.Signer)}({context.Signer}) is " + - $"not equal to {nameof(ValidatorDelegatee)}({ValidatorDelegatee})." - ); + throw new ArgumentOutOfRangeException( + nameof(Share), Share, "Share must be positive."); } - var world = context.PreviousState; - var repository = new ValidatorRepository(world, context); - repository.UndelegateValidator(ValidatorDelegatee, Share); + var guildRepository = new GuildRepository(context.PreviousState, context); + var guildDelegatee = guildRepository.GetGuildDelegatee(context.Signer); + var guildDelegator = guildRepository.GetGuildDelegator(context.Signer); + guildDelegator.Undelegate(guildDelegatee, Share, context.BlockIndex); + + var validatorRepository = new ValidatorRepository(guildRepository); + var validatorDelegatee = validatorRepository.GetValidatorDelegatee(context.Signer); + var validatorDelegator = validatorRepository.GetValidatorDelegator(context.Signer); + validatorDelegatee.Unbond(validatorDelegator, Share, context.BlockIndex); - return repository.World; + return validatorRepository.World; } } } diff --git a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs index 0fb6cd5ddc..beca295e51 100644 --- a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs +++ b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs @@ -31,9 +31,9 @@ public override IWorld Execute(IActionContext context) validatorDelegatee.Deactivate(); repository.SetValidatorDelegatee(validatorDelegatee); var guildRepository = new GuildRepository(repository.World, repository.ActionContext); - var validatorDelegateeForGuildParticipant = guildRepository.GetValidatorDelegateeForGuildParticipant(deactivated.OperatorAddress); + var validatorDelegateeForGuildParticipant = guildRepository.GetGuildDelegatee(deactivated.OperatorAddress); validatorDelegateeForGuildParticipant.Deactivate(); - guildRepository.SetValidatorDelegateeForGuildParticipant(validatorDelegateeForGuildParticipant); + guildRepository.SetGuildDelgatee(validatorDelegateeForGuildParticipant); repository.UpdateWorld(guildRepository.World); } @@ -43,9 +43,9 @@ public override IWorld Execute(IActionContext context) validatorDelegatee.Activate(); repository.SetValidatorDelegatee(validatorDelegatee); var guildRepository = new GuildRepository(repository.World, repository.ActionContext); - var validatorDelegateeForGuildParticipant = guildRepository.GetValidatorDelegateeForGuildParticipant(activated.OperatorAddress); + var validatorDelegateeForGuildParticipant = guildRepository.GetGuildDelegatee(activated.OperatorAddress); validatorDelegateeForGuildParticipant.Activate(); - guildRepository.SetValidatorDelegateeForGuildParticipant(validatorDelegateeForGuildParticipant); + guildRepository.SetGuildDelgatee(validatorDelegateeForGuildParticipant); repository.UpdateWorld(guildRepository.World); } diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index 28a140d574..0468fc52e2 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -106,13 +106,13 @@ public static Address GetGuildBanAccountAddress(Address guildAddress) => /// /// An address of an account having . /// - public static readonly Address ValidatorDelegateeForGuildParticipantMetadata + public static readonly Address GuildDelegateeMetadata = new Address("0000000000000000000000000000000000000210"); /// /// An address of an account having . /// - public static readonly Address GuildParticipantMetadata + public static readonly Address GuildDelegatorMetadata = new Address("0000000000000000000000000000000000000211"); /// diff --git a/Lib9c/Delegation/LumpSumRewardsRecord.cs b/Lib9c/Delegation/LumpSumRewardsRecord.cs index 7c927edfd4..dc4c04b4bf 100644 --- a/Lib9c/Delegation/LumpSumRewardsRecord.cs +++ b/Lib9c/Delegation/LumpSumRewardsRecord.cs @@ -144,18 +144,21 @@ public LumpSumRewardsRecord MoveAddress(Address address) LastStartHeight); public LumpSumRewardsRecord AddLumpSumRewards(IEnumerable rewards) - => rewards.Aggregate(this, (accum, next) => AddLumpSumRewards(next)); + => rewards.Aggregate(this, (accum, next) => AddLumpSumRewards(accum, next)); public LumpSumRewardsRecord AddLumpSumRewards(FungibleAssetValue rewards) + => AddLumpSumRewards(this, rewards); + + public static LumpSumRewardsRecord AddLumpSumRewards(LumpSumRewardsRecord record, FungibleAssetValue rewards) => new LumpSumRewardsRecord( - Address, - StartHeight, - TotalShares, - Delegators, - LumpSumRewards.TryGetValue(rewards.Currency, out var cumulative) - ? LumpSumRewards.SetItem(rewards.Currency, cumulative + rewards) + record.Address, + record.StartHeight, + record.TotalShares, + record.Delegators, + record.LumpSumRewards.TryGetValue(rewards.Currency, out var cumulative) + ? record.LumpSumRewards.SetItem(rewards.Currency, cumulative + rewards) : throw new ArgumentException($"Invalid reward currency: {rewards.Currency}"), - LastStartHeight); + record.LastStartHeight); public LumpSumRewardsRecord RemoveDelegator(Address delegator) => new LumpSumRewardsRecord( diff --git a/Lib9c/Model/Guild/Guild.cs b/Lib9c/Model/Guild/Guild.cs index 5120e90ba8..c30d184cd2 100644 --- a/Lib9c/Model/Guild/Guild.cs +++ b/Lib9c/Model/Guild/Guild.cs @@ -5,13 +5,14 @@ using Libplanet.Crypto; using Nekoyume.Action; using Nekoyume.TypedAddress; +using Nekoyume.ValidatorDelegation; namespace Nekoyume.Model.Guild { public class Guild : IBencodable, IEquatable { private const string StateTypeName = "guild"; - private const long StateVersion = 1; + private const long StateVersion = 2; public readonly AgentAddress GuildMasterAddress; @@ -26,6 +27,7 @@ public Guild( Address = address; GuildMasterAddress = guildMasterAddress; ValidatorAddress = validatorAddress; + Repository = repository; } public Guild( @@ -48,13 +50,21 @@ public Guild( throw new FailedLoadStateException("Un-deserializable state."); } + if (integer == 1) + { + throw new FailedLoadStateException("Does not support version 1."); + } + Address = address; GuildMasterAddress = new AgentAddress(list[2]); ValidatorAddress = new AgentAddress(list[3]); + Repository = repository; } public GuildAddress Address { get; } + public GuildRepository Repository { get; } + public List Bencoded => List.Empty .Add(StateTypeName) .Add(StateVersion) @@ -63,6 +73,20 @@ public Guild( IValue IBencodable.Bencoded => Bencoded; + public void ClaimReward(Address validatorAddress, long height) + { + var guildDelegatee = Repository.GetGuildDelegatee(validatorAddress); + var guildDelegator = Repository.GetGuildDelegator(Address); + guildDelegator.ClaimReward(guildDelegatee, height); + + var validatorRepository = new ValidatorRepository(Repository); + var validatorDelegatee = validatorRepository.GetValidatorDelegatee(validatorAddress); + var validatorDelegator = validatorRepository.GetValidatorDelegator(Address); + validatorDelegator.ClaimReward(validatorDelegatee, height); + + Repository.UpdateWorld(validatorRepository.World); + } + public bool Equals(Guild? other) { if (ReferenceEquals(null, other)) return false; diff --git a/Lib9c/Model/Guild/ValidatorDelegateeForGuildParticipant.cs b/Lib9c/Model/Guild/GuildDelegatee.cs similarity index 83% rename from Lib9c/Model/Guild/ValidatorDelegateeForGuildParticipant.cs rename to Lib9c/Model/Guild/GuildDelegatee.cs index 306442ffb9..079fe52377 100644 --- a/Lib9c/Model/Guild/ValidatorDelegateeForGuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildDelegatee.cs @@ -8,10 +8,10 @@ namespace Nekoyume.Model.Guild { - public class ValidatorDelegateeForGuildParticipant - : Delegatee, IEquatable + public class GuildDelegatee + : Delegatee, IEquatable { - public ValidatorDelegateeForGuildParticipant( + public GuildDelegatee( Address address, IEnumerable rewardCurrencies, GuildRepository repository) @@ -31,7 +31,7 @@ public ValidatorDelegateeForGuildParticipant( { } - public ValidatorDelegateeForGuildParticipant( + public GuildDelegatee( Address address, GuildRepository repository) : base( @@ -50,7 +50,7 @@ public void Deactivate() Metadata.DelegationPoolAddress = ValidatorDelegatee.InactiveDelegationPoolAddress; } - public bool Equals(ValidatorDelegateeForGuildParticipant? other) + public bool Equals(GuildDelegatee? other) => Metadata.Equals(other?.Metadata); } } diff --git a/Lib9c/Model/Guild/GuildDelegator.cs b/Lib9c/Model/Guild/GuildDelegator.cs new file mode 100644 index 0000000000..5184114888 --- /dev/null +++ b/Lib9c/Model/Guild/GuildDelegator.cs @@ -0,0 +1,34 @@ +#nullable enable +using System; +using Libplanet.Crypto; +using Nekoyume.Delegation; + +namespace Nekoyume.Model.Guild +{ + public class GuildDelegator + : Delegator, IEquatable + { + public GuildDelegator( + Address address, + Address delegationPoolAddress, + GuildRepository repository) + : base( + address: address, + accountAddress: repository.DelegatorAccountAddress, + delegationPoolAddress: delegationPoolAddress, + rewardAddress: address, + repository: repository) + { + } + + public GuildDelegator( + Address address, + GuildRepository repository) + : base(address: address, repository: repository) + { + } + + public bool Equals(GuildDelegator? other) + => Metadata.Equals(other?.Metadata); + } +} diff --git a/Lib9c/Model/Guild/GuildParticipant.cs b/Lib9c/Model/Guild/GuildParticipant.cs index 490106c280..bdf782cd5b 100644 --- a/Lib9c/Model/Guild/GuildParticipant.cs +++ b/Lib9c/Model/Guild/GuildParticipant.cs @@ -3,10 +3,9 @@ using System.Numerics; using Bencodex; using Bencodex.Types; +using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Action; -using Nekoyume.Delegation; -using Nekoyume.Model.Stake; using Nekoyume.TypedAddress; using Nekoyume.ValidatorDelegation; @@ -14,11 +13,10 @@ namespace Nekoyume.Model.Guild { // It Does not inherit from `Delegator`, since `Validator` related functionalities // will be moved to lower level library. - public class GuildParticipant - : Delegator, IBencodable, IEquatable + public class GuildParticipant : IBencodable, IEquatable { private const string StateTypeName = "guild_participant"; - private const long StateVersion = 1; + private const long StateVersion = 2; public readonly GuildAddress GuildAddress; @@ -26,21 +24,16 @@ public GuildParticipant( AgentAddress address, GuildAddress guildAddress, GuildRepository repository) - : base( - address: address, - accountAddress: repository.DelegatorAccountAddress, - delegationPoolAddress: StakeState.DeriveAddress(address), - rewardAddress: address, - repository: repository) { + Address = address; GuildAddress = guildAddress; + Repository = repository; } public GuildParticipant( AgentAddress address, IValue bencoded, GuildRepository repository) - : base(address: address, repository: repository) { if (bencoded is not List list) { @@ -57,10 +50,25 @@ public GuildParticipant( throw new FailedLoadStateException("Un-deserializable state."); } + if (integer == 1) + { + throw new FailedLoadStateException("State version 1 is not supported."); + } + + Address = address; GuildAddress = new GuildAddress(list[2]); + Repository = repository; } - public new AgentAddress Address => new AgentAddress(base.Address); + public AgentAddress Address { get; } + + public Address DelegationPoolAddress + => Repository.GetGuildDelegator(Address).DelegationPoolAddress; + + public Address RewardAddress + => Repository.GetGuildDelegator(Address).RewardAddress; + + public GuildRepository Repository { get; } public List Bencoded => List.Empty .Add(StateTypeName) @@ -71,18 +79,17 @@ public GuildParticipant( public void Delegate(Guild guild, FungibleAssetValue fav, long height) { - var repository = (GuildRepository)Repository; - if (fav.Sign <= 0) { throw new ArgumentOutOfRangeException( nameof(fav), fav, "Fungible asset value must be positive."); } - var validatorDelegateeForGuildParticipant = repository.GetValidatorDelegateeForGuildParticipant(guild.ValidatorAddress); - base.Delegate(validatorDelegateeForGuildParticipant, fav, height); + var guildDelegatee = Repository.GetGuildDelegatee(guild.ValidatorAddress); + var guildDelegator = Repository.GetGuildDelegator(Address); + guildDelegator.Delegate(guildDelegatee, fav, height); - var validatorRepository = new ValidatorRepository(Repository.World, Repository.ActionContext); + var validatorRepository = new ValidatorRepository(Repository); var validatorDelegatee = validatorRepository.GetValidatorDelegatee(guild.ValidatorAddress); var validatorDelegator = validatorRepository.GetValidatorDelegator(guild.Address); validatorDelegatee.Bond(validatorDelegator, fav, height); @@ -92,8 +99,6 @@ public void Delegate(Guild guild, FungibleAssetValue fav, long height) public void Undelegate(Guild guild, BigInteger share, long height) { - var repository = (GuildRepository)Repository; - if (share.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -106,10 +111,11 @@ public void Undelegate(Guild guild, BigInteger share, long height) nameof(height), height, "Height must be positive."); } - var validatorDelegateeForGuildParticipant = repository.GetValidatorDelegateeForGuildParticipant(guild.ValidatorAddress); - base.Undelegate(validatorDelegateeForGuildParticipant, share, height); + var guildDelegatee = Repository.GetGuildDelegatee(guild.ValidatorAddress); + var guildDelegator = Repository.GetGuildDelegator(Address); + guildDelegator.Undelegate(guildDelegatee, share, height); - var validatorRepository = new ValidatorRepository(Repository.World, Repository.ActionContext); + var validatorRepository = new ValidatorRepository(Repository); var validatorDelegatee = validatorRepository.GetValidatorDelegatee(guild.ValidatorAddress); var validatorDelegator = validatorRepository.GetValidatorDelegator(guild.Address); validatorDelegatee.Unbond(validatorDelegator, share, height); @@ -120,8 +126,6 @@ public void Undelegate(Guild guild, BigInteger share, long height) public void Redelegate( Guild srcGuild, Guild dstGuild, BigInteger share, long height) { - var repository = (GuildRepository)Repository; - if (share.Sign <= 0) { throw new ArgumentOutOfRangeException( @@ -134,11 +138,12 @@ public void Redelegate( nameof(height), height, "Height must be positive."); } - var srcValidatorDelegateeForGuildParticipant = repository.GetValidatorDelegateeForGuildParticipant(srcGuild.ValidatorAddress); - var dstValidatorDelegateeForGuildParticipant = repository.GetValidatorDelegateeForGuildParticipant(dstGuild.ValidatorAddress); - base.Redelegate(srcValidatorDelegateeForGuildParticipant, dstValidatorDelegateeForGuildParticipant, share, height); + var srcGuildDelegatee = Repository.GetGuildDelegatee(srcGuild.ValidatorAddress); + var dstGuildDelegator = Repository.GetGuildDelegatee(dstGuild.ValidatorAddress); + var guildDelegator = Repository.GetGuildDelegator(Address); + guildDelegator.Redelegate(srcGuildDelegatee, dstGuildDelegator, share, height); - var validatorRepository = new ValidatorRepository(Repository.World, Repository.ActionContext); + var validatorRepository = new ValidatorRepository(Repository); var srcValidatorDelegatee = validatorRepository.GetValidatorDelegatee(srcGuild.ValidatorAddress); var srcValidatorDelegator = validatorRepository.GetValidatorDelegator(srcGuild.Address); var fav = srcValidatorDelegatee.Unbond(srcValidatorDelegator, share, height); @@ -149,6 +154,13 @@ public void Redelegate( Repository.UpdateWorld(validatorRepository.World); } + public void ClaimReward(Guild guild, long height) + { + var guildDelegatee = Repository.GetGuildDelegatee(guild.ValidatorAddress); + var guildDelegator = Repository.GetGuildDelegator(Address); + guildDelegator.ClaimReward(guildDelegatee, height); + } + public bool Equals(GuildParticipant? other) { if (ReferenceEquals(null, other)) return false; diff --git a/Lib9c/Model/Guild/GuildRepository.cs b/Lib9c/Model/Guild/GuildRepository.cs index c61836adaa..f1394e99e5 100644 --- a/Lib9c/Model/Guild/GuildRepository.cs +++ b/Lib9c/Model/Guild/GuildRepository.cs @@ -5,11 +5,18 @@ using Nekoyume.Action; using Nekoyume.Delegation; using Nekoyume.TypedAddress; +using Nekoyume.Model.Stake; namespace Nekoyume.Model.Guild { public class GuildRepository : DelegationRepository { + private readonly Address guildAddress = Addresses.Guild; + private readonly Address guildParticipantAddress = Addresses.GuildParticipant; + + private IAccount _guildAccount; + private IAccount _guildParticipantAccount; + public GuildRepository(IDelegationRepository repository) : this(repository.World, repository.ActionContext) { @@ -19,63 +26,65 @@ public GuildRepository(IWorld world, IActionContext actionContext) : base( world: world, actionContext: actionContext, - delegateeAccountAddress: Addresses.ValidatorDelegateeForGuildParticipantMetadata, - delegatorAccountAddress: Addresses.GuildParticipant, - delegateeMetadataAccountAddress: Addresses.ValidatorDelegateeForGuildParticipantMetadata, - delegatorMetadataAccountAddress: Addresses.GuildParticipantMetadata, + delegateeAccountAddress: Addresses.GuildDelegateeMetadata, + delegatorAccountAddress: Addresses.GuildDelegatorMetadata, + delegateeMetadataAccountAddress: Addresses.GuildDelegateeMetadata, + delegatorMetadataAccountAddress: Addresses.GuildDelegatorMetadata, bondAccountAddress: Addresses.GuildBond, unbondLockInAccountAddress: Addresses.GuildUnbondLockIn, rebondGraceAccountAddress: Addresses.GuildRebondGrace, unbondingSetAccountAddress: Addresses.GuildUnbondingSet, lumpSumRewardRecordAccountAddress: Addresses.GuildLumpSumRewardsRecord) { + _guildAccount = world.GetAccount(guildAddress); + _guildParticipantAccount = world.GetAccount(guildParticipantAddress); } - public ValidatorDelegateeForGuildParticipant GetValidatorDelegateeForGuildParticipant(Address address) - => delegateeAccount.GetState(address) is IValue bencoded - ? new ValidatorDelegateeForGuildParticipant( - address, - this) - : throw new FailedLoadStateException("Validator delegatee for guild participant does not exist."); + public override IWorld World => base.World + .SetAccount(guildAddress, _guildAccount) + .SetAccount(guildParticipantAddress, _guildParticipantAccount); - public override IDelegatee GetDelegatee(Address address) - => GetValidatorDelegateeForGuildParticipant(address); + public GuildDelegatee GetGuildDelegatee(Address address) + => new GuildDelegatee(address, this); - public GuildParticipant GetGuildParticipant(Address address) - => delegatorAccount.GetState(address) is IValue bencoded - ? new GuildParticipant( - new AgentAddress(address), - bencoded, - this) - : throw new FailedLoadStateException("Guild participant does not exist."); + public override IDelegatee GetDelegatee(Address address) + => GetGuildDelegatee(address); + public GuildDelegator GetGuildDelegator(Address address) + { + try + { + return new GuildDelegator(address, this); + } + catch (FailedLoadStateException) + { + return new GuildDelegator( + address, + StakeState.DeriveAddress(address), + this); + } + } public override IDelegator GetDelegator(Address address) - => GetGuildParticipant(address); + => GetGuildDelegator(address); - public void SetValidatorDelegateeForGuildParticipant(ValidatorDelegateeForGuildParticipant validatorDelegateeForGuild) + public void SetGuildDelgatee(GuildDelegatee guildDelegatee) { - SetDelegateeMetadata(validatorDelegateeForGuild.Metadata); + SetDelegateeMetadata(guildDelegatee.Metadata); } public override void SetDelegatee(IDelegatee delegatee) - => SetValidatorDelegateeForGuildParticipant(delegatee as ValidatorDelegateeForGuildParticipant); + => SetGuildDelgatee(delegatee as GuildDelegatee); - public void SetGuildParticipant(GuildParticipant guildParticipant) + public void SetGuildDelegator(GuildDelegator guildDelegator) { - delegatorAccount = delegatorAccount.SetState( - guildParticipant.Address, guildParticipant.Bencoded); - SetDelegatorMetadata(guildParticipant.Metadata); + SetDelegatorMetadata(guildDelegator.Metadata); } - public override void SetDelegator(IDelegator delegator) - => SetGuildParticipant(delegator as GuildParticipant); - public void RemoveGuildParticipant(Address guildParticipantAddress) - { - delegatorAccount = delegatorAccount.RemoveState(guildParticipantAddress); - } + public override void SetDelegator(IDelegator delegator) + => SetGuildDelegator(delegator as GuildDelegator); public Guild GetGuild(Address address) - => delegateeAccount.GetState(address) is IValue bencoded + => _guildAccount.GetState(address) is IValue bencoded ? new Guild( new GuildAddress(address), bencoded, @@ -84,8 +93,34 @@ public Guild GetGuild(Address address) public void SetGuild(Guild guild) { - delegateeAccount = delegateeAccount.SetState( + _guildAccount = _guildAccount.SetState( guild.Address, guild.Bencoded); } + + public GuildParticipant GetGuildParticipant(Address address) + => _guildParticipantAccount.GetState(address) is IValue bencoded + ? new GuildParticipant( + new AgentAddress(address), + bencoded, + this) + : throw new FailedLoadStateException("Guild participant does not exist."); + + public void SetGuildParticipant(GuildParticipant guildParticipant) + { + _guildParticipantAccount = _guildParticipantAccount.SetState( + guildParticipant.Address, guildParticipant.Bencoded); + } + + public void RemoveGuildParticipant(Address guildParticipantAddress) + { + _guildParticipantAccount = _guildParticipantAccount.RemoveState(guildParticipantAddress); + } + + public override void UpdateWorld(IWorld world) + { + base.UpdateWorld(world); + _guildAccount = world.GetAccount(guildAddress); + _guildParticipantAccount = world.GetAccount(guildParticipantAddress); + } } } diff --git a/Lib9c/Module/Guild/ValidatorDelegateeForGuildModule.cs b/Lib9c/Module/Guild/GuildDelegateeModule.cs similarity index 54% rename from Lib9c/Module/Guild/ValidatorDelegateeForGuildModule.cs rename to Lib9c/Module/Guild/GuildDelegateeModule.cs index b727b1dab9..d92f827569 100644 --- a/Lib9c/Module/Guild/ValidatorDelegateeForGuildModule.cs +++ b/Lib9c/Module/Guild/GuildDelegateeModule.cs @@ -1,7 +1,6 @@ #nullable enable using System; using System.Diagnostics.CodeAnalysis; -using Lib9c; using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Model.Guild; @@ -11,16 +10,16 @@ namespace Nekoyume.Module.Guild { - public static class ValidatorDelegateeForGuildModule + public static class GuildDelegateeModule { - public static bool TryGetValidatorDelegateeForGuild( + public static bool TryGetGuildDelegatee( this GuildRepository repository, Address address, - [NotNullWhen(true)] out ValidatorDelegateeForGuildParticipant? validatorDelegateeForGuildParticipant) + [NotNullWhen(true)] out GuildDelegatee? validatorDelegateeForGuildParticipant) { try { - validatorDelegateeForGuildParticipant = repository.GetValidatorDelegateeForGuildParticipant(address); + validatorDelegateeForGuildParticipant = repository.GetGuildDelegatee(address); return true; } catch @@ -30,31 +29,29 @@ public static bool TryGetValidatorDelegateeForGuild( } } - public static GuildRepository CreateValidatorDelegateeForGuildParticipant( - this GuildRepository repository) + public static GuildDelegatee CreateGuildDelegatee( + this GuildRepository repository, + Address address) { - var context = repository.ActionContext; - var signer = context.Signer; - - if (repository.TryGetValidatorDelegateeForGuild(context.Signer, out _)) + if (repository.TryGetGuildDelegatee(address, out _)) { throw new InvalidOperationException("The signer already has a validator delegatee for guild."); } var validatorRepository = new ValidatorRepository(repository.World, repository.ActionContext); - if (!validatorRepository.TryGetValidatorDelegatee(context.Signer, out _)) + if (!validatorRepository.TryGetValidatorDelegatee(address, out _)) { throw new InvalidOperationException("The signer does not have a validator delegatee."); } - var validatorDelegateeForGuildParticipant = new ValidatorDelegateeForGuildParticipant( - signer, + var guildDelegatee = new GuildDelegatee( + address, new Currency[] { repository.World.GetGoldCurrency() }, repository); - repository.SetValidatorDelegateeForGuildParticipant(validatorDelegateeForGuildParticipant); + repository.SetGuildDelgatee(guildDelegatee); - return repository; + return guildDelegatee; } } } diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs index 19c1299f2b..13f792cbef 100644 --- a/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs @@ -28,24 +28,18 @@ public static bool TryGetValidatorDelegatee( } } - public static ValidatorRepository CreateValidatorDelegatee( + public static ValidatorDelegatee CreateValidatorDelegatee( this ValidatorRepository repository, PublicKey publicKey, BigInteger commissionPercentage) { var context = repository.ActionContext; - var signer = context.Signer; - if (!publicKey.Address.Equals(signer)) - { - throw new ArgumentException("The public key does not match the signer."); - } - - if (repository.TryGetValidatorDelegatee(context.Signer, out _)) + if (repository.TryGetValidatorDelegatee(publicKey.Address, out _)) { throw new InvalidOperationException("The signer already has a validator delegatee."); } var validatorDelegatee = new ValidatorDelegatee( - signer, + publicKey.Address, publicKey, commissionPercentage, context.BlockIndex, @@ -54,7 +48,7 @@ public static ValidatorRepository CreateValidatorDelegatee( repository.SetValidatorDelegatee(validatorDelegatee); - return repository; + return validatorDelegatee; } } } diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs index 85ac432962..d352e6cf51 100644 --- a/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorDelegatorModule.cs @@ -1,101 +1,12 @@ #nullable enable using System.Diagnostics.CodeAnalysis; -using System.Numerics; using Libplanet.Crypto; -using Libplanet.Types.Assets; using Nekoyume.ValidatorDelegation; namespace Nekoyume.Module.ValidatorDelegation { public static class ValidatorDelegatorModule { - public static ValidatorRepository DelegateValidator( - this ValidatorRepository repository, - Address validatorAddress, - FungibleAssetValue fav) - => DelegateValidator(repository, validatorAddress, validatorAddress, fav); - - - public static ValidatorRepository DelegateValidator( - this ValidatorRepository repository, - Address validatorAddress, - Address rewardAddress, - FungibleAssetValue fav) - { - var context = repository.ActionContext; - var delegatorAddress = context.Signer; - var validatorDelegator = repository.GetValidatorDelegator(delegatorAddress); - var validatorDelegatee = repository.GetValidatorDelegatee(validatorAddress); - validatorDelegator.Delegate(validatorDelegatee, fav, context.BlockIndex); - - return repository; - } - - public static ValidatorRepository UndelegateValidator( - this ValidatorRepository repository, - Address validatorAddress, - BigInteger share) - => UndelegateValidator(repository, validatorAddress, validatorAddress, share); - - public static ValidatorRepository UndelegateValidator( - this ValidatorRepository repository, - Address validatorAddress, - Address rewardAddress, - BigInteger share) - { - var context = repository.ActionContext; - var delegatorAddress = context.Signer; - var validatorDelegator = repository.GetValidatorDelegator(delegatorAddress); - var validatorDelegatee = repository.GetValidatorDelegatee(validatorAddress); - validatorDelegator.Undelegate(validatorDelegatee, share, context.BlockIndex); - - return repository; - } - - public static ValidatorRepository RedelegateValidator( - this ValidatorRepository repository, - Address srcValidatorAddress, - Address dstValidatorAddress, - BigInteger share) - => RedelegateValidator(repository, srcValidatorAddress, dstValidatorAddress, dstValidatorAddress, share); - - public static ValidatorRepository RedelegateValidator( - this ValidatorRepository repository, - Address srcValidatorAddress, - Address dstValidatorAddress, - Address rewardAddress, - BigInteger share) - { - var context = repository.ActionContext; - var delegatorAddress = context.Signer; - var validatorDelegator = repository.GetValidatorDelegator(delegatorAddress); - var srcValidatorDelegatee = repository.GetValidatorDelegatee(srcValidatorAddress); - var dstValidatorDelegatee = repository.GetValidatorDelegatee(dstValidatorAddress); - validatorDelegator.Redelegate(srcValidatorDelegatee, dstValidatorDelegatee, share, context.BlockIndex); - - return repository; - } - - public static ValidatorRepository ClaimRewardValidator( - this ValidatorRepository repository, - Address validatorAddress) - => ClaimRewardValidator(repository, validatorAddress, validatorAddress); - - public static ValidatorRepository ClaimRewardValidator( - this ValidatorRepository repository, - Address address, - Address rewardAddress) - { - var context = repository.ActionContext; - var delegatorAddress = context.Signer; - var validatorDelegator = repository.GetValidatorDelegator(delegatorAddress); - var validatorDelegatee = repository.GetValidatorDelegatee(address); - - validatorDelegator.ClaimReward(validatorDelegatee, context.BlockIndex); - - return repository; - } - public static bool TryGetValidatorDelegator( this ValidatorRepository repository, Address address, diff --git a/Lib9c/ValidatorDelegation/ValidatorRepository.cs b/Lib9c/ValidatorDelegation/ValidatorRepository.cs index 2526bede6d..69e7563c38 100644 --- a/Lib9c/ValidatorDelegation/ValidatorRepository.cs +++ b/Lib9c/ValidatorDelegation/ValidatorRepository.cs @@ -13,7 +13,7 @@ public sealed class ValidatorRepository : DelegationRepository { private readonly Address validatorListAddress = Addresses.ValidatorList; - private IAccount _validatorList; + private IAccount _validatorListAccount; public ValidatorRepository(IDelegationRepository repository) : this(repository.World, repository.ActionContext) @@ -34,11 +34,11 @@ public ValidatorRepository(IWorld world, IActionContext actionContext) Addresses.ValidatorUnbondingSet, Addresses.ValidatorLumpSumRewardsRecord) { - _validatorList = world.GetAccount(validatorListAddress); + _validatorListAccount = world.GetAccount(validatorListAddress); } public override IWorld World => base.World - .SetAccount(validatorListAddress, _validatorList); + .SetAccount(validatorListAddress, _validatorListAccount); public ValidatorDelegatee GetValidatorDelegatee(Address address) => delegateeAccount.GetState(address) is IValue bencoded @@ -71,7 +71,7 @@ public override IDelegator GetDelegator(Address address) public ValidatorList GetValidatorList() { - IValue? value = _validatorList.GetState(ValidatorList.Address); + IValue? value = _validatorListAccount.GetState(ValidatorList.Address); return value is IValue bencoded ? new ValidatorList(bencoded) : new ValidatorList(); @@ -97,7 +97,7 @@ public override void SetDelegator(IDelegator delegator) public void SetValidatorList(ValidatorList validatorList) { - _validatorList = _validatorList.SetState( + _validatorListAccount = _validatorListAccount.SetState( ValidatorList.Address, validatorList.Bencoded); } @@ -111,7 +111,7 @@ public void SetCommissionPercentage(Address address, BigInteger commissionPercen public override void UpdateWorld(IWorld world) { base.UpdateWorld(world); - _validatorList = world.GetAccount(validatorListAddress); + _validatorListAccount = world.GetAccount(validatorListAddress); } } } From 7e3524002cb2a23dd474ec75202c7c7293abc2be Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 13 Nov 2024 17:31:29 +0900 Subject: [PATCH 147/206] test: Fix tests --- .../Action/Guild/ClaimRewardGuildTest.cs | 145 ------------- .../ClaimRewardTest.cs} | 199 ++++++++++++------ .Lib9c.Tests/Action/Guild/GuildTestBase.cs | 5 +- .../Migration/MigratePlanetariumGuildTest.cs | 5 +- .Lib9c.Tests/Action/StakeTest.cs | 5 +- ...wardTest.cs => AllocateGuildRewardTest.cs} | 28 +-- .../DelegateValidatorTest.cs | 16 +- .../ReleaseValidatorUnbondingsTest.cs | 10 +- .../UndelegateValidatorTest.cs | 21 +- .../ValidatorDelegationTestBase.cs | 49 ++++- .Lib9c.Tests/Delegation/DelegateeTest.cs | 27 ++- .Lib9c.Tests/Delegation/DelegationFixture.cs | 17 +- .Lib9c.Tests/Delegation/DelegatorTest.cs | 149 +++++++++---- .Lib9c.Tests/Delegation/DummyDelegatee.cs | 5 +- .Lib9c.Tests/Delegation/TestDelegatee.cs | 5 +- .Lib9c.Tests/Model/Guild/GuildTest.cs | 2 - .Lib9c.Tests/Policy/BlockPolicyTest.cs | 4 +- 17 files changed, 372 insertions(+), 320 deletions(-) delete mode 100644 .Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs rename .Lib9c.Tests/Action/{ValidatorDelegation/ClaimRewardValidatorTest.cs => Guild/ClaimRewardTest.cs} (63%) rename .Lib9c.Tests/Action/ValidatorDelegation/{AllocateRewardTest.cs => AllocateGuildRewardTest.cs} (92%) diff --git a/.Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs b/.Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs deleted file mode 100644 index df741fd800..0000000000 --- a/.Lib9c.Tests/Action/Guild/ClaimRewardGuildTest.cs +++ /dev/null @@ -1,145 +0,0 @@ -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(Skip = "Allow after bond on actual block height")] - 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); - } - } - - [Fact(Skip = "Allow after bond on actual block height")] - 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); - } - } -} diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs b/.Lib9c.Tests/Action/Guild/ClaimRewardTest.cs similarity index 63% rename from .Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs rename to .Lib9c.Tests/Action/Guild/ClaimRewardTest.cs index dcca4a3add..ce71229d70 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ClaimRewardValidatorTest.cs +++ b/.Lib9c.Tests/Action/Guild/ClaimRewardTest.cs @@ -1,12 +1,14 @@ #nullable enable -namespace Lib9c.Tests.Action.ValidatorDelegation; +namespace Lib9c.Tests.Action.Guild; using System; using System.Collections.Generic; using System.Linq; +using Lib9c.Tests.Action.ValidatorDelegation; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; +using Nekoyume.Action.Guild; using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.Guild; using Nekoyume.Model.Stake; @@ -15,11 +17,13 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using Nekoyume.ValidatorDelegation; using Xunit; -public class ClaimRewardValidatorTest : ValidatorDelegationTestBase +public class ClaimRewardTest : ValidatorDelegationTestBase { private interface IClaimRewardFixture { - FungibleAssetValue TotalReward { get; } + FungibleAssetValue TotalGuildAllocateReward { get; } + + FungibleAssetValue TotalAllocateReward { get; } PrivateKey ValidatorKey { get; } @@ -53,10 +57,10 @@ private interface IClaimRewardFixture [Fact] public void Serialization() { - var action = new ClaimRewardValidator(); + var action = new ClaimGuildReward(); var plainValue = action.PlainValue; - var deserialized = new ClaimRewardValidator(); + var deserialized = new ClaimGuildReward(); deserialized.LoadPlainValue(plainValue); } @@ -68,15 +72,16 @@ public void Execute() var validatorKey = new PrivateKey(); var height = 1L; var validatorGold = DelegationCurrency * 10; - var allocatedReward = RewardCurrency * 100; + var allocatedGuildRewards = GuildAllocateRewardCurrency * 100; + var allocatedReward = AllocateRewardCurrency * 100; world = EnsureToMintAsset(world, validatorKey, validatorGold, height++); world = EnsurePromotedValidator(world, validatorKey, validatorGold, height++); - world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedReward, ref height); + world = EnsureRewardAllocatedValidator(world, validatorKey, allocatedGuildRewards, allocatedReward, ref height); // When - var expectedBalance = allocatedReward; + var expectedBalance = allocatedGuildRewards; var lastCommit = CreateLastCommit(validatorKey, height - 1); - var claimRewardValidator = new ClaimRewardValidatorSelf(); + var claimRewardValidator = new ClaimValidatorRewardSelf(); var actionContext = new ActionContext { PreviousState = world, @@ -87,17 +92,19 @@ public void Execute() world = claimRewardValidator.Execute(actionContext); // Then - var actualBalance = world.GetBalance(validatorKey.Address, RewardCurrency); + var actualBalance = world.GetBalance(validatorKey.Address, GuildAllocateRewardCurrency); Assert.Equal(expectedBalance, actualBalance); } [Theory] - [InlineData(33.33)] - [InlineData(11.11)] - [InlineData(10)] - [InlineData(1)] - public void Execute_Theory_OneDelegator(decimal totalReward) + [InlineData(33.33, 33.33)] + [InlineData(11.11, 11.11)] + [InlineData(10, 10)] + [InlineData(1, 1)] + public void Execute_Theory_OneDelegator( + decimal totalGuildReward, + decimal totalReward) { var delegatorInfos = new[] { @@ -121,7 +128,8 @@ public void Execute_Theory_OneDelegator(decimal totalReward) var fixture = new StaticFixture { DelegatorLength = 1, - TotalReward = FungibleAssetValue.Parse(RewardCurrency, $"{totalReward}"), + TotalGuildAllocateReward = FungibleAssetValue.Parse(GuildAllocateRewardCurrency, $"{totalGuildReward}"), + TotalAllocateReward = FungibleAssetValue.Parse(RewardCurrency, $"{totalReward}"), ValidatorKey = new PrivateKey(), ValidatorBalance = DelegationCurrency * 100, ValidatorCash = DelegationCurrency * 10, @@ -132,20 +140,22 @@ public void Execute_Theory_OneDelegator(decimal totalReward) } [Theory] - [InlineData(0.1)] - [InlineData(1)] - [InlineData(3)] - [InlineData(5)] - [InlineData(7)] - [InlineData(9)] - [InlineData(11.11)] - [InlineData(11.12)] - [InlineData(33.33)] - [InlineData(33.34)] - [InlineData(34.27)] - [InlineData(34.28)] - [InlineData(34.29)] - public void Execute_Theory_TwoDelegators(decimal totalReward) + [InlineData(0.1, 0.1)] + [InlineData(1, 1)] + [InlineData(3, 3)] + [InlineData(5, 5)] + [InlineData(7, 7)] + [InlineData(9, 9)] + [InlineData(11.11, 11.11)] + [InlineData(11.12, 11.12)] + [InlineData(33.33, 33.33)] + [InlineData(33.34, 33.34)] + [InlineData(34.27, 34.27)] + [InlineData(34.28, 34.28)] + [InlineData(34.29, 34.29)] + public void Execute_Theory_TwoDelegators( + decimal totalGuildReward, + decimal totalReward) { var delegatorInfos = new[] { @@ -180,7 +190,8 @@ public void Execute_Theory_TwoDelegators(decimal totalReward) var fixture = new StaticFixture { DelegatorLength = 2, - TotalReward = FungibleAssetValue.Parse(RewardCurrency, $"{totalReward}"), + TotalGuildAllocateReward = FungibleAssetValue.Parse(GuildAllocateRewardCurrency, $"{totalGuildReward}"), + TotalAllocateReward = FungibleAssetValue.Parse(AllocateRewardCurrency, $"{totalReward}"), ValidatorKey = new PrivateKey(), ValidatorBalance = DelegationCurrency * 100, ValidatorCash = DelegationCurrency * 10, @@ -222,7 +233,8 @@ private void ExecuteWithFixture(IClaimRewardFixture fixture) var actionContext = new ActionContext(); var validatorBalance = fixture.ValidatorBalance; var validatorCash = fixture.ValidatorCash; - var totalReward = fixture.TotalReward; + var totalGuildReward = fixture.TotalGuildAllocateReward; + var totalReward = fixture.TotalAllocateReward; int seed = 0; world = EnsureToMintAsset(world, validatorKey, validatorBalance, height++); world = EnsurePromotedValidator(world, validatorKey, validatorCash, height++); @@ -232,41 +244,68 @@ private void ExecuteWithFixture(IClaimRewardFixture fixture) world = guildParticipantInfos.Aggregate(world, (w, i) => EnsureJoinGuild( w, i.Key.Address, i.GuildMasterAddress, validatorKey.Address, height++)); - world = EnsureRewardAllocatedValidator(world, validatorKey, totalReward, ref height); + world = EnsureRewardAllocatedValidator(world, validatorKey, totalGuildReward, totalReward, ref height); // Calculate expected values for comparison with actual values. var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedGuildRepository = new GuildRepository(expectedRepository); var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); var expectedTotalShares = expectedDelegatee.TotalShares; var expectedValidatorShare = expectedRepository.GetBond(expectedDelegatee, validatorKey.Address).Share; var expectedDelegatorShares = delegatorKeys - .Select(item => expectedRepository.GetBond(expectedDelegatee, item.Address).Share) + .Select(item => expectedRepository.GetBond( + expectedDelegatee, + (Address)expectedGuildRepository.GetJoinedGuild(new AgentAddress(item.Address))!).Share) .ToArray(); var expectedProposerReward - = CalculatePropserReward(totalReward) + CalculateBonusPropserReward(1, 1, totalReward); - var expectedReward = totalReward - expectedProposerReward; + = CalculatePropserReward(totalGuildReward) + CalculateBonusPropserReward(1, 1, totalGuildReward); + var expectedGuildReward = totalGuildReward - expectedProposerReward; var expectedCommission = CalculateCommission( - expectedReward, expectedDelegatee.CommissionPercentage); - var expectedClaim = expectedReward - expectedCommission; - var expectedValidatorClaim = CalculateClaim( - expectedValidatorShare, expectedTotalShares, expectedClaim); - var expectedDelegatorClaims = CreateArray( + expectedGuildReward, expectedDelegatee.CommissionPercentage); + var expectedGuildClaim = expectedGuildReward - expectedCommission; + var expectedValidatorGuildClaim = CalculateClaim( + expectedValidatorShare, expectedTotalShares, expectedGuildClaim); + var expectedDelegatorGuildClaims = CreateArray( length, - i => CalculateClaim(expectedDelegatorShares[i], expectedTotalShares, expectedClaim)); + i => CalculateClaim(expectedDelegatorShares[i], expectedTotalShares, expectedGuildClaim)); var expectedValidatorBalance = validatorBalance; expectedValidatorBalance -= validatorCash; - var expectedValidatorReward = expectedProposerReward; - expectedValidatorReward += expectedCommission; - expectedValidatorReward += expectedValidatorClaim; + var expectedValidatorGuildReward = expectedProposerReward; + expectedValidatorGuildReward += expectedCommission; + expectedValidatorGuildReward += expectedValidatorGuildClaim; var expectedDelegatorBalances = CreateArray(length, i => DelegationCurrency * 0); + var expectedRemainGuildReward = totalGuildReward; + expectedRemainGuildReward -= expectedProposerReward; + expectedRemainGuildReward -= expectedCommission; + expectedRemainGuildReward -= expectedValidatorGuildClaim; + for (var i = 0; i < length; i++) + { + expectedRemainGuildReward -= expectedDelegatorGuildClaims[i]; + } + + var expectedValidatorReward = totalReward.DivRem(10).Quotient; + var expectedValidatorClaim = expectedValidatorReward; + var expectedTotalGuildRewards = (totalReward - expectedValidatorReward).DivRem(10).Quotient; + var expectedTotalGuildParticipantRewards = totalReward - expectedValidatorReward - expectedTotalGuildRewards; + var expectedGuildClaims = CreateArray( + length, + i => CalculateClaim(expectedDelegatorShares[i], expectedTotalShares, expectedTotalGuildRewards)); + expectedValidatorClaim += CalculateClaim(expectedValidatorShare, expectedTotalShares, expectedTotalGuildRewards); + var expectedGuildParticipantClaims = CreateArray( + length, + i => CalculateClaim(expectedDelegatorShares[i], expectedTotalShares, expectedTotalGuildParticipantRewards)); + expectedValidatorClaim += CalculateClaim(expectedValidatorShare, expectedTotalShares, expectedTotalGuildParticipantRewards); var expectedRemainReward = totalReward; - expectedRemainReward -= expectedProposerReward; - expectedRemainReward -= expectedCommission; expectedRemainReward -= expectedValidatorClaim; for (var i = 0; i < length; i++) { - expectedRemainReward -= expectedDelegatorClaims[i]; + expectedRemainReward -= expectedGuildClaims[i]; + } + + for (var i = 0; i < length; i++) + { + expectedRemainReward -= expectedGuildParticipantClaims[i]; } // When @@ -278,7 +317,20 @@ var expectedProposerReward Signer = validatorKey.Address, LastCommit = lastCommit, }; - world = new ClaimRewardValidatorSelf().Execute(actionContext); + world = new ClaimValidatorRewardSelf().Execute(actionContext); + + for (var i = 0; i < length; i++) + { + actionContext = new ActionContext + { + PreviousState = world, + BlockIndex = height++, + Signer = delegatorKeys[i].Address, + LastCommit = lastCommit, + }; + world = new ClaimGuildReward().Execute(actionContext); + } + for (var i = 0; i < length; i++) { actionContext = new ActionContext @@ -288,36 +340,56 @@ var expectedProposerReward Signer = delegatorKeys[i].Address, LastCommit = lastCommit, }; - world = new ClaimRewardValidator().Execute(actionContext); + world = new ClaimReward().Execute(actionContext); } // Then var validatorRepository = new ValidatorRepository(world, actionContext); var guildRepository = new GuildRepository(world, actionContext); var delegatee = validatorRepository.GetValidatorDelegatee(validatorKey.Address); - var actualRemainReward = world.GetBalance(delegatee.RewardRemainderPoolAddress, RewardCurrency); + var actualRemainGuildReward = world.GetBalance(delegatee.RewardRemainderPoolAddress, GuildAllocateRewardCurrency); var actualValidatorBalance = world.GetBalance(StakeState.DeriveAddress(validatorKey.Address), DelegationCurrency); - var actualValidatorReward = world.GetBalance(validatorKey.Address, RewardCurrency); + var actualValidatorGuildReward = world.GetBalance(validatorKey.Address, GuildAllocateRewardCurrency); var actualDelegatorBalances = delegatorKeys .Select(item => world.GetBalance(item.Address, DelegationCurrency)) .ToArray(); - var actualDelegatorRewards = delegatorKeys + var actualDelegatorGuildRewards = delegatorKeys .Select(item => world.GetBalance( guildRepository.GetJoinedGuild( new AgentAddress(item.Address)) ?? throw new Exception($"Delegator {item.Address} does not joind to guild."), - RewardCurrency)) + GuildAllocateRewardCurrency)) .ToArray(); - Assert.Equal(expectedRemainReward, actualRemainReward); + + var actualRemainReward = world.GetBalance(delegatee.RewardRemainderPoolAddress, AllocateRewardCurrency); + var actualValidatorReward = world.GetBalance(validatorKey.Address, AllocateRewardCurrency); + var actualGuildRewards = delegatorKeys + .Select(item => world.GetBalance( + guildRepository.GetJoinedGuild( + new AgentAddress(item.Address)) + ?? throw new Exception($"Delegator {item.Address} does not joind to guild."), + AllocateRewardCurrency)) + .ToArray(); + var actualGuildParticipantRewards = delegatorKeys + .Select(item => world.GetBalance( + item.Address, + AllocateRewardCurrency)) + .ToArray(); + + Assert.Equal(expectedRemainGuildReward, actualRemainGuildReward); Assert.Equal(expectedValidatorBalance, actualValidatorBalance); Assert.Equal(expectedDelegatorBalances, actualDelegatorBalances); - Assert.Equal(expectedValidatorReward, actualValidatorReward); - Assert.Equal(expectedDelegatorClaims, actualDelegatorRewards); + Assert.Equal(expectedValidatorGuildReward, actualValidatorGuildReward); + Assert.Equal(expectedDelegatorGuildClaims, actualDelegatorGuildRewards); + Assert.Equal(expectedValidatorClaim, actualValidatorReward); + Assert.Equal(expectedGuildClaims, actualGuildRewards); + Assert.Equal(expectedGuildParticipantClaims, actualGuildParticipantRewards); + Assert.Equal(expectedRemainReward, actualRemainReward); foreach (var key in guildParticipantKeys) { Assert.Throws( - () => new ClaimRewardValidator().Execute(new ActionContext + () => new ClaimGuildReward().Execute(new ActionContext { PreviousState = world, BlockIndex = height++, @@ -347,7 +419,9 @@ private struct StaticFixture : IClaimRewardFixture { public int DelegatorLength { get; set; } - public FungibleAssetValue TotalReward { get; set; } + public FungibleAssetValue TotalGuildAllocateReward { get; set; } + + public FungibleAssetValue TotalAllocateReward { get; set; } public PrivateKey ValidatorKey { get; set; } @@ -370,7 +444,8 @@ public RandomFixture(int randomSeed) DelegatorLength = _random.Next(3, 100); GuildParticipantLength = _random.Next(1, 50); ValidatorKey = new PrivateKey(); - TotalReward = GetRandomFAV(RewardCurrency, _random); + TotalGuildAllocateReward = GetRandomFAV(GuildAllocateRewardCurrency, _random); + TotalAllocateReward = GetRandomFAV(AllocateRewardCurrency, _random); ValidatorBalance = GetRandomFAV(DelegationCurrency, _random); ValidatorCash = GetRandomCash(_random, ValidatorBalance); DelegatorInfos = CreateArray(DelegatorLength, _ => @@ -398,7 +473,9 @@ public RandomFixture(int randomSeed) public int GuildParticipantLength { get; } - public FungibleAssetValue TotalReward { get; } + public FungibleAssetValue TotalGuildAllocateReward { get; } + + public FungibleAssetValue TotalAllocateReward { get; } public PrivateKey ValidatorKey { get; } diff --git a/.Lib9c.Tests/Action/Guild/GuildTestBase.cs b/.Lib9c.Tests/Action/Guild/GuildTestBase.cs index b900ce4b9f..291c6aed39 100644 --- a/.Lib9c.Tests/Action/Guild/GuildTestBase.cs +++ b/.Lib9c.Tests/Action/Guild/GuildTestBase.cs @@ -53,7 +53,10 @@ protected static IWorld EnsureToCreateValidator( var validatorRepository = new ValidatorRepository(world, actionContext); validatorRepository.CreateValidatorDelegatee(validatorPublicKey, commissionPercentage); - return validatorRepository.World; + var guildRepository = new GuildRepository(validatorRepository); + guildRepository.CreateGuildDelegatee(validatorAddress); + + return guildRepository.World; } protected static IWorld EnsureToMakeGuild( diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs index 82a8a1444f..4189498e5f 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs @@ -21,6 +21,9 @@ public void Execute() { var guildAddress = AddressUtil.CreateGuildAddress(); var world = EnsureLegacyPlanetariumGuild(World, guildAddress); + var repo = new GuildRepository(world, new ActionContext()); + + Assert.Throws(() => repo.GetGuild(guildAddress)); var action = new MigratePlanetariumGuild(); var actionContext = new ActionContext @@ -30,7 +33,7 @@ public void Execute() }; world = action.Execute(actionContext); - var repo = new GuildRepository(world, new ActionContext()); + repo.UpdateWorld(world); var guild = repo.GetGuild(guildAddress); Assert.Throws(() => repo.GetGuildParticipant(GuildConfig.PlanetariumGuildOwner)); diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index 839d4566e8..24f0dc24b6 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -476,10 +476,11 @@ private IWorld Execute( if (guildRepository.TryGetGuildParticipant(new AgentAddress(signer), out var guildParticipant)) { var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); - var bond = guildRepository.GetBond(guild, signer); + var validator = guildRepository.GetGuildDelegatee(guild.ValidatorAddress); + var bond = guildRepository.GetBond(validator, signer); var amountNCG = _ncg * amount; var expectedGG = DelegationUtil.GetGuildCoinFromNCG(amountNCG); - var expectedShare = guild.ShareFromFAV(expectedGG); + var expectedShare = validator.ShareFromFAV(expectedGG); Assert.Equal(expectedShare, bond.Share); } diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateGuildRewardTest.cs similarity index 92% rename from .Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs rename to .Lib9c.Tests/Action/ValidatorDelegation/AllocateGuildRewardTest.cs index 6a7f41c4ad..789736d50f 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/AllocateRewardTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/AllocateGuildRewardTest.cs @@ -17,7 +17,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using Nekoyume.ValidatorDelegation; using Xunit; -public class AllocateRewardTest : ValidatorDelegationTestBase +public class AllocateGuildRewardTest : ValidatorDelegationTestBase { private interface IAllocateRewardFixture { @@ -58,10 +58,10 @@ PrivateKey GetProposerKey(List validators) [Fact] public void Serialization() { - var action = new AllocateReward(); + var action = new AllocateGuildReward(); var plainValue = action.PlainValue; - var deserialized = new AllocateReward(); + var deserialized = new AllocateGuildReward(); deserialized.LoadPlainValue(plainValue); } @@ -70,7 +70,7 @@ public void Execute() { var fixture = new StaticFixture { - TotalReward = RewardCurrency * 1000, + TotalReward = GuildAllocateRewardCurrency * 1000, ValidatorsInfos = CreateArray(4, i => new ValidatorInfo { Key = new PrivateKey(), @@ -96,7 +96,7 @@ public void Execute_Theory(int validatorCount, double totalReward) { var fixture = new StaticFixture { - TotalReward = FungibleAssetValue.Parse(RewardCurrency, $"{totalReward:R}"), + TotalReward = FungibleAssetValue.Parse(GuildAllocateRewardCurrency, $"{totalReward:R}"), ValidatorsInfos = CreateArray(validatorCount, i => new ValidatorInfo { Key = new PrivateKey(), @@ -114,7 +114,7 @@ public void Execute_WithoutReward_Throw() { var fixture = new StaticFixture { - TotalReward = RewardCurrency * 0, + TotalReward = GuildAllocateRewardCurrency * 0, ValidatorsInfos = CreateArray(4, i => new ValidatorInfo { Key = new PrivateKey(), @@ -205,7 +205,7 @@ var expectedProposerReward // When var lastCommit = new BlockCommit(height - 1, round: 0, votes[0].BlockHash, votes); - var allocateReward = new AllocateReward(); + var allocateReward = new AllocateGuildReward(); actionContext = new ActionContext { PreviousState = world, @@ -220,8 +220,8 @@ var expectedProposerReward .OfType() .Aggregate(BigInteger.Zero, (accum, next) => accum + next); var actualRepository = new ValidatorRepository(world, actionContext); - var actualAllocatedReward = RewardCurrency * 0; - var actualCommunityFund = world.GetBalance(Addresses.CommunityPool, RewardCurrency); + var actualAllocatedReward = GuildAllocateRewardCurrency * 0; + var actualCommunityFund = world.GetBalance(Addresses.CommunityPool, GuildAllocateRewardCurrency); foreach (var (vote, index) in votes.Select((v, i) => (v, i))) { if (vote.ValidatorPower is not { } validatorPower) @@ -233,14 +233,14 @@ var expectedProposerReward var actualDelegatee = actualRepository.GetValidatorDelegatee(validatorAddress); var validatorRewardAddress = actualDelegatee.CurrentLumpSumRewardsRecordAddress(); var actualDelegationBalance = world.GetBalance(validatorAddress, DelegationCurrency); - var actualCommission = world.GetBalance(validatorAddress, RewardCurrency); - var actualUnclaimedReward = world.GetBalance(validatorRewardAddress, RewardCurrency); + var actualCommission = world.GetBalance(validatorAddress, GuildAllocateRewardCurrency); + var actualUnclaimedReward = world.GetBalance(validatorRewardAddress, GuildAllocateRewardCurrency); var isProposer = vote.ValidatorPublicKey.Equals(proposerKey.PublicKey); if (vote.Flag == VoteFlag.Null) { - Assert.Equal(RewardCurrency * 0, actualCommission); - Assert.Equal(RewardCurrency * 0, actualUnclaimedReward); + Assert.Equal(GuildAllocateRewardCurrency * 0, actualCommission); + Assert.Equal(GuildAllocateRewardCurrency * 0, actualUnclaimedReward); Assert.False(isProposer); continue; } @@ -298,7 +298,7 @@ private class RandomFixture : IAllocateRewardFixture public RandomFixture(int randomSeed) { _random = new Random(randomSeed); - TotalReward = GetRandomFAV(RewardCurrency, _random); + TotalReward = GetRandomFAV(GuildAllocateRewardCurrency, _random); ValidatorsInfos = CreateArray(_random.Next(1, 200), i => { var balance = GetRandomFAV(DelegationCurrency, _random); diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs index 3a4790116c..246c1df300 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/DelegateValidatorTest.cs @@ -30,14 +30,12 @@ private interface IDelegateValidatorFixture [Fact] public void Serialization() { - var address = new PrivateKey().Address; var gold = DelegationCurrency * 10; - var action = new DelegateValidator(address, gold); + var action = new DelegateValidator(gold); var plainValue = action.PlainValue; var deserialized = new DelegateValidator(); deserialized.LoadPlainValue(plainValue); - Assert.Equal(address, deserialized.ValidatorDelegatee); Assert.Equal(gold, deserialized.FAV); } @@ -55,7 +53,7 @@ public void Execute() world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 100, height++); // When - var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorGold); + var delegateValidator = new DelegateValidator(delegatorGold); var actionContext = new ActionContext { PreviousState = world, @@ -130,7 +128,7 @@ public void Execute_WithInvalidCurrency_Throw() Signer = validatorKey.Address, BlockIndex = height++, }; - var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorDollar); + var delegateValidator = new DelegateValidator(delegatorDollar); // Then Assert.Throws(() => delegateValidator.Execute(actionContext)); @@ -156,7 +154,7 @@ public void Execute_WithInsufficientBalance_Throw() Signer = validatorKey.Address, BlockIndex = height++, }; - var delegateValidator = new DelegateValidator(validatorKey.Address, DelegationCurrency * 11); + var delegateValidator = new DelegateValidator(DelegationCurrency * 11); // Then Assert.Throws(() => delegateValidator.Execute(actionContext)); @@ -177,7 +175,7 @@ public void Execute_ToInvalidValidator_Throw() PreviousState = world, Signer = validatorKey.Address, }; - var delegateValidator = new DelegateValidator(validatorKey.Address, DelegationCurrency * 10); + var delegateValidator = new DelegateValidator(DelegationCurrency * 10); // Then Assert.Throws(() => delegateValidator.Execute(actionContext)); @@ -203,7 +201,7 @@ public void Execute_ToTombstonedValidator_Throw() PreviousState = world, Signer = validatorKey.Address, }; - var delegateValidator = new DelegateValidator(validatorKey.Address, delegatorGold); + var delegateValidator = new DelegateValidator(delegatorGold); // Then Assert.Throws(() => delegateValidator.Execute(actionContext)); @@ -228,7 +226,7 @@ private void ExecuteWithFixture(IDelegateValidatorFixture fixture) var expectedValidatorBalance = validatorBalance - validatorCash - validatorCashToDelegate; var expectedPower = validatorCash.RawValue + validatorCashToDelegate.RawValue; - var delegateValidator = new DelegateValidator(validatorKey.Address, validatorCashToDelegate); + var delegateValidator = new DelegateValidator(validatorCashToDelegate); actionContext = new ActionContext { PreviousState = world, diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs index 80d7125485..79b1840e4f 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ReleaseValidatorUnbondingsTest.cs @@ -2,9 +2,9 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System.Numerics; -using Libplanet.Action.State; using Libplanet.Crypto; using Nekoyume.Action.ValidatorDelegation; +using Nekoyume.Model.Guild; using Nekoyume.ValidatorDelegation; using Xunit; @@ -37,8 +37,8 @@ public void Execute() world = EnsureUnbondingValidator(world, validatorKey.Address, share, height); // When - var expectedRepository = new ValidatorRepository(world, actionContext); - var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedRepository = new GuildRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetGuildDelegatee(validatorKey.Address); var expectedUnbondingSet = expectedRepository.GetUnbondingSet(); var expectedReleaseCount = expectedUnbondingSet.UnbondingRefs.Count; var expectedDepositGold = expectedDelegatee.FAVFromShare(share); @@ -80,7 +80,7 @@ public void Execute_ThereIsNoUnbonding_AtEarlyHeight() world = EnsureUnbondingValidator(world, validatorKey.Address, share, height); // When - var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedRepository = new GuildRepository(world, actionContext); var expectedUnbondingSet = expectedRepository.GetUnbondingSet(); var expectedReleaseCount = expectedUnbondingSet.UnbondingRefs.Count; @@ -94,7 +94,7 @@ public void Execute_ThereIsNoUnbonding_AtEarlyHeight() world = releaseValidatorUnbondings.Execute(actionContext); // Then - var actualRepository = new ValidatorRepository(world, actionContext); + var actualRepository = new GuildRepository(world, actionContext); var actualUnbondingSet = actualRepository.GetUnbondingSet(); var actualReleaseCount = actualUnbondingSet.UnbondingRefs.Count; diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs index 71e7840aca..24dc21628d 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/UndelegateValidatorTest.cs @@ -2,9 +2,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System; using System.Collections.Generic; -using System.Linq; using System.Numerics; -using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Action; @@ -32,14 +30,12 @@ private interface IUndelegateValidatorFixture [Fact] public void Serialization() { - var address = new PrivateKey().Address; var share = BigInteger.One; - var action = new UndelegateValidator(address, share); + var action = new UndelegateValidator(share); var plainValue = action.PlainValue; var deserialized = new UndelegateValidator(); deserialized.LoadPlainValue(plainValue); - Assert.Equal(address, deserialized.ValidatorDelegatee); Assert.Equal(share, deserialized.Share); } @@ -59,7 +55,7 @@ public void Execute() var expectedRepository = new ValidatorRepository(world, actionContext); var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); var expectedBond = expectedRepository.GetBond(expectedDelegatee, validatorKey.Address); - var undelegateValidator = new UndelegateValidator(validatorKey.Address, expectedBond.Share); + var undelegateValidator = new UndelegateValidator(expectedBond.Share); actionContext = new ActionContext { PreviousState = world, @@ -134,7 +130,7 @@ public void Execute_FromInvalidValidtor_Throw() Signer = validatorKey.Address, BlockIndex = height++, }; - var undelegateValidator = new UndelegateValidator(new PrivateKey().Address, 10); + var undelegateValidator = new UndelegateValidator(10); // Then Assert.Throws( @@ -161,7 +157,7 @@ public void Execute_WithNotPositiveShare_Throw(long share) Signer = validatorKey.Address, BlockIndex = height++, }; - var undelegateValidator = new UndelegateValidator(validatorKey.Address, share); + var undelegateValidator = new UndelegateValidator(share); // Then Assert.Throws( @@ -190,7 +186,7 @@ public void Execute_FromJailedValidator() var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); var expectedBond = expectedRepository.GetBond(expectedDelegatee, validatorKey.Address); - var undelegateValidator = new UndelegateValidator(validatorKey.Address, 10); + var undelegateValidator = new UndelegateValidator(10); world = undelegateValidator.Execute(actionContext); // Then @@ -223,7 +219,7 @@ public void Execute_FromTombstonedValidator() var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); var expectedBond = expectedRepository.GetBond(expectedDelegatee, validatorKey.Address); - var undelegateValidator = new UndelegateValidator(validatorKey.Address, 10); + var undelegateValidator = new UndelegateValidator(10); world = undelegateValidator.Execute(actionContext); // Then @@ -252,7 +248,7 @@ public void Execute_JailsValidatorWhenUndelegationCausesLowDelegation() var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); var expectedJailed = expectedDelegatee.Jailed; - var undelegateValidator = new UndelegateValidator(validatorKey.Address, 10); + var undelegateValidator = new UndelegateValidator(10); actionContext = new ActionContext { PreviousState = world, @@ -290,8 +286,7 @@ private void ExecuteWithFixture(IUndelegateValidatorFixture fixture) var expectedValidatorPower = expectedValidator.Power - fixture.ValidatorInfo.SubtractShare; var subtractShare = fixture.ValidatorInfo.SubtractShare; - var undelegateValidator = new UndelegateValidator( - validatorKey.Address, subtractShare); + var undelegateValidator = new UndelegateValidator(subtractShare); actionContext = new ActionContext { PreviousState = world, diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index 2a6ba8aa24..dd0a135545 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -6,7 +6,6 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using System.Collections.Immutable; using System.Linq; using System.Numerics; -using Lib9c.Tests.Util; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Mocks; @@ -28,9 +27,15 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; public class ValidatorDelegationTestBase { - protected static readonly Currency GoldCurrency = Currency.Uncapped("NCG", 2, null); - protected static readonly Currency DelegationCurrency = Currencies.GuildGold; - protected static readonly Currency RewardCurrency = Currencies.Mead; + protected static readonly Currency NCG = Currency.Uncapped("NCG", 2, null); + protected static readonly Currency GuildGold = Currencies.GuildGold; + protected static readonly Currency Mead = Currencies.Mead; + protected static readonly Currency GoldCurrency = NCG; + protected static readonly Currency DelegationCurrency = GuildGold; + protected static readonly Currency[] GuildRewardCurrencies = new Currency[] { NCG, Mead }; + protected static readonly Currency RewardCurrency = NCG; + protected static readonly Currency GuildAllocateRewardCurrency = Mead; + protected static readonly Currency AllocateRewardCurrency = NCG; protected static readonly Currency Dollar = Currency.Uncapped("dollar", 2, null); private static readonly int _maximumIntegerLength = 15; @@ -153,8 +158,7 @@ protected static IWorld EnsureUnbondingValidator( BlockIndex = blockHeight, Signer = validatorAddress, }; - var undelegateValidator = new UndelegateValidator( - validatorAddress, share); + var undelegateValidator = new UndelegateValidator(share); return undelegateValidator.Execute(actionContext); } @@ -347,7 +351,11 @@ protected static IWorld EnsureUnjailedValidator( } protected static IWorld EnsureRewardAllocatedValidator( - IWorld world, PrivateKey validatorKey, FungibleAssetValue reward, ref long blockHeight) + IWorld world, + PrivateKey validatorKey, + FungibleAssetValue guildReward, + FungibleAssetValue reward, + ref long blockHeight) { if (blockHeight < 0) { @@ -371,11 +379,21 @@ protected static IWorld EnsureRewardAllocatedValidator( Signer = validatorKey.Address, LastCommit = lastCommit2, }; - world = world.MintAsset(actionContext2, GoldCurrencyState.Address, reward); - world = world.TransferAsset( - actionContext2, GoldCurrencyState.Address, Addresses.RewardPool, reward); + + world = world.MintAsset(actionContext2, Addresses.RewardPool, guildReward); + + actionContext2 = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight, + Signer = validatorKey.Address, + LastCommit = lastCommit2, + }; + + world = world.MintAsset(actionContext2, Addresses.RewardPool, reward); var lastCommit3 = CreateLastCommit(validatorKey, blockHeight - 1); + var actionContext3 = new ActionContext { PreviousState = world, @@ -383,6 +401,17 @@ protected static IWorld EnsureRewardAllocatedValidator( Signer = validatorKey.Address, LastCommit = lastCommit3, }; + + world = new AllocateGuildReward().Execute(actionContext3); + + actionContext3 = new ActionContext + { + PreviousState = world, + BlockIndex = blockHeight, + Signer = validatorKey.Address, + LastCommit = lastCommit3, + }; + world = new AllocateReward().Execute(actionContext3); return world; diff --git a/.Lib9c.Tests/Delegation/DelegateeTest.cs b/.Lib9c.Tests/Delegation/DelegateeTest.cs index 180dee62d9..10dae9b102 100644 --- a/.Lib9c.Tests/Delegation/DelegateeTest.cs +++ b/.Lib9c.Tests/Delegation/DelegateeTest.cs @@ -22,8 +22,11 @@ public void Ctor() var address = new Address("0xe8327129891e1A0B2E3F0bfa295777912295942a"); var delegatee = new TestDelegatee(address, _fixture.TestRepository.DelegateeAccountAddress, _fixture.TestRepository); Assert.Equal(address, delegatee.Address); - Assert.Equal(DelegationFixture.TestCurrency, delegatee.DelegationCurrency); + Assert.Equal(DelegationFixture.TestDelegationCurrency, delegatee.DelegationCurrency); + Assert.Equal(new Currency[] { DelegationFixture.TestRewardCurrency }, delegatee.RewardCurrencies); Assert.Equal(3, delegatee.UnbondingPeriod); + Assert.Equal(5, delegatee.MaxUnbondLockInEntries); + Assert.Equal(5, delegatee.MaxRebondGraceEntries); } [Fact] @@ -218,16 +221,28 @@ public void ClearRemainderRewards() var bondedShare1 = testDelegatee.Bond(testDelegator1, bonding1, 10L); var bondedShare2 = testDelegatee.Bond(testDelegator2, bonding2, 10L); - repo.MintAsset(testDelegatee.RewardPoolAddress, testDelegatee.RewardCurrency * 10); + foreach (var currency in testDelegatee.RewardCurrencies) + { + repo.MintAsset(testDelegatee.RewardPoolAddress, currency * 10); + } + testDelegatee.CollectRewards(11L); testDelegatee.DistributeReward(testDelegator1, 11L); - var remainder = repo.GetBalance(DelegationFixture.FixedPoolAddress, testDelegatee.RewardCurrency); - Assert.Equal(testDelegatee.RewardCurrency * 0, remainder); + + foreach (var currency in testDelegatee.RewardCurrencies) + { + var remainder = repo.GetBalance(DelegationFixture.FixedPoolAddress, currency); + Assert.Equal(currency * 0, remainder); + } testDelegatee.DistributeReward(testDelegator2, 11L); - remainder = repo.GetBalance(DelegationFixture.FixedPoolAddress, testDelegatee.RewardCurrency); - Assert.Equal(new FungibleAssetValue(testDelegatee.RewardCurrency, 0, 1), remainder); + + foreach (var currency in testDelegatee.RewardCurrencies) + { + var remainder = repo.GetBalance(DelegationFixture.FixedPoolAddress, currency); + Assert.Equal(new FungibleAssetValue(currency, 0, 1), remainder); + } } [Fact] diff --git a/.Lib9c.Tests/Delegation/DelegationFixture.cs b/.Lib9c.Tests/Delegation/DelegationFixture.cs index 4b6a94241f..961775d217 100644 --- a/.Lib9c.Tests/Delegation/DelegationFixture.cs +++ b/.Lib9c.Tests/Delegation/DelegationFixture.cs @@ -1,5 +1,7 @@ namespace Lib9c.Tests.Delegation { + using System.Collections.Generic; + using System.Linq; using Lib9c.Tests.Action; using Libplanet.Action.State; using Libplanet.Crypto; @@ -11,7 +13,8 @@ namespace Lib9c.Tests.Delegation public class DelegationFixture { - public static readonly Currency TestCurrency = Currency.Uncapped("test-del", 5, null); + public static readonly Currency TestDelegationCurrency = Currency.Uncapped("test-del", 5, null); + public static readonly Currency TestRewardCurrency = Currency.Uncapped("test-reward", 5, null); public static readonly Address FixedPoolAddress = new Address("0x75b21EbC56e5dAc817A1128Fb05d45853183117c"); public DelegationFixture() @@ -61,13 +64,17 @@ public DelegationFixture() public DummyDelegator DummyDelegator1 { get; } - public static FungibleAssetValue TotalRewardsOfRecords(IDelegatee delegatee, IDelegationRepository repo) + public static FungibleAssetValue[] TotalRewardsOfRecords(IDelegatee delegatee, IDelegationRepository repo) { - var reward = delegatee.RewardCurrency * 0; + var rewards = delegatee.RewardCurrencies.Select(r => r * 0).ToArray(); var record = repo.GetCurrentLumpSumRewardsRecord(delegatee); + while (true) { - reward += repo.World.GetBalance(record.Address, delegatee.RewardCurrency); + foreach (var reward in rewards.Select((v, i) => (v, i))) + { + rewards[reward.i] += repo.World.GetBalance(record.Address, reward.v.Currency); + } if (record.LastStartHeight is null) { @@ -77,7 +84,7 @@ public static FungibleAssetValue TotalRewardsOfRecords(IDelegatee delegatee, IDe record = repo.GetLumpSumRewardsRecord(delegatee, record.LastStartHeight.Value); } - return reward; + return rewards; } } } diff --git a/.Lib9c.Tests/Delegation/DelegatorTest.cs b/.Lib9c.Tests/Delegation/DelegatorTest.cs index 1f8cab0c87..c599e2fb25 100644 --- a/.Lib9c.Tests/Delegation/DelegatorTest.cs +++ b/.Lib9c.Tests/Delegation/DelegatorTest.cs @@ -297,8 +297,12 @@ public void RewardOnDelegate() repo.MintAsset(delegator1.Address, delegatorInitialBalance); repo.MintAsset(delegator2.Address, delegatorInitialBalance); - var reward = delegatee.DelegationCurrency * 100; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + var rewards = delegatee.RewardCurrencies.Select(r => r * 100); + foreach (var reward in rewards) + { + repo.MintAsset(delegatee.RewardPoolAddress, reward); + } + // EndBlock after delegatee's reward delegatee.CollectRewards(10L); @@ -318,28 +322,42 @@ public void RewardOnDelegate() var totalShares = delegatee.TotalShares; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + foreach (var reward in rewards) + { + repo.MintAsset(delegatee.RewardPoolAddress, reward); + } + // EndBlock after delegatee's reward delegatee.CollectRewards(10L); delegatingFAV1 = delegatee.DelegationCurrency * 10; delegator1.Delegate(delegatee, delegatingFAV1, 11L); delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); + delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); + var delegator1RewardBalances = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegator1.Address, c)); var collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); - var reward1 = (reward * share1).DivRem(totalShares, out _); - Assert.Equal(delegatorInitialBalance - delegatingFAV1 * 2 + reward1, delegator1Balance); + var rewards1 = rewards.Select(r => (r * share1).DivRem(totalShares, out _)); + Assert.Equal(delegatorInitialBalance - delegatingFAV1 * 2, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); - Assert.Equal(reward * 2 - reward1, collectedRewards); + Assert.Equal(rewards1, delegator1RewardBalances); + Assert.Equal(rewards.Zip(rewards1, (f, s) => f * 2 - s), collectedRewards); delegator2.Delegate(delegatee, delegatingFAV2, 11L); delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); + delegator1RewardBalances = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegator1.Address, c)); + var delegator2RewardBalances = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegator2.Address, c)); collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); - var reward2 = (reward * share2).DivRem(totalShares, out _); - Assert.Equal(delegatorInitialBalance - delegatingFAV1 * 2 + reward1, delegator1Balance); - Assert.Equal(delegatorInitialBalance - delegatingFAV2 * 2 + reward2, delegator2Balance); - Assert.Equal(delegatee.RewardCurrency * 0, collectedRewards); + var rewards2 = rewards.Select(r => (r * share2).DivRem(totalShares, out _)); + Assert.Equal(delegatorInitialBalance - delegatingFAV1 * 2, delegator1Balance); + Assert.Equal(delegatorInitialBalance - delegatingFAV2 * 2, delegator2Balance); + Assert.Equal(rewards1, delegator1RewardBalances); + Assert.Equal(rewards2, delegator2RewardBalances); + Assert.Equal(delegatee.RewardCurrencies.Select(c => c * 0), collectedRewards); } [Fact] @@ -353,8 +371,12 @@ public void RewardOnUndelegate() repo.MintAsset(delegator1.Address, delegatorInitialBalance); repo.MintAsset(delegator2.Address, delegatorInitialBalance); - var reward = delegatee.RewardCurrency * 100; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + var rewards = delegatee.RewardCurrencies.Select(r => r * 100); + foreach (var reward in rewards) + { + repo.MintAsset(delegatee.RewardPoolAddress, reward); + } + // EndBlock after delegatee's reward delegatee.CollectRewards(10L); @@ -374,29 +396,42 @@ public void RewardOnUndelegate() var totalShares = delegatee.TotalShares; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + foreach (var reward in rewards) + { + repo.MintAsset(delegatee.RewardPoolAddress, reward); + } + // EndBlock after delegatee's reward delegatee.CollectRewards(10L); var shareToUndelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; delegator1.Undelegate(delegatee, shareToUndelegate, 11L); delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); + var delegator1RewardBalances = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegator1.Address, c)); var collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); - var reward1 = (reward * share1).DivRem(totalShares, out _); - Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); + var rewards1 = rewards.Select(r => (r * share1).DivRem(totalShares, out _)); + Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); - Assert.Equal(reward * 2 - reward1, collectedRewards); + Assert.Equal(rewards1, delegator1RewardBalances); + Assert.Equal(rewards.Zip(rewards1, (f, s) => f * 2 - s), collectedRewards); shareToUndelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.Undelegate(delegatee, shareToUndelegate, 11L); delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); + delegator1RewardBalances = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegator1.Address, c)); + var delegator2RewardBalances = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegator2.Address, c)); collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); - var reward2 = (reward * share2).DivRem(totalShares, out _); - Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); - Assert.Equal(delegatorInitialBalance - delegatingFAV2 + reward2, delegator2Balance); - Assert.Equal(delegatee.RewardCurrency * 0, collectedRewards); + var rewards2 = rewards.Select(r => (r * share2).DivRem(totalShares, out _)); + Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); + Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); + Assert.Equal(rewards1, delegator1RewardBalances); + Assert.Equal(rewards2, delegator2RewardBalances); + Assert.Equal(delegatee.RewardCurrencies.Select(c => c * 0), collectedRewards); } [Fact] @@ -411,8 +446,12 @@ public void RewardOnRedelegate() repo.MintAsset(delegator1.Address, delegatorInitialBalance); repo.MintAsset(delegator2.Address, delegatorInitialBalance); - var reward = delegatee.RewardCurrency * 100; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + var rewards = delegatee.RewardCurrencies.Select(r => r * 100); + foreach (var reward in rewards) + { + repo.MintAsset(delegatee.RewardPoolAddress, reward); + } + // EndBlock after delegatee's reward delegatee.CollectRewards(10L); @@ -432,29 +471,42 @@ public void RewardOnRedelegate() var totalShares = delegatee.TotalShares; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + foreach (var reward in rewards) + { + repo.MintAsset(delegatee.RewardPoolAddress, reward); + } + // EndBlock after delegatee's reward delegatee.CollectRewards(10L); var shareToRedelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; delegator1.Redelegate(delegatee, dstDelegatee, shareToRedelegate, 11L); delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); + var delegator1RewardBalances = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegator1.Address, c)); var collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); - var reward1 = (reward * share1).DivRem(totalShares, out _); - Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); + var rewards1 = rewards.Select(r => (r * share1).DivRem(totalShares, out _)); + Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); - Assert.Equal(reward * 2 - reward1, collectedRewards); + Assert.Equal(rewards1, delegator1RewardBalances); + Assert.Equal(rewards.Zip(rewards1, (f, s) => f * 2 - s), collectedRewards); shareToRedelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.Redelegate(delegatee, dstDelegatee, shareToRedelegate, 11L); delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); + delegator1RewardBalances = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegator1.Address, c)); + var delegator2RewardBalances = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegator2.Address, c)); collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); - var reward2 = (reward * share2).DivRem(totalShares, out _); - Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); - Assert.Equal(delegatorInitialBalance - delegatingFAV2 + reward2, delegator2Balance); - Assert.Equal(delegatee.RewardCurrency * 0, collectedRewards); + var rewards2 = rewards.Select(r => (r * share2).DivRem(totalShares, out _)); + Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); + Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); + Assert.Equal(rewards1, delegator1RewardBalances); + Assert.Equal(rewards2, delegator2RewardBalances); + Assert.Equal(delegatee.RewardCurrencies.Select(c => c * 0), collectedRewards); } [Fact] @@ -469,8 +521,12 @@ public void RewardOnClaim() repo.MintAsset(delegator1.Address, delegatorInitialBalance); repo.MintAsset(delegator2.Address, delegatorInitialBalance); - var reward = delegatee.DelegationCurrency * 100; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + var rewards = delegatee.RewardCurrencies.Select(r => r * 100); + foreach (var reward in rewards) + { + repo.MintAsset(delegatee.RewardPoolAddress, reward); + } + // EndBlock after delegatee's reward delegatee.CollectRewards(10L); @@ -490,29 +546,42 @@ public void RewardOnClaim() var totalShares = delegatee.TotalShares; - repo.MintAsset(delegatee.RewardPoolAddress, reward); + foreach (var reward in rewards) + { + repo.MintAsset(delegatee.RewardPoolAddress, reward); + } + // EndBlock after delegatee's reward delegatee.CollectRewards(10L); var shareToRedelegate = repo.GetBond(delegatee, delegator1.Address).Share / 3; delegator1.ClaimReward(delegatee, 11L); delegator1Balance = repo.World.GetBalance(delegator1.Address, delegatee.DelegationCurrency); + var delegator1RewardBalances = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegator1.Address, c)); var collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); - var reward1 = (reward * share1).DivRem(totalShares, out _); - Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); + var rewards1 = rewards.Select(r => (r * share1).DivRem(totalShares, out _)); + Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); - Assert.Equal(reward * 2 - reward1, collectedRewards); + Assert.Equal(rewards1, delegator1RewardBalances); + Assert.Equal(rewards.Zip(rewards1, (f, s) => f * 2 - s), collectedRewards); shareToRedelegate = repo.GetBond(delegatee, delegator2.Address).Share / 2; delegator2.ClaimReward(delegatee, 11L); delegator2Balance = repo.World.GetBalance(delegator2.Address, delegatee.DelegationCurrency); + delegator1RewardBalances = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegator1.Address, c)); + var delegator2RewardBalances = delegatee.RewardCurrencies.Select( + c => repo.World.GetBalance(delegator2.Address, c)); collectedRewards = DelegationFixture.TotalRewardsOfRecords(delegatee, repo); - var reward2 = (reward * share2).DivRem(totalShares, out _); - Assert.Equal(delegatorInitialBalance - delegatingFAV1 + reward1, delegator1Balance); - Assert.Equal(delegatorInitialBalance - delegatingFAV2 + reward2, delegator2Balance); - Assert.Equal(delegatee.RewardCurrency * 0, collectedRewards); + var rewards2 = rewards.Select(r => (r * share2).DivRem(totalShares, out _)); + Assert.Equal(delegatorInitialBalance - delegatingFAV1, delegator1Balance); + Assert.Equal(delegatorInitialBalance - delegatingFAV2, delegator2Balance); + Assert.Equal(rewards1, delegator1RewardBalances); + Assert.Equal(rewards2, delegator2RewardBalances); + Assert.Equal(delegatee.RewardCurrencies.Select(c => c * 0), collectedRewards); } } } diff --git a/.Lib9c.Tests/Delegation/DummyDelegatee.cs b/.Lib9c.Tests/Delegation/DummyDelegatee.cs index b95a5e9b92..1ffaa83a82 100644 --- a/.Lib9c.Tests/Delegation/DummyDelegatee.cs +++ b/.Lib9c.Tests/Delegation/DummyDelegatee.cs @@ -1,5 +1,6 @@ using Lib9c.Tests.Delegation; using Libplanet.Crypto; +using Libplanet.Types.Assets; using Nekoyume.Delegation; public sealed class DummyDelegatee : Delegatee @@ -13,8 +14,8 @@ public DummyDelegatee(Address address, Address accountAddress, DummyRepository r : base( address, accountAddress, - DelegationFixture.TestCurrency, - DelegationFixture.TestCurrency, + DelegationFixture.TestDelegationCurrency, + new Currency[] { DelegationFixture.TestRewardCurrency }, DelegationAddress.DelegationPoolAddress(address, accountAddress), DelegationAddress.RewardPoolAddress(address, accountAddress), DelegationFixture.FixedPoolAddress, diff --git a/.Lib9c.Tests/Delegation/TestDelegatee.cs b/.Lib9c.Tests/Delegation/TestDelegatee.cs index 4087644a5b..f1cfc67963 100644 --- a/.Lib9c.Tests/Delegation/TestDelegatee.cs +++ b/.Lib9c.Tests/Delegation/TestDelegatee.cs @@ -1,6 +1,7 @@ namespace Lib9c.Tests.Delegation { using Libplanet.Crypto; + using Libplanet.Types.Assets; using Nekoyume.Delegation; public sealed class TestDelegatee : Delegatee @@ -14,8 +15,8 @@ public TestDelegatee(Address address, Address accountAddress, TestRepository rep : base( address, accountAddress, - DelegationFixture.TestCurrency, - DelegationFixture.TestCurrency, + DelegationFixture.TestDelegationCurrency, + new Currency[] { DelegationFixture.TestRewardCurrency }, DelegationAddress.DelegationPoolAddress(address, accountAddress), DelegationAddress.RewardPoolAddress(address, accountAddress), DelegationFixture.FixedPoolAddress, diff --git a/.Lib9c.Tests/Model/Guild/GuildTest.cs b/.Lib9c.Tests/Model/Guild/GuildTest.cs index 769034c41b..890b1a60b0 100644 --- a/.Lib9c.Tests/Model/Guild/GuildTest.cs +++ b/.Lib9c.Tests/Model/Guild/GuildTest.cs @@ -30,7 +30,6 @@ public Task Snapshot() AddressUtil.CreateGuildAddress(), new AgentAddress("0xd928ae87311dead490c986c24cc23c37eff892f2"), validatorAddress, - Currency.Legacy("NCG", 2, null), repository); return Verifier.Verify(guild.Bencoded); @@ -47,7 +46,6 @@ public void Serialization() guildAddress, AddressUtil.CreateAgentAddress(), validatorAddress, - Currency.Legacy("NCG", 2, null), repository); repository.SetGuild(guild); var newGuild = new Nekoyume.Model.Guild.Guild( diff --git a/.Lib9c.Tests/Policy/BlockPolicyTest.cs b/.Lib9c.Tests/Policy/BlockPolicyTest.cs index 3dd2acc2f8..43e0da19a8 100644 --- a/.Lib9c.Tests/Policy/BlockPolicyTest.cs +++ b/.Lib9c.Tests/Policy/BlockPolicyTest.cs @@ -347,7 +347,7 @@ public void EarnMiningMeadWhenSuccessMining() // Total 10 + 0.05 + 0.2 + 0.475 = 10.725 blockChain.Append(block, commit); - var rewardCurrency = ValidatorDelegatee.ValidatorRewardCurrency; + var rewardCurrency = Currencies.Mead; var actualBalance = blockChain .GetNextWorldState() .GetBalance(adminAddress, rewardCurrency); @@ -361,7 +361,7 @@ public void EarnMiningMeadWhenSuccessMining() // After claimed, mead have to be used? blockChain.MakeTransaction( adminPrivateKey, - new ActionBase[] { new ClaimRewardValidatorSelf(), }, + new ActionBase[] { new ClaimValidatorRewardSelf(), }, gasLimit: 1, maxGasPrice: Currencies.Mead * 1 ); From d2ac0cff41f1ae0a147c3068f0b58192d2e9285e Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 13 Nov 2024 22:27:02 +0900 Subject: [PATCH 148/206] fix: Fix undeterministic ienumerables --- Lib9c/Delegation/Delegatee.cs | 4 ++-- Lib9c/Delegation/DelegateeMetadata.cs | 6 +++--- Lib9c/Delegation/IDelegatee.cs | 2 +- Lib9c/Delegation/IDelegateeMetadata.cs | 2 +- Lib9c/Delegation/LumpSumRewardsRecord.cs | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index f72ccdf791..e18ae6d3e8 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -75,7 +75,7 @@ private Delegatee(DelegateeMetadata metadata, IDelegationRepository repository) public Currency DelegationCurrency => Metadata.DelegationCurrency; - public ImmutableHashSet RewardCurrencies => Metadata.RewardCurrencies; + public ImmutableSortedSet RewardCurrencies => Metadata.RewardCurrencies; public Address DelegationPoolAddress => Metadata.DelegationPoolAddress; @@ -455,7 +455,7 @@ record = Repository.GetLumpSumRewardsRecord(this, lastStartHeight) private void TransferReward(T delegator, BigInteger share, LumpSumRewardsRecord record) { - ImmutableDictionary reward = record.RewardsDuringPeriod(share); + ImmutableSortedDictionary reward = record.RewardsDuringPeriod(share); foreach (var rewardEach in reward) { if (rewardEach.Value.Sign > 0) diff --git a/Lib9c/Delegation/DelegateeMetadata.cs b/Lib9c/Delegation/DelegateeMetadata.cs index 69527635cb..d61a9f0354 100644 --- a/Lib9c/Delegation/DelegateeMetadata.cs +++ b/Lib9c/Delegation/DelegateeMetadata.cs @@ -128,7 +128,7 @@ private DelegateeMetadata( DelegateeAddress = delegateeAddress; DelegateeAccountAddress = delegateeAccountAddress; DelegationCurrency = delegationCurrency; - RewardCurrencies = rewardCurrencies.ToImmutableHashSet(); + RewardCurrencies = rewardCurrencies.ToImmutableSortedSet(_currencyComparer); DelegationPoolAddress = delegationPoolAddress; RewardPoolAddress = rewardPoolAddress; RewardRemainderPoolAddress = rewardRemainderPoolAddress; @@ -156,7 +156,7 @@ public Address Address public Currency DelegationCurrency { get; } - public ImmutableHashSet RewardCurrencies { get; } + public ImmutableSortedSet RewardCurrencies { get; } public Address DelegationPoolAddress { get; internal set; } @@ -189,7 +189,7 @@ public Address Address // TODO : Better serialization public List Bencoded => List.Empty .Add(DelegationCurrency.Serialize()) - .Add(new List(RewardCurrencies.OrderBy(c => c, _currencyComparer).Select(c => c.Serialize()))) + .Add(new List(RewardCurrencies.Select(c => c.Serialize()))) .Add(DelegationPoolAddress.Bencoded) .Add(RewardPoolAddress.Bencoded) .Add(RewardRemainderPoolAddress.Bencoded) diff --git a/Lib9c/Delegation/IDelegatee.cs b/Lib9c/Delegation/IDelegatee.cs index d39f05cb82..458214a81b 100644 --- a/Lib9c/Delegation/IDelegatee.cs +++ b/Lib9c/Delegation/IDelegatee.cs @@ -15,7 +15,7 @@ public interface IDelegatee Currency DelegationCurrency { get; } - ImmutableHashSet RewardCurrencies { get; } + ImmutableSortedSet RewardCurrencies { get; } Address DelegationPoolAddress { get; } diff --git a/Lib9c/Delegation/IDelegateeMetadata.cs b/Lib9c/Delegation/IDelegateeMetadata.cs index 52ecf054d7..8a135ef541 100644 --- a/Lib9c/Delegation/IDelegateeMetadata.cs +++ b/Lib9c/Delegation/IDelegateeMetadata.cs @@ -19,7 +19,7 @@ public interface IDelegateeMetadata : IBencodable Currency DelegationCurrency { get; } - ImmutableHashSet RewardCurrencies { get; } + ImmutableSortedSet RewardCurrencies { get; } Address DelegationPoolAddress { get; } diff --git a/Lib9c/Delegation/LumpSumRewardsRecord.cs b/Lib9c/Delegation/LumpSumRewardsRecord.cs index dc4c04b4bf..e2e79b3e4b 100644 --- a/Lib9c/Delegation/LumpSumRewardsRecord.cs +++ b/Lib9c/Delegation/LumpSumRewardsRecord.cs @@ -169,9 +169,9 @@ public LumpSumRewardsRecord RemoveDelegator(Address delegator) LumpSumRewards, LastStartHeight); - public ImmutableDictionary RewardsDuringPeriod(BigInteger share) + public ImmutableSortedDictionary RewardsDuringPeriod(BigInteger share) => LumpSumRewards.Keys.Select(k => RewardsDuringPeriod(share, k)) - .ToImmutableDictionary(f => f.Currency); + .ToImmutableSortedDictionary(f => f.Currency, f => f, _currencyComparer); public FungibleAssetValue RewardsDuringPeriod(BigInteger share, Currency currency) => LumpSumRewards.TryGetValue(currency, out var reward) From 5bde1984524e3a48a0335d3dc22944c1a89ec070 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 14 Nov 2024 01:01:56 +0900 Subject: [PATCH 149/206] feat: Add migration state by action --- .../Guild/Migration/GuildMigrationConfig.cs | 4 +- .../Migration/LegacyModels/MigrationModule.cs | 24 ++++++++ .../Migration/MigrateDelegationHeight.cs | 60 +++++++++++++++++++ Lib9c/Addresses.cs | 16 +++++ Lib9c/Module/Guild/GuildParticipantModule.cs | 6 +- 5 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs create mode 100644 Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs diff --git a/Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs b/Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs index dc1ecbfb03..a77b12e20f 100644 --- a/Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs +++ b/Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs @@ -3,6 +3,8 @@ namespace Nekoyume.Action.Guild.Migration // TODO: [GuildMigration] Remove this class when the migration is done. public static class GuildMigrationConfig { - public static readonly long MigrateDelegationHeight = 12302304L; + public static readonly long OdinDelegationMigrationHeight = 12302304L; + + public static readonly long HeimdallDelegationMigrationHeight = 12302304L; } } diff --git a/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs b/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs new file mode 100644 index 0000000000..437bcb2bfc --- /dev/null +++ b/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs @@ -0,0 +1,24 @@ +using Bencodex.Types; +using Libplanet.Action.State; +using Nekoyume.Extensions; + +namespace Nekoyume.Action.Guild.Migration.LegacyModels +{ + public static class MigrationModule + { + public static long? GetDelegationMigrationHeight(this IWorldState worldState) + => worldState + .GetAccountState(Addresses.Migration) + .GetState(Addresses.DelegationMigrationHeight) is Integer height + ? height + : null; + + public static IWorld SetDelegationMigrationHeight(this IWorld world, long height) + => world + .MutateAccount( + Addresses.Migration, + account => account.SetState( + Addresses.DelegationMigrationHeight, + (Integer)height)); + } +} diff --git a/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs b/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs new file mode 100644 index 0000000000..06cb1d38b2 --- /dev/null +++ b/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs @@ -0,0 +1,60 @@ +using System; +using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.State; +using Nekoyume.Action.Guild.Migration.LegacyModels; + +namespace Nekoyume.Action.Guild.Migration +{ + // TODO: [GuildMigration] Remove this class when the migration is done. + /// + /// An action to migrate the delegation height. + /// + [ActionType(TypeIdentifier)] + public class MigrateDelegationHeight : ActionBase + { + public const string TypeIdentifier = "migrate_delegation_height"; + + private const string HeightKey = "h"; + + public long Height { get; private set; } + + [Obsolete("Don't call in code.", error: false)] + public MigrateDelegationHeight() + { + } + + public MigrateDelegationHeight(long height) + { + Height = height; + } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", Dictionary.Empty + .Add(HeightKey, Height)); + + public override void LoadPlainValue(IValue plainValue) + { + if (plainValue is not Dictionary root || + !root.TryGetValue((Text)"values", out var rawValues) || + rawValues is not Dictionary values || + !values.TryGetValue((Text)HeightKey, out var rawHeight) || + rawHeight is not Integer height) + { + throw new InvalidCastException(); + } + + Height = height; + } + + public override IWorld Execute(IActionContext context) + { + GasTracer.UseGas(1); + + var world = context.PreviousState; + + return world.SetDelegationMigrationHeight(Height); + } + } +} diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index 0468fc52e2..2b4b113481 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -234,6 +234,22 @@ public static readonly Address NonValidatorDelegatee #endregion + #region Migration + + /// + /// An account address for migration. + /// + public static readonly Address Migration + = new Address("0000000000000000000000000000000000000400"); + + /// + /// An address for delegation height migration. + /// + public static readonly Address DelegationMigrationHeight + = new Address("0000000000000000000000000000000000000000"); + + #endregion + public static Address GetSheetAddress() where T : ISheet => GetSheetAddress(typeof(T).Name); public static Address GetSheetAddress(string sheetName) => TableSheet.Derive(sheetName); diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index 86c7b0d2aa..ad62398b44 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -6,6 +6,7 @@ using Lib9c; using Libplanet.Types.Assets; using Nekoyume.Action.Guild.Migration; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Model.Guild; using Nekoyume.TypedAddress; using Nekoyume.ValidatorDelegation; @@ -145,7 +146,10 @@ public static GuildRepository Delegate( var height = repository.ActionContext.BlockIndex; // TODO: Remove below unnecessary height condition after migration. - height = Math.Max(height, GuildMigrationConfig.MigrateDelegationHeight); + if (repository.World.GetDelegationMigrationHeight() is long migrationHeight) + { + height = Math.Max(height, migrationHeight); + } var guildParticipant = repository.GetGuildParticipant(guildParticipantAddress); var guild = repository.GetGuild(guildParticipant.GuildAddress); From a7d61171ea5dedb1b4d8fda4270684565371a008 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 14 Nov 2024 01:06:48 +0900 Subject: [PATCH 150/206] test: Fix snapshot fixture --- .../Model/Guild/GuildParticipantTest.Snapshot.verified.txt | 2 +- .Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.verified.txt b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.verified.txt index 1483b43646..d6de105e44 100644 --- a/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.verified.txt +++ b/.Lib9c.Tests/Model/Guild/GuildParticipantTest.Snapshot.verified.txt @@ -7,7 +7,7 @@ { EncodingLength: 3, Kind: Integer, - Value: 1 + Value: 2 }, [ 217, diff --git a/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt b/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt index 4abde14fa4..72cb503bb0 100644 --- a/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt +++ b/.Lib9c.Tests/Model/Guild/GuildTest.Snapshot.verified.txt @@ -7,7 +7,7 @@ { EncodingLength: 3, Kind: Integer, - Value: 1 + Value: 2 }, [ 217, From b71f9b69212470b38a60ca806ee734ae6ecca052 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Wed, 6 Nov 2024 17:54:27 +0900 Subject: [PATCH 151/206] Transfer RewardPool address in action cost --- .Lib9c.Tests/Action/BuyTest.cs | 10 ++-------- .Lib9c.Tests/Action/ItemEnhancementTest.cs | 6 +----- .Lib9c.Tests/Action/PetEnhancement0Test.cs | 14 -------------- .Lib9c.Tests/Action/RaidTest.cs | 5 +---- .Lib9c.Tests/Action/Scenario/MarketScenarioTest.cs | 8 ++------ Lib9c/Action/AuraSummon.cs | 7 +------ Lib9c/Action/BattleArena.cs | 4 +--- Lib9c/Action/Buy.cs | 5 +---- Lib9c/Action/BuyProduct.cs | 10 ++-------- Lib9c/Action/CombinationEquipment.cs | 6 +----- Lib9c/Action/EventDungeonBattle.cs | 2 +- Lib9c/Action/ItemEnhancement.cs | 6 +----- Lib9c/Action/ItemEnhancement11.cs | 5 +---- Lib9c/Action/ItemEnhancement12.cs | 6 +----- Lib9c/Action/ItemEnhancement13.cs | 6 +----- Lib9c/Action/JoinArena.cs | 3 +-- Lib9c/Action/JoinArena1.cs | 3 +-- Lib9c/Action/JoinArena3.cs | 5 ++--- Lib9c/Action/PetEnhancement.cs | 9 ++------- Lib9c/Action/Raid.cs | 6 +----- Lib9c/Action/RuneEnhancement.cs | 11 +++-------- Lib9c/Action/RuneSummon.cs | 7 +------ Lib9c/Action/UnlockRuneSlot.cs | 5 +---- 23 files changed, 29 insertions(+), 120 deletions(-) diff --git a/.Lib9c.Tests/Action/BuyTest.cs b/.Lib9c.Tests/Action/BuyTest.cs index 446209e98d..dcd2213134 100644 --- a/.Lib9c.Tests/Action/BuyTest.cs +++ b/.Lib9c.Tests/Action/BuyTest.cs @@ -439,10 +439,7 @@ public void Execute(params OrderData[] orderDataList) Assert.Equal(30, nextBuyerAvatarState.mailBox.Count); - var arenaSheet = _tableSheets.ArenaSheet; - var arenaData = arenaSheet.GetRoundByBlockIndex(100); - var feeStoreAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); - var goldCurrencyGold = nextState.GetBalance(feeStoreAddress, goldCurrencyState); + var goldCurrencyGold = nextState.GetBalance(Addresses.RewardPool, goldCurrencyState); Assert.Equal(totalTax, goldCurrencyGold); var buyerGold = nextState.GetBalance(_buyerAgentAddress, goldCurrencyState); var prevBuyerGold = _initialState.GetBalance(_buyerAgentAddress, goldCurrencyState); @@ -944,10 +941,7 @@ public void Execute_With_Testbed() var buyerGold = nextState.GetBalance(result.GetAgentState().address, goldCurrencyState); Assert.Equal(prevBuyerGold - totalPrice, buyerGold); - var arenaSheet = _tableSheets.ArenaSheet; - var arenaData = arenaSheet.GetRoundByBlockIndex(100); - var feeStoreAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); - var goldCurrencyGold = nextState.GetBalance(feeStoreAddress, goldCurrencyState); + var goldCurrencyGold = nextState.GetBalance(Addresses.RewardPool, goldCurrencyState); Assert.Equal(totalTax, goldCurrencyGold); foreach (var (agentAddress, expectedGold) in agentRevenue) diff --git a/.Lib9c.Tests/Action/ItemEnhancementTest.cs b/.Lib9c.Tests/Action/ItemEnhancementTest.cs index b5fa83694e..6fd01c1d9f 100644 --- a/.Lib9c.Tests/Action/ItemEnhancementTest.cs +++ b/.Lib9c.Tests/Action/ItemEnhancementTest.cs @@ -311,13 +311,9 @@ public void Execute( nextState.GetBalance(_agentAddress, _currency) ); - var arenaSheet = _tableSheets.ArenaSheet; - var arenaData = arenaSheet.GetRoundByBlockIndex(1); - var feeStoreAddress = - ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); Assert.Equal( expectedCost * _currency, - nextState.GetBalance(feeStoreAddress, _currency) + nextState.GetBalance(Addresses.RewardPool, _currency) ); Assert.Equal(30, nextAvatarState.mailBox.Count); diff --git a/.Lib9c.Tests/Action/PetEnhancement0Test.cs b/.Lib9c.Tests/Action/PetEnhancement0Test.cs index f909ef1479..25584ec6c5 100644 --- a/.Lib9c.Tests/Action/PetEnhancement0Test.cs +++ b/.Lib9c.Tests/Action/PetEnhancement0Test.cs @@ -154,20 +154,6 @@ public void Execute_Throw_PetCostNotFoundException() removePetCostRowWithTargetPetLevel: true)); } - [Fact] - public void Execute_Throw_RoundNotFoundException() - { - Assert.Throws(() => - Execute( - _initialStatesWithAvatarStateV2, - _firstRoundStartBlockIndex - 1, - _agentAddr, - _avatarAddr, - _targetPetId, - 0, - 1)); - } - [Theory] [InlineData(0, 1)] public void Execute_Throw_NotEnoughFungibleAssetValueException( diff --git a/.Lib9c.Tests/Action/RaidTest.cs b/.Lib9c.Tests/Action/RaidTest.cs index 5526cb0cfe..ae829f198d 100644 --- a/.Lib9c.Tests/Action/RaidTest.cs +++ b/.Lib9c.Tests/Action/RaidTest.cs @@ -386,10 +386,7 @@ int runeId2 { Assert.Equal(0 * _goldCurrency, nextState.GetBalance(_agentAddress, _goldCurrency)); Assert.Equal(purchaseCount + 1, nextState.GetRaiderState(raiderAddress).PurchaseCount); - var arenaData = _tableSheets.ArenaSheet.GetRoundByBlockIndex(ctx.BlockIndex); - var feeAddress = - ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); - Assert.True(nextState.GetBalance(feeAddress, _goldCurrency) > 0 * _goldCurrency); + Assert.True(nextState.GetBalance(Addresses.RewardPool, _goldCurrency) > 0 * _goldCurrency); } Assert.True(nextState.TryGetLegacyState(worldBossKillRewardRecordAddress, out List rawRewardInfo)); diff --git a/.Lib9c.Tests/Action/Scenario/MarketScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/MarketScenarioTest.cs index dd6c3f0b77..ab0bb70b44 100644 --- a/.Lib9c.Tests/Action/Scenario/MarketScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/MarketScenarioTest.cs @@ -306,8 +306,6 @@ public void Register_And_Buy() var latestState = action3.Execute(ctx); var buyerAvatarState = latestState.GetAvatarState(_buyerAvatarAddress); - var arenaData = _tableSheets.ArenaSheet.GetRoundByBlockIndex(3L); - var feeStoreAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); var totalTax = 0 * _currency; foreach (var group in action3.ProductInfos.GroupBy(p => p.AgentAddress)) { @@ -348,7 +346,7 @@ public void Register_And_Buy() Assert.True(totalTax > 0 * _currency); Assert.Equal(0 * _currency, latestState.GetBalance(_buyerAgentAddress, _currency)); - Assert.Equal(totalTax, latestState.GetBalance(feeStoreAddress, _currency)); + Assert.Equal(totalTax, latestState.GetBalance(Addresses.RewardPool, _currency)); } [Fact] @@ -1082,8 +1080,6 @@ public void HardFork() }); var buyerAvatarState = tradedState.GetAvatarState(_buyerAvatarAddress); - var arenaData = _tableSheets.ArenaSheet.GetRoundByBlockIndex(3L); - var feeStoreAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); var totalTax = 0 * _currency; foreach (var group in buyAction.ProductInfos.GroupBy(p => p.AgentAddress)) { @@ -1124,7 +1120,7 @@ public void HardFork() Assert.True(totalTax > 0 * _currency); Assert.Equal(0 * _currency, tradedState.GetBalance(_buyerAgentAddress, _currency)); - Assert.Equal(totalTax, tradedState.GetBalance(feeStoreAddress, _currency)); + Assert.Equal(totalTax, tradedState.GetBalance(Addresses.RewardPool, _currency)); } [Theory] diff --git a/Lib9c/Action/AuraSummon.cs b/Lib9c/Action/AuraSummon.cs index d6157ecf68..c9ce10ee41 100644 --- a/Lib9c/Action/AuraSummon.cs +++ b/Lib9c/Action/AuraSummon.cs @@ -224,15 +224,10 @@ public override IWorld Execute(IActionContext context) // Transfer Cost NCG first for fast-fail if (summonRow.CostNcg > 0L) { - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = - ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); - states = states.TransferAsset( context, context.Signer, - feeStoreAddress, + Addresses.RewardPool, states.GetGoldCurrency() * summonRow.CostNcg * SummonCount ); } diff --git a/Lib9c/Action/BattleArena.cs b/Lib9c/Action/BattleArena.cs index 8a63536b0d..3443654466 100644 --- a/Lib9c/Action/BattleArena.cs +++ b/Lib9c/Action/BattleArena.cs @@ -318,8 +318,6 @@ public override IWorld Execute(IActionContext context) } else { - var arenaAddr = - ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); var goldCurrency = states.GetGoldCurrency(); var ticketBalance = ArenaHelper.GetTicketPrice(roundData, myArenaInformation, goldCurrency); @@ -332,7 +330,7 @@ public override IWorld Execute(IActionContext context) purchasedCountDuringInterval++; states = states - .TransferAsset(context, context.Signer, arenaAddr, ticketBalance) + .TransferAsset(context, context.Signer, Addresses.RewardPool, ticketBalance) .SetLegacyState(purchasedCountAddr, purchasedCountDuringInterval); } diff --git a/Lib9c/Action/Buy.cs b/Lib9c/Action/Buy.cs index b866ebf8f9..354c8908fd 100644 --- a/Lib9c/Action/Buy.cs +++ b/Lib9c/Action/Buy.cs @@ -249,13 +249,10 @@ public override IWorld Execute(IActionContext context) var taxedPrice = order.Price - tax; // Transfer tax. - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); states = states.TransferAsset( context, context.Signer, - feeStoreAddress, + Addresses.RewardPool, tax); // Transfer seller. diff --git a/Lib9c/Action/BuyProduct.cs b/Lib9c/Action/BuyProduct.cs index 34cc190634..001cc56fee 100644 --- a/Lib9c/Action/BuyProduct.cs +++ b/Lib9c/Action/BuyProduct.cs @@ -168,9 +168,6 @@ private IWorld Buy(IActionContext context, IProductInfo productInfo, Address sel buyerAvatarState.UpdateQuestRewards(materialSheet); // Transfer tax. - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); var tax = product.Price.DivRem(100, out _) * Action.Buy.TaxRate; var taxedPrice = product.Price - tax; @@ -182,7 +179,7 @@ private IWorld Buy(IActionContext context, IProductInfo productInfo, Address sel .SetLegacyState(productsStateAddress, productsState.Serialize()) .SetAvatarState(sellerAvatarAddress, sellerAvatarState) .SetLegacyState(ProductReceipt.DeriveAddress(productId), receipt.Serialize()) - .TransferAsset(context, context.Signer, feeStoreAddress, tax) + .TransferAsset(context, context.Signer, Addresses.RewardPool, tax) .TransferAsset(context, context.Signer, sellerAgentAddress, taxedPrice); return states; @@ -299,13 +296,10 @@ private static IWorld Buy_Order(PurchaseInfo purchaseInfo, IActionContext contex var taxedPrice = order.Price - tax; // Transfer tax. - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); states = states.TransferAsset( context, context.Signer, - feeStoreAddress, + Addresses.RewardPool, tax); // Transfer seller. diff --git a/Lib9c/Action/CombinationEquipment.cs b/Lib9c/Action/CombinationEquipment.cs index c4854257f8..2e1e333a06 100644 --- a/Lib9c/Action/CombinationEquipment.cs +++ b/Lib9c/Action/CombinationEquipment.cs @@ -374,14 +374,10 @@ public override IWorld Execute(IActionContext context) // Transfer Required NCG if (costNcg > 0L) { - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); - states = states.TransferAsset( context, context.Signer, - feeStoreAddress, + Addresses.RewardPool, states.GetGoldCurrency() * costNcg ); } diff --git a/Lib9c/Action/EventDungeonBattle.cs b/Lib9c/Action/EventDungeonBattle.cs index a32e57eb8f..0e31d6ce7a 100644 --- a/Lib9c/Action/EventDungeonBattle.cs +++ b/Lib9c/Action/EventDungeonBattle.cs @@ -279,7 +279,7 @@ is Bencodex.Types.List serializedEventDungeonInfoList states = states.TransferAsset( context, context.Signer, - Addresses.EventDungeon, + Addresses.RewardPool, cost); } diff --git a/Lib9c/Action/ItemEnhancement.cs b/Lib9c/Action/ItemEnhancement.cs index 470de6b26b..08626193fa 100644 --- a/Lib9c/Action/ItemEnhancement.cs +++ b/Lib9c/Action/ItemEnhancement.cs @@ -371,11 +371,7 @@ public override IWorld Execute(IActionContext context) var requiredNcg = targetCostRow.Cost - startCostRow.Cost; if (requiredNcg > 0) { - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = - ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); - states = states.TransferAsset(ctx, ctx.Signer, feeStoreAddress, + states = states.TransferAsset(ctx, ctx.Signer, Addresses.RewardPool, states.GetGoldCurrency() * requiredNcg); } diff --git a/Lib9c/Action/ItemEnhancement11.cs b/Lib9c/Action/ItemEnhancement11.cs index baa3d1820e..c2348213fd 100644 --- a/Lib9c/Action/ItemEnhancement11.cs +++ b/Lib9c/Action/ItemEnhancement11.cs @@ -290,10 +290,7 @@ public override IWorld Execute(IActionContext context) var requiredNcg = row.Cost; if (requiredNcg > 0) { - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); - states = states.TransferAsset(ctx, ctx.Signer, feeStoreAddress, states.GetGoldCurrency() * requiredNcg); + states = states.TransferAsset(ctx, ctx.Signer, Addresses.RewardPool, states.GetGoldCurrency() * requiredNcg); } // Unequip items diff --git a/Lib9c/Action/ItemEnhancement12.cs b/Lib9c/Action/ItemEnhancement12.cs index 6a2d36acc6..ad7523433b 100644 --- a/Lib9c/Action/ItemEnhancement12.cs +++ b/Lib9c/Action/ItemEnhancement12.cs @@ -362,11 +362,7 @@ public override IWorld Execute(IActionContext context) var requiredNcg = targetCostRow.Cost - startCostRow.Cost; if (requiredNcg > 0) { - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = - ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); - states = states.TransferAsset(ctx, ctx.Signer, feeStoreAddress, + states = states.TransferAsset(ctx, ctx.Signer, Addresses.RewardPool, states.GetGoldCurrency() * requiredNcg); } diff --git a/Lib9c/Action/ItemEnhancement13.cs b/Lib9c/Action/ItemEnhancement13.cs index 08f502af18..8eea3cfaf3 100644 --- a/Lib9c/Action/ItemEnhancement13.cs +++ b/Lib9c/Action/ItemEnhancement13.cs @@ -365,11 +365,7 @@ public override IWorld Execute(IActionContext context) var requiredNcg = targetCostRow.Cost - startCostRow.Cost; if (requiredNcg > 0) { - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = - ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); - states = states.TransferAsset(ctx, ctx.Signer, feeStoreAddress, + states = states.TransferAsset(ctx, ctx.Signer, Addresses.RewardPool, states.GetGoldCurrency() * requiredNcg); } diff --git a/Lib9c/Action/JoinArena.cs b/Lib9c/Action/JoinArena.cs index af5162660c..3c64fa1915 100644 --- a/Lib9c/Action/JoinArena.cs +++ b/Lib9c/Action/JoinArena.cs @@ -173,8 +173,7 @@ public override IWorld Execute(IActionContext context) $"required {fee}, but balance is {crystalBalance}"); } - var arenaAddr = ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); - states = states.TransferAsset(context, context.Signer, arenaAddr, fee); + states = states.TransferAsset(context, context.Signer, Addresses.RewardPool, fee); } // check medal diff --git a/Lib9c/Action/JoinArena1.cs b/Lib9c/Action/JoinArena1.cs index 319a790c66..404641d30e 100644 --- a/Lib9c/Action/JoinArena1.cs +++ b/Lib9c/Action/JoinArena1.cs @@ -145,8 +145,7 @@ public override IWorld Execute(IActionContext context) $"required {fee}, but balance is {crystalBalance}"); } - var arenaAdr = ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); - states = states.TransferAsset(context, context.Signer, arenaAdr, fee); + states = states.TransferAsset(context, context.Signer, Addresses.RewardPool, fee); } // check medal diff --git a/Lib9c/Action/JoinArena3.cs b/Lib9c/Action/JoinArena3.cs index 6d0afa20da..3127638ed4 100644 --- a/Lib9c/Action/JoinArena3.cs +++ b/Lib9c/Action/JoinArena3.cs @@ -85,7 +85,7 @@ public override IWorld Execute(IActionContext context) { throw new FailedLoadStateException($"[{nameof(JoinArena)}] Aborted as the avatar state of the signer was failed to load."); } - + if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( out var world)) { @@ -162,8 +162,7 @@ public override IWorld Execute(IActionContext context) $"required {fee}, but balance is {crystalBalance}"); } - var arenaAdr = ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); - states = states.TransferAsset(context, context.Signer, arenaAdr, fee); + states = states.TransferAsset(context, context.Signer, Addresses.RewardPool, fee); } // check medal diff --git a/Lib9c/Action/PetEnhancement.cs b/Lib9c/Action/PetEnhancement.cs index dcf1739972..e6a65f0e41 100644 --- a/Lib9c/Action/PetEnhancement.cs +++ b/Lib9c/Action/PetEnhancement.cs @@ -125,11 +125,6 @@ public override IWorld Execute(IActionContext context) petState.Level, TargetLevel); - var arenaSheet = sheets.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = ArenaHelper.DeriveArenaAddress( - arenaData.ChampionshipId, - arenaData.Round); if (ncgQuantity > 0) { var ncgCost = ncgQuantity * ncgCurrency; @@ -143,7 +138,7 @@ public override IWorld Execute(IActionContext context) currentNcg); } - states = states.TransferAsset(context, context.Signer, feeStoreAddress, ncgCost); + states = states.TransferAsset(context, context.Signer, Addresses.RewardPool, ncgCost); } if (soulStoneQuantity > 0) @@ -164,7 +159,7 @@ public override IWorld Execute(IActionContext context) states = states.TransferAsset( context, AvatarAddress, - feeStoreAddress, + Addresses.RewardPool, soulStoneCost); } diff --git a/Lib9c/Action/Raid.cs b/Lib9c/Action/Raid.cs index ab7a737749..6fef1ed1a1 100644 --- a/Lib9c/Action/Raid.cs +++ b/Lib9c/Action/Raid.cs @@ -149,11 +149,7 @@ public override IWorld Execute(IActionContext context) throw new ExceedTicketPurchaseLimitException(""); } var goldCurrency = states.GetGoldCurrency(); - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeAddress = - ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); - states = states.TransferAsset(context, context.Signer, feeAddress, + states = states.TransferAsset(context, context.Signer, Addresses.RewardPool, WorldBossHelper.CalculateTicketPrice(row, raiderState, goldCurrency)); raiderState.PurchaseCount++; } diff --git a/Lib9c/Action/RuneEnhancement.cs b/Lib9c/Action/RuneEnhancement.cs index 6d10145a19..a7a57b9eed 100644 --- a/Lib9c/Action/RuneEnhancement.cs +++ b/Lib9c/Action/RuneEnhancement.cs @@ -152,27 +152,22 @@ public override IWorld Execute(IActionContext context) runeState.LevelUp(levelUpResult.LevelUpCount); states = states.SetRuneState(AvatarAddress, allRuneState); - var arenaSheet = sheets.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = - ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); - // Burn costs if (levelUpResult.NcgCost > 0) { - states = states.TransferAsset(context, context.Signer, feeStoreAddress, + states = states.TransferAsset(context, context.Signer, Addresses.RewardPool, levelUpResult.NcgCost * ncgCurrency); } if (levelUpResult.CrystalCost > 0) { - states = states.TransferAsset(context, context.Signer, feeStoreAddress, + states = states.TransferAsset(context, context.Signer, Addresses.RewardPool, levelUpResult.CrystalCost * crystalCurrency); } if (levelUpResult.RuneCost > 0) { - states = states.TransferAsset(context, AvatarAddress, feeStoreAddress, + states = states.TransferAsset(context, AvatarAddress, Addresses.RewardPool, levelUpResult.RuneCost * runeCurrency); } diff --git a/Lib9c/Action/RuneSummon.cs b/Lib9c/Action/RuneSummon.cs index e459e88884..2be9561c1a 100644 --- a/Lib9c/Action/RuneSummon.cs +++ b/Lib9c/Action/RuneSummon.cs @@ -90,15 +90,10 @@ public override IWorld Execute(IActionContext context) // Transfer Cost NCG first for fast-fail if (summonRow.CostNcg > 0L) { - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = - ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); - states = states.TransferAsset( context, context.Signer, - feeStoreAddress, + Addresses.RewardPool, states.GetGoldCurrency() * summonRow.CostNcg * SummonCount ); } diff --git a/Lib9c/Action/UnlockRuneSlot.cs b/Lib9c/Action/UnlockRuneSlot.cs index dbb8b58c2f..ad4cfb60a2 100644 --- a/Lib9c/Action/UnlockRuneSlot.cs +++ b/Lib9c/Action/UnlockRuneSlot.cs @@ -78,9 +78,6 @@ public override IWorld Execute(IActionContext context) } var gameConfigState = states.GetGameConfigState(); - var arenaSheet = sheets.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); int cost; Currency currency; switch (slot.RuneSlotType) @@ -107,7 +104,7 @@ public override IWorld Execute(IActionContext context) raidSlotState.Unlock(SlotIndex); return states - .TransferAsset(context, context.Signer, feeStoreAddress, cost * currency) + .TransferAsset(context, context.Signer, Addresses.RewardPool, cost * currency) .SetLegacyState(adventureSlotStateAddress, adventureSlotState.Serialize()) .SetLegacyState(arenaSlotStateAddress, arenaSlotState.Serialize()) .SetLegacyState(raidSlotStateAddress, raidSlotState.Serialize()); From db7c6bdbdf68d7f62d965abd30ec4991e4881e35 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 14 Nov 2024 01:49:32 +0900 Subject: [PATCH 152/206] chore: Minor fix for migration --- .../Guild/Migration/GuildMigrationConfig.cs | 10 --- .../Migration/LegacyModels/MigrationModule.cs | 11 +++- .../Guild/Migration/MigrateDelegation.cs | 65 +++++++++++++++---- .../Migration/MigratePlanetariumGuild.cs | 1 - Lib9c/Addresses.cs | 6 -- 5 files changed, 61 insertions(+), 32 deletions(-) delete mode 100644 Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs diff --git a/Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs b/Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs deleted file mode 100644 index a77b12e20f..0000000000 --- a/Lib9c/Action/Guild/Migration/GuildMigrationConfig.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Nekoyume.Action.Guild.Migration -{ - // TODO: [GuildMigration] Remove this class when the migration is done. - public static class GuildMigrationConfig - { - public static readonly long OdinDelegationMigrationHeight = 12302304L; - - public static readonly long HeimdallDelegationMigrationHeight = 12302304L; - } -} diff --git a/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs b/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs index 437bcb2bfc..2836f18353 100644 --- a/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs +++ b/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs @@ -1,15 +1,22 @@ using Bencodex.Types; using Libplanet.Action.State; +using Libplanet.Crypto; using Nekoyume.Extensions; namespace Nekoyume.Action.Guild.Migration.LegacyModels { public static class MigrationModule { + /// + /// An address for delegation height migration. + /// + public static readonly Address DelegationMigrationHeight + = new Address("0000000000000000000000000000000000000000"); + public static long? GetDelegationMigrationHeight(this IWorldState worldState) => worldState .GetAccountState(Addresses.Migration) - .GetState(Addresses.DelegationMigrationHeight) is Integer height + .GetState(DelegationMigrationHeight) is Integer height ? height : null; @@ -18,7 +25,7 @@ public static IWorld SetDelegationMigrationHeight(this IWorld world, long height .MutateAccount( Addresses.Migration, account => account.SetState( - Addresses.DelegationMigrationHeight, + DelegationMigrationHeight, (Integer)height)); } } diff --git a/Lib9c/Action/Guild/Migration/MigrateDelegation.cs b/Lib9c/Action/Guild/Migration/MigrateDelegation.cs index 3e4102ffbe..65dbec9dde 100644 --- a/Lib9c/Action/Guild/Migration/MigrateDelegation.cs +++ b/Lib9c/Action/Guild/Migration/MigrateDelegation.cs @@ -10,6 +10,7 @@ using Nekoyume.Model.Stake; using Nekoyume.Model.State; using Nekoyume.Module; +using Libplanet.Crypto; namespace Nekoyume.Action.Guild.Migration { @@ -79,25 +80,63 @@ public override IWorld Execute(IActionContext context) world = result.Value.world; } + // Migrate guild participant state from legacy to new var value = world.GetAccountState(Addresses.GuildParticipant).GetState(Target) as List; - var legacyGuildParticipant = new LegacyGuildParticipant(value); + var repository = new GuildRepository(world, context); - var guildParticipant = new GuildParticipant( - Target, - legacyGuildParticipant.GuildAddress, - repository); - repository.SetGuildParticipant(guildParticipant); - - // Migrate delegation - var guild = repository.GetGuild(guildParticipant.GuildAddress); - var guildGold = repository.GetBalance(guildParticipant.DelegationPoolAddress, Currencies.GuildGold); - if (guildGold.RawValue > 0) + if (repository.GetJoinedGuild(GuildConfig.PlanetariumGuildOwner) is not { } planetariumGuildAddress) + { + throw new NullReferenceException("Planetarium guild is not found."); + } + + if (!repository.TryGetGuild(planetariumGuildAddress, out var planetariumGuild)) + { + throw new GuildMigrationFailedException("Planetarium guild is not found."); + } + + if (planetariumGuild.GuildMasterAddress != GuildConfig.PlanetariumGuildOwner) + { + throw new GuildMigrationFailedException("Unexpected guild master."); + } + + try { - repository.Delegate(guildParticipant.Address, guildGold); + var legacyGuildParticipant = new LegacyGuildParticipant(value); + var guildParticipant = new GuildParticipant( + Target, + legacyGuildParticipant.GuildAddress, + repository); + repository.SetGuildParticipant(guildParticipant); + + // Migrate delegation + var guild = repository.GetGuild(guildParticipant.GuildAddress); + var guildGold = repository.GetBalance(guildParticipant.DelegationPoolAddress, Currencies.GuildGold); + if (guildGold.RawValue > 0) + { + repository.Delegate(guildParticipant.Address, guildGold); + } + + return repository.World; } + catch (FailedLoadStateException) + { + var pledgeAddress = ((Address)Target).GetPledgeAddress(); - return repository.World; + // Patron contract structure: + // [0] = PatronAddress + // [1] = IsApproved + // [2] = Mead amount to refill. + if (!world.TryGetLegacyState(pledgeAddress, out List list) || list.Count < 3 || + list[0] is not Binary || list[0].ToAddress() != MeadConfig.PatronAddress || + list[1] is not Bencodex.Types.Boolean approved || !approved) + { + throw new GuildMigrationFailedException("Unexpected pledge structure."); + } + + repository.JoinGuild(planetariumGuildAddress, Target); + return repository.World; + } } } } diff --git a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs index deca751572..8f768061e8 100644 --- a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs +++ b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs @@ -1,6 +1,5 @@ using System; using Bencodex.Types; -using Lib9c; using Libplanet.Action.State; using Libplanet.Action; using Nekoyume.Action.ValidatorDelegation; diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index 2b4b113481..630c84f818 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -242,12 +242,6 @@ public static readonly Address NonValidatorDelegatee public static readonly Address Migration = new Address("0000000000000000000000000000000000000400"); - /// - /// An address for delegation height migration. - /// - public static readonly Address DelegationMigrationHeight - = new Address("0000000000000000000000000000000000000000"); - #endregion public static Address GetSheetAddress() where T : ISheet => GetSheetAddress(typeof(T).Name); From 91239dd012ee44eef77c568c4c210ce04aa6d602 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 14 Nov 2024 10:02:48 +0900 Subject: [PATCH 153/206] feat: Intermediate unstaking for migration --- .../Migration/MigratePlanetariumGuild.cs | 19 +++++- .../MigratePlanetariumGuildUnbond.cs | 66 ------------------ Lib9c/Action/Stake.cs | 68 +++++++++++++++---- Lib9c/Module/Guild/GuildParticipantModule.cs | 1 - .../ValidatorDelegation/ValidatorDelegatee.cs | 3 +- 5 files changed, 74 insertions(+), 83 deletions(-) delete mode 100644 Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs diff --git a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs index 8f768061e8..ad484a4740 100644 --- a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs +++ b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs @@ -5,6 +5,8 @@ using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.Guild; using Nekoyume.Action.Guild.Migration.LegacyModels; +using Lib9c; +using Nekoyume.Module.Guild; namespace Nekoyume.Action.Guild.Migration { @@ -47,7 +49,8 @@ public override IWorld Execute(IActionContext context) var guildMasterValue = world .GetAccountState(Addresses.GuildParticipant) .GetState(GuildConfig.PlanetariumGuildOwner) as List; - var guildAddress = new LegacyGuildParticipant(guildMasterValue).GuildAddress; + var legacyGuildMaster = new LegacyGuildParticipant(guildMasterValue); + var guildAddress = legacyGuildMaster.GuildAddress; // MigratePlanetariumGuild var guildValue = world @@ -61,6 +64,20 @@ public override IWorld Execute(IActionContext context) repository); repository.SetGuild(guild); + // MigratePlanetariumGuildMaster + var guildParticipant = new GuildParticipant( + GuildConfig.PlanetariumGuildOwner, + guildAddress, + repository); + repository.SetGuildParticipant(guildParticipant); + + // Migrate delegation + var guildGold = repository.GetBalance(guildParticipant.DelegationPoolAddress, Currencies.GuildGold); + if (guildGold.RawValue > 0) + { + repository.Delegate(guildParticipant.Address, guildGold); + } + return repository.World; } } diff --git a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs deleted file mode 100644 index d5824692b4..0000000000 --- a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuildUnbond.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using Bencodex.Types; -using Libplanet.Action.State; -using Libplanet.Action; -using Nekoyume.Model.Guild; -using Nekoyume.Model.State; -using Nekoyume.ValidatorDelegation; - -namespace Nekoyume.Action.Guild.Migration -{ - // TODO: [GuildMigration] Remove this class when the migration is done. - // Enable this action on PoS release. - /// - /// An action to migrate the planetarium guild. - /// - // [ActionType(TypeIdentifier)] - public class MigratePlanetariumGuildUnbond : ActionBase - { - public const string TypeIdentifier = "migrate_planetarium_guild_unbond"; - - [Obsolete("Don't call in code.", error: false)] - public MigratePlanetariumGuildUnbond() - { - } - - public override IValue PlainValue => Dictionary.Empty - .Add("type_id", TypeIdentifier) - .Add("values", Null.Value); - - public override void LoadPlainValue(IValue plainValue) - { - if (plainValue is not Dictionary root || - !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 guildRepository = new GuildRepository(world, context); - - var guildAddress = guildRepository.GetGuildParticipant(GuildConfig.PlanetariumGuildOwner).GuildAddress; - var guild = guildRepository.GetGuild(guildAddress); - var validatorDelegateeForGuildParticipant - = guildRepository.GetGuildDelegatee(guild.ValidatorAddress); - - // TODO: [GuildMigration] Replace below height when determined. - validatorDelegateeForGuildParticipant.Metadata.UpdateUnbondingPeriod(LegacyStakeState.LockupInterval); - guildRepository.SetGuildDelgatee(validatorDelegateeForGuildParticipant); - - var repository = new ValidatorRepository(guildRepository); - var validatorDelegatee = repository.GetValidatorDelegatee(guild.ValidatorAddress); - - // TODO: [GuildMigration] Replace below height when determined. - validatorDelegatee.Metadata.UpdateUnbondingPeriod(LegacyStakeState.LockupInterval); - repository.SetValidatorDelegatee(validatorDelegatee); - - return repository.World; - } - } -} diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index 2c488fc5d0..3ebd0d63c2 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -23,6 +23,7 @@ using Nekoyume.ValidatorDelegation; using Serilog; using static Lib9c.SerializeKeys; +using static Nekoyume.Model.WorldInformation; namespace Nekoyume.Action { @@ -146,6 +147,17 @@ public override IWorld Execute(IActionContext context) throw new StakeExistingClaimableException(); } + // NOTE: When the staking state is locked up. + // TODO: Remove this condition after the migration is done. + if (stakeStateV2.CancellableBlockIndex > context.BlockIndex) + { + // NOTE: Cannot re-contract with less balance. + if (targetStakeBalance < stakedBalance) + { + throw new RequiredBlockIndexException(); + } + } + if (stakeStateV2.StateVersion == 2) { if (!StakeStateUtils.TryMigrateV2ToV3(context, states, stakeStateAddress, stakeStateV2, out var result)) @@ -210,30 +222,58 @@ private static IWorld ContractNewStake( var gg = GetGuildCoinFromNCG(-additionalBalance); var guildRepository = new GuildRepository(state, context); + + // TODO : [GuildMigration] Remove below code when the migration is done. if (guildRepository.TryGetGuildParticipant(agentAddress, out var guildParticipant)) { var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); var guildDelegatee = guildRepository.GetGuildDelegatee(guild.ValidatorAddress); var share = guildDelegatee.ShareFromFAV(gg); - guildParticipant.Undelegate(guild, share, height); - state = guildRepository.World; + + var guildDelegator = guildRepository.GetGuildDelegator(agentAddress); + guildDelegatee.Unbond(guildDelegator, share, height); + + var validatorRepository = new ValidatorRepository(guildRepository); + var validatorDelegatee = validatorRepository.GetValidatorDelegatee(guild.ValidatorAddress); + var validatorDelegator = validatorRepository.GetValidatorDelegator(guild.Address); + validatorDelegatee.Unbond(validatorDelegator, share, height); + + state = validatorRepository.World; + + state = state.BurnAsset(context, guildDelegatee.DelegationPoolAddress, gg); } else { - var delegateeAddress = Addresses.NonValidatorDelegatee; - var delegatorAddress = context.Signer; - var repository = new GuildRepository(state, context); - var unbondLockInAddress = DelegationAddress.UnbondLockInAddress(delegateeAddress, repository.DelegateeAccountAddress, delegatorAddress); - var unbondLockIn = new UnbondLockIn( - unbondLockInAddress, ValidatorDelegatee.ValidatorMaxUnbondLockInEntries, delegateeAddress, delegatorAddress, null); - unbondLockIn = unbondLockIn.LockIn( - gg, height, height + ValidatorDelegatee.ValidatorUnbondingPeriod); - repository.SetUnbondLockIn(unbondLockIn); - repository.SetUnbondingSet( - repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); - state = repository.World; + state = state.BurnAsset(context, Addresses.NonValidatorDelegatee, gg); } + state = state.TransferAsset(context, stakeStateAddr, context.Signer, -additionalBalance); + + // TODO : [GuildMigration] Revive below code when the migration is done. + // if (guildRepository.TryGetGuildParticipant(agentAddress, out var guildParticipant)) + // { + // var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); + // var guildDelegatee = guildRepository.GetGuildDelegatee(guild.ValidatorAddress); + // var share = guildDelegatee.ShareFromFAV(gg); + // guildParticipant.Undelegate(guild, share, height); + // state = guildRepository.World; + // } + // else + // { + // var delegateeAddress = Addresses.NonValidatorDelegatee; + // var delegatorAddress = context.Signer; + // var repository = new GuildRepository(state, context); + // var unbondLockInAddress = DelegationAddress.UnbondLockInAddress(delegateeAddress, repository.DelegateeAccountAddress, delegatorAddress); + // var unbondLockIn = new UnbondLockIn( + // unbondLockInAddress, ValidatorDelegatee.ValidatorMaxUnbondLockInEntries, delegateeAddress, delegatorAddress, null); + // unbondLockIn = unbondLockIn.LockIn( + // gg, height, height + ValidatorDelegatee.ValidatorUnbondingPeriod); + // repository.SetUnbondLockIn(unbondLockIn); + // repository.SetUnbondingSet( + // repository.GetUnbondingSet().SetUnbonding(unbondLockIn)); + // state = repository.World; + // } + if ((stakedBalance + additionalBalance).Sign == 0) { return state.MutateAccount( diff --git a/Lib9c/Module/Guild/GuildParticipantModule.cs b/Lib9c/Module/Guild/GuildParticipantModule.cs index ad62398b44..8d631e1aec 100644 --- a/Lib9c/Module/Guild/GuildParticipantModule.cs +++ b/Lib9c/Module/Guild/GuildParticipantModule.cs @@ -5,7 +5,6 @@ using Bencodex.Types; using Lib9c; using Libplanet.Types.Assets; -using Nekoyume.Action.Guild.Migration; using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Model.Guild; using Nekoyume.TypedAddress; diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index bf4513a069..fd350c5800 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -10,6 +10,7 @@ using Libplanet.Types.Assets; using Libplanet.Types.Consensus; using Nekoyume.Delegation; +using Nekoyume.Model.State; namespace Nekoyume.ValidatorDelegation { @@ -89,7 +90,7 @@ public ValidatorDelegatee( public static Currency ValidatorDelegationCurrency => Currencies.GuildGold; // TODO: [MigrateGuild] Change unbonding period after migration. - public static long ValidatorUnbondingPeriod => 0L; + public static long ValidatorUnbondingPeriod => LegacyStakeState.LockupInterval; public static int ValidatorMaxUnbondLockInEntries => 2; From b8a50312377303f64b1ccabc67e8c0869c19e9b5 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 14 Nov 2024 13:55:00 +0900 Subject: [PATCH 154/206] feat: Make action cost conditionally --- Lib9c/Action/AuraSummon.cs | 13 ++++++++++++- Lib9c/Action/BattleArena.cs | 12 +++++++++++- Lib9c/Action/Buy.cs | 15 +++++++++++++++ Lib9c/Action/BuyProduct.cs | 26 ++++++++++++++++++++++++-- Lib9c/Action/CombinationEquipment.cs | 14 +++++++++++++- Lib9c/Action/EventDungeonBattle.cs | 15 +++++++++++++-- Lib9c/Action/ItemEnhancement.cs | 13 ++++++++++++- Lib9c/Action/ItemEnhancement11.cs | 13 ++++++++++++- Lib9c/Action/ItemEnhancement12.cs | 13 ++++++++++++- Lib9c/Action/ItemEnhancement13.cs | 13 ++++++++++++- Lib9c/Action/JoinArena.cs | 11 ++++++++++- Lib9c/Action/JoinArena1.cs | 11 ++++++++++- Lib9c/Action/JoinArena3.cs | 11 ++++++++++- Lib9c/Action/PetEnhancement.cs | 13 ++++++++++++- Lib9c/Action/Raid.cs | 14 +++++++++++++- Lib9c/Action/RuneEnhancement.cs | 17 ++++++++++++++--- Lib9c/Action/RuneSummon.cs | 13 ++++++++++++- Lib9c/Action/UnlockRuneSlot.cs | 13 ++++++++++++- 18 files changed, 229 insertions(+), 21 deletions(-) diff --git a/Lib9c/Action/AuraSummon.cs b/Lib9c/Action/AuraSummon.cs index c9ce10ee41..f340df00b0 100644 --- a/Lib9c/Action/AuraSummon.cs +++ b/Lib9c/Action/AuraSummon.cs @@ -9,6 +9,7 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Nekoyume.Action.Exceptions; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Extensions; using Nekoyume.Model.Item; @@ -224,10 +225,20 @@ public override IWorld Execute(IActionContext context) // Transfer Cost NCG first for fast-fail if (summonRow.CostNcg > 0L) { + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + feeAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + } + states = states.TransferAsset( context, context.Signer, - Addresses.RewardPool, + feeAddress, states.GetGoldCurrency() * summonRow.CostNcg * SummonCount ); } diff --git a/Lib9c/Action/BattleArena.cs b/Lib9c/Action/BattleArena.cs index 3443654466..4615c2e78b 100644 --- a/Lib9c/Action/BattleArena.cs +++ b/Lib9c/Action/BattleArena.cs @@ -7,6 +7,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Battle; using Nekoyume.Exceptions; @@ -329,8 +330,17 @@ public override IWorld Execute(IActionContext context) } purchasedCountDuringInterval++; + + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + feeAddress = ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); + } + states = states - .TransferAsset(context, context.Signer, Addresses.RewardPool, ticketBalance) + .TransferAsset(context, context.Signer, feeAddress, ticketBalance) .SetLegacyState(purchasedCountAddr, purchasedCountDuringInterval); } diff --git a/Lib9c/Action/Buy.cs b/Lib9c/Action/Buy.cs index 354c8908fd..c03194d2dd 100644 --- a/Lib9c/Action/Buy.cs +++ b/Lib9c/Action/Buy.cs @@ -10,6 +10,7 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Model.EnumType; using Nekoyume.Model.Mail; @@ -18,6 +19,7 @@ using Nekoyume.TableData; using Serilog; using static Lib9c.SerializeKeys; +using static Nekoyume.TableData.ArenaSheet; namespace Nekoyume.Action { @@ -255,6 +257,19 @@ public override IWorld Execute(IActionContext context) Addresses.RewardPool, tax); + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + feeAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + } + + states = states + .TransferAsset(context, context.Signer, feeAddress, tax); + // Transfer seller. states = states.TransferAsset( context, diff --git a/Lib9c/Action/BuyProduct.cs b/Lib9c/Action/BuyProduct.cs index 001cc56fee..2c8fd7505e 100644 --- a/Lib9c/Action/BuyProduct.cs +++ b/Lib9c/Action/BuyProduct.cs @@ -9,6 +9,7 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Battle; using Nekoyume.Model.EnumType; @@ -174,12 +175,23 @@ private IWorld Buy(IActionContext context, IProductInfo productInfo, Address sel // Receipt var receipt = new ProductReceipt(productId, sellerAvatarAddress, buyerAvatarState.address, product.Price, context.BlockIndex); + + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + feeAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + } + states = states .RemoveLegacyState(productAddress) .SetLegacyState(productsStateAddress, productsState.Serialize()) .SetAvatarState(sellerAvatarAddress, sellerAvatarState) .SetLegacyState(ProductReceipt.DeriveAddress(productId), receipt.Serialize()) - .TransferAsset(context, context.Signer, Addresses.RewardPool, tax) + .TransferAsset(context, context.Signer, feeAddress, tax) .TransferAsset(context, context.Signer, sellerAgentAddress, taxedPrice); return states; @@ -296,10 +308,20 @@ private static IWorld Buy_Order(PurchaseInfo purchaseInfo, IActionContext contex var taxedPrice = order.Price - tax; // Transfer tax. + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + feeAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + } + states = states.TransferAsset( context, context.Signer, - Addresses.RewardPool, + feeAddress, tax); // Transfer seller. diff --git a/Lib9c/Action/CombinationEquipment.cs b/Lib9c/Action/CombinationEquipment.cs index 2e1e333a06..405610e32a 100644 --- a/Lib9c/Action/CombinationEquipment.cs +++ b/Lib9c/Action/CombinationEquipment.cs @@ -8,6 +8,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Extensions; using Nekoyume.Helper; @@ -374,10 +375,21 @@ public override IWorld Execute(IActionContext context) // Transfer Required NCG if (costNcg > 0L) { + // Transfer tax. + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + feeAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + } + states = states.TransferAsset( context, context.Signer, - Addresses.RewardPool, + feeAddress, states.GetGoldCurrency() * costNcg ); } diff --git a/Lib9c/Action/EventDungeonBattle.cs b/Lib9c/Action/EventDungeonBattle.cs index 0e31d6ce7a..12ea17bc61 100644 --- a/Lib9c/Action/EventDungeonBattle.cs +++ b/Lib9c/Action/EventDungeonBattle.cs @@ -8,6 +8,8 @@ using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; +using Nekoyume.Action.Guild.Migration.LegacyModels; +using Nekoyume.Arena; using Nekoyume.Battle; using Nekoyume.Exceptions; using Nekoyume.Extensions; @@ -276,11 +278,20 @@ is Bencodex.Types.List serializedEventDungeonInfoList currency); if (cost.Sign > 0) { + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + feeAddress = Addresses.EventDungeon; + } + states = states.TransferAsset( context, context.Signer, - Addresses.RewardPool, - cost); + feeAddress, + cost + ); } // NOTE: The number of ticket purchases should be increased diff --git a/Lib9c/Action/ItemEnhancement.cs b/Lib9c/Action/ItemEnhancement.cs index 08626193fa..a757d58560 100644 --- a/Lib9c/Action/ItemEnhancement.cs +++ b/Lib9c/Action/ItemEnhancement.cs @@ -9,6 +9,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Extensions; using Nekoyume.Helper; @@ -371,7 +372,17 @@ public override IWorld Execute(IActionContext context) var requiredNcg = targetCostRow.Cost - startCostRow.Cost; if (requiredNcg > 0) { - states = states.TransferAsset(ctx, ctx.Signer, Addresses.RewardPool, + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + feeAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + } + + states = states.TransferAsset(ctx, ctx.Signer, feeAddress, states.GetGoldCurrency() * requiredNcg); } diff --git a/Lib9c/Action/ItemEnhancement11.cs b/Lib9c/Action/ItemEnhancement11.cs index c2348213fd..23fd47b565 100644 --- a/Lib9c/Action/ItemEnhancement11.cs +++ b/Lib9c/Action/ItemEnhancement11.cs @@ -11,6 +11,7 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Extensions; using Nekoyume.Helper; @@ -290,7 +291,17 @@ public override IWorld Execute(IActionContext context) var requiredNcg = row.Cost; if (requiredNcg > 0) { - states = states.TransferAsset(ctx, ctx.Signer, Addresses.RewardPool, states.GetGoldCurrency() * requiredNcg); + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + feeAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + } + + states = states.TransferAsset(ctx, ctx.Signer, feeAddress, states.GetGoldCurrency() * requiredNcg); } // Unequip items diff --git a/Lib9c/Action/ItemEnhancement12.cs b/Lib9c/Action/ItemEnhancement12.cs index ad7523433b..10fc68c4e2 100644 --- a/Lib9c/Action/ItemEnhancement12.cs +++ b/Lib9c/Action/ItemEnhancement12.cs @@ -11,6 +11,7 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Extensions; using Nekoyume.Helper; @@ -362,7 +363,17 @@ public override IWorld Execute(IActionContext context) var requiredNcg = targetCostRow.Cost - startCostRow.Cost; if (requiredNcg > 0) { - states = states.TransferAsset(ctx, ctx.Signer, Addresses.RewardPool, + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + feeAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + } + + states = states.TransferAsset(ctx, ctx.Signer, feeAddress, states.GetGoldCurrency() * requiredNcg); } diff --git a/Lib9c/Action/ItemEnhancement13.cs b/Lib9c/Action/ItemEnhancement13.cs index 8eea3cfaf3..2f866d1578 100644 --- a/Lib9c/Action/ItemEnhancement13.cs +++ b/Lib9c/Action/ItemEnhancement13.cs @@ -11,6 +11,7 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Extensions; using Nekoyume.Helper; @@ -365,7 +366,17 @@ public override IWorld Execute(IActionContext context) var requiredNcg = targetCostRow.Cost - startCostRow.Cost; if (requiredNcg > 0) { - states = states.TransferAsset(ctx, ctx.Signer, Addresses.RewardPool, + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + feeAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + } + + states = states.TransferAsset(ctx, ctx.Signer, feeAddress, states.GetGoldCurrency() * requiredNcg); } diff --git a/Lib9c/Action/JoinArena.cs b/Lib9c/Action/JoinArena.cs index 3c64fa1915..0de20477d8 100644 --- a/Lib9c/Action/JoinArena.cs +++ b/Lib9c/Action/JoinArena.cs @@ -8,6 +8,7 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Nekoyume.Action.Exceptions.Arena; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Battle; using Nekoyume.Extensions; @@ -173,7 +174,15 @@ public override IWorld Execute(IActionContext context) $"required {fee}, but balance is {crystalBalance}"); } - states = states.TransferAsset(context, context.Signer, Addresses.RewardPool, fee); + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + feeAddress = ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); + } + + states = states.TransferAsset(context, context.Signer, feeAddress, fee); } // check medal diff --git a/Lib9c/Action/JoinArena1.cs b/Lib9c/Action/JoinArena1.cs index 404641d30e..5ac087ace0 100644 --- a/Lib9c/Action/JoinArena1.cs +++ b/Lib9c/Action/JoinArena1.cs @@ -7,6 +7,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Extensions; using Nekoyume.Helper; @@ -145,7 +146,15 @@ public override IWorld Execute(IActionContext context) $"required {fee}, but balance is {crystalBalance}"); } - states = states.TransferAsset(context, context.Signer, Addresses.RewardPool, fee); + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + feeAddress = ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); + } + + states = states.TransferAsset(context, context.Signer, feeAddress, fee); } // check medal diff --git a/Lib9c/Action/JoinArena3.cs b/Lib9c/Action/JoinArena3.cs index 3127638ed4..7573de49e7 100644 --- a/Lib9c/Action/JoinArena3.cs +++ b/Lib9c/Action/JoinArena3.cs @@ -7,6 +7,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Extensions; using Nekoyume.Helper; @@ -162,7 +163,15 @@ public override IWorld Execute(IActionContext context) $"required {fee}, but balance is {crystalBalance}"); } - states = states.TransferAsset(context, context.Signer, Addresses.RewardPool, fee); + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + feeAddress = ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); + } + + states = states.TransferAsset(context, context.Signer, feeAddress, fee); } // check medal diff --git a/Lib9c/Action/PetEnhancement.cs b/Lib9c/Action/PetEnhancement.cs index e6a65f0e41..31194dec54 100644 --- a/Lib9c/Action/PetEnhancement.cs +++ b/Lib9c/Action/PetEnhancement.cs @@ -5,6 +5,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Exceptions; using Nekoyume.Extensions; @@ -138,7 +139,17 @@ public override IWorld Execute(IActionContext context) currentNcg); } - states = states.TransferAsset(context, context.Signer, Addresses.RewardPool, ncgCost); + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + feeAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + } + + states = states.TransferAsset(context, context.Signer, feeAddress, ncgCost); } if (soulStoneQuantity > 0) diff --git a/Lib9c/Action/Raid.cs b/Lib9c/Action/Raid.cs index 6fef1ed1a1..f331b75801 100644 --- a/Lib9c/Action/Raid.cs +++ b/Lib9c/Action/Raid.cs @@ -8,6 +8,7 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Battle; using Nekoyume.Extensions; @@ -149,7 +150,18 @@ public override IWorld Execute(IActionContext context) throw new ExceedTicketPurchaseLimitException(""); } var goldCurrency = states.GetGoldCurrency(); - states = states.TransferAsset(context, context.Signer, Addresses.RewardPool, + + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + feeAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + } + + states = states.TransferAsset(context, context.Signer, feeAddress, WorldBossHelper.CalculateTicketPrice(row, raiderState, goldCurrency)); raiderState.PurchaseCount++; } diff --git a/Lib9c/Action/RuneEnhancement.cs b/Lib9c/Action/RuneEnhancement.cs index a7a57b9eed..d01babfd8d 100644 --- a/Lib9c/Action/RuneEnhancement.cs +++ b/Lib9c/Action/RuneEnhancement.cs @@ -7,6 +7,7 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Extensions; using Nekoyume.Helper; @@ -152,22 +153,32 @@ public override IWorld Execute(IActionContext context) runeState.LevelUp(levelUpResult.LevelUpCount); states = states.SetRuneState(AvatarAddress, allRuneState); + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + feeAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + } + // Burn costs if (levelUpResult.NcgCost > 0) { - states = states.TransferAsset(context, context.Signer, Addresses.RewardPool, + states = states.TransferAsset(context, context.Signer, feeAddress, levelUpResult.NcgCost * ncgCurrency); } if (levelUpResult.CrystalCost > 0) { - states = states.TransferAsset(context, context.Signer, Addresses.RewardPool, + states = states.TransferAsset(context, context.Signer, feeAddress, levelUpResult.CrystalCost * crystalCurrency); } if (levelUpResult.RuneCost > 0) { - states = states.TransferAsset(context, AvatarAddress, Addresses.RewardPool, + states = states.TransferAsset(context, AvatarAddress, feeAddress, levelUpResult.RuneCost * runeCurrency); } diff --git a/Lib9c/Action/RuneSummon.cs b/Lib9c/Action/RuneSummon.cs index 2be9561c1a..c9b7b9980b 100644 --- a/Lib9c/Action/RuneSummon.cs +++ b/Lib9c/Action/RuneSummon.cs @@ -11,6 +11,7 @@ using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Action.Exceptions; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Extensions; using Nekoyume.Model.State; @@ -90,10 +91,20 @@ public override IWorld Execute(IActionContext context) // Transfer Cost NCG first for fast-fail if (summonRow.CostNcg > 0L) { + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + feeAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + } + states = states.TransferAsset( context, context.Signer, - Addresses.RewardPool, + feeAddress, states.GetGoldCurrency() * summonRow.CostNcg * SummonCount ); } diff --git a/Lib9c/Action/UnlockRuneSlot.cs b/Lib9c/Action/UnlockRuneSlot.cs index ad4cfb60a2..a3b127cc11 100644 --- a/Lib9c/Action/UnlockRuneSlot.cs +++ b/Lib9c/Action/UnlockRuneSlot.cs @@ -9,6 +9,7 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Extensions; using Nekoyume.Model.EnumType; @@ -103,8 +104,18 @@ public override IWorld Execute(IActionContext context) arenaSlotState.Unlock(SlotIndex); raidSlotState.Unlock(SlotIndex); + var feeAddress = Addresses.RewardPool; + // TODO: [GuildMigration] Remove this after migration + if (states.GetDelegationMigrationHeight() is long migrationHeight + && context.BlockIndex < migrationHeight) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + feeAddress = ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + } + return states - .TransferAsset(context, context.Signer, Addresses.RewardPool, cost * currency) + .TransferAsset(context, context.Signer, feeAddress, cost * currency) .SetLegacyState(adventureSlotStateAddress, adventureSlotState.Serialize()) .SetLegacyState(arenaSlotStateAddress, arenaSlotState.Serialize()) .SetLegacyState(raidSlotStateAddress, raidSlotState.Serialize()); From a317379ef6b80f762af56562c61c979c9cf55df6 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 14 Nov 2024 14:18:25 +0900 Subject: [PATCH 155/206] test: Fix invalid test --- .../Action/Guild/Migration/MigratePlanetariumGuildTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs index 4189498e5f..6f5686bd34 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumGuildTest.cs @@ -24,6 +24,7 @@ public void Execute() var repo = new GuildRepository(world, new ActionContext()); Assert.Throws(() => repo.GetGuild(guildAddress)); + Assert.Throws(() => repo.GetGuildParticipant(GuildConfig.PlanetariumGuildOwner)); var action = new MigratePlanetariumGuild(); var actionContext = new ActionContext @@ -36,7 +37,7 @@ public void Execute() repo.UpdateWorld(world); var guild = repo.GetGuild(guildAddress); - Assert.Throws(() => repo.GetGuildParticipant(GuildConfig.PlanetariumGuildOwner)); + var guildOwner = repo.GetGuildParticipant(GuildConfig.PlanetariumGuildOwner); } private static IWorld EnsureLegacyPlanetariumGuild(IWorld world, GuildAddress guildAddress) From 225d93615627f8abee61a94f16a07db7cff05d93 Mon Sep 17 00:00:00 2001 From: hyeon Date: Thu, 14 Nov 2024 15:16:15 +0900 Subject: [PATCH 156/206] Extend Thor Tx. size to 1MiB --- Lib9c.Policy/Policy/MaxTransactionsBytesPolicy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c.Policy/Policy/MaxTransactionsBytesPolicy.cs b/Lib9c.Policy/Policy/MaxTransactionsBytesPolicy.cs index 8803461f77..51d4b24752 100644 --- a/Lib9c.Policy/Policy/MaxTransactionsBytesPolicy.cs +++ b/Lib9c.Policy/Policy/MaxTransactionsBytesPolicy.cs @@ -78,7 +78,7 @@ private MaxTransactionsBytesPolicy( // Note: Thor has been started after v200250 .Add(new SpannedSubPolicy( startIndex: 1L, - value: 1024L * 500L)); // 500 KiB + value: 1024L * 1024L)); // 1 MiB // Note: For internal testing. public static IVariableSubPolicy OdinInternal => From a6466322e8eefeaf25f37dabdda5d41c3f53bad5 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 14 Nov 2024 15:29:23 +0900 Subject: [PATCH 157/206] feat: Add lazy migration --- .../Action/ValidatorDelegation/AllocateGuildReward.cs | 8 ++++++++ Lib9c/Action/ValidatorDelegation/AllocateReward.cs | 8 ++++++++ Lib9c/Action/ValidatorDelegation/RecordProposer.cs | 11 ++++++++++- .../ValidatorDelegation/ReleaseValidatorUnbondings.cs | 7 +++++++ Lib9c/Action/ValidatorDelegation/SlashValidator.cs | 7 +++++++ Lib9c/Action/ValidatorDelegation/UpdateValidators.cs | 7 +++++++ 6 files changed, 47 insertions(+), 1 deletion(-) diff --git a/Lib9c/Action/ValidatorDelegation/AllocateGuildReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateGuildReward.cs index 219fde53d0..64d557e150 100644 --- a/Lib9c/Action/ValidatorDelegation/AllocateGuildReward.cs +++ b/Lib9c/Action/ValidatorDelegation/AllocateGuildReward.cs @@ -10,6 +10,7 @@ using Nekoyume.Module.ValidatorDelegation; using Libplanet.Types.Blocks; using Lib9c; +using Nekoyume.Action.Guild.Migration.LegacyModels; namespace Nekoyume.Action.ValidatorDelegation { @@ -28,6 +29,13 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { var world = context.PreviousState; + + if (world.GetDelegationMigrationHeight() is not { } migrationHeight + || context.BlockIndex < migrationHeight) + { + return world; + } + var repository = new ValidatorRepository(world, context); var rewardCurrency = Currencies.Mead; var proposerInfo = repository.GetProposerInfo(); diff --git a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs index f82721b06a..20e6feecd4 100644 --- a/Lib9c/Action/ValidatorDelegation/AllocateReward.cs +++ b/Lib9c/Action/ValidatorDelegation/AllocateReward.cs @@ -12,6 +12,7 @@ using Nekoyume.Module.Guild; using Nekoyume.ValidatorDelegation; using Nekoyume.Module.ValidatorDelegation; +using Nekoyume.Action.Guild.Migration.LegacyModels; namespace Nekoyume.Action.ValidatorDelegation { @@ -30,6 +31,13 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { var world = context.PreviousState; + + if (world.GetDelegationMigrationHeight() is not { } migrationHeight + || context.BlockIndex < migrationHeight) + { + return world; + } + var rewardCurrency = world.GetGoldCurrency(); var repository = new GuildRepository(world, context); diff --git a/Lib9c/Action/ValidatorDelegation/RecordProposer.cs b/Lib9c/Action/ValidatorDelegation/RecordProposer.cs index 38eedcd71e..dc1fc59089 100644 --- a/Lib9c/Action/ValidatorDelegation/RecordProposer.cs +++ b/Lib9c/Action/ValidatorDelegation/RecordProposer.cs @@ -1,8 +1,10 @@ using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.State; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Extensions; using Nekoyume.ValidatorDelegation; +using static Nekoyume.Model.WorldInformation; namespace Nekoyume.Action.ValidatorDelegation { @@ -30,7 +32,14 @@ public override void LoadPlainValue(IValue plainValue) /// public override IWorld Execute(IActionContext context) { - return context.PreviousState.MutateAccount( + var world = context.PreviousState; + + if (world.GetDelegationMigrationHeight() is null) + { + return world; + } + + return world.MutateAccount( Addresses.ValidatorList, account => account.SetState( ProposerInfo.Address, diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs index 9f1375cd8e..280f7f7a8e 100644 --- a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -15,6 +15,7 @@ using Nekoyume.Module; using Nekoyume.Model.Stake; using Lib9c; +using Nekoyume.Action.Guild.Migration.LegacyModels; namespace Nekoyume.Action.ValidatorDelegation { @@ -35,6 +36,12 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { var world = context.PreviousState; + + if (world.GetDelegationMigrationHeight() is null) + { + return world; + } + var repository = new GuildRepository(world, context); var unbondingSet = repository.GetUnbondingSet(); var unbondings = unbondingSet.UnbondingsToRelease(context.BlockIndex); diff --git a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs index 400b9375aa..59ca7beba6 100644 --- a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs @@ -6,6 +6,7 @@ using Libplanet.Action.State; using Libplanet.Types.Consensus; using Libplanet.Types.Evidence; +using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Model.Guild; using Nekoyume.Module.ValidatorDelegation; using Nekoyume.ValidatorDelegation; @@ -33,6 +34,12 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { var world = context.PreviousState; + + if (world.GetDelegationMigrationHeight() is null) + { + return world; + } + var repository = new ValidatorRepository(world, context); var abstainHistory = repository.GetAbstainHistory(); diff --git a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs index beca295e51..89b1d9c0c8 100644 --- a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs +++ b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs @@ -5,6 +5,7 @@ using Libplanet.Types.Consensus; using Nekoyume.ValidatorDelegation; using Nekoyume.Model.Guild; +using Nekoyume.Action.Guild.Migration.LegacyModels; namespace Nekoyume.Action.ValidatorDelegation { @@ -21,6 +22,12 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { var world = context.PreviousState; + + if (world.GetDelegationMigrationHeight() is null) + { + return world; + } + var prevValidators = world.GetValidatorSet().Validators; var repository = new ValidatorRepository(world, context); var validators = repository.GetValidatorList().ActiveSet(); From b47048e1ad59bdae7c753e9eb8b582bddf5e035f Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 14 Nov 2024 15:51:39 +0900 Subject: [PATCH 158/206] Reveal promote validator to public --- .../Guild/Migration/LegacyModels/MigrationModule.cs | 10 +++++++++- Lib9c/Action/ValidatorDelegation/PromoteValidator.cs | 10 +++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs b/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs index 2836f18353..4d2724c6f5 100644 --- a/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs +++ b/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs @@ -1,3 +1,4 @@ +using System; using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; @@ -21,11 +22,18 @@ public static readonly Address DelegationMigrationHeight : null; public static IWorld SetDelegationMigrationHeight(this IWorld world, long height) - => world + { + if (world.GetDelegationMigrationHeight() is long) + { + throw new InvalidOperationException("Cannot overwrite delegation migration index."); + } + + return world .MutateAccount( Addresses.Migration, account => account.SetState( DelegationMigrationHeight, (Integer)height)); + } } } diff --git a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs index 7c20d82482..63f892c011 100644 --- a/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/PromoteValidator.cs @@ -63,11 +63,11 @@ public override void LoadPlainValue(IValue plainValue) public override IWorld Execute(IActionContext context) { var world = ExecutePublic(context); - if (context.Signer != ValidatorConfig.PlanetariumValidatorAddress) - { - throw new InvalidOperationException( - $"This action is not allowed for {context.Signer}."); - } + // if (context.Signer != ValidatorConfig.PlanetariumValidatorAddress) + // { + // throw new InvalidOperationException( + // $"This action is not allowed for {context.Signer}."); + // } return world; } From 6c5f9f4efa8fbbcce25b6f1811c6a27ef7d7ef74 Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 14 Nov 2024 17:11:22 +0900 Subject: [PATCH 159/206] test: Fix test failures for StakeState --- .Lib9c.Tests/Action/StakeTest.cs | 28 +++++++++++++++++----------- .Lib9c.Tests/Util/DelegationUtil.cs | 16 +++++++++------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index 24f0dc24b6..5de72ac960 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -334,10 +334,13 @@ public void Execute_Success_When_Exist_StakeStateV3( long previousAmount, long amount) { + var interval = previousAmount < amount + ? LegacyStakeState.RewardInterval : LegacyStakeState.LockupInterval; var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); - var stakeStateV2 = new StakeState( + var stakeState = new StakeState( contract: new Contract(_stakePolicySheet), startedBlockIndex: 0L, + receivedBlockIndex: interval, stateVersion: 3); var world = _initialState; var height = 0L; @@ -357,11 +360,11 @@ public void Execute_Success_When_Exist_StakeStateV3( var guildRepository = new GuildRepository(world, new ActionContext { Signer = _agentAddr }); var guildParticipant = guildRepository.GetGuildParticipant(_agentAddr); var guild = guildRepository.GetGuild(guildParticipant.GuildAddress); - guildParticipant.Delegate(guild, gg, height++); + guildParticipant.Delegate(guild, gg, height); world = guildRepository.World; } - world = world.SetLegacyState(stakeStateAddr, stakeStateV2.Serialize()); + world = world.SetLegacyState(stakeStateAddr, stakeState.Serialize()); if (amount - previousAmount > 0) { @@ -370,7 +373,7 @@ public void Execute_Success_When_Exist_StakeStateV3( } var nextState = Execute( - height, + height + interval, world, new TestRandom(), _agentAddr, @@ -378,8 +381,8 @@ public void Execute_Success_When_Exist_StakeStateV3( if (amount > 0) { - Assert.True(nextState.TryGetStakeState(_agentAddr, out StakeState stakeState)); - Assert.Equal(3, stakeState.StateVersion); + Assert.True(nextState.TryGetStakeState(_agentAddr, out StakeState nextStakeState)); + Assert.Equal(3, nextStakeState.StateVersion); } world = DelegationUtil.EnsureStakeReleased( @@ -407,10 +410,13 @@ public void Execute_Success_When_Exist_StakeStateV3_Without_Guild( long previousAmount, long amount) { + var interval = previousAmount < amount + ? LegacyStakeState.RewardInterval : LegacyStakeState.LockupInterval; var stakeStateAddr = StakeState.DeriveAddress(_agentAddr); - var stakeStateV2 = new StakeState( + var stakeState = new StakeState( contract: new Contract(_stakePolicySheet), startedBlockIndex: 0L, + receivedBlockIndex: interval, stateVersion: 3); var world = _initialState; var height = 0L; @@ -427,7 +433,7 @@ public void Execute_Success_When_Exist_StakeStateV3_Without_Guild( new ActionContext(), stakeStateAddr, Addresses.NonValidatorDelegatee, gg); } - world = world.SetLegacyState(stakeStateAddr, stakeStateV2.Serialize()); + world = world.SetLegacyState(stakeStateAddr, stakeState.Serialize()); if (amount - previousAmount > 0) { @@ -436,7 +442,7 @@ public void Execute_Success_When_Exist_StakeStateV3_Without_Guild( } var nextState = Execute( - height, + height + interval, world, new TestRandom(), _agentAddr, @@ -444,8 +450,8 @@ public void Execute_Success_When_Exist_StakeStateV3_Without_Guild( if (amount > 0) { - Assert.True(nextState.TryGetStakeState(_agentAddr, out StakeState stakeState)); - Assert.Equal(3, stakeState.StateVersion); + Assert.True(nextState.TryGetStakeState(_agentAddr, out StakeState nextStakeState)); + Assert.Equal(3, nextStakeState.StateVersion); } world = DelegationUtil.EnsureStakeReleased( diff --git a/.Lib9c.Tests/Util/DelegationUtil.cs b/.Lib9c.Tests/Util/DelegationUtil.cs index 5a6e2c41e8..292fc9759f 100644 --- a/.Lib9c.Tests/Util/DelegationUtil.cs +++ b/.Lib9c.Tests/Util/DelegationUtil.cs @@ -99,13 +99,15 @@ public static IWorld EnsureStakeReleased( throw new ArgumentOutOfRangeException(nameof(blockHeight)); } - var actionContext = new ActionContext - { - PreviousState = world, - BlockIndex = blockHeight, - }; - var releaseValidatorUnbondings = new ReleaseValidatorUnbondings(); - return releaseValidatorUnbondings.Execute(actionContext); + // TODO : [GuildMigration] Revive below code when the migration is done. + // var actionContext = new ActionContext + // { + // PreviousState = world, + // BlockIndex = blockHeight, + // }; + // var releaseValidatorUnbondings = new ReleaseValidatorUnbondings(); + // return releaseValidatorUnbondings.Execute(actionContext); + return world; } } } From c20edc17c331c4cbc32e91461f2d6a37b5e2466e Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 14 Nov 2024 17:28:24 +0900 Subject: [PATCH 160/206] test: Fix test failure for StakeAndClaimScenarioTest --- .Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs index a42ca83217..ac4c3e119c 100644 --- a/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs @@ -114,7 +114,7 @@ public void Test() state = DelegationUtil.MakeGuild(state, _agentAddr, validatorKey.Address, 0L); // Withdraw stake via stake3. - state = Stake3(state, _agentAddr, 0, stake2BlockIndex); + state = Stake3(state, _agentAddr, 0, stake2BlockIndex + LegacyStakeState.LockupInterval + 1); state = DelegationUtil.EnsureStakeReleased(state, stake2BlockIndex + ValidatorDelegatee.ValidatorUnbondingPeriod); From 80b14d3ee987b49c90c60adc3de918184054e092 Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 14 Nov 2024 17:57:44 +0900 Subject: [PATCH 161/206] test: Fix a test failures caused by the absence of migration height. --- .Lib9c.Tests/Action/BuyTest.cs | 4 +++- .../Action/ValidatorDelegation/ValidatorDelegationTestBase.cs | 4 +++- Lib9c/Action/InitializeStates.cs | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.Lib9c.Tests/Action/BuyTest.cs b/.Lib9c.Tests/Action/BuyTest.cs index dcd2213134..4ae96846b2 100644 --- a/.Lib9c.Tests/Action/BuyTest.cs +++ b/.Lib9c.Tests/Action/BuyTest.cs @@ -15,6 +15,7 @@ namespace Lib9c.Tests.Action using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; + using Nekoyume.Action.Guild.Migration.LegacyModels; using Nekoyume.Arena; using Nekoyume.Model; using Nekoyume.Model.Item; @@ -104,7 +105,8 @@ public BuyTest(ITestOutputHelper outputHelper) .SetAgentState(_buyerAgentAddress, buyerAgentState) .SetAvatarState(_buyerAvatarAddress, _buyerAvatarState) .SetLegacyState(Addresses.Shop, new ShopState().Serialize()) - .MintAsset(context, _buyerAgentAddress, _goldCurrencyState.Currency * 100); + .MintAsset(context, _buyerAgentAddress, _goldCurrencyState.Currency * 100) + .SetDelegationMigrationHeight(0); } public static IEnumerable GetExecuteMemberData() diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs index dd0a135545..7cd66f9f5d 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/ValidatorDelegationTestBase.cs @@ -24,6 +24,7 @@ namespace Lib9c.Tests.Action.ValidatorDelegation; using Nekoyume.TypedAddress; using Nekoyume.ValidatorDelegation; using Xunit; +using Nekoyume.Action.Guild.Migration.LegacyModels; public class ValidatorDelegationTestBase { @@ -44,7 +45,8 @@ public ValidatorDelegationTestBase() var world = new World(MockUtil.MockModernWorldState); var goldCurrencyState = new GoldCurrencyState(GoldCurrency); World = world - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) + .SetDelegationMigrationHeight(0); } protected static BlockHash EmptyBlockHash { get; } diff --git a/Lib9c/Action/InitializeStates.cs b/Lib9c/Action/InitializeStates.cs index f60f467983..c49a1671e5 100644 --- a/Lib9c/Action/InitializeStates.cs +++ b/Lib9c/Action/InitializeStates.cs @@ -16,6 +16,7 @@ using Nekoyume.Module.Guild; using Nekoyume.Module.ValidatorDelegation; using Nekoyume.ValidatorDelegation; +using Nekoyume.Action.Guild.Migration.LegacyModels; namespace Nekoyume.Action { @@ -156,7 +157,8 @@ public override IWorld Execute(IActionContext context) .SetLegacyState(RedeemCodeState.Address, RedeemCode) .SetLegacyState(ActivatedAccountsState.Address, ActivatedAccounts) .SetLegacyState(GoldCurrencyState.Address, GoldCurrency) - .SetLegacyState(Addresses.GoldDistribution, GoldDistributions); + .SetLegacyState(Addresses.GoldDistribution, GoldDistributions) + .SetDelegationMigrationHeight(0); if (!(AdminAddressState is null)) { From a9a49cb1d687ecac2e5036188097e5a62ab4c0e3 Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 14 Nov 2024 18:20:51 +0900 Subject: [PATCH 162/206] refactor: Remove Unnecessary code for migration --- Lib9c/Action/Buy.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Lib9c/Action/Buy.cs b/Lib9c/Action/Buy.cs index c03194d2dd..a202e16be0 100644 --- a/Lib9c/Action/Buy.cs +++ b/Lib9c/Action/Buy.cs @@ -251,12 +251,6 @@ public override IWorld Execute(IActionContext context) var taxedPrice = order.Price - tax; // Transfer tax. - states = states.TransferAsset( - context, - context.Signer, - Addresses.RewardPool, - tax); - var feeAddress = Addresses.RewardPool; // TODO: [GuildMigration] Remove this after migration if (states.GetDelegationMigrationHeight() is long migrationHeight From db75815024a0cc949e84e78be69dca43b372aa64 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 14 Nov 2024 20:32:52 +0900 Subject: [PATCH 163/206] chore: Remove obsolete for cancellable block index --- Lib9c/Model/Stake/StakeState.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib9c/Model/Stake/StakeState.cs b/Lib9c/Model/Stake/StakeState.cs index 261983e225..6ac1e628c7 100644 --- a/Lib9c/Model/Stake/StakeState.cs +++ b/Lib9c/Model/Stake/StakeState.cs @@ -18,7 +18,6 @@ public static Address DeriveAddress(Address address) => public readonly long StartedBlockIndex; public readonly long ReceivedBlockIndex; - [Obsolete("Not used because of guild system")] public long CancellableBlockIndex => StartedBlockIndex + Contract.LockupInterval; From 7c937766cf4bd3888db61573b22fc1b26e062d24 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 14 Nov 2024 20:56:08 +0900 Subject: [PATCH 164/206] chore: Remove obsolete from migration actions --- Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs | 1 - Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs b/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs index 06cb1d38b2..48d1ad2b9d 100644 --- a/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs +++ b/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs @@ -19,7 +19,6 @@ public class MigrateDelegationHeight : ActionBase public long Height { get; private set; } - [Obsolete("Don't call in code.", error: false)] public MigrateDelegationHeight() { } diff --git a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs index ad484a4740..cd7dbc37fa 100644 --- a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs +++ b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs @@ -19,7 +19,6 @@ public class MigratePlanetariumGuild : ActionBase { public const string TypeIdentifier = "migrate_planetarium_guild"; - [Obsolete("Don't call in code.", error: false)] public MigratePlanetariumGuild() { } From e7b4bd5856bdc7dfa77e8009c3648c1e8a1f71ad Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 14 Nov 2024 20:57:09 +0900 Subject: [PATCH 165/206] bump: Libplanet 5.4.0 --- .Libplanet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.Libplanet b/.Libplanet index c1a1c5e2e3..2740bf1c24 160000 --- a/.Libplanet +++ b/.Libplanet @@ -1 +1 @@ -Subproject commit c1a1c5e2e377b147dc96e7dc480bca3481c41a99 +Subproject commit 2740bf1c2461d547532796edfd310f7fe71677f7 From c0b1e05239318b49cba2dc76ce736770d4f28303 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Thu, 14 Nov 2024 20:57:37 +0900 Subject: [PATCH 166/206] Allow buff limit in stat calculate --- .Lib9c.Tests/Model/CharacterStatsTest.cs | 26 +++++++++++++++++ Lib9c/Model/Stat/CharacterStats.cs | 37 ++++++++++++++---------- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/.Lib9c.Tests/Model/CharacterStatsTest.cs b/.Lib9c.Tests/Model/CharacterStatsTest.cs index 7ea14684d7..bc191d0014 100644 --- a/.Lib9c.Tests/Model/CharacterStatsTest.cs +++ b/.Lib9c.Tests/Model/CharacterStatsTest.cs @@ -38,5 +38,31 @@ public void DeBuffLimit() stats.AddBuff(deBuff2, deBuffLimitSheet); Assert.Equal(0, stats.CRI); } + + [Fact] + public void BuffLimit() + { + var stats = + new CharacterStats( + _tableSheets.CharacterSheet[GameConfig.DefaultAvatarCharacterId], + 1); + var deBuffLimitSheet = new DeBuffLimitSheet(); + // +60% atk with no limit + var buff = new StatBuff(_tableSheets.StatBuffSheet[503011]); + var groupId = buff.RowData.GroupId; + var atk = stats.ATK; + stats.AddBuff(buff, deBuffLimitSheet); + Assert.Equal(atk * 1.6m, stats.ATK); + + // reset stats + stats.RemoveBuff(buff); + Assert.Equal(atk, stats.ATK); + // +60% atk but limit 50% stats + deBuffLimitSheet.Set($"group_id,percentage\n{groupId},50"); + var modifier = deBuffLimitSheet[groupId].GetModifier(buff.RowData.StatType); + stats.AddBuff(buff, deBuffLimitSheet); + Assert.Equal(atk * 1.5m, modifier.GetModifiedAll(atk)); + Assert.Equal(atk * 1.5m, stats.ATK); + } } } diff --git a/Lib9c/Model/Stat/CharacterStats.cs b/Lib9c/Model/Stat/CharacterStats.cs index fc4dd30ce0..b40a18c2fe 100644 --- a/Lib9c/Model/Stat/CharacterStats.cs +++ b/Lib9c/Model/Stat/CharacterStats.cs @@ -617,28 +617,35 @@ int runeLevelBonus SetCollections(collectionStatModifiers); } + /// + /// Returns a based on the upper limit from . + /// + /// for modify stats. + /// Upper limit sheet data. + /// if buff modify stats 100% but limit 50% set in , + /// it will return 50%, else return 100% , + /// if de-buff modify stats -100% but limit -50% set in , + /// it will return -50%, else return -100% + /// private StatModifier GetBuffModifier(Buff.StatBuff buff, DeBuffLimitSheet deBuffLimitSheet) { var modifier = buff.GetModifier(); - if (buff.IsDebuff()) + try { - try - { - var statType = modifier.StatType; - var limitModifier = deBuffLimitSheet[buff.RowData.GroupId].GetModifier(statType); - var stat = _statMap.GetStatAsLong(statType); - var buffModified = modifier.GetModifiedValue(stat); - var maxModified = (long)limitModifier.GetModifiedValue(stat); - if (maxModified > buffModified) - { - return limitModifier; - } - } - catch (KeyNotFoundException) + var statType = modifier.StatType; + var limitModifier = deBuffLimitSheet[buff.RowData.GroupId].GetModifier(statType); + var stat = _statMap.GetStatAsLong(statType); + var buffModified = modifier.GetModifiedValue(stat); + var maxModified = (long)limitModifier.GetModifiedValue(stat); + if (buff.IsDebuff() && maxModified > buffModified || buff.IsBuff() && maxModified < buffModified) { - // pass + return limitModifier; } } + catch (KeyNotFoundException) + { + // pass + } return modifier; } From 8f704af64c8542598d170631c961b39ca784edff Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Thu, 14 Nov 2024 21:01:50 +0900 Subject: [PATCH 167/206] Rename DebuffLimitSheet to BuffLimitSheet --- .Lib9c.Tests/Action/RaidTest.cs | 4 +-- .../Action/Scenario/AuraScenarioTest.cs | 2 +- .../Action/Scenario/GrimoireScenarioTest.cs | 2 +- .../AdventureBossSimulatorTest.cs | 4 +-- .Lib9c.Tests/Model/ArenaSimulatorTest.cs | 14 +++++----- .Lib9c.Tests/Model/BattleLogTest.cs | 2 +- .Lib9c.Tests/Model/CharacterStatsTest.cs | 20 +++++++------- .Lib9c.Tests/Model/PlayerTest.cs | 26 +++++++++---------- .Lib9c.Tests/Model/RaidSimulatorV3Test.cs | 6 ++--- .../Model/Skill/Adventure/DoubleAttackTest.cs | 2 +- .../Model/Skill/Adventure/NormalAttackTest.cs | 6 ++--- .../Skill/Adventure/ShatterStrikeTest.cs | 4 +-- .../Model/Skill/Raid/NormalAttackTest.cs | 2 +- .Lib9c.Tests/Model/StageSimulatorTest.cs | 6 ++--- .Lib9c.Tests/TableSheets.cs | 2 +- .../AdventureBoss/ExploreAdventureBoss.cs | 6 ++--- Lib9c/Action/BattleArena.cs | 6 ++--- Lib9c/Action/EventDungeonBattle.cs | 6 ++--- Lib9c/Action/HackAndSlash.cs | 6 ++--- Lib9c/Action/Raid.cs | 4 +-- Lib9c/Arena/ArenaSimulator.cs | 6 ++--- Lib9c/Arena/IArenaSimulator.cs | 2 +- .../AdventureBoss/AdventureBossSimulator.cs | 4 +-- Lib9c/Battle/RaidSimulator.cs | 4 +-- Lib9c/Battle/Simulator.cs | 2 +- Lib9c/Battle/StageSimulator.cs | 4 +-- Lib9c/Model/Character/ArenaCharacter.cs | 6 ++--- Lib9c/Model/Character/CharacterBase.cs | 4 +-- Lib9c/Model/Stat/CharacterStats.cs | 22 ++++++++-------- ...eBuffLimitSheet.csv => BuffLimitSheet.csv} | 0 ...{DeBuffLimitSheet.cs => BuffLimitSheet.cs} | 6 ++--- 31 files changed, 94 insertions(+), 96 deletions(-) rename Lib9c/TableCSV/Skill/{DeBuffLimitSheet.csv => BuffLimitSheet.csv} (100%) rename Lib9c/TableData/Skill/{DeBuffLimitSheet.cs => BuffLimitSheet.cs} (80%) diff --git a/.Lib9c.Tests/Action/RaidTest.cs b/.Lib9c.Tests/Action/RaidTest.cs index 8159364ab9..a42d42298d 100644 --- a/.Lib9c.Tests/Action/RaidTest.cs +++ b/.Lib9c.Tests/Action/RaidTest.cs @@ -265,7 +265,7 @@ int runeId2 raidSimulatorSheets, _tableSheets.CostumeStatSheet, new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); simulator.Simulate(); @@ -539,7 +539,7 @@ public void Execute_With_Reward() _tableSheets.GetRaidSimulatorSheets(), _tableSheets.CostumeStatSheet, new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); simulator.Simulate(); diff --git a/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs index ca69be09e4..bf349e0191 100644 --- a/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs @@ -267,7 +267,7 @@ public void Arena() _tableSheets.GetArenaSimulatorSheets(), new List(), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet); // Check player, enemy equip aura foreach (var spawn in log.OfType()) diff --git a/.Lib9c.Tests/Action/Scenario/GrimoireScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/GrimoireScenarioTest.cs index b3fbeef251..876738a23b 100644 --- a/.Lib9c.Tests/Action/Scenario/GrimoireScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/GrimoireScenarioTest.cs @@ -267,7 +267,7 @@ public void Arena() _tableSheets.GetArenaSimulatorSheets(), new List(), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); // Check player, enemy equip charm diff --git a/.Lib9c.Tests/Model/AdventureBoss/AdventureBossSimulatorTest.cs b/.Lib9c.Tests/Model/AdventureBoss/AdventureBossSimulatorTest.cs index 4d42fbfdae..79f6da065c 100644 --- a/.Lib9c.Tests/Model/AdventureBoss/AdventureBossSimulatorTest.cs +++ b/.Lib9c.Tests/Model/AdventureBoss/AdventureBossSimulatorTest.cs @@ -72,7 +72,7 @@ public AdventureBossSimulator Simulate() _tableSheets.MaterialItemSheet ), statModifiers, - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); @@ -149,7 +149,7 @@ public void AddBreakthrough(bool simulate, int bossId, int firstFloor, int lastF { new (StatType.ATK, StatModifier.OperationType.Add, 100), }, - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); } diff --git a/.Lib9c.Tests/Model/ArenaSimulatorTest.cs b/.Lib9c.Tests/Model/ArenaSimulatorTest.cs index 8a741890c6..a7b0683193 100644 --- a/.Lib9c.Tests/Model/ArenaSimulatorTest.cs +++ b/.Lib9c.Tests/Model/ArenaSimulatorTest.cs @@ -79,7 +79,7 @@ public void Simulate() new (StatType.DEF, StatModifier.OperationType.Add, 1), new (StatType.HP, StatModifier.OperationType.Add, 100), }, - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var row = @@ -135,7 +135,7 @@ public void HpIncreasingModifier(int? modifier) var myDigest = new ArenaPlayerDigest(_avatarState1, _arenaAvatarState1); var enemyDigest = new ArenaPlayerDigest(_avatarState2, _arenaAvatarState2); var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); - var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), _tableSheets.DeBuffLimitSheet, _tableSheets.BuffLinkSheet); + var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet); var expectedHpModifier = modifier ?? 2; Assert.Equal(_random, simulator.Random); @@ -180,7 +180,7 @@ public void TestSpeedModifierBySkill() var myDigest = new ArenaPlayerDigest(_avatarState1, arenaAvatarState1); var enemyDigest = new ArenaPlayerDigest(_avatarState2, arenaAvatarState2); var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); - var unskilledLog = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), _tableSheets.DeBuffLimitSheet, _tableSheets.BuffLinkSheet); + var unskilledLog = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet); // foreach (var log in unskilledLog) // { // _testOutputHelper.WriteLine($"{log.Character.Id} :: {log}"); @@ -233,7 +233,7 @@ public void TestSpeedModifierBySkill() myDigest = new ArenaPlayerDigest(_avatarState1, arenaAvatarState1); enemyDigest = new ArenaPlayerDigest(_avatarState2, arenaAvatarState2); arenaSheets = _tableSheets.GetArenaSimulatorSheets(); - var skilledLog = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), _tableSheets.DeBuffLimitSheet, _tableSheets.BuffLinkSheet); + var skilledLog = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet); // foreach (var log in skilledLog) // { // _testOutputHelper.WriteLine($"{log.Character.Id} :: {log}"); @@ -277,7 +277,7 @@ public void Thorns() var myDigest = new ArenaPlayerDigest(avatarState1, arenaAvatarState1); var enemyDigest = new ArenaPlayerDigest(avatarState2, arenaAvatarState2); var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); - var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), _tableSheets.DeBuffLimitSheet, _tableSheets.BuffLinkSheet, true); + var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet, true); var ticks = log.Events .OfType() .ToList(); @@ -338,7 +338,7 @@ public void Bleed() var myDigest = new ArenaPlayerDigest(avatarState1, runes, runeSlotState); var enemyDigest = new ArenaPlayerDigest(avatarState2, runes, runeSlotState); var arenaSheets = _tableSheets.GetArenaSimulatorSheets(runeOptionSheet); - var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets, modifiers, modifiers, _tableSheets.DeBuffLimitSheet, _tableSheets.BuffLinkSheet, true); + var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets, modifiers, modifiers, _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet, true); var spawns = log.Events.OfType().ToList(); Assert.All(spawns, spawn => Assert.Equal(finalAtk, spawn.Character.ATK)); var ticks = log.Events @@ -382,7 +382,7 @@ public void IceShield() var myDigest = new ArenaPlayerDigest(avatarState1, new List(), new List { equipment, }, runes, runeSlotState); var enemyDigest = new ArenaPlayerDigest(avatarState2, new List(), new List { equipment, }, runes, runeSlotState); var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); - var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), _tableSheets.DeBuffLimitSheet, _tableSheets.BuffLinkSheet, true); + var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet, true); var spawns = log.Events.OfType().ToList(); Assert.All(spawns, spawn => Assert.Equal(baseAtk, spawn.Character.ATK)); var ticks = log.Events diff --git a/.Lib9c.Tests/Model/BattleLogTest.cs b/.Lib9c.Tests/Model/BattleLogTest.cs index 0b5564ba51..cc6abb82ed 100644 --- a/.Lib9c.Tests/Model/BattleLogTest.cs +++ b/.Lib9c.Tests/Model/BattleLogTest.cs @@ -55,7 +55,7 @@ public void IsClearBeforeSimulate() _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); Assert.False(simulator.Log.IsClear); diff --git a/.Lib9c.Tests/Model/CharacterStatsTest.cs b/.Lib9c.Tests/Model/CharacterStatsTest.cs index bc191d0014..aa0412c8f7 100644 --- a/.Lib9c.Tests/Model/CharacterStatsTest.cs +++ b/.Lib9c.Tests/Model/CharacterStatsTest.cs @@ -22,20 +22,20 @@ public void DeBuffLimit() new CharacterStats( _tableSheets.CharacterSheet[GameConfig.DefaultAvatarCharacterId], 1); - var deBuffLimitSheet = new DeBuffLimitSheet(); + var buffLimitSheet = new BuffLimitSheet(); // -100% def but limit -50% stats var deBuff = new StatBuff(_tableSheets.StatBuffSheet[503012]); var groupId = deBuff.RowData.GroupId; - deBuffLimitSheet.Set($"group_id,percentage\n{groupId},-50"); + buffLimitSheet.Set($"group_id,percentage\n{groupId},-50"); var def = stats.DEF; - stats.AddBuff(deBuff, deBuffLimitSheet); - var modifier = deBuffLimitSheet[groupId].GetModifier(deBuff.RowData.StatType); + stats.AddBuff(deBuff, buffLimitSheet); + var modifier = buffLimitSheet[groupId].GetModifier(deBuff.RowData.StatType); Assert.Equal(modifier.GetModifiedAll(def), stats.DEF); // -500% critical with no limit var deBuff2 = new StatBuff(_tableSheets.StatBuffSheet[204003]); Assert.True(stats.CRI > 0); - stats.AddBuff(deBuff2, deBuffLimitSheet); + stats.AddBuff(deBuff2, buffLimitSheet); Assert.Equal(0, stats.CRI); } @@ -46,21 +46,21 @@ public void BuffLimit() new CharacterStats( _tableSheets.CharacterSheet[GameConfig.DefaultAvatarCharacterId], 1); - var deBuffLimitSheet = new DeBuffLimitSheet(); + var buffLimitSheet = new BuffLimitSheet(); // +60% atk with no limit var buff = new StatBuff(_tableSheets.StatBuffSheet[503011]); var groupId = buff.RowData.GroupId; var atk = stats.ATK; - stats.AddBuff(buff, deBuffLimitSheet); + stats.AddBuff(buff, buffLimitSheet); Assert.Equal(atk * 1.6m, stats.ATK); // reset stats stats.RemoveBuff(buff); Assert.Equal(atk, stats.ATK); // +60% atk but limit 50% stats - deBuffLimitSheet.Set($"group_id,percentage\n{groupId},50"); - var modifier = deBuffLimitSheet[groupId].GetModifier(buff.RowData.StatType); - stats.AddBuff(buff, deBuffLimitSheet); + buffLimitSheet.Set($"group_id,percentage\n{groupId},50"); + var modifier = buffLimitSheet[groupId].GetModifier(buff.RowData.StatType); + stats.AddBuff(buff, buffLimitSheet); Assert.Equal(atk * 1.5m, modifier.GetModifiedAll(atk)); Assert.Equal(atk * 1.5m, stats.ATK); } diff --git a/.Lib9c.Tests/Model/PlayerTest.cs b/.Lib9c.Tests/Model/PlayerTest.cs index 993ed7f4cf..fe3c12e8eb 100644 --- a/.Lib9c.Tests/Model/PlayerTest.cs +++ b/.Lib9c.Tests/Model/PlayerTest.cs @@ -63,7 +63,7 @@ public void TickAlive() _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var player = simulator.Player; @@ -100,7 +100,7 @@ public void TickDead() _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var player = simulator.Player; @@ -154,7 +154,7 @@ public void UseDoubleAttack(SkillCategory skillCategory) _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var player = simulator.Player; @@ -208,7 +208,7 @@ public void UseAuraSkill() _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - new DeBuffLimitSheet(), + new BuffLimitSheet(), _tableSheets.BuffLinkSheet ); var player = simulator.Player; @@ -266,7 +266,7 @@ public void UseAuraBuffWithFood() _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - new DeBuffLimitSheet(), + new BuffLimitSheet(), _tableSheets.BuffLinkSheet ); var player = simulator.Player; @@ -397,7 +397,7 @@ public void GetExpV3(int nextLevel, bool log) _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var player = simulator.Player; @@ -479,7 +479,7 @@ public void GetStun() _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var player = simulator.Player; @@ -551,7 +551,7 @@ public void GiveStun() _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var skill = SkillFactory.Get(_tableSheets.SkillSheet[700004], 0, 100, 0, StatType.NONE); @@ -637,7 +637,7 @@ public void Vampiric(int duration, int percent) _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var player = simulator.Player; @@ -752,7 +752,7 @@ public void StatsLayerTest() new CostumeStatSheet(), new List(), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var player = simulator.Player; @@ -825,7 +825,7 @@ public void StatsLayerTest() var addBuffAtk = addBuffModifier.GetModifiedValue(costumeLayerAtk); Assert.Equal(10, addBuffAtk); - player.Stats.SetBuffs(statBuffs, _tableSheets.DeBuffLimitSheet); + player.Stats.SetBuffs(statBuffs, _tableSheets.BuffLimitSheet); Assert.Equal(player.ATK, collectionLayerAtk + addBuffAtk + percentageBuffAtk); // 20 + 1 + 18 + 1829 + 235 + 100 + 1662 // Assert.Equal(3865, player.ATK); @@ -930,7 +930,7 @@ public void IncreaseHpForArena() statBuffs.Add(percentageBuff); var percentageModifier = percentageBuff.GetModifier(); var percentageBuffAtk = (long)percentageModifier.GetModifiedValue(arenaHp); - player.Stats.SetBuffs(statBuffs, _tableSheets.DeBuffLimitSheet); + player.Stats.SetBuffs(statBuffs, _tableSheets.BuffLimitSheet); Assert.Equal(arenaHp + percentageBuffAtk, player.HP); Assert.Equal(arenaHp, player.Stats.StatWithoutBuffs.HP); } @@ -967,7 +967,7 @@ public void IceShield() _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var player = simulator.Player; diff --git a/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs b/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs index cfe6f63851..6ae2e93699 100644 --- a/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs +++ b/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs @@ -57,7 +57,7 @@ public void Simulate() { new (StatType.DEF, StatModifier.OperationType.Percentage, 100), }, - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet); Assert.Equal(_random, simulator.Random); Assert.Equal(simulator.Player.Stats.BaseStats.DEF * 2, simulator.Player.Stats.DEF); @@ -101,7 +101,7 @@ public void TestSpeedMultiplierBySkill() _tableSheets.GetRaidSimulatorSheets(), _tableSheets.CostumeStatSheet, new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var player = simulator.Player; @@ -144,7 +144,7 @@ public void TestSpeedMultiplierBySkill() _tableSheets.GetRaidSimulatorSheets(), _tableSheets.CostumeStatSheet, new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); player = simulator.Player; diff --git a/.Lib9c.Tests/Model/Skill/Adventure/DoubleAttackTest.cs b/.Lib9c.Tests/Model/Skill/Adventure/DoubleAttackTest.cs index 7706a73163..6f82e7a135 100644 --- a/.Lib9c.Tests/Model/Skill/Adventure/DoubleAttackTest.cs +++ b/.Lib9c.Tests/Model/Skill/Adventure/DoubleAttackTest.cs @@ -98,7 +98,7 @@ bool copyCharacter _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet, copyCharacter ); diff --git a/.Lib9c.Tests/Model/Skill/Adventure/NormalAttackTest.cs b/.Lib9c.Tests/Model/Skill/Adventure/NormalAttackTest.cs index 6f97259c1c..ea509f25aa 100644 --- a/.Lib9c.Tests/Model/Skill/Adventure/NormalAttackTest.cs +++ b/.Lib9c.Tests/Model/Skill/Adventure/NormalAttackTest.cs @@ -68,7 +68,7 @@ public void Use(bool copyCharacter) _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet, copyCharacter ); @@ -134,7 +134,7 @@ public void FocusSkill() _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var player = new Player(avatarState, simulator); @@ -184,7 +184,7 @@ public void FocusSkill() _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); player = new Player(avatarState, simulator); diff --git a/.Lib9c.Tests/Model/Skill/Adventure/ShatterStrikeTest.cs b/.Lib9c.Tests/Model/Skill/Adventure/ShatterStrikeTest.cs index 6032c52bdf..e5329270fd 100644 --- a/.Lib9c.Tests/Model/Skill/Adventure/ShatterStrikeTest.cs +++ b/.Lib9c.Tests/Model/Skill/Adventure/ShatterStrikeTest.cs @@ -70,7 +70,7 @@ public void Use(int enemyHp, int ratioBp, bool expectedEnemyDead, bool copyChara _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet, copyCharacter, gameConfigState.ShatterStrikeMaxDamage @@ -151,7 +151,7 @@ public void Use_MaxDamage(bool copyCharacter) _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet, copyCharacter, gameConfigState.ShatterStrikeMaxDamage diff --git a/.Lib9c.Tests/Model/Skill/Raid/NormalAttackTest.cs b/.Lib9c.Tests/Model/Skill/Raid/NormalAttackTest.cs index 99b0309e29..911866e761 100644 --- a/.Lib9c.Tests/Model/Skill/Raid/NormalAttackTest.cs +++ b/.Lib9c.Tests/Model/Skill/Raid/NormalAttackTest.cs @@ -45,7 +45,7 @@ public void FocusSkill() { new (StatType.DEF, StatModifier.OperationType.Percentage, 100), }, - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var player = simulator.Player; diff --git a/.Lib9c.Tests/Model/StageSimulatorTest.cs b/.Lib9c.Tests/Model/StageSimulatorTest.cs index 846b365296..7b4180135a 100644 --- a/.Lib9c.Tests/Model/StageSimulatorTest.cs +++ b/.Lib9c.Tests/Model/StageSimulatorTest.cs @@ -71,7 +71,7 @@ public void Simulate() { new (StatType.ATK, StatModifier.OperationType.Add, 100), }, - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); @@ -133,7 +133,7 @@ public void TestSpeedModifierBySkill() _tableSheets.StageSheet[3], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var unskilledPlayer = simulator.Player; @@ -188,7 +188,7 @@ public void TestSpeedModifierBySkill() _tableSheets.StageSheet[3], _tableSheets.MaterialItemSheet), new List(), - _tableSheets.DeBuffLimitSheet, + _tableSheets.BuffLimitSheet, _tableSheets.BuffLinkSheet ); var skilledPlayer = simulator.Player; diff --git a/.Lib9c.Tests/TableSheets.cs b/.Lib9c.Tests/TableSheets.cs index 5f15693b32..dec49de91b 100644 --- a/.Lib9c.Tests/TableSheets.cs +++ b/.Lib9c.Tests/TableSheets.cs @@ -249,7 +249,7 @@ public TableSheets(Dictionary sheets, bool ignoreFailedGetProper public CollectionSheet CollectionSheet { get; private set; } - public DeBuffLimitSheet DeBuffLimitSheet { get; set; } + public BuffLimitSheet BuffLimitSheet { get; set; } public BuffLinkSheet BuffLinkSheet { get; set; } diff --git a/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs b/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs index 8d3f455a35..9214cf00ed 100644 --- a/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs +++ b/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs @@ -142,7 +142,7 @@ public override IWorld Execute(IActionContext context) typeof(CollectionSheet), typeof(EnemySkillSheet), typeof(CostumeStatSheet), - typeof(DeBuffLimitSheet), + typeof(BuffLimitSheet), typeof(BuffLinkSheet), typeof(ItemRequirementSheet), typeof(EquipmentItemRecipeSheet), @@ -226,7 +226,7 @@ public override IWorld Execute(IActionContext context) var enemySkillSheet = sheets.GetSheet(); var costumeStatSheet = sheets.GetSheet(); var materialItemSheet = sheets.GetSheet(); - var deBuffLimitSheet = sheets.GetSheet(); + var buffLimitSheet = sheets.GetSheet(); var buffLinkSheet = sheets.GetSheet(); if (gameConfigState is null) { @@ -290,7 +290,7 @@ public override IWorld Execute(IActionContext context) costumeStatSheet, rewards, collectionModifiers, - deBuffLimitSheet, + buffLimitSheet, buffLinkSheet, false, gameConfigState.ShatterStrikeMaxDamage diff --git a/Lib9c/Action/BattleArena.cs b/Lib9c/Action/BattleArena.cs index a802db8b61..683c71a1c7 100644 --- a/Lib9c/Action/BattleArena.cs +++ b/Lib9c/Action/BattleArena.cs @@ -133,7 +133,7 @@ public override IWorld Execute(IActionContext context) typeof(MaterialItemSheet), typeof(RuneListSheet), typeof(RuneLevelBonusSheet), - typeof(DeBuffLimitSheet), + typeof(BuffLimitSheet), typeof(BuffLinkSheet), }; if (collectionExist) @@ -373,7 +373,7 @@ public override IWorld Execute(IActionContext context) enemyRuneSlotState); var previousMyScore = myArenaScore.Score; var arenaSheets = sheets.GetArenaSimulatorSheets(); - var deBuffLimitSheet = sheets.GetSheet(); + var buffLimitSheet = sheets.GetSheet(); var winCount = 0; var defeatCount = 0; var rewards = new List(); @@ -405,7 +405,7 @@ public override IWorld Execute(IActionContext context) arenaSheets, collectionModifiers[myAvatarAddress], collectionModifiers[enemyAvatarAddress], - deBuffLimitSheet, + buffLimitSheet, buffLinkSheet, true); if (log.Result.Equals(ArenaLog.ArenaResult.Win)) diff --git a/Lib9c/Action/EventDungeonBattle.cs b/Lib9c/Action/EventDungeonBattle.cs index e357c1697a..4e22162716 100644 --- a/Lib9c/Action/EventDungeonBattle.cs +++ b/Lib9c/Action/EventDungeonBattle.cs @@ -165,7 +165,7 @@ public override IWorld Execute(IActionContext context) typeof(MaterialItemSheet), typeof(RuneListSheet), typeof(RuneLevelBonusSheet), - typeof(DeBuffLimitSheet), + typeof(BuffLimitSheet), typeof(BuffLinkSheet), }; if (collectionExist) @@ -348,7 +348,7 @@ is Bencodex.Types.List serializedEventDungeonInfoList collectionModifiers = collectionState.GetModifiers(collectionSheet); } - var deBuffLimitSheet = sheets.GetSheet(); + var buffLimitSheet = sheets.GetSheet(); var simulator = new StageSimulator( random, avatarState, @@ -371,7 +371,7 @@ is Bencodex.Types.List serializedEventDungeonInfoList sheets.GetSheet(), PlayCount), collectionModifiers, - deBuffLimitSheet, + buffLimitSheet, sheets.GetSheet(), shatterStrikeMaxDamage: gameConfigState.ShatterStrikeMaxDamage); simulator.Simulate(); diff --git a/Lib9c/Action/HackAndSlash.cs b/Lib9c/Action/HackAndSlash.cs index 8fab7c36f3..13561f5acb 100644 --- a/Lib9c/Action/HackAndSlash.cs +++ b/Lib9c/Action/HackAndSlash.cs @@ -191,7 +191,7 @@ public IWorld Execute( typeof(StakeActionPointCoefficientSheet), typeof(RuneListSheet), typeof(RuneLevelBonusSheet), - typeof(DeBuffLimitSheet), + typeof(BuffLimitSheet), typeof(BuffLinkSheet), }; if (collectionExist) @@ -553,7 +553,7 @@ public IWorld Execute( collectionModifiers = collectionState.GetModifiers(collectionSheet); } - var deBuffLimitSheet = sheets.GetSheet(); + var buffLimitSheet = sheets.GetSheet(); var buffLinkSheet = sheets.GetSheet(); for (var i = 0; i < TotalPlayCount; i++) { @@ -579,7 +579,7 @@ public IWorld Execute( costumeStatSheet, rewards, collectionModifiers, - deBuffLimitSheet, + buffLimitSheet, buffLinkSheet, false, gameConfigState.ShatterStrikeMaxDamage); diff --git a/Lib9c/Action/Raid.cs b/Lib9c/Action/Raid.cs index 4775f90581..d16af31540 100644 --- a/Lib9c/Action/Raid.cs +++ b/Lib9c/Action/Raid.cs @@ -86,7 +86,7 @@ public override IWorld Execute(IActionContext context) typeof(RuneSheet), typeof(RuneListSheet), typeof(RuneLevelBonusSheet), - typeof(DeBuffLimitSheet), + typeof(BuffLimitSheet), typeof(BuffLinkSheet), typeof(MaterialItemSheet), }; @@ -237,7 +237,7 @@ public override IWorld Execute(IActionContext context) raidSimulatorSheets, sheets.GetSheet(), collectionModifiers, - sheets.GetSheet(), + sheets.GetSheet(), sheets.GetSheet(), shatterStrikeMaxDamage: gameConfigState.ShatterStrikeMaxDamage ); diff --git a/Lib9c/Arena/ArenaSimulator.cs b/Lib9c/Arena/ArenaSimulator.cs index 43848bdd0a..6b9c112f66 100644 --- a/Lib9c/Arena/ArenaSimulator.cs +++ b/Lib9c/Arena/ArenaSimulator.cs @@ -23,7 +23,7 @@ public class ArenaSimulator : IArenaSimulator public int HpModifier { get; } public long ShatterStrikeMaxDamage { get; private set; } - public DeBuffLimitSheet DeBuffLimitSheet { get; private set; } + public BuffLimitSheet BuffLimitSheet { get; private set; } public BuffLinkSheet BuffLinkSheet { get; set; } public ArenaSimulator(IRandom random, @@ -43,12 +43,12 @@ public ArenaLog Simulate( ArenaSimulatorSheets sheets, List challengerCollectionModifiers, List enemyCollectionModifiers, - DeBuffLimitSheet deBuffLimitSheet, + BuffLimitSheet buffLimitSheet, BuffLinkSheet buffLinkSheet, bool setExtraValueBuffBeforeGetBuffs = false) { Log = new ArenaLog(); - DeBuffLimitSheet = deBuffLimitSheet; + BuffLimitSheet = buffLimitSheet; BuffLinkSheet = buffLinkSheet; var players = SpawnPlayers(this, challenger, enemy, sheets, Log, challengerCollectionModifiers, enemyCollectionModifiers, setExtraValueBuffBeforeGetBuffs); Turn = 1; diff --git a/Lib9c/Arena/IArenaSimulator.cs b/Lib9c/Arena/IArenaSimulator.cs index 98c8977cd8..d91ab41fdd 100644 --- a/Lib9c/Arena/IArenaSimulator.cs +++ b/Lib9c/Arena/IArenaSimulator.cs @@ -11,7 +11,7 @@ public interface IArenaSimulator public IRandom Random { get; } public int Turn { get; } public long ShatterStrikeMaxDamage { get; } - public DeBuffLimitSheet DeBuffLimitSheet { get; } + public BuffLimitSheet BuffLimitSheet { get; } BuffLinkSheet BuffLinkSheet { get; set; } } } diff --git a/Lib9c/Battle/AdventureBoss/AdventureBossSimulator.cs b/Lib9c/Battle/AdventureBoss/AdventureBossSimulator.cs index 6beea1906d..6da8f4359f 100644 --- a/Lib9c/Battle/AdventureBoss/AdventureBossSimulator.cs +++ b/Lib9c/Battle/AdventureBoss/AdventureBossSimulator.cs @@ -66,7 +66,7 @@ public AdventureBossSimulator( CostumeStatSheet costumeStatSheet, List waveRewards, List collectionModifiers, - DeBuffLimitSheet deBuffLimitSheet, + BuffLimitSheet buffLimitSheet, BuffLinkSheet buffLinkSheet, bool logEvent = true, long shatterStrikeMaxDamage = 400_000 @@ -80,7 +80,7 @@ public AdventureBossSimulator( shatterStrikeMaxDamage ) { - DeBuffLimitSheet = deBuffLimitSheet; + BuffLimitSheet = buffLimitSheet; BuffLinkSheet = buffLinkSheet; var runeOptionSheet = simulatorSheets.RuneOptionSheet; var skillSheet = simulatorSheets.SkillSheet; diff --git a/Lib9c/Battle/RaidSimulator.cs b/Lib9c/Battle/RaidSimulator.cs index 7a1da8f0f1..0046a801e5 100644 --- a/Lib9c/Battle/RaidSimulator.cs +++ b/Lib9c/Battle/RaidSimulator.cs @@ -39,12 +39,12 @@ public RaidSimulator(int bossId, RaidSimulatorSheets simulatorSheets, CostumeStatSheet costumeStatSheet, List collectionModifiers, - DeBuffLimitSheet deBuffLimitSheet, + BuffLimitSheet buffLimitSheet, BuffLinkSheet buffLinkSheet, long shatterStrikeMaxDamage = 400_000) : base(random, avatarState, foods, simulatorSheets, shatterStrikeMaxDamage: shatterStrikeMaxDamage) { - DeBuffLimitSheet = deBuffLimitSheet; + BuffLimitSheet = buffLimitSheet; BuffLinkSheet = buffLinkSheet; var runeOptionSheet = simulatorSheets.RuneOptionSheet; var skillSheet = simulatorSheets.SkillSheet; diff --git a/Lib9c/Battle/Simulator.cs b/Lib9c/Battle/Simulator.cs index 917541ba9a..8857857e29 100644 --- a/Lib9c/Battle/Simulator.cs +++ b/Lib9c/Battle/Simulator.cs @@ -28,7 +28,7 @@ public abstract class Simulator : ISimulator public readonly CharacterSheet CharacterSheet; public readonly CharacterLevelSheet CharacterLevelSheet; public readonly EquipmentItemSetEffectSheet EquipmentItemSetEffectSheet; - public DeBuffLimitSheet DeBuffLimitSheet { get; protected set; } + public BuffLimitSheet BuffLimitSheet { get; protected set; } public BuffLinkSheet BuffLinkSheet { get; set; } public long ShatterStrikeMaxDamage { get; private set; } diff --git a/Lib9c/Battle/StageSimulator.cs b/Lib9c/Battle/StageSimulator.cs index 64b440bf22..d57996b7a6 100644 --- a/Lib9c/Battle/StageSimulator.cs +++ b/Lib9c/Battle/StageSimulator.cs @@ -51,7 +51,7 @@ public StageSimulator(IRandom random, CostumeStatSheet costumeStatSheet, List waveRewards, List collectionModifiers, - DeBuffLimitSheet deBuffLimitSheet, + BuffLimitSheet buffLimitSheet, BuffLinkSheet buffLinkSheet, bool logEvent = true, long shatterStrikeMaxDamage = 400_000 @@ -65,7 +65,7 @@ public StageSimulator(IRandom random, shatterStrikeMaxDamage ) { - DeBuffLimitSheet = deBuffLimitSheet; + BuffLimitSheet = buffLimitSheet; BuffLinkSheet = buffLinkSheet; var runeOptionSheet = simulatorSheets.RuneOptionSheet; var skillSheet = simulatorSheets.SkillSheet; diff --git a/Lib9c/Model/Character/ArenaCharacter.cs b/Lib9c/Model/Character/ArenaCharacter.cs index 7c836d2476..03e38b4dc4 100644 --- a/Lib9c/Model/Character/ArenaCharacter.cs +++ b/Lib9c/Model/Character/ArenaCharacter.cs @@ -750,7 +750,7 @@ private void RemoveBuffs() if (!isBuffRemoved) return; - Stats.SetBuffs(StatBuffs, Simulator.DeBuffLimitSheet); + Stats.SetBuffs(StatBuffs, Simulator.BuffLimitSheet); } [Obsolete("Use RemoveBuffs")] @@ -772,7 +772,7 @@ private void RemoveBuffsV1() if (isApply) { - Stats.SetBuffs(StatBuffs, Simulator.DeBuffLimitSheet); + Stats.SetBuffs(StatBuffs, Simulator.BuffLimitSheet); } } @@ -814,7 +814,7 @@ public void SpawnV2(ArenaCharacter target) clone.SetStack(stack); } Buffs[stat.RowData.GroupId] = clone; - Stats.AddBuff(clone, Simulator.DeBuffLimitSheet, updateImmediate); + Stats.AddBuff(clone, Simulator.BuffLimitSheet, updateImmediate); break; } case ActionBuff action: diff --git a/Lib9c/Model/Character/CharacterBase.cs b/Lib9c/Model/Character/CharacterBase.cs index 4eac7e6120..c165ef9809 100644 --- a/Lib9c/Model/Character/CharacterBase.cs +++ b/Lib9c/Model/Character/CharacterBase.cs @@ -338,7 +338,7 @@ private void RemoveBuffs() if (!isBuffRemoved) return; - Stats.SetBuffs(StatBuffs, Simulator.DeBuffLimitSheet); + Stats.SetBuffs(StatBuffs, Simulator.BuffLimitSheet); if (Simulator.LogEvent) { Simulator.Log.Add(new RemoveBuffs((CharacterBase) Clone())); @@ -383,7 +383,7 @@ protected virtual void EndTurn() clone.SetStack(stack); } Buffs[stat.RowData.GroupId] = clone; - Stats.AddBuff(clone, Simulator.DeBuffLimitSheet, updateImmediate); + Stats.AddBuff(clone, Simulator.BuffLimitSheet, updateImmediate); break; } case ActionBuff action: diff --git a/Lib9c/Model/Stat/CharacterStats.cs b/Lib9c/Model/Stat/CharacterStats.cs index b40a18c2fe..e9d7e4888a 100644 --- a/Lib9c/Model/Stat/CharacterStats.cs +++ b/Lib9c/Model/Stat/CharacterStats.cs @@ -263,18 +263,18 @@ public CharacterStats SetRunes(IEnumerable value, bool updateImmed /// Set stats based on buffs. /// /// - /// + /// /// /// public CharacterStats SetBuffs(IEnumerable value, - DeBuffLimitSheet deBuffLimitSheet, bool updateImmediate = true) + BuffLimitSheet buffLimitSheet, bool updateImmediate = true) { _buffStatModifiers.Clear(); if (!(value is null)) { foreach (var buff in value) { - AddBuff(buff, deBuffLimitSheet, false); + AddBuff(buff, buffLimitSheet, false); } } @@ -323,9 +323,9 @@ public CharacterStats SetCollections(IEnumerable statModifiers, return this; } - public void AddBuff(Buff.StatBuff buff, DeBuffLimitSheet deBuffLimitSheet, bool updateImmediate = true) + public void AddBuff(Buff.StatBuff buff, BuffLimitSheet buffLimitSheet, bool updateImmediate = true) { - var modifier = GetBuffModifier(buff, deBuffLimitSheet); + var modifier = GetBuffModifier(buff, buffLimitSheet); _buffStatModifiers[buff.RowData.GroupId] = modifier; if (updateImmediate) @@ -618,22 +618,22 @@ int runeLevelBonus } /// - /// Returns a based on the upper limit from . + /// Returns a based on the upper limit from . /// /// for modify stats. - /// Upper limit sheet data. - /// if buff modify stats 100% but limit 50% set in , + /// Upper limit sheet data. + /// if buff modify stats 100% but limit 50% set in , /// it will return 50%, else return 100% , - /// if de-buff modify stats -100% but limit -50% set in , + /// if de-buff modify stats -100% but limit -50% set in , /// it will return -50%, else return -100% /// - private StatModifier GetBuffModifier(Buff.StatBuff buff, DeBuffLimitSheet deBuffLimitSheet) + private StatModifier GetBuffModifier(Buff.StatBuff buff, BuffLimitSheet buffLimitSheet) { var modifier = buff.GetModifier(); try { var statType = modifier.StatType; - var limitModifier = deBuffLimitSheet[buff.RowData.GroupId].GetModifier(statType); + var limitModifier = buffLimitSheet[buff.RowData.GroupId].GetModifier(statType); var stat = _statMap.GetStatAsLong(statType); var buffModified = modifier.GetModifiedValue(stat); var maxModified = (long)limitModifier.GetModifiedValue(stat); diff --git a/Lib9c/TableCSV/Skill/DeBuffLimitSheet.csv b/Lib9c/TableCSV/Skill/BuffLimitSheet.csv similarity index 100% rename from Lib9c/TableCSV/Skill/DeBuffLimitSheet.csv rename to Lib9c/TableCSV/Skill/BuffLimitSheet.csv diff --git a/Lib9c/TableData/Skill/DeBuffLimitSheet.cs b/Lib9c/TableData/Skill/BuffLimitSheet.cs similarity index 80% rename from Lib9c/TableData/Skill/DeBuffLimitSheet.cs rename to Lib9c/TableData/Skill/BuffLimitSheet.cs index 817049bb2f..55c51b7958 100644 --- a/Lib9c/TableData/Skill/DeBuffLimitSheet.cs +++ b/Lib9c/TableData/Skill/BuffLimitSheet.cs @@ -1,12 +1,10 @@ -using System; using System.Collections.Generic; using Nekoyume.Model.Stat; -using Org.BouncyCastle.Tls; using static Nekoyume.TableData.TableExtensions; namespace Nekoyume.TableData { - public class DeBuffLimitSheet : Sheet + public class BuffLimitSheet : Sheet { public class Row : SheetRow { @@ -28,7 +26,7 @@ public StatModifier GetModifier(StatType statType) } } - public DeBuffLimitSheet() : base(nameof(DeBuffLimitSheet)) + public BuffLimitSheet() : base(nameof(BuffLimitSheet)) { } } From 428ae6419e713006be905aff01457a27fd78a99c Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Thu, 14 Nov 2024 21:22:32 +0900 Subject: [PATCH 168/206] Avoid NRE in deprecated actions --- Lib9c/Model/Stat/CharacterStats.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib9c/Model/Stat/CharacterStats.cs b/Lib9c/Model/Stat/CharacterStats.cs index e9d7e4888a..2d327ab1a3 100644 --- a/Lib9c/Model/Stat/CharacterStats.cs +++ b/Lib9c/Model/Stat/CharacterStats.cs @@ -646,6 +646,10 @@ private StatModifier GetBuffModifier(Buff.StatBuff buff, BuffLimitSheet buffLimi { // pass } + catch (NullReferenceException) + { + // pass + } return modifier; } From 5d4a9d66e340ecec597f4f73644456829330c704 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 15 Nov 2024 01:43:25 +0900 Subject: [PATCH 169/206] fix: Fix de(activate) validator equality --- .../ValidatorDelegation/UpdateValidators.cs | 14 ++++++----- .../ValidatorDelegation/ValidatorDelegatee.cs | 24 ++++++++++++------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs index 89b1d9c0c8..fb1c87075b 100644 --- a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs +++ b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs @@ -32,25 +32,27 @@ public override IWorld Execute(IActionContext context) var repository = new ValidatorRepository(world, context); var validators = repository.GetValidatorList().ActiveSet(); - foreach (var deactivated in prevValidators.Except(validators)) + foreach (var deactivated in prevValidators.Select(v => v.OperatorAddress) + .Except(validators.Select(v => v.OperatorAddress))) { - var validatorDelegatee = repository.GetValidatorDelegatee(deactivated.OperatorAddress); + var validatorDelegatee = repository.GetValidatorDelegatee(deactivated); validatorDelegatee.Deactivate(); repository.SetValidatorDelegatee(validatorDelegatee); var guildRepository = new GuildRepository(repository.World, repository.ActionContext); - var validatorDelegateeForGuildParticipant = guildRepository.GetGuildDelegatee(deactivated.OperatorAddress); + var validatorDelegateeForGuildParticipant = guildRepository.GetGuildDelegatee(deactivated); validatorDelegateeForGuildParticipant.Deactivate(); guildRepository.SetGuildDelgatee(validatorDelegateeForGuildParticipant); repository.UpdateWorld(guildRepository.World); } - foreach (var activated in validators.Except(prevValidators)) + foreach (var activated in validators.Select(v => v.OperatorAddress) + .Except(prevValidators.Select(v => v.OperatorAddress))) { - var validatorDelegatee = repository.GetValidatorDelegatee(activated.OperatorAddress); + var validatorDelegatee = repository.GetValidatorDelegatee(activated); validatorDelegatee.Activate(); repository.SetValidatorDelegatee(validatorDelegatee); var guildRepository = new GuildRepository(repository.World, repository.ActionContext); - var validatorDelegateeForGuildParticipant = guildRepository.GetGuildDelegatee(activated.OperatorAddress); + var validatorDelegateeForGuildParticipant = guildRepository.GetGuildDelegatee(activated); validatorDelegateeForGuildParticipant.Activate(); guildRepository.SetGuildDelgatee(validatorDelegateeForGuildParticipant); repository.UpdateWorld(guildRepository.World); diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index fd350c5800..c3fa0d537b 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -202,10 +202,14 @@ public void Activate() ValidatorRepository repository = (ValidatorRepository)Repository; IsActive = true; Metadata.DelegationPoolAddress = ActiveDelegationPoolAddress; - repository.TransferAsset( - InactiveDelegationPoolAddress, - ActiveDelegationPoolAddress, - TotalDelegated); + + if (TotalDelegated.Sign > 0) + { + repository.TransferAsset( + InactiveDelegationPoolAddress, + ActiveDelegationPoolAddress, + TotalDelegated); + } } public void Deactivate() @@ -213,10 +217,14 @@ public void Deactivate() ValidatorRepository repository = (ValidatorRepository)Repository; IsActive = false; Metadata.DelegationPoolAddress = InactiveDelegationPoolAddress; - repository.TransferAsset( - ActiveDelegationPoolAddress, - InactiveDelegationPoolAddress, - TotalDelegated); + + if (TotalDelegated.Sign > 0) + { + repository.TransferAsset( + ActiveDelegationPoolAddress, + InactiveDelegationPoolAddress, + TotalDelegated); + } } public void OnDelegationChanged(object? sender, long height) From 9cdc33c73d3693665f1936de0d5e0f30146b70cf Mon Sep 17 00:00:00 2001 From: hyeon Date: Fri, 15 Nov 2024 16:53:07 +0900 Subject: [PATCH 170/206] Fix broken tests --- .Lib9c.Tests/Action/CreatePledgeTest.cs | 24 ++- .Lib9c.Tests/Action/EventDungeonBattleTest.cs | 4 +- .../Guild/AcceptGuildApplicationTest.cs | 88 -------- .../Guild/CancelGuildApplicationTest.cs | 67 ------ .../Guild/Migration/GuildMigrationCtrlTest.cs | 104 ---------- .../Migration/MigratePledgeToGuildTest.cs | 192 ------------------ .../Guild/RejectGuildApplicationTest.cs | 89 -------- .Lib9c.Tests/Lib9c.Tests.csproj | 4 + .../Tx/Begin/AutoJoinGuildTest.cs | 114 ----------- Lib9c/Action/CreatePledge.cs | 28 ++- 10 files changed, 42 insertions(+), 672 deletions(-) delete mode 100644 .Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs delete mode 100644 .Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs delete mode 100644 .Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs delete mode 100644 .Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs delete mode 100644 .Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs delete mode 100644 .Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs diff --git a/.Lib9c.Tests/Action/CreatePledgeTest.cs b/.Lib9c.Tests/Action/CreatePledgeTest.cs index 1aada104df..1d0e7b9332 100644 --- a/.Lib9c.Tests/Action/CreatePledgeTest.cs +++ b/.Lib9c.Tests/Action/CreatePledgeTest.cs @@ -31,6 +31,7 @@ public CreatePledgeTest(ITestOutputHelper outputHelper) [InlineData(false, false, typeof(PermissionDeniedException))] public void Execute(bool admin, bool plPatron, Type exc) { + var validatorKey = new PrivateKey(); var adminAddress = new PrivateKey().Address; var poolAddress = new PrivateKey().Address; var adminState = new AdminState(adminAddress, 150L); @@ -43,9 +44,19 @@ public void Execute(bool admin, bool plPatron, Type exc) var agentAddress = new Nekoyume.TypedAddress.AgentAddress(pledgedAddress); var context = new ActionContext(); var states = new World(MockUtil.MockModernWorldState) + .SetLegacyState( + Addresses.GoldCurrency, + new GoldCurrencyState(Libplanet.Types.Assets.Currency.Legacy("NCG", 2, null)) + .Serialize()) .SetLegacyState(Addresses.Admin, adminState.Serialize()) .MintAsset(context, patronAddress, 4 * 500 * mead); + states = Lib9c.Tests.Util.DelegationUtil.EnsureValidatorPromotionReady( + states, + validatorKey.PublicKey, + 0L + ); + var agentAddresses = new List<(Address, Address)> { (pledgedAddress, pledgeAddress), @@ -68,6 +79,7 @@ public void Execute(bool admin, bool plPatron, Type exc) { Signer = singer, PreviousState = states, + Miner = validatorKey.Address, }; if (exc is null) @@ -76,22 +88,22 @@ public void Execute(bool admin, bool plPatron, Type exc) Assert.Equal(0 * mead, nextState.GetBalance(patronAddress, mead)); Assert.Equal(4 * mead, nextState.GetBalance(pledgedAddress, mead)); - var repo = new GuildRepository(states, context); + var repository = new GuildRepository(nextState, context); var planetariumGuildOwner = Nekoyume.Action.Guild.GuildConfig.PlanetariumGuildOwner; - var guildAddress = repo.GetJoinedGuild(planetariumGuildOwner); + var guildAddress = repository.GetJoinedGuild(planetariumGuildOwner); Assert.NotNull(guildAddress); - Assert.True(repo.TryGetGuild(guildAddress.Value, out var guild)); + Assert.True(repository.TryGetGuild(guildAddress.Value, out var guild)); Assert.Equal(planetariumGuildOwner, guild.GuildMasterAddress); if (!plPatron) { - Assert.Null(repo.GetJoinedGuild(agentAddress)); + Assert.Null(repository.GetJoinedGuild(agentAddress)); } else { var joinedGuildAddress = Assert.IsType( - repo.GetJoinedGuild(agentAddress)); - Assert.True(repo.TryGetGuild(joinedGuildAddress, out var joinedGuild)); + repository.GetJoinedGuild(agentAddress)); + Assert.True(repository.TryGetGuild(joinedGuildAddress, out var joinedGuild)); Assert.Equal( Nekoyume.Action.Guild.GuildConfig.PlanetariumGuildOwner, joinedGuild.GuildMasterAddress diff --git a/.Lib9c.Tests/Action/EventDungeonBattleTest.cs b/.Lib9c.Tests/Action/EventDungeonBattleTest.cs index 08e5fd2b12..4aaec59d70 100644 --- a/.Lib9c.Tests/Action/EventDungeonBattleTest.cs +++ b/.Lib9c.Tests/Action/EventDungeonBattleTest.cs @@ -185,9 +185,7 @@ out var balance ) ); Assert.Equal(0 * _ncgCurrency, balance); - var arenaData = _tableSheets.ArenaSheet.GetRoundByBlockIndex(scheduleRow.StartBlockIndex); - var feeStoreAddress = - Nekoyume.Arena.ArenaHelper.DeriveArenaAddress(arenaData.ChampionshipId, arenaData.Round); + var feeStoreAddress = Addresses.RewardPool; Assert.Equal(ncgHas, nextStates.GetBalance(feeStoreAddress, _ncgCurrency)); } diff --git a/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs b/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs deleted file mode 100644 index 3f5633d8b7..0000000000 --- a/.Lib9c.Tests/Action/Guild/AcceptGuildApplicationTest.cs +++ /dev/null @@ -1,88 +0,0 @@ -namespace Lib9c.Tests.Action.Guild -{ - using System; - using Lib9c.Tests.Util; - using Libplanet.Action.State; - using Libplanet.Mocks; - using Nekoyume.Action.Guild; - using Nekoyume.Module.Guild; - using Xunit; - - public class AcceptGuildApplicationTest - { - [Fact] - public void Serialization() - { - var agentAddress = AddressUtil.CreateAgentAddress(); - var action = new AcceptGuildApplication(agentAddress); - var plainValue = action.PlainValue; - - var deserialized = new AcceptGuildApplication(); - deserialized.LoadPlainValue(plainValue); - Assert.Equal(agentAddress, deserialized.Target); - } - - [Fact] - public void Execute() - { - var appliedMemberAddress = AddressUtil.CreateAgentAddress(); - var nonAppliedMemberAddress = AddressUtil.CreateAgentAddress(); - var guildMasterAddress = AddressUtil.CreateAgentAddress(); - var guildAddress = AddressUtil.CreateGuildAddress(); - - var world = new World(MockUtil.MockModernWorldState) - .MakeGuild(guildAddress, guildMasterAddress) - .ApplyGuild(appliedMemberAddress, guildAddress); - - // These cases should fail because the member didn't apply the guild and - // non-guild-master-addresses cannot accept the guild application. - Assert.Throws( - () => new AcceptGuildApplication(nonAppliedMemberAddress).Execute( - new ActionContext - { - PreviousState = world, - Signer = guildMasterAddress, - })); - Assert.Throws( - () => new AcceptGuildApplication(nonAppliedMemberAddress).Execute( - new ActionContext - { - PreviousState = world, - Signer = appliedMemberAddress, - })); - Assert.Throws( - () => new AcceptGuildApplication(nonAppliedMemberAddress).Execute( - new ActionContext - { - PreviousState = world, - Signer = nonAppliedMemberAddress, - })); - - // These cases should fail because non-guild-master-addresses cannot accept the guild application. - Assert.Throws( - () => new AcceptGuildApplication(appliedMemberAddress).Execute( - new ActionContext - { - PreviousState = world, - Signer = appliedMemberAddress, - })); - Assert.Throws( - () => new AcceptGuildApplication(appliedMemberAddress).Execute( - new ActionContext - { - PreviousState = world, - Signer = nonAppliedMemberAddress, - })); - - world = new AcceptGuildApplication(appliedMemberAddress).Execute( - new ActionContext - { - PreviousState = world, - Signer = guildMasterAddress, - }); - - Assert.False(world.TryGetGuildApplication(appliedMemberAddress, out _)); - Assert.Equal(guildAddress, world.GetJoinedGuild(appliedMemberAddress)); - } - } -} diff --git a/.Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs b/.Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs deleted file mode 100644 index 92518090f6..0000000000 --- a/.Lib9c.Tests/Action/Guild/CancelGuildApplicationTest.cs +++ /dev/null @@ -1,67 +0,0 @@ -namespace Lib9c.Tests.Action.Guild -{ - using System; - using Lib9c.Tests.Util; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Mocks; - using Nekoyume.Action.Guild; - using Nekoyume.Module.Guild; - using Nekoyume.TypedAddress; - using Xunit; - - public class CancelGuildApplicationTest - { - [Fact] - public void Serialization() - { - var action = new CancelGuildApplication(); - var plainValue = action.PlainValue; - - var deserialized = new CancelGuildApplication(); - deserialized.LoadPlainValue(plainValue); - } - - [Fact] - public void Execute() - { - var privateKey = new PrivateKey(); - var signer = new AgentAddress(privateKey.Address); - var guildMasterAddress = AddressUtil.CreateAgentAddress(); - var guildAddress = AddressUtil.CreateGuildAddress(); - - var action = new CancelGuildApplication(); - var world = new World(MockUtil.MockModernWorldState) - .MakeGuild(guildAddress, guildMasterAddress); - Assert.Throws( - () => action.Execute( - new ActionContext - { - PreviousState = world, - Signer = signer, - })); - - var otherAddress = AddressUtil.CreateAgentAddress(); - world = world.ApplyGuild(otherAddress, guildAddress); - - // It should fail because other agent applied the guild but the signer didn't apply. - Assert.Throws( - () => action.Execute( - new ActionContext - { - PreviousState = world, - Signer = signer, - })); - - world = world.ApplyGuild(signer, guildAddress); - world = action.Execute( - new ActionContext - { - PreviousState = world, - Signer = signer, - }); - - Assert.False(world.TryGetGuildApplication(signer, out _)); - } - } -} diff --git a/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs b/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs deleted file mode 100644 index a16212586d..0000000000 --- a/.Lib9c.Tests/Action/Guild/Migration/GuildMigrationCtrlTest.cs +++ /dev/null @@ -1,104 +0,0 @@ -namespace Lib9c.Tests.Action.Guild.Migration -{ - using Bencodex.Types; - using Lib9c.Tests.Util; - using Libplanet.Action.State; - using Libplanet.Mocks; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Action.Guild; - using Nekoyume.Action.Guild.Migration; - using Nekoyume.Action.Guild.Migration.Controls; - using Nekoyume.Extensions; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.Module.Guild; - using Nekoyume.TypedAddress; - using Xunit; - - public class GuildMigrationCtrlTest - { - [Fact] - public void MigratePlanetariumPledgeToGuild_When_WithUnapprovedPledgeContract() - { - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var target = AddressUtil.CreateAgentAddress(); - var pledgeAddress = target.GetPledgeAddress(); - var world = new World(MockUtil.MockModernWorldState) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress) - .SetLegacyState( - pledgeAddress, - new List( - MeadConfig.PatronAddress.Serialize(), - false.Serialize(), // Unapproved - RequestPledge.DefaultRefillMead.Serialize())); - - Assert.Null(world.GetJoinedGuild(target)); - Assert.Throws( - () => - GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(world, target)); - } - - [Fact] - public void MigratePlanetariumPledgeToGuild_When_WithPledgeContract() - { - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var target = AddressUtil.CreateAgentAddress(); - var pledgeAddress = target.GetPledgeAddress(); - var world = new World(MockUtil.MockModernWorldState) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress) - .SetLegacyState( - pledgeAddress, - new List( - MeadConfig.PatronAddress.Serialize(), - true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize())); - - Assert.Null(world.GetJoinedGuild(target)); - world = GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(world, target); - - var joinedGuildAddress = Assert.IsType(world.GetJoinedGuild(target)); - Assert.True(world.TryGetGuild(joinedGuildAddress, out var guild)); - Assert.Equal(GuildConfig.PlanetariumGuildOwner, guild.GuildMasterAddress); - } - - [Fact] - public void MigratePlanetariumPledgeToGuild_When_WithoutPledgeContract() - { - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var target = AddressUtil.CreateAgentAddress(); - var world = new World(MockUtil.MockModernWorldState) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress); - - Assert.Null(world.GetJoinedGuild(target)); - Assert.Throws( - () => - GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(world, target)); - } - - [Fact] - public void MigratePlanetariumPledgeToGuild_When_WithoutGuildYet() - { - var target = AddressUtil.CreateAgentAddress(); - var pledgeAddress = target.GetPledgeAddress(); - var world = new World(MockUtil.MockModernWorldState) - .SetLegacyState( - pledgeAddress, - new List( - MeadConfig.PatronAddress.Serialize(), - true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize())); - - Assert.Null(world.GetJoinedGuild(target)); - Assert.Throws( - () => - GuildMigrationCtrl.MigratePlanetariumPledgeToGuild(world, target)); - } - } -} diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs deleted file mode 100644 index d03042788e..0000000000 --- a/.Lib9c.Tests/Action/Guild/Migration/MigratePledgeToGuildTest.cs +++ /dev/null @@ -1,192 +0,0 @@ -namespace Lib9c.Tests.Action.Guild.Migration -{ - using System; - using Bencodex.Types; - using Lib9c.Tests.Action; - using Lib9c.Tests.Util; - using Libplanet.Action.State; - using Libplanet.Mocks; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Action.Guild; - using Nekoyume.Action.Guild.Migration; - using Nekoyume.Action.Loader; - using Nekoyume.Extensions; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.Module.Guild; - using Nekoyume.TypedAddress; - using Xunit; - - public class MigratePledgeToGuildTest - { - [Fact] - public void Serialization() - { - var agentAddress = AddressUtil.CreateAgentAddress(); - var action = new MigratePledgeToGuild(agentAddress); - var plainValue = action.PlainValue; - - var actionLoader = new NCActionLoader(); - var deserialized = - Assert.IsType(actionLoader.LoadAction(0, plainValue)); - - Assert.Equal(agentAddress, deserialized.Target); - } - - [Fact] - public void Execute_When_WithPledgeContract() - { - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var target = AddressUtil.CreateAgentAddress(); - var caller = AddressUtil.CreateAgentAddress(); - var pledgeAddress = target.GetPledgeAddress(); - var world = new World(MockUtil.MockModernWorldState) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress) - .SetLegacyState( - pledgeAddress, - new List( - MeadConfig.PatronAddress.Serialize(), - true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize())); - - Assert.Null(world.GetJoinedGuild(target)); - var action = new MigratePledgeToGuild(target); - - // Migrate by other. - var newWorld = action.Execute( - new ActionContext - { - PreviousState = world, - Signer = caller, - }); - - var joinedGuildAddress = Assert.IsType(newWorld.GetJoinedGuild(target)); - Assert.True(newWorld.TryGetGuild(joinedGuildAddress, out var guild)); - Assert.Equal(GuildConfig.PlanetariumGuildOwner, guild.GuildMasterAddress); - - // Migrate by itself. - newWorld = action.Execute( - new ActionContext - { - PreviousState = world, - Signer = target, - }); - - joinedGuildAddress = Assert.IsType(newWorld.GetJoinedGuild(target)); - Assert.True(newWorld.TryGetGuild(joinedGuildAddress, out guild)); - Assert.Equal(GuildConfig.PlanetariumGuildOwner, guild.GuildMasterAddress); - } - - [Fact] - public void Execute_When_WithUnapprovedPledgeContract() - { - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var target = AddressUtil.CreateAgentAddress(); - var pledgeAddress = target.GetPledgeAddress(); - var caller = AddressUtil.CreateAgentAddress(); - var world = new World(MockUtil.MockModernWorldState) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress) - .SetLegacyState( - pledgeAddress, - new List( - MeadConfig.PatronAddress.Serialize(), - false.Serialize(), - RequestPledge.DefaultRefillMead.Serialize())); - - Assert.Null(world.GetJoinedGuild(target)); - var action = new MigratePledgeToGuild(target); - - // Migrate by other. - Assert.Throws( - () => action.Execute( - new ActionContext - { - PreviousState = world, - Signer = caller, - })); - - // Migrate by itself. - Assert.Throws( - () => action.Execute( - new ActionContext - { - PreviousState = world, - Signer = target, - })); - } - - [Fact] - public void Execute_When_WithoutPledgeContract() - { - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var target = AddressUtil.CreateAgentAddress(); - var caller = AddressUtil.CreateAgentAddress(); - var world = new World(MockUtil.MockModernWorldState) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress); - - Assert.Null(world.GetJoinedGuild(target)); - var action = new MigratePledgeToGuild(target); - - // Migrate by other. - Assert.Throws( - () => action.Execute( - new ActionContext - { - PreviousState = world, - Signer = caller, - })); - - // Migrate by itself. - Assert.Throws( - () => action.Execute( - new ActionContext - { - PreviousState = world, - Signer = target, - })); - } - - [Fact] - public void Execute_When_WithoutGuildYet() - { - var target = AddressUtil.CreateAgentAddress(); - var caller = AddressUtil.CreateAgentAddress(); - var pledgeAddress = target.GetPledgeAddress(); - var world = new World(MockUtil.MockModernWorldState) - .SetLegacyState( - pledgeAddress, - new List( - MeadConfig.PatronAddress.Serialize(), - true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize())); - - Assert.Null(world.GetJoinedGuild(target)); - var action = new MigratePledgeToGuild(target); - - // Migrate by other. - Assert.Throws( - () => action.Execute( - new ActionContext - { - PreviousState = world, - Signer = caller, - })); - - // Migrate by itself. - Assert.Throws( - () => action.Execute( - new ActionContext - { - PreviousState = world, - Signer = target, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs b/.Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs deleted file mode 100644 index f43462d9c8..0000000000 --- a/.Lib9c.Tests/Action/Guild/RejectGuildApplicationTest.cs +++ /dev/null @@ -1,89 +0,0 @@ -namespace Lib9c.Tests.Action.Guild -{ - using System; - using Lib9c.Tests.Util; - using Libplanet.Action.State; - using Libplanet.Mocks; - using Nekoyume.Action.Guild; - using Nekoyume.Action.Loader; - using Nekoyume.Module.Guild; - using Xunit; - - public class RejectGuildApplicationTest - { - [Fact] - public void Serialization() - { - var agentAddress = AddressUtil.CreateAgentAddress(); - var action = new RejectGuildApplication(agentAddress); - var plainValue = action.PlainValue; - - var deserialized = new RejectGuildApplication(); - deserialized.LoadPlainValue(plainValue); - Assert.Equal(agentAddress, deserialized.Target); - } - - [Fact] - public void Execute() - { - var appliedMemberAddress = AddressUtil.CreateAgentAddress(); - var nonAppliedMemberAddress = AddressUtil.CreateAgentAddress(); - var guildMasterAddress = AddressUtil.CreateAgentAddress(); - var guildAddress = AddressUtil.CreateGuildAddress(); - - var world = new World(MockUtil.MockModernWorldState) - .MakeGuild(guildAddress, guildMasterAddress) - .ApplyGuild(appliedMemberAddress, guildAddress); - - // These cases should fail because the member didn't apply the guild and - // non-guild-master-addresses cannot reject the guild application. - Assert.Throws( - () => new RejectGuildApplication(nonAppliedMemberAddress).Execute( - new ActionContext - { - PreviousState = world, - Signer = guildMasterAddress, - })); - Assert.Throws( - () => new RejectGuildApplication(nonAppliedMemberAddress).Execute( - new ActionContext - { - PreviousState = world, - Signer = appliedMemberAddress, - })); - Assert.Throws( - () => new RejectGuildApplication(nonAppliedMemberAddress).Execute( - new ActionContext - { - PreviousState = world, - Signer = nonAppliedMemberAddress, - })); - - // These cases should fail because non-guild-master-addresses cannot reject the guild application. - Assert.Throws( - () => new RejectGuildApplication(appliedMemberAddress).Execute( - new ActionContext - { - PreviousState = world, - Signer = appliedMemberAddress, - })); - Assert.Throws( - () => new RejectGuildApplication(appliedMemberAddress).Execute( - new ActionContext - { - PreviousState = world, - Signer = nonAppliedMemberAddress, - })); - - world = new RejectGuildApplication(appliedMemberAddress).Execute( - new ActionContext - { - PreviousState = world, - Signer = guildMasterAddress, - }); - - Assert.False(world.TryGetGuildApplication(appliedMemberAddress, out _)); - Assert.Null(world.GetJoinedGuild(appliedMemberAddress)); - } - } -} diff --git a/.Lib9c.Tests/Lib9c.Tests.csproj b/.Lib9c.Tests/Lib9c.Tests.csproj index 56635007f8..5236efc10e 100644 --- a/.Lib9c.Tests/Lib9c.Tests.csproj +++ b/.Lib9c.Tests/Lib9c.Tests.csproj @@ -66,4 +66,8 @@ + + + + diff --git a/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs b/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs deleted file mode 100644 index a67a1165f3..0000000000 --- a/.Lib9c.Tests/PolicyAction/Tx/Begin/AutoJoinGuildTest.cs +++ /dev/null @@ -1,114 +0,0 @@ -namespace Lib9c.Tests.PolicyAction.Tx.Begin -{ - using System; - using Bencodex.Types; - using Lib9c.Tests.Action; - using Lib9c.Tests.Util; - using Libplanet.Action.State; - using Libplanet.Mocks; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Action.Guild; - using Nekoyume.Extensions; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.Module.Guild; - using Nekoyume.PolicyAction.Tx.Begin; - using Nekoyume.TypedAddress; - using Xunit; - - public class AutoJoinGuildTest - { - [Fact] - public void RunAsPolicyActionOnly() - { - Assert.Throws( - () => new AutoJoinGuild().Execute( - new ActionContext - { - IsPolicyAction = false, - })); - } - - [Fact] - public void Execute_When_WithPledgeContract() - { - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var agentAddress = AddressUtil.CreateAgentAddress(); - var pledgeAddress = agentAddress.GetPledgeAddress(); - var world = new World(MockUtil.MockModernWorldState) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress) - .SetLegacyState( - pledgeAddress, - new List( - MeadConfig.PatronAddress.Serialize(), - true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize())); - - Assert.Null(world.GetJoinedGuild(agentAddress)); - var action = new AutoJoinGuild(); - world = action.Execute( - new ActionContext - { - PreviousState = world, - Signer = agentAddress, - IsPolicyAction = true, - }); - - var joinedGuildAddress = Assert.IsType(world.GetJoinedGuild(agentAddress)); - Assert.True(world.TryGetGuild(joinedGuildAddress, out var guild)); - Assert.Equal(GuildConfig.PlanetariumGuildOwner, guild.GuildMasterAddress); - } - - [Fact] - public void Execute_When_WithoutPledgeContract() - { - var guildMasterAddress = GuildConfig.PlanetariumGuildOwner; - var guildAddress = AddressUtil.CreateGuildAddress(); - var agentAddress = AddressUtil.CreateAgentAddress(); - var world = new World(MockUtil.MockModernWorldState) - .MakeGuild(guildAddress, guildMasterAddress) - .JoinGuild(guildAddress, guildMasterAddress); - - Assert.Null(world.GetJoinedGuild(agentAddress)); - var action = new AutoJoinGuild(); - world = action.Execute( - new ActionContext - { - PreviousState = world, - Signer = agentAddress, - IsPolicyAction = true, - }); - - Assert.Null(world.GetJoinedGuild(agentAddress)); - } - - [Fact] - public void Execute_When_WithoutGuildYet() - { - var agentAddress = AddressUtil.CreateAgentAddress(); - var pledgeAddress = agentAddress.GetPledgeAddress(); - var world = new World(MockUtil.MockModernWorldState) - .SetLegacyState( - pledgeAddress, - new List( - MeadConfig.PatronAddress.Serialize(), - true.Serialize(), - RequestPledge.DefaultRefillMead.Serialize())); - - Assert.Null(world.GetJoinedGuild(agentAddress)); - var action = new AutoJoinGuild(); - world = action.Execute( - new ActionContext - { - PreviousState = world, - Signer = agentAddress, - IsPolicyAction = true, - }); - - Assert.Null(world.GetJoinedGuild(agentAddress)); - } - } -} diff --git a/Lib9c/Action/CreatePledge.cs b/Lib9c/Action/CreatePledge.cs index 56c4def165..598133bea6 100644 --- a/Lib9c/Action/CreatePledge.cs +++ b/Lib9c/Action/CreatePledge.cs @@ -36,20 +36,22 @@ public class CreatePledge : ActionBase .Add(tuple.Item1.Serialize()) .Add(tuple.Item2.Serialize()) )))); + public override void LoadPlainValue(IValue plainValue) { var list = (List)((Dictionary)plainValue)["values"]; PatronAddress = list[0].ToAddress(); Mead = (Integer)list[1]; - var serialized = (List) list[2]; + var serialized = (List)list[2]; var agentAddresses = new List<(Address, Address)>(); foreach (var value in serialized) { - var innerList = (List) value; + var innerList = (List)value; var agentAddress = innerList[0].ToAddress(); var pledgeAddress = innerList[1].ToAddress(); agentAddresses.Add((agentAddress, pledgeAddress)); } + AgentAddresses = agentAddresses; } @@ -69,22 +71,30 @@ public override IWorld Execute(IActionContext context) if (repository.GetJoinedGuild(planetariumGuildOwner) is null) { var random = context.GetRandom(); - var guildAddress = new Nekoyume.TypedAddress.GuildAddress(random.GenerateAddress()); - repository = repository.MakeGuild(guildAddress, new Nekoyume.TypedAddress.AgentAddress(planetariumGuildOwner)); + var guildAddr = new Nekoyume.TypedAddress.GuildAddress(random.GenerateAddress()); + var guild = new Model.Guild.Guild(guildAddr, planetariumGuildOwner, + context.Miner, repository); + repository.SetGuild(guild); + repository = repository.JoinGuild(guildAddr, planetariumGuildOwner); } + + var guildAddress = + (Nekoyume.TypedAddress.GuildAddress) + repository.GetJoinedGuild(planetariumGuildOwner)!; foreach (var (agentAddress, pledgeAddress) in AgentAddresses) { - if (PatronAddress == MeadConfig.PatronAddress && repository.GetJoinedGuild(planetariumGuildOwner) is { } guildAddress) + if (PatronAddress == MeadConfig.PatronAddress) { - repository = repository.JoinGuild(guildAddress, new Nekoyume.TypedAddress.AgentAddress(agentAddress)); + repository = repository.JoinGuild(guildAddress, + new Nekoyume.TypedAddress.AgentAddress(agentAddress)); } - states = states + repository.UpdateWorld(repository.World .TransferAsset(context, PatronAddress, agentAddress, mead) - .SetLegacyState(pledgeAddress, contractList); + .SetLegacyState(pledgeAddress, contractList) + ); } - repository.UpdateWorld(states); return repository.World; } } From 4304f976effca07ce878fabf86e8151a8e47e46c Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Fri, 15 Nov 2024 18:15:56 +0900 Subject: [PATCH 171/206] Delete migrate_pledge_to_guild --- .../src/actions/migrate_pledge_to_guild.ts | 25 ------------------- .../actions/migrate_pledge_to_guild.test.ts | 21 ---------------- 2 files changed, 46 deletions(-) delete mode 100644 integrations/javascript/@planetarium/lib9c/src/actions/migrate_pledge_to_guild.ts delete mode 100644 integrations/javascript/@planetarium/lib9c/tests/actions/migrate_pledge_to_guild.test.ts diff --git a/integrations/javascript/@planetarium/lib9c/src/actions/migrate_pledge_to_guild.ts b/integrations/javascript/@planetarium/lib9c/src/actions/migrate_pledge_to_guild.ts deleted file mode 100644 index 4eec55ee3b..0000000000 --- a/integrations/javascript/@planetarium/lib9c/src/actions/migrate_pledge_to_guild.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { Address } from "@planetarium/account"; -import { BencodexDictionary, type Value } from "@planetarium/bencodex"; -import { PolymorphicAction } from "./common.js"; - -export type MigrateDelegationArgs = { - target: Address; -}; - -export class MigrateDelegation extends PolymorphicAction { - protected readonly type_id: string = "migrate_delegation"; - - private readonly target: Address; - - constructor({ target }: MigrateDelegationArgs) { - super(); - - this.target = target; - } - - protected plain_value(): Value { - const targetKey = "t" as const; - - return new BencodexDictionary([[targetKey, this.target.toBytes()]]); - } -} diff --git a/integrations/javascript/@planetarium/lib9c/tests/actions/migrate_pledge_to_guild.test.ts b/integrations/javascript/@planetarium/lib9c/tests/actions/migrate_pledge_to_guild.test.ts deleted file mode 100644 index 7a9bd383da..0000000000 --- a/integrations/javascript/@planetarium/lib9c/tests/actions/migrate_pledge_to_guild.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { describe } from "vitest"; -import { MigratePledgeToGuild } from "../../src/index.js"; -import { runTests } from "./common.js"; -import { agentAddress } from "./fixtures.js"; - -describe("MigratePledgeToGuild", () => { - describe("odin", () => { - runTests("valid case", [ - new MigratePledgeToGuild({ - target: agentAddress, - }), - ]); - }); - describe("heimdall", () => { - runTests("valid case", [ - new MigratePledgeToGuild({ - target: agentAddress, - }), - ]); - }); -}); From 1547450e68af78d9727a8b364d32bd5ebafdb589 Mon Sep 17 00:00:00 2001 From: jonny Date: Fri, 15 Nov 2024 18:38:59 +0900 Subject: [PATCH 172/206] add metafile --- .../{DeBuffLimitSheet.csv.meta => BuffLimitSheet.csv.meta} | 2 +- Lib9c/TableCSV/StakeRegularRewardSheet_V9.csv.meta | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) rename Lib9c/TableCSV/Skill/{DeBuffLimitSheet.csv.meta => BuffLimitSheet.csv.meta} (75%) create mode 100644 Lib9c/TableCSV/StakeRegularRewardSheet_V9.csv.meta diff --git a/Lib9c/TableCSV/Skill/DeBuffLimitSheet.csv.meta b/Lib9c/TableCSV/Skill/BuffLimitSheet.csv.meta similarity index 75% rename from Lib9c/TableCSV/Skill/DeBuffLimitSheet.csv.meta rename to Lib9c/TableCSV/Skill/BuffLimitSheet.csv.meta index 2c364b77be..d745cc1df3 100644 --- a/Lib9c/TableCSV/Skill/DeBuffLimitSheet.csv.meta +++ b/Lib9c/TableCSV/Skill/BuffLimitSheet.csv.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c9cc235dd2a1a4165a77897cf67590bd +guid: 9fc1acd3cc4b356409506a3c3f0ad3de TextScriptImporter: externalObjects: {} userData: diff --git a/Lib9c/TableCSV/StakeRegularRewardSheet_V9.csv.meta b/Lib9c/TableCSV/StakeRegularRewardSheet_V9.csv.meta new file mode 100644 index 0000000000..b1bb8a1fa0 --- /dev/null +++ b/Lib9c/TableCSV/StakeRegularRewardSheet_V9.csv.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 503661fbe6bc85049a9816a1c21c29a8 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 55217fa2d1f2d9f4752952b39c2a8266793f1da6 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 18 Nov 2024 09:08:22 +0900 Subject: [PATCH 173/206] fix: Stake without guild not to send guild gold --- .Lib9c.Tests/Action/ApprovePledgeTest.cs | 5 ++- .Lib9c.Tests/Delegation/DelegatorTest.cs | 12 +++--- .../Migration/MigratePlanetariumGuild.cs | 3 +- Lib9c/Action/Stake.cs | 11 ----- .../ReleaseValidatorUnbondings.cs | 35 ++++++++-------- .../ValidatorDelegation/SlashValidator.cs | 8 ++-- .../ValidatorDelegation/UpdateValidators.cs | 12 +++--- .../ValidatorDelegation/ValidatorConfig.cs | 13 ------ Lib9c/Delegation/IUnbonding.cs | 2 +- Lib9c/Delegation/RebondGrace.cs | 6 ++- Lib9c/Delegation/UnbondLockIn.cs | 22 ++++------ Lib9c/Module/Guild/GuildDelegateeModule.cs | 6 +-- .../ValidatorUnbondingModule.cs | 41 ------------------- 13 files changed, 52 insertions(+), 124 deletions(-) delete mode 100644 Lib9c/Action/ValidatorDelegation/ValidatorConfig.cs delete mode 100644 Lib9c/Module/ValidatorDelegation/ValidatorUnbondingModule.cs diff --git a/.Lib9c.Tests/Action/ApprovePledgeTest.cs b/.Lib9c.Tests/Action/ApprovePledgeTest.cs index 7f93df5d73..ad75f5b149 100644 --- a/.Lib9c.Tests/Action/ApprovePledgeTest.cs +++ b/.Lib9c.Tests/Action/ApprovePledgeTest.cs @@ -58,6 +58,7 @@ public void Execute(int mead) public void Execute_JoinGuild(int mead) { var address = new PrivateKey().Address; + var validatorKey = new PrivateKey(); var patron = MeadConfig.PatronAddress; var contractAddress = address.Derive(nameof(RequestPledge)); var guildAddress = AddressUtil.CreateGuildAddress(); @@ -71,7 +72,7 @@ public void Execute_JoinGuild(int mead) ); states = DelegationUtil.EnsureValidatorPromotionReady( - states, ValidatorConfig.PlanetariumValidatorPublicKey, 0L); + states, validatorKey.PublicKey, 0L); states = new GuildRepository( states, @@ -79,7 +80,7 @@ public void Execute_JoinGuild(int mead) { Signer = GuildConfig.PlanetariumGuildOwner, }) - .MakeGuild(guildAddress, ValidatorConfig.PlanetariumValidatorAddress).World; + .MakeGuild(guildAddress, validatorKey.Address).World; var action = new ApprovePledge { diff --git a/.Lib9c.Tests/Delegation/DelegatorTest.cs b/.Lib9c.Tests/Delegation/DelegatorTest.cs index c599e2fb25..e230a91fec 100644 --- a/.Lib9c.Tests/Delegation/DelegatorTest.cs +++ b/.Lib9c.Tests/Delegation/DelegatorTest.cs @@ -141,7 +141,7 @@ public void Undelegate() Assert.Equal(unbondLockIn.Address, Assert.Single(unbondingSet.FlattenedUnbondingRefs).Address); Assert.Equal(2, unbondLockIn.Entries.Count); - unbondLockIn = unbondLockIn.Release(10L + delegatee.UnbondingPeriod - 1); + unbondLockIn = unbondLockIn.Release(10L + delegatee.UnbondingPeriod - 1, out _); delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.DelegationCurrency); delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); Assert.Equal(2, unbondLockIn.Entries.Count); @@ -162,7 +162,7 @@ public void Undelegate() Assert.Equal(delegatorInitialBalance - delegatingFAV, delegatorBalance); Assert.Equal(delegatingFAV, delegateeBalance); - unbondLockIn = unbondLockIn.Release(10L + delegatee.UnbondingPeriod); + unbondLockIn = unbondLockIn.Release(10L + delegatee.UnbondingPeriod, out _); delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.DelegationCurrency); delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); entriesByExpireHeight = Assert.Single(unbondLockIn.Entries); @@ -175,7 +175,7 @@ public void Undelegate() Assert.Equal(delegatorInitialBalance - delegatingFAV + undelegatingFAV, delegatorBalance); Assert.Equal(delegatingFAV - undelegatingFAV, delegateeBalance); - unbondLockIn = unbondLockIn.Release(12L + delegatee.UnbondingPeriod); + unbondLockIn = unbondLockIn.Release(12L + delegatee.UnbondingPeriod, out _); delegatorBalance = repo.World.GetBalance(delegator.Address, delegatee.DelegationCurrency); delegateeBalance = repo.World.GetBalance(delegatee.DelegationPoolAddress, delegatee.DelegationCurrency); Assert.Empty(unbondLockIn.Entries); @@ -253,7 +253,7 @@ public void Redelegate() Assert.Equal(rebondGrace.Address, Assert.Single(unbondingSet.FlattenedUnbondingRefs).Address); Assert.Equal(2, rebondGrace.Entries.Count); - rebondGrace = rebondGrace.Release(10L + delegatee1.UnbondingPeriod - 1); + rebondGrace = rebondGrace.Release(10L + delegatee1.UnbondingPeriod - 1, out _); Assert.Equal(2, rebondGrace.Entries.Count); entriesByExpireHeight = rebondGrace.Entries.ElementAt(0); Assert.Equal(10L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); @@ -272,7 +272,7 @@ public void Redelegate() Assert.Equal(12L, entry.CreationHeight); Assert.Equal(12L + delegatee1.UnbondingPeriod, entry.ExpireHeight); - rebondGrace = rebondGrace.Release(10L + delegatee1.UnbondingPeriod); + rebondGrace = rebondGrace.Release(10L + delegatee1.UnbondingPeriod, out _); entriesByExpireHeight = Assert.Single(rebondGrace.Entries); Assert.Equal(12L + delegatee1.UnbondingPeriod, entriesByExpireHeight.Key); entry = Assert.Single(entriesByExpireHeight.Value); @@ -282,7 +282,7 @@ public void Redelegate() Assert.Equal(12L, entry.CreationHeight); Assert.Equal(12L + delegatee1.UnbondingPeriod, entry.ExpireHeight); - rebondGrace = rebondGrace.Release(12L + delegatee1.UnbondingPeriod); + rebondGrace = rebondGrace.Release(12L + delegatee1.UnbondingPeriod, out _); Assert.Empty(rebondGrace.Entries); } diff --git a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs index cd7dbc37fa..216a39ded5 100644 --- a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs +++ b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs @@ -2,7 +2,6 @@ using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Action; -using Nekoyume.Action.ValidatorDelegation; using Nekoyume.Model.Guild; using Nekoyume.Action.Guild.Migration.LegacyModels; using Lib9c; @@ -59,7 +58,7 @@ public override IWorld Execute(IActionContext context) var guild = new Model.Guild.Guild( guildAddress, legacyGuild.GuildMasterAddress, - ValidatorConfig.PlanetariumValidatorAddress, + context.Miner, repository); repository.SetGuild(guild); diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index 3ebd0d63c2..d26a10b0b5 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -210,12 +210,6 @@ private static IWorld ContractNewStake( guildParticipant.Delegate(guild, gg, height); state = guildRepository.World; } - else - { - state = state - .TransferAsset(context, stakeStateAddr, Addresses.NonValidatorDelegatee, gg); - } - } else if (additionalBalance.Sign < 0) { @@ -239,13 +233,8 @@ private static IWorld ContractNewStake( validatorDelegatee.Unbond(validatorDelegator, share, height); state = validatorRepository.World; - state = state.BurnAsset(context, guildDelegatee.DelegationPoolAddress, gg); } - else - { - state = state.BurnAsset(context, Addresses.NonValidatorDelegatee, gg); - } state = state.TransferAsset(context, stakeStateAddr, context.Signer, -additionalBalance); diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs index 280f7f7a8e..6a7d98f8ae 100644 --- a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -46,20 +46,18 @@ public override IWorld Execute(IActionContext context) var unbondingSet = repository.GetUnbondingSet(); var unbondings = unbondingSet.UnbondingsToRelease(context.BlockIndex); - unbondings = unbondings.Select(unbonding => unbonding.Release(context.BlockIndex)).ToImmutableArray(); - foreach (var unbonding in unbondings) { switch (unbonding) { case UnbondLockIn unbondLockIn: - { - repository.SetUnbondLockIn(unbondLockIn); - repository.UpdateWorld( - Unstake(repository.World, context, unbondLockIn.DelegatorAddress)); - } + unbondLockIn.Release(context.BlockIndex, out var releasedFAV); + repository.SetUnbondLockIn(unbondLockIn); + repository.UpdateWorld( + Unstake(repository.World, context, unbondLockIn, releasedFAV)); break; case RebondGrace rebondGrace: + rebondGrace.Release(context.BlockIndex, out _); repository.SetRebondGrace(rebondGrace); break; default: @@ -72,9 +70,10 @@ public override IWorld Execute(IActionContext context) return repository.World; } - private IWorld Unstake(IWorld world, IActionContext context, Address address) + private IWorld Unstake( + IWorld world, IActionContext context, UnbondLockIn unbondLockIn, FungibleAssetValue? releasedFAV) { - var agentAddress = new AgentAddress(address); + var agentAddress = new AgentAddress(unbondLockIn.DelegatorAddress); var guildRepository = new GuildRepository(world, context); var goldCurrency = world.GetGoldCurrency(); if (guildRepository.TryGetGuildParticipant(agentAddress, out var guildParticipant)) @@ -87,20 +86,20 @@ private IWorld Unstake(IWorld world, IActionContext context, Address address) var (ncg, _) = ConvertToGoldCurrency(gg, goldCurrency); world = world.BurnAsset(context, stakeStateAddress, gg); world = world.TransferAsset( - context, stakeStateAddress, address, ncg); + context, stakeStateAddress, agentAddress, ncg); } } - else if (!IsValidator(world, context, address)) + else if (!IsValidator(world, context, unbondLockIn.DelegateeAddress)) { - var stakeStateAddress = StakeState.DeriveAddress(address); - var gg = world.GetBalance(stakeStateAddress, Currencies.GuildGold); - if (gg.Sign > 0) + if (releasedFAV is not FungibleAssetValue gg) { - var (ncg, _) = ConvertToGoldCurrency(gg, goldCurrency); - world = world.BurnAsset(context, stakeStateAddress, gg); - world = world.TransferAsset( - context, stakeStateAddress, address, ncg); + return world; } + + var stakeStateAddress = StakeState.DeriveAddress(agentAddress); + var (ncg, _) = ConvertToGoldCurrency(gg, goldCurrency); + world = world.TransferAsset( + context, stakeStateAddress, agentAddress, ncg); } return world; diff --git a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs index 59ca7beba6..5d9fc74550 100644 --- a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs @@ -56,8 +56,8 @@ public override IWorld Execute(IActionContext context) validatorDelegatee.Jail(context.BlockIndex + AbstainJailTime); var guildRepository = new GuildRepository(repository.World, repository.ActionContext); - var validatorDelegateeForGuildParticipant = guildRepository.GetGuildDelegatee(abstain.Address); - validatorDelegateeForGuildParticipant.Slash(LivenessSlashFactor, context.BlockIndex, context.BlockIndex); + var guildDelegatee = guildRepository.GetGuildDelegatee(abstain.Address); + guildDelegatee.Slash(LivenessSlashFactor, context.BlockIndex, context.BlockIndex); repository.UpdateWorld(guildRepository.World); } @@ -76,8 +76,8 @@ public override IWorld Execute(IActionContext context) validatorDelegatee.Tombstone(); var guildRepository = new GuildRepository(repository.World, repository.ActionContext); - var validatorDelegateeForGuildParticipant = guildRepository.GetGuildDelegatee(e.TargetAddress); - validatorDelegateeForGuildParticipant.Slash(DuplicateVoteSlashFactor, e.Height, context.BlockIndex); + var guildDelegatee = guildRepository.GetGuildDelegatee(e.TargetAddress); + guildDelegatee.Slash(DuplicateVoteSlashFactor, e.Height, context.BlockIndex); repository.UpdateWorld(guildRepository.World); break; default: diff --git a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs index fb1c87075b..ff6b42355d 100644 --- a/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs +++ b/Lib9c/Action/ValidatorDelegation/UpdateValidators.cs @@ -39,9 +39,9 @@ public override IWorld Execute(IActionContext context) validatorDelegatee.Deactivate(); repository.SetValidatorDelegatee(validatorDelegatee); var guildRepository = new GuildRepository(repository.World, repository.ActionContext); - var validatorDelegateeForGuildParticipant = guildRepository.GetGuildDelegatee(deactivated); - validatorDelegateeForGuildParticipant.Deactivate(); - guildRepository.SetGuildDelgatee(validatorDelegateeForGuildParticipant); + var guildDelegatee = guildRepository.GetGuildDelegatee(deactivated); + guildDelegatee.Deactivate(); + guildRepository.SetGuildDelgatee(guildDelegatee); repository.UpdateWorld(guildRepository.World); } @@ -52,9 +52,9 @@ public override IWorld Execute(IActionContext context) validatorDelegatee.Activate(); repository.SetValidatorDelegatee(validatorDelegatee); var guildRepository = new GuildRepository(repository.World, repository.ActionContext); - var validatorDelegateeForGuildParticipant = guildRepository.GetGuildDelegatee(activated); - validatorDelegateeForGuildParticipant.Activate(); - guildRepository.SetGuildDelgatee(validatorDelegateeForGuildParticipant); + var guildDelegatee = guildRepository.GetGuildDelegatee(activated); + guildDelegatee.Activate(); + guildRepository.SetGuildDelgatee(guildDelegatee); repository.UpdateWorld(guildRepository.World); } diff --git a/Lib9c/Action/ValidatorDelegation/ValidatorConfig.cs b/Lib9c/Action/ValidatorDelegation/ValidatorConfig.cs deleted file mode 100644 index 985f631c29..0000000000 --- a/Lib9c/Action/ValidatorDelegation/ValidatorConfig.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Libplanet.Crypto; - -namespace Nekoyume.Action.ValidatorDelegation -{ - public static class ValidatorConfig - { - public static readonly Address PlanetariumValidatorAddress = - new Address("0x8E1b572db70aB80bb02783A0D2c594A0edE6db28"); - - public static readonly PublicKey PlanetariumValidatorPublicKey = - PublicKey.FromHex("03a0f95711564d10c60ba1889d068c26cb8e5fcd5211d5aeb8810e133d629aa306"); - } -} diff --git a/Lib9c/Delegation/IUnbonding.cs b/Lib9c/Delegation/IUnbonding.cs index 4248733506..fae5adbea5 100644 --- a/Lib9c/Delegation/IUnbonding.cs +++ b/Lib9c/Delegation/IUnbonding.cs @@ -14,7 +14,7 @@ public interface IUnbonding bool IsEmpty { get; } - IUnbonding Release(long height); + IUnbonding Release(long height, out FungibleAssetValue? releasedFAV); IUnbonding Slash( BigInteger slashFactor, diff --git a/Lib9c/Delegation/RebondGrace.cs b/Lib9c/Delegation/RebondGrace.cs index f8ab128390..ac158207d1 100644 --- a/Lib9c/Delegation/RebondGrace.cs +++ b/Lib9c/Delegation/RebondGrace.cs @@ -111,8 +111,10 @@ public List Bencoded IValue IBencodable.Bencoded => Bencoded; - public RebondGrace Release(long height) + public RebondGrace Release(long height, out FungibleAssetValue? releasedFAV) { + releasedFAV = null; + if (height <= 0) { throw new ArgumentOutOfRangeException( @@ -137,7 +139,7 @@ public RebondGrace Release(long height) return UpdateEntries(updatedEntries); } - IUnbonding IUnbonding.Release(long height) => Release(height); + IUnbonding IUnbonding.Release(long height, out FungibleAssetValue? releasedFAV) => Release(height, out releasedFAV); public RebondGrace Slash( BigInteger slashFactor, diff --git a/Lib9c/Delegation/UnbondLockIn.cs b/Lib9c/Delegation/UnbondLockIn.cs index dd86120b0d..22987f5cac 100644 --- a/Lib9c/Delegation/UnbondLockIn.cs +++ b/Lib9c/Delegation/UnbondLockIn.cs @@ -137,7 +137,7 @@ public List Bencoded IValue IBencodable.Bencoded => Bencoded; - public UnbondLockIn Release(long height) + public UnbondLockIn Release(long height, out FungibleAssetValue? releasedFAV) { CannotMutateRelationsWithoutRepository(); if (height <= 0) @@ -149,7 +149,7 @@ public UnbondLockIn Release(long height) } var updatedEntries = Entries; - FungibleAssetValue? releasingFAV = null; + releasedFAV = null; foreach (var (expireHeight, entries) in updatedEntries) { if (expireHeight <= height) @@ -157,8 +157,8 @@ public UnbondLockIn Release(long height) FungibleAssetValue entriesFAV = entries .Select(e => e.UnbondingFAV) .Aggregate((accum, next) => accum + next); - releasingFAV = releasingFAV.HasValue - ? releasingFAV.Value + entriesFAV + releasedFAV = releasedFAV.HasValue + ? releasedFAV.Value + entriesFAV : entriesFAV; updatedEntries = updatedEntries.Remove(expireHeight); } @@ -168,7 +168,7 @@ public UnbondLockIn Release(long height) } } - if (releasingFAV.HasValue) + if (releasedFAV.HasValue) { if (DelegateeAddress != Addresses.NonValidatorDelegatee) { @@ -177,22 +177,14 @@ public UnbondLockIn Release(long height) _repository!.TransferAsset( delegateeMetadata.DelegationPoolAddress, delegatorMetadata.DelegationPoolAddress, - releasingFAV.Value); - } - else - { - var stakeStateAddress = StakeState.DeriveAddress(DelegatorAddress); - _repository!.TransferAsset( - Addresses.NonValidatorDelegatee, - stakeStateAddress, - releasingFAV.Value); + releasedFAV.Value); } } return UpdateEntries(updatedEntries); } - IUnbonding IUnbonding.Release(long height) => Release(height); + IUnbonding IUnbonding.Release(long height, out FungibleAssetValue? releasedFAV) => Release(height, out releasedFAV); public UnbondLockIn Slash( BigInteger slashFactor, diff --git a/Lib9c/Module/Guild/GuildDelegateeModule.cs b/Lib9c/Module/Guild/GuildDelegateeModule.cs index d92f827569..16a721a797 100644 --- a/Lib9c/Module/Guild/GuildDelegateeModule.cs +++ b/Lib9c/Module/Guild/GuildDelegateeModule.cs @@ -15,16 +15,16 @@ public static class GuildDelegateeModule public static bool TryGetGuildDelegatee( this GuildRepository repository, Address address, - [NotNullWhen(true)] out GuildDelegatee? validatorDelegateeForGuildParticipant) + [NotNullWhen(true)] out GuildDelegatee? guildDelegatee) { try { - validatorDelegateeForGuildParticipant = repository.GetGuildDelegatee(address); + guildDelegatee = repository.GetGuildDelegatee(address); return true; } catch { - validatorDelegateeForGuildParticipant = null; + guildDelegatee = null; return false; } } diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorUnbondingModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorUnbondingModule.cs deleted file mode 100644 index f1a6030c0f..0000000000 --- a/Lib9c/Module/ValidatorDelegation/ValidatorUnbondingModule.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using Nekoyume.Delegation; -using Nekoyume.ValidatorDelegation; - -namespace Nekoyume.Module.ValidatorDelegation -{ - public static class ValidatorUnbondingModule - { - public static ValidatorRepository ReleaseUnbondings( - this ValidatorRepository repository) - { - var context = repository.ActionContext; - var unbondingSet = repository.GetUnbondingSet(); - var unbondings = unbondingSet.UnbondingsToRelease(context.BlockIndex); - - IUnbonding released; - foreach (var unbonding in unbondings) - { - released = unbonding.Release(context.BlockIndex); - - switch (released) - { - case UnbondLockIn unbondLockIn: - repository.SetUnbondLockIn(unbondLockIn); - break; - case RebondGrace rebondGrace: - repository.SetRebondGrace(rebondGrace); - break; - default: - throw new ArgumentException("Invalid unbonding type."); - } - - unbondingSet = unbondingSet.SetUnbonding(released); - } - - repository.SetUnbondingSet(unbondingSet); - - return repository; - } - } -} From 2e8e26de9d0e6f6fec8820350a2ff91070ec27c3 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 18 Nov 2024 10:30:31 +0900 Subject: [PATCH 174/206] test: Fix unstake test --- .Lib9c.Tests/Action/StakeTest.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index 5de72ac960..9185cc33b1 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -429,8 +429,6 @@ public void Execute_Success_When_Exist_StakeStateV3_Without_Guild( world = world.MintAsset(new ActionContext(), _agentAddr, ncgToStake); world = world.TransferAsset( new ActionContext(), _agentAddr, stakeStateAddr, ncgToStake); - world = world.TransferAsset( - new ActionContext(), stakeStateAddr, Addresses.NonValidatorDelegatee, gg); } world = world.SetLegacyState(stakeStateAddr, stakeState.Serialize()); @@ -459,7 +457,10 @@ public void Execute_Success_When_Exist_StakeStateV3_Without_Guild( var expectedBalance = _ncg * Math.Max(0, previousAmount - amount); var actualBalance = world.GetBalance(_agentAddr, _ncg); + var nonValidatorDelegateeBalance = world.GetBalance( + Addresses.NonValidatorDelegatee, Currencies.GuildGold); Assert.Equal(expectedBalance, actualBalance); + Assert.Equal(Currencies.GuildGold * 0, nonValidatorDelegateeBalance); } private IWorld Execute( From f0d5b6beb58ed8b554ad35c9d1a7ebe7dff34739 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 18 Nov 2024 10:33:17 +0900 Subject: [PATCH 175/206] fix: Add sign checker for fav --- Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs index 6a7d98f8ae..236b865368 100644 --- a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -91,7 +91,7 @@ private IWorld Unstake( } else if (!IsValidator(world, context, unbondLockIn.DelegateeAddress)) { - if (releasedFAV is not FungibleAssetValue gg) + if (releasedFAV is not FungibleAssetValue gg || gg.Sign < 1) { return world; } From bfc2e1715f426f59a73ec70ae8fe95d63a2b2084 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 18 Nov 2024 10:43:41 +0900 Subject: [PATCH 176/206] fix: Burn Guild Gold --- Lib9c/Action/Stake.cs | 7 ++++++- .../ValidatorDelegation/ReleaseValidatorUnbondings.cs | 5 +++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index d26a10b0b5..81cbdef1da 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -235,8 +235,13 @@ private static IWorld ContractNewStake( state = validatorRepository.World; state = state.BurnAsset(context, guildDelegatee.DelegationPoolAddress, gg); } + else + { + state = state.BurnAsset(context, stakeStateAddr, gg); + } - state = state.TransferAsset(context, stakeStateAddr, context.Signer, -additionalBalance); + state = state + .TransferAsset(context, stakeStateAddr, context.Signer, -additionalBalance); // TODO : [GuildMigration] Revive below code when the migration is done. // if (guildRepository.TryGetGuildParticipant(agentAddress, out var guildParticipant)) diff --git a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs index 236b865368..c68def6be6 100644 --- a/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs +++ b/Lib9c/Action/ValidatorDelegation/ReleaseValidatorUnbondings.cs @@ -98,8 +98,9 @@ private IWorld Unstake( var stakeStateAddress = StakeState.DeriveAddress(agentAddress); var (ncg, _) = ConvertToGoldCurrency(gg, goldCurrency); - world = world.TransferAsset( - context, stakeStateAddress, agentAddress, ncg); + world = world + .TransferAsset(context, stakeStateAddress, agentAddress, ncg) + .BurnAsset(context, stakeStateAddress, gg); } return world; From 0ac599d640acb4045d2812621d4d0cf2bd513c89 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 18 Nov 2024 10:57:30 +0900 Subject: [PATCH 177/206] test: Fix test to check GuildGold balance --- .Lib9c.Tests/Action/StakeTest.cs | 2 ++ Lib9c/Action/Stake.cs | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index 9185cc33b1..4441136909 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -459,8 +459,10 @@ public void Execute_Success_When_Exist_StakeStateV3_Without_Guild( var actualBalance = world.GetBalance(_agentAddr, _ncg); var nonValidatorDelegateeBalance = world.GetBalance( Addresses.NonValidatorDelegatee, Currencies.GuildGold); + var stakeBalance = world.GetBalance(stakeStateAddr, Currencies.GuildGold); Assert.Equal(expectedBalance, actualBalance); Assert.Equal(Currencies.GuildGold * 0, nonValidatorDelegateeBalance); + Assert.Equal(Currencies.GuildGold * amount, stakeBalance); } private IWorld Execute( diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index 81cbdef1da..b87ce5a9c2 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -9,7 +9,6 @@ using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; -using Nekoyume.Delegation; using Nekoyume.Exceptions; using Nekoyume.Extensions; using Nekoyume.Model.Guild; @@ -23,7 +22,6 @@ using Nekoyume.ValidatorDelegation; using Serilog; using static Lib9c.SerializeKeys; -using static Nekoyume.Model.WorldInformation; namespace Nekoyume.Action { From e726d20d227f15185ea3aaa0b8ae9f270f5a47fb Mon Sep 17 00:00:00 2001 From: hyeon Date: Mon, 18 Nov 2024 12:18:24 +0900 Subject: [PATCH 178/206] Update github actions to latest major version --- .../workflows/lib9c_plugin_build_and_push_s3.yaml | 2 +- .github/workflows/main.yml | 12 ++++++------ .github/workflows/publish.yml | 2 +- .github/workflows/release.yaml | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/lib9c_plugin_build_and_push_s3.yaml b/.github/workflows/lib9c_plugin_build_and_push_s3.yaml index f60fb3becf..6de468a7ef 100644 --- a/.github/workflows/lib9c_plugin_build_and_push_s3.yaml +++ b/.github/workflows/lib9c_plugin_build_and_push_s3.yaml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-dotnet@v3 + - uses: actions/setup-dotnet@v4 with: dotnet-version: 6.0.400 - name: Publish Lib9c.Plugin diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 41987ed276..16c96c48ea 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -42,7 +42,7 @@ jobs: fi popd - name: Setup .NET Core - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 6.0.400 - name: Install dependencies @@ -56,7 +56,7 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - - uses: actions/setup-dotnet@v3 + - uses: actions/setup-dotnet@v4 with: dotnet-version: 6.0.400 - name: Enforce net5.0 target with custom patch @@ -74,7 +74,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Setup .NET Core - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 6.0.400 - uses: pnpm/action-setup@v4 @@ -117,7 +117,7 @@ jobs: env: WEB_FLOW_KEY_URL: https://github.com/web-flow.gpg - name: Setup .NET Core - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 6.0.400 - name: Collect available action type ids @@ -126,13 +126,13 @@ jobs: dotnet run --property WarningLevel=0 --project .Lib9c.Tools/Lib9c.Tools.csproj -- action list > publish/all_action_type_ids.txt dotnet run --property WarningLevel=0 --project .Lib9c.Tools/Lib9c.Tools.csproj -- action list --obsolete-only --json-path=publish/obsolete_action_types.json - name: Publish available action type ids - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./publish destination_dir: ${{ github.ref_name }} - name: Publish available action type ids for latest - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./publish diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 880d8e0835..a192c35632 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-dotnet@v3 + - uses: actions/setup-dotnet@v4 with: dotnet-version: 6.0.400 - name: build diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index da7f3aabb8..bfa126f040 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-dotnet@v3 + - uses: actions/setup-dotnet@v4 with: dotnet-version: 6.0.x - run: .github/bin/dist-pack.sh From 93e195db85d5242f92d7da5546764410be7a3efc Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 18 Nov 2024 15:07:02 +0900 Subject: [PATCH 179/206] feat: Add admin fix action --- .../FixToRefundFromNonValidatorTest.cs | 135 ++++++++++++++++++ .../Migration/FixToRefundFromNonValidator.cs | 101 +++++++++++++ 2 files changed, 236 insertions(+) create mode 100644 .Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs create mode 100644 Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs diff --git a/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs b/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs new file mode 100644 index 0000000000..66491c9c30 --- /dev/null +++ b/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs @@ -0,0 +1,135 @@ +namespace Lib9c.Tests.Action.Guild.Migration +{ + using System; + using System.Collections.Generic; + using Lib9c.Tests.Fixtures.TableCSV.Stake; + using Lib9c.Tests.Util; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Types.Assets; + using Nekoyume; + using Nekoyume.Action; + using Nekoyume.Action.Guild.Migration; + using Nekoyume.Extensions; + using Nekoyume.Model.Stake; + using Nekoyume.Model.State; + using Nekoyume.Module; + using Nekoyume.TableData.Stake; + using Xunit; + + // TODO: Remove this test class after the migration is completed. + public class FixToRefundFromNonValidatorTest : GuildTestBase + { + private IWorld _world; + private Currency _ncg; + private Currency _gg; + private Address _agentAddress; + private Address _stakeAddress; + private Address _adminAddress; + + public FixToRefundFromNonValidatorTest() + { + _agentAddress = new PrivateKey().Address; + _adminAddress = new PrivateKey().Address; + _stakeAddress = StakeState.DeriveAddress(_agentAddress); + _ncg = World.GetGoldCurrency(); + _gg = Currencies.GuildGold; + var sheetsOverride = new Dictionary + { + { + "StakeRegularFixedRewardSheet_V1", + StakeRegularFixedRewardSheetFixtures.V1 + }, + { + "StakeRegularFixedRewardSheet_V2", + StakeRegularFixedRewardSheetFixtures.V2 + }, + { + "StakeRegularRewardSheet_V1", + StakeRegularRewardSheetFixtures.V1 + }, + { + "StakeRegularRewardSheet_V2", + StakeRegularRewardSheetFixtures.V2 + }, + { + nameof(StakePolicySheet), + StakePolicySheetFixtures.V2 + }, + }; + (_, _, _, _world) = InitializeUtil.InitializeStates( + agentAddr: _agentAddress, + sheetsOverride: sheetsOverride); + var stakePolicySheet = _world.GetSheet(); + var contract = new Contract(stakePolicySheet); + var adminState = new AdminState(_adminAddress, 100L); + var stakeState = new StakeState(new Contract(stakePolicySheet), 1L); + + _world = World + .SetLegacyState(Addresses.Admin, adminState.Serialize()) + .SetLegacyState(_stakeAddress, stakeState.Serialize()) + .MintAsset(new ActionContext { }, Addresses.NonValidatorDelegatee, _gg * 10000) + .MintAsset(new ActionContext { }, _stakeAddress, _ncg * 10) + .MintAsset(new ActionContext { }, _stakeAddress, _gg * 5); + } + + [Fact] + public void Execute() + { + var world = new FixToRefundFromNonValidator(_agentAddress).Execute(new ActionContext + { + PreviousState = _world, + Signer = _adminAddress, + }); + + Assert.Equal(_gg * 10, world.GetBalance(_stakeAddress, _gg)); + Assert.Equal(_gg * (10000 - 5), world.GetBalance(Addresses.NonValidatorDelegatee, _gg)); + } + + [Fact] + public void AssertWhenExecutedByNonAdmin() + { + Assert.Throws(() => + { + new FixToRefundFromNonValidator(_agentAddress).Execute(new ActionContext + { + PreviousState = _world, + Signer = new PrivateKey().Address, + BlockIndex = 2L, + }); + }); + } + + [Fact] + public void AssertWhenHasSufficientGG() + { + var world = _world + .MintAsset(new ActionContext { }, _stakeAddress, _gg * 5); + Assert.Throws(() => + { + new FixToRefundFromNonValidator(_agentAddress).Execute(new ActionContext + { + PreviousState = world, + Signer = _adminAddress, + BlockIndex = 2L, + }); + }); + } + + [Fact] + public void AssertWhenLegacyStakeState() + { + var stakeState = new LegacyStakeState(_stakeAddress, 0L); + var world = _world.SetLegacyState(_stakeAddress, stakeState.Serialize()); + Assert.Throws(() => + { + new FixToRefundFromNonValidator(_agentAddress).Execute(new ActionContext + { + PreviousState = World, + Signer = _adminAddress, + BlockIndex = 2L, + }); + }); + } + } +} diff --git a/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs b/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs new file mode 100644 index 0000000000..a27a63d7de --- /dev/null +++ b/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs @@ -0,0 +1,101 @@ +using System; +using Bencodex.Types; +using Lib9c; +using Libplanet.Action.State; +using Libplanet.Action; +using Libplanet.Types.Assets; +using Nekoyume.Model.Stake; +using Nekoyume.Model.State; +using Nekoyume.Module; +using Libplanet.Crypto; + +namespace Nekoyume.Action.Guild.Migration +{ + // TODO: [GuildMigration] Remove this class when the migration is done. + /// + /// An action to fix refund from non-validator. + /// + [ActionType(TypeIdentifier)] + public class FixToRefundFromNonValidator : ActionBase + { + public const string TypeIdentifier = "fix_to_refund_from_non_validator"; + + private const string TargetKey = "t"; + + public Address Target { get; private set; } + + [Obsolete("Don't call in code.", error: false)] + public FixToRefundFromNonValidator() + { + } + + public FixToRefundFromNonValidator(Address target) + { + Target = target; + } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", Dictionary.Empty + .Add(TargetKey, Target.Bencoded)); + + public override void LoadPlainValue(IValue plainValue) + { + if (plainValue is not Dictionary root || + !root.TryGetValue((Text)"values", out var rawValues) || + rawValues is not Dictionary values || + !values.TryGetValue((Text)TargetKey, out var rawTarget) || + rawTarget is not Binary target) + { + throw new InvalidCastException(); + } + + Target = new Address(target); + } + + public override IWorld Execute(IActionContext context) + { + GasTracer.UseGas(1); + + var world = context.PreviousState; + + if (!TryGetAdminState(context, out AdminState adminState)) + { + throw new InvalidOperationException("Couldn't find admin state"); + } + + if (context.Signer != adminState.AdminAddress) + { + throw new PermissionDeniedException(adminState, context.Signer); + } + + var stakeStateAddress = StakeState.DeriveAddress(Target); + + if (!world.TryGetStakeState(Target, out var stakeState) + || stakeState.StateVersion != 3) + { + throw new InvalidOperationException( + "Target is not valid for refunding from non-validator."); + } + + var ncgStaked = world.GetBalance(stakeStateAddress, world.GetGoldCurrency()); + var ggStaked = world.GetBalance(stakeStateAddress, Currencies.GuildGold); + + var requiredGG = GetGuildCoinFromNCG(ncgStaked) - ggStaked; + + if (requiredGG.Sign != 1) + { + throw new InvalidOperationException( + "Target has sufficient amount of guild gold."); + } + + return world.TransferAsset(context, Addresses.NonValidatorDelegatee, stakeStateAddress, requiredGG); + } + + private static FungibleAssetValue GetGuildCoinFromNCG(FungibleAssetValue balance) + { + return FungibleAssetValue.Parse(Currencies.GuildGold, + balance.GetQuantityString(true)); + } + } +} From dcfa7a47083768d995f01e7e74efb46744de0587 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 18 Nov 2024 15:57:30 +0900 Subject: [PATCH 180/206] fix: Fix refund admin action to handle multiple addresses --- .../FixToRefundFromNonValidatorTest.cs | 8 ++--- .../Migration/FixToRefundFromNonValidator.cs | 32 +++++++++++++------ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs b/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs index 66491c9c30..3f2a646d74 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs @@ -76,7 +76,7 @@ public FixToRefundFromNonValidatorTest() [Fact] public void Execute() { - var world = new FixToRefundFromNonValidator(_agentAddress).Execute(new ActionContext + var world = new FixToRefundFromNonValidator(new Address[] { _agentAddress }).Execute(new ActionContext { PreviousState = _world, Signer = _adminAddress, @@ -91,7 +91,7 @@ public void AssertWhenExecutedByNonAdmin() { Assert.Throws(() => { - new FixToRefundFromNonValidator(_agentAddress).Execute(new ActionContext + new FixToRefundFromNonValidator(new Address[] { _agentAddress }).Execute(new ActionContext { PreviousState = _world, Signer = new PrivateKey().Address, @@ -107,7 +107,7 @@ public void AssertWhenHasSufficientGG() .MintAsset(new ActionContext { }, _stakeAddress, _gg * 5); Assert.Throws(() => { - new FixToRefundFromNonValidator(_agentAddress).Execute(new ActionContext + new FixToRefundFromNonValidator(new Address[] { _agentAddress }).Execute(new ActionContext { PreviousState = world, Signer = _adminAddress, @@ -123,7 +123,7 @@ public void AssertWhenLegacyStakeState() var world = _world.SetLegacyState(_stakeAddress, stakeState.Serialize()); Assert.Throws(() => { - new FixToRefundFromNonValidator(_agentAddress).Execute(new ActionContext + new FixToRefundFromNonValidator(new Address[] { _agentAddress }).Execute(new ActionContext { PreviousState = World, Signer = _adminAddress, diff --git a/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs b/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs index a27a63d7de..1fee77142f 100644 --- a/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs +++ b/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Bencodex.Types; using Lib9c; using Libplanet.Action.State; @@ -8,6 +9,7 @@ using Nekoyume.Model.State; using Nekoyume.Module; using Libplanet.Crypto; +using System.Linq; namespace Nekoyume.Action.Guild.Migration { @@ -20,37 +22,37 @@ public class FixToRefundFromNonValidator : ActionBase { public const string TypeIdentifier = "fix_to_refund_from_non_validator"; - private const string TargetKey = "t"; + private const string TargetsKey = "t"; - public Address Target { get; private set; } + public List
Targets { get; private set; } [Obsolete("Don't call in code.", error: false)] public FixToRefundFromNonValidator() { } - public FixToRefundFromNonValidator(Address target) + public FixToRefundFromNonValidator(IEnumerable
targets) { - Target = target; + Targets = targets.ToList(); } public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) .Add("values", Dictionary.Empty - .Add(TargetKey, Target.Bencoded)); + .Add(TargetsKey, new List(Targets.Select(t => t.Bencoded)))); public override void LoadPlainValue(IValue plainValue) { if (plainValue is not Dictionary root || !root.TryGetValue((Text)"values", out var rawValues) || rawValues is not Dictionary values || - !values.TryGetValue((Text)TargetKey, out var rawTarget) || - rawTarget is not Binary target) + !values.TryGetValue((Text)TargetsKey, out var rawTarget) || + rawTarget is not List targets) { throw new InvalidCastException(); } - Target = new Address(target); + Targets = targets.Select(t => new Address(t)).ToList(); } public override IWorld Execute(IActionContext context) @@ -69,9 +71,19 @@ public override IWorld Execute(IActionContext context) throw new PermissionDeniedException(adminState, context.Signer); } - var stakeStateAddress = StakeState.DeriveAddress(Target); + foreach (var target in Targets) + { + world = RefundFromNonValidator(context, world, target); + } + + return world; + } + + private IWorld RefundFromNonValidator(IActionContext context, IWorld world, Address target) + { + var stakeStateAddress = StakeState.DeriveAddress(target); - if (!world.TryGetStakeState(Target, out var stakeState) + if (!world.TryGetStakeState(target, out var stakeState) || stakeState.StateVersion != 3) { throw new InvalidOperationException( From 61cf9f9700f101c4b7d66207d5691a66619cce15 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 18 Nov 2024 16:16:53 +0900 Subject: [PATCH 181/206] fix: Remove obsolete from admin action --- Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs b/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs index 1fee77142f..1f807cb93e 100644 --- a/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs +++ b/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs @@ -26,7 +26,6 @@ public class FixToRefundFromNonValidator : ActionBase public List
Targets { get; private set; } - [Obsolete("Don't call in code.", error: false)] public FixToRefundFromNonValidator() { } From 9b7dfb05e6cf41d8addc8f1c99512b927c400028 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 19 Nov 2024 21:45:34 +0900 Subject: [PATCH 182/206] WIP --- .Lib9c.Tests/Action/DamageHelperTest.cs | 16 +++++++ .../Model/Skill/Arena/ArenaCombatTest.cs | 47 +++++++++++++++++++ Lib9c/Battle/DamageHelper.cs | 24 ++++++++++ Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs | 2 +- Lib9c/Model/Skill/AttackSkill.cs | 2 +- 5 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 .Lib9c.Tests/Action/DamageHelperTest.cs create mode 100644 Lib9c/Battle/DamageHelper.cs diff --git a/.Lib9c.Tests/Action/DamageHelperTest.cs b/.Lib9c.Tests/Action/DamageHelperTest.cs new file mode 100644 index 0000000000..505766b578 --- /dev/null +++ b/.Lib9c.Tests/Action/DamageHelperTest.cs @@ -0,0 +1,16 @@ +namespace Lib9c.Tests.Action +{ + using Xunit; + + public class DamageHelperTest + { + [Theory] + [InlineData(long.MaxValue, 0.5)] + [InlineData(4000L, 0.6)] + [InlineData(long.MinValue, 0)] + public void GetDamageReductionRate(long drr, decimal expected) + { + Assert.Equal(expected, Nekoyume.Battle.DamageHelper.GetDamageReductionRate(drr)); + } + } +} diff --git a/.Lib9c.Tests/Model/Skill/Arena/ArenaCombatTest.cs b/.Lib9c.Tests/Model/Skill/Arena/ArenaCombatTest.cs index 7361379a36..1bb52f5441 100644 --- a/.Lib9c.Tests/Model/Skill/Arena/ArenaCombatTest.cs +++ b/.Lib9c.Tests/Model/Skill/Arena/ArenaCombatTest.cs @@ -269,5 +269,52 @@ public void DispelOnDuration_Nothing() Assert.True(battleStatus.SkillInfos.First().Affected); Assert.Contains(600001, challenger.Buffs.Values.Select(bf => bf.BuffInfo.Id)); } + + [Fact] + public void Drr() + { + var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); + var myDigest = new ArenaPlayerDigest(_avatar1, _arenaAvatar1); + var enemyDigest = new ArenaPlayerDigest(_avatar2, _arenaAvatar2); + var simulator = new ArenaSimulator(new TestRandom()); + var challenger = new ArenaCharacter( + simulator, + myDigest, + arenaSheets, + simulator.HpModifier, + new List() + ); + var enemy = new ArenaCharacter( + simulator, + enemyDigest, + arenaSheets, + simulator.HpModifier, + new List() + ); + var statBuffRow = new Nekoyume.TableData.StatBuffSheet.Row(); + var csv = $"101000,101000,20,10,Self,DRR,Add,{int.MaxValue},true"; + statBuffRow.Set(csv.Split(",")); + var buff = new StatBuff(statBuffRow); + enemy.AddBuff(buff); + Assert.True(enemy.DRR > 1000000); + var attackRow = + _tableSheets.SkillSheet.Values.First(bf => bf.Id == 100000); + var attack = new ArenaNormalAttack(attackRow, 100, 100, 0, StatType.NONE); + var arenaSkill = attack.Use( + challenger, + enemy, + simulator.Turn, + BuffFactory.GetBuffs( + challenger.Stats, + attack, + _tableSheets.SkillBuffSheet, + _tableSheets.StatBuffSheet, + _tableSheets.SkillActionBuffSheet, + _tableSheets.ActionBuffSheet + ) + ); + var info = arenaSkill.SkillInfos.Single(); + Assert.True(info.Effect > 1); + } } } diff --git a/Lib9c/Battle/DamageHelper.cs b/Lib9c/Battle/DamageHelper.cs new file mode 100644 index 0000000000..c93e4d680e --- /dev/null +++ b/Lib9c/Battle/DamageHelper.cs @@ -0,0 +1,24 @@ +namespace Nekoyume.Battle +{ + public static class DamageHelper + { + /// + /// calculate damage reduction rate. + /// + /// enemy stats + /// damage reduction rate. + public static decimal GetDamageReductionRate(long drr) + { + switch (drr) + { + case > 5000L: + drr = 5000L; + break; + case < 0L: + return 0m; + } + + return 1 - drr / 10000m; + } + } +} diff --git a/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs b/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs index a96f0a6584..cf5e9236d3 100644 --- a/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs +++ b/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs @@ -62,7 +62,7 @@ protected ArenaAttackSkill( var finalDEF = Math.Clamp(target.DEF - caster.ArmorPenetration, 0, long.MaxValue); damage = Math.Max(damage - finalDEF, 1); // Apply damage reduce - damage = (int)((damage - target.DRV) * (1 - target.DRR / 10000m)); + damage = (int)((damage - target.DRV) * DamageHelper.GetDamageReductionRate(target.DRR)); // ShatterStrike has max damage limitation if (SkillRow.SkillCategory is SkillCategory.ShatterStrike) diff --git a/Lib9c/Model/Skill/AttackSkill.cs b/Lib9c/Model/Skill/AttackSkill.cs index f04958476c..acda25eaa1 100644 --- a/Lib9c/Model/Skill/AttackSkill.cs +++ b/Lib9c/Model/Skill/AttackSkill.cs @@ -67,7 +67,7 @@ protected AttackSkill( // Apply multiple hits damage = (long) (damage * multiplier); // Apply damage reduction - damage = (long) ((damage - target.DRV) * (1 - target.DRR / 10000m)); + damage = (long) ((damage - target.DRV) * DamageHelper.GetDamageReductionRate(target.DRR)); if (damage < 1) { From b400655c2a15c3563541a63cdca79ebf6a306eca Mon Sep 17 00:00:00 2001 From: ilgyu Date: Tue, 19 Nov 2024 22:39:52 +0900 Subject: [PATCH 183/206] fix: Fix Refund admin action --- .../FixToRefundFromNonValidatorTest.cs | 134 +++++++----------- .../Migration/FixToRefundFromNonValidator.cs | 72 +++++----- 2 files changed, 89 insertions(+), 117 deletions(-) diff --git a/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs b/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs index 3f2a646d74..7f240a0759 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs @@ -1,132 +1,104 @@ namespace Lib9c.Tests.Action.Guild.Migration { using System; - using System.Collections.Generic; - using Lib9c.Tests.Fixtures.TableCSV.Stake; - using Lib9c.Tests.Util; + using System.Linq; using Libplanet.Action.State; using Libplanet.Crypto; - using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; using Nekoyume.Action.Guild.Migration; - using Nekoyume.Extensions; using Nekoyume.Model.Stake; using Nekoyume.Model.State; using Nekoyume.Module; - using Nekoyume.TableData.Stake; using Xunit; // TODO: Remove this test class after the migration is completed. public class FixToRefundFromNonValidatorTest : GuildTestBase { private IWorld _world; - private Currency _ncg; - private Currency _gg; - private Address _agentAddress; - private Address _stakeAddress; private Address _adminAddress; public FixToRefundFromNonValidatorTest() { - _agentAddress = new PrivateKey().Address; _adminAddress = new PrivateKey().Address; - _stakeAddress = StakeState.DeriveAddress(_agentAddress); - _ncg = World.GetGoldCurrency(); - _gg = Currencies.GuildGold; - var sheetsOverride = new Dictionary - { - { - "StakeRegularFixedRewardSheet_V1", - StakeRegularFixedRewardSheetFixtures.V1 - }, - { - "StakeRegularFixedRewardSheet_V2", - StakeRegularFixedRewardSheetFixtures.V2 - }, - { - "StakeRegularRewardSheet_V1", - StakeRegularRewardSheetFixtures.V1 - }, - { - "StakeRegularRewardSheet_V2", - StakeRegularRewardSheetFixtures.V2 - }, - { - nameof(StakePolicySheet), - StakePolicySheetFixtures.V2 - }, - }; - (_, _, _, _world) = InitializeUtil.InitializeStates( - agentAddr: _agentAddress, - sheetsOverride: sheetsOverride); - var stakePolicySheet = _world.GetSheet(); - var contract = new Contract(stakePolicySheet); var adminState = new AdminState(_adminAddress, 100L); - var stakeState = new StakeState(new Contract(stakePolicySheet), 1L); - _world = World .SetLegacyState(Addresses.Admin, adminState.Serialize()) - .SetLegacyState(_stakeAddress, stakeState.Serialize()) - .MintAsset(new ActionContext { }, Addresses.NonValidatorDelegatee, _gg * 10000) - .MintAsset(new ActionContext { }, _stakeAddress, _ncg * 10) - .MintAsset(new ActionContext { }, _stakeAddress, _gg * 5); + .MintAsset(new ActionContext { }, Addresses.NonValidatorDelegatee, Currencies.GuildGold * 500); } [Fact] - public void Execute() + public void PlainValue() { - var world = new FixToRefundFromNonValidator(new Address[] { _agentAddress }).Execute(new ActionContext - { - PreviousState = _world, - Signer = _adminAddress, - }); + var addresses = Enumerable.Range(0, 5).Select(_ => new PrivateKey().Address).ToList(); + var amounts = Enumerable.Range(0, 5).Select(i => (i + 1) * 10).ToList(); - Assert.Equal(_gg * 10, world.GetBalance(_stakeAddress, _gg)); - Assert.Equal(_gg * (10000 - 5), world.GetBalance(Addresses.NonValidatorDelegatee, _gg)); + var plainValue = new FixToRefundFromNonValidator( + addresses, + amounts + ).PlainValue; + + var recon = new FixToRefundFromNonValidator(); + recon.LoadPlainValue(plainValue); + Assert.Equal(addresses, recon.Targets); + Assert.Equal(amounts, recon.Amounts); } [Fact] - public void AssertWhenExecutedByNonAdmin() + public void Execute() { - Assert.Throws(() => + var addresses = Enumerable.Range(0, 5).Select(_ => new PrivateKey().Address).ToList(); + var amounts = Enumerable.Range(0, 5).Select(i => (i + 1) * 10).ToList(); + + var world = new FixToRefundFromNonValidator( + addresses, + amounts + ).Execute(new ActionContext { - new FixToRefundFromNonValidator(new Address[] { _agentAddress }).Execute(new ActionContext - { - PreviousState = _world, - Signer = new PrivateKey().Address, - BlockIndex = 2L, - }); + PreviousState = _world, + Signer = _adminAddress, + BlockIndex = 2L, }); + + foreach (var item in addresses.Select((a, i) => (a, i))) + { + Assert.Equal( + Currencies.GuildGold * ((item.i + 1) * 10), + world.GetBalance(StakeState.DeriveAddress(item.a), Currencies.GuildGold)); + } + + Assert.Equal( + Currencies.GuildGold * (500 - amounts.Sum()), + world.GetBalance(Addresses.NonValidatorDelegatee, Currencies.GuildGold)); } [Fact] - public void AssertWhenHasSufficientGG() + public void AssertWhenDifferentLengthArgument() { - var world = _world - .MintAsset(new ActionContext { }, _stakeAddress, _gg * 5); - Assert.Throws(() => + var addresses = Enumerable.Range(0, 5).Select(_ => new PrivateKey().Address).ToList(); + var amounts = Enumerable.Range(0, 4).Select(i => (i + 1) * 10).ToList(); + + Assert.Throws(() => { - new FixToRefundFromNonValidator(new Address[] { _agentAddress }).Execute(new ActionContext - { - PreviousState = world, - Signer = _adminAddress, - BlockIndex = 2L, - }); + new FixToRefundFromNonValidator(addresses, amounts); }); } [Fact] - public void AssertWhenLegacyStakeState() + public void AssertWhenExecutedByNonAdmin() { - var stakeState = new LegacyStakeState(_stakeAddress, 0L); - var world = _world.SetLegacyState(_stakeAddress, stakeState.Serialize()); - Assert.Throws(() => + var addresses = Enumerable.Range(0, 5).Select(_ => new PrivateKey().Address).ToList(); + var amounts = Enumerable.Range(0, 5).Select(i => (i + 1) * 10); + + Assert.Throws(() => { - new FixToRefundFromNonValidator(new Address[] { _agentAddress }).Execute(new ActionContext + new FixToRefundFromNonValidator( + addresses, + amounts + ).Execute(new ActionContext { - PreviousState = World, - Signer = _adminAddress, + PreviousState = _world, + Signer = new PrivateKey().Address, BlockIndex = 2L, }); }); diff --git a/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs b/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs index 1f807cb93e..1d22886e50 100644 --- a/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs +++ b/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs @@ -1,15 +1,13 @@ using System; using System.Collections.Generic; +using System.Linq; using Bencodex.Types; using Lib9c; -using Libplanet.Action.State; using Libplanet.Action; -using Libplanet.Types.Assets; +using Libplanet.Action.State; +using Libplanet.Crypto; using Nekoyume.Model.Stake; using Nekoyume.Model.State; -using Nekoyume.Module; -using Libplanet.Crypto; -using System.Linq; namespace Nekoyume.Action.Guild.Migration { @@ -24,21 +22,34 @@ public class FixToRefundFromNonValidator : ActionBase private const string TargetsKey = "t"; + private const string AmountsKey = "a"; + public List
Targets { get; private set; } + public List Amounts { get; private set; } + public FixToRefundFromNonValidator() { } - public FixToRefundFromNonValidator(IEnumerable
targets) + public FixToRefundFromNonValidator( + IEnumerable
targets, + IEnumerable amounts) { Targets = targets.ToList(); + Amounts = amounts.ToList(); + + if (Targets.Count != Amounts.Count) + { + throw new ArgumentException("The number of targets and amounts must be the same."); + } } public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) .Add("values", Dictionary.Empty - .Add(TargetsKey, new List(Targets.Select(t => t.Bencoded)))); + .Add(TargetsKey, new List(Targets.Select(t => t.Bencoded))) + .Add(AmountsKey, new List(Amounts))); public override void LoadPlainValue(IValue plainValue) { @@ -46,12 +57,20 @@ public override void LoadPlainValue(IValue plainValue) !root.TryGetValue((Text)"values", out var rawValues) || rawValues is not Dictionary values || !values.TryGetValue((Text)TargetsKey, out var rawTarget) || - rawTarget is not List targets) + rawTarget is not List targets || + !values.TryGetValue((Text)AmountsKey, out var rawAmounts) || + rawAmounts is not List amounts) { throw new InvalidCastException(); } Targets = targets.Select(t => new Address(t)).ToList(); + Amounts = amounts.Select(a => (int)(Integer)a).ToList(); + + if (Targets.Count != Amounts.Count) + { + throw new ArgumentException("The number of targets and amounts must be the same."); + } } public override IWorld Execute(IActionContext context) @@ -70,43 +89,24 @@ public override IWorld Execute(IActionContext context) throw new PermissionDeniedException(adminState, context.Signer); } - foreach (var target in Targets) + foreach (var ta in Targets.Zip(Amounts, (f, s) => (f, s))) { - world = RefundFromNonValidator(context, world, target); + world = RefundFromNonValidator(context, world, ta); } return world; } - private IWorld RefundFromNonValidator(IActionContext context, IWorld world, Address target) + private IWorld RefundFromNonValidator(IActionContext context, IWorld world, (Address, int) ta) { + var (target, amount) = ta; var stakeStateAddress = StakeState.DeriveAddress(target); - if (!world.TryGetStakeState(target, out var stakeState) - || stakeState.StateVersion != 3) - { - throw new InvalidOperationException( - "Target is not valid for refunding from non-validator."); - } - - var ncgStaked = world.GetBalance(stakeStateAddress, world.GetGoldCurrency()); - var ggStaked = world.GetBalance(stakeStateAddress, Currencies.GuildGold); - - var requiredGG = GetGuildCoinFromNCG(ncgStaked) - ggStaked; - - if (requiredGG.Sign != 1) - { - throw new InvalidOperationException( - "Target has sufficient amount of guild gold."); - } - - return world.TransferAsset(context, Addresses.NonValidatorDelegatee, stakeStateAddress, requiredGG); - } - - private static FungibleAssetValue GetGuildCoinFromNCG(FungibleAssetValue balance) - { - return FungibleAssetValue.Parse(Currencies.GuildGold, - balance.GetQuantityString(true)); + return world.TransferAsset( + context, + Addresses.NonValidatorDelegatee, + stakeStateAddress, + Currencies.GuildGold * amount); } } } From 3b06f8443f90150bd4de7bdcb13bf497b70be036 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 20 Nov 2024 11:47:58 +0900 Subject: [PATCH 184/206] chore: Fix refund action to accept enumerable of tuples --- .../FixToRefundFromNonValidatorTest.cs | 48 +++++-------------- .../Migration/FixToRefundFromNonValidator.cs | 38 +++++---------- 2 files changed, 23 insertions(+), 63 deletions(-) diff --git a/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs b/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs index 7f240a0759..bf5217d27b 100644 --- a/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs +++ b/.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs @@ -30,72 +30,46 @@ public FixToRefundFromNonValidatorTest() [Fact] public void PlainValue() { - var addresses = Enumerable.Range(0, 5).Select(_ => new PrivateKey().Address).ToList(); - var amounts = Enumerable.Range(0, 5).Select(i => (i + 1) * 10).ToList(); - - var plainValue = new FixToRefundFromNonValidator( - addresses, - amounts - ).PlainValue; + var targets = Enumerable.Range(0, 5).Select(i => (new PrivateKey().Address, (i + 1) * 10)).ToList(); + var plainValue = new FixToRefundFromNonValidator(targets).PlainValue; var recon = new FixToRefundFromNonValidator(); recon.LoadPlainValue(plainValue); - Assert.Equal(addresses, recon.Targets); - Assert.Equal(amounts, recon.Amounts); + Assert.Equal(targets, recon.Targets); } [Fact] public void Execute() { - var addresses = Enumerable.Range(0, 5).Select(_ => new PrivateKey().Address).ToList(); - var amounts = Enumerable.Range(0, 5).Select(i => (i + 1) * 10).ToList(); + var targets = Enumerable.Range(0, 5).Select(i => (new PrivateKey().Address, (i + 1) * 10)).ToList(); - var world = new FixToRefundFromNonValidator( - addresses, - amounts - ).Execute(new ActionContext + var world = new FixToRefundFromNonValidator(targets).Execute(new ActionContext { PreviousState = _world, Signer = _adminAddress, BlockIndex = 2L, }); - foreach (var item in addresses.Select((a, i) => (a, i))) + foreach (var item in targets) { Assert.Equal( - Currencies.GuildGold * ((item.i + 1) * 10), - world.GetBalance(StakeState.DeriveAddress(item.a), Currencies.GuildGold)); + Currencies.GuildGold * item.Item2, + world.GetBalance(StakeState.DeriveAddress(item.Item1), Currencies.GuildGold)); } Assert.Equal( - Currencies.GuildGold * (500 - amounts.Sum()), + Currencies.GuildGold * (500 - targets.Select(t => t.Item2).Sum()), world.GetBalance(Addresses.NonValidatorDelegatee, Currencies.GuildGold)); } - [Fact] - public void AssertWhenDifferentLengthArgument() - { - var addresses = Enumerable.Range(0, 5).Select(_ => new PrivateKey().Address).ToList(); - var amounts = Enumerable.Range(0, 4).Select(i => (i + 1) * 10).ToList(); - - Assert.Throws(() => - { - new FixToRefundFromNonValidator(addresses, amounts); - }); - } - [Fact] public void AssertWhenExecutedByNonAdmin() { - var addresses = Enumerable.Range(0, 5).Select(_ => new PrivateKey().Address).ToList(); - var amounts = Enumerable.Range(0, 5).Select(i => (i + 1) * 10); + var targets = Enumerable.Range(0, 5).Select(i => (new PrivateKey().Address, (i + 1) * 10)).ToList(); Assert.Throws(() => { - new FixToRefundFromNonValidator( - addresses, - amounts - ).Execute(new ActionContext + new FixToRefundFromNonValidator(targets).Execute(new ActionContext { PreviousState = _world, Signer = new PrivateKey().Address, diff --git a/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs b/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs index 1d22886e50..16e613d35e 100644 --- a/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs +++ b/Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs @@ -22,34 +22,25 @@ public class FixToRefundFromNonValidator : ActionBase private const string TargetsKey = "t"; - private const string AmountsKey = "a"; - - public List
Targets { get; private set; } - - public List Amounts { get; private set; } + public List<(Address Address, int Amount)> Targets { get; private set; } public FixToRefundFromNonValidator() { } public FixToRefundFromNonValidator( - IEnumerable
targets, - IEnumerable amounts) + IEnumerable<(Address, int)> targets) { Targets = targets.ToList(); - Amounts = amounts.ToList(); - - if (Targets.Count != Amounts.Count) - { - throw new ArgumentException("The number of targets and amounts must be the same."); - } } public override IValue PlainValue => Dictionary.Empty .Add("type_id", TypeIdentifier) .Add("values", Dictionary.Empty - .Add(TargetsKey, new List(Targets.Select(t => t.Bencoded))) - .Add(AmountsKey, new List(Amounts))); + .Add( + TargetsKey, + new List(Targets.Select(t => + new List(t.Item1.Bencoded, (Integer)t.Item2))))); public override void LoadPlainValue(IValue plainValue) { @@ -57,20 +48,15 @@ public override void LoadPlainValue(IValue plainValue) !root.TryGetValue((Text)"values", out var rawValues) || rawValues is not Dictionary values || !values.TryGetValue((Text)TargetsKey, out var rawTarget) || - rawTarget is not List targets || - !values.TryGetValue((Text)AmountsKey, out var rawAmounts) || - rawAmounts is not List amounts) + rawTarget is not List targets) { throw new InvalidCastException(); } - Targets = targets.Select(t => new Address(t)).ToList(); - Amounts = amounts.Select(a => (int)(Integer)a).ToList(); - - if (Targets.Count != Amounts.Count) - { - throw new ArgumentException("The number of targets and amounts must be the same."); - } + Targets = targets.Select( + t => ( + new Address(((List)t)[0]), + (int)(Integer)((List)t)[1])).ToList(); } public override IWorld Execute(IActionContext context) @@ -89,7 +75,7 @@ public override IWorld Execute(IActionContext context) throw new PermissionDeniedException(adminState, context.Signer); } - foreach (var ta in Targets.Zip(Amounts, (f, s) => (f, s))) + foreach (var ta in Targets) { world = RefundFromNonValidator(context, world, ta); } From 21a5cec69f72e0085202c71fff1b618fa70eda63 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:48:56 +0900 Subject: [PATCH 185/206] fix test --- .Lib9c.Tests/Model/Skill/Adventure/CombatTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.Lib9c.Tests/Model/Skill/Adventure/CombatTest.cs b/.Lib9c.Tests/Model/Skill/Adventure/CombatTest.cs index 25fc9d57bd..99cfa8fd7a 100644 --- a/.Lib9c.Tests/Model/Skill/Adventure/CombatTest.cs +++ b/.Lib9c.Tests/Model/Skill/Adventure/CombatTest.cs @@ -61,7 +61,8 @@ public CombatTest() [InlineData(10, 10, 5000, 120, 50)] [InlineData(0, 1000, 0, 999, 1)] [InlineData(1000, 0, 0, 999, 1)] - [InlineData(0, 0, 10000, int.MaxValue, 1)] + [InlineData(0, 0, 10000, 300, 150)] + [InlineData(0, 0, 10000, int.MaxValue, 300)] public void CalculateDEFAndDamageReduction(int def, int drv, int drr, int enemyATK, int expectedDamage) { _player.Stats.SetStatForTest(StatType.DEF, def); From ffea5e010bc06d877e18d5293d26c0199936ee78 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 20 Nov 2024 17:37:25 +0900 Subject: [PATCH 186/206] add buff limit add value --- .Lib9c.Tests/Model/CharacterStatsTest.cs | 39 +++++++++++++++++++++--- Lib9c/TableCSV/Skill/BuffLimitSheet.csv | 10 +++--- Lib9c/TableData/Skill/BuffLimitSheet.cs | 10 ++++-- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/.Lib9c.Tests/Model/CharacterStatsTest.cs b/.Lib9c.Tests/Model/CharacterStatsTest.cs index aa0412c8f7..f53fafa65c 100644 --- a/.Lib9c.Tests/Model/CharacterStatsTest.cs +++ b/.Lib9c.Tests/Model/CharacterStatsTest.cs @@ -16,7 +16,7 @@ public CharacterStatsTest() } [Fact] - public void DeBuffLimit() + public void DeBuffLimitPercentage() { var stats = new CharacterStats( @@ -26,7 +26,7 @@ public void DeBuffLimit() // -100% def but limit -50% stats var deBuff = new StatBuff(_tableSheets.StatBuffSheet[503012]); var groupId = deBuff.RowData.GroupId; - buffLimitSheet.Set($"group_id,percentage\n{groupId},-50"); + buffLimitSheet.Set($"group_id,percentage\n{groupId},Percentage,-50"); var def = stats.DEF; stats.AddBuff(deBuff, buffLimitSheet); var modifier = buffLimitSheet[groupId].GetModifier(deBuff.RowData.StatType); @@ -40,7 +40,7 @@ public void DeBuffLimit() } [Fact] - public void BuffLimit() + public void BuffLimitPercentage() { var stats = new CharacterStats( @@ -58,11 +58,42 @@ public void BuffLimit() stats.RemoveBuff(buff); Assert.Equal(atk, stats.ATK); // +60% atk but limit 50% stats - buffLimitSheet.Set($"group_id,percentage\n{groupId},50"); + buffLimitSheet.Set($"group_id,modify_type,percentage\n{groupId},Percentage,50"); var modifier = buffLimitSheet[groupId].GetModifier(buff.RowData.StatType); stats.AddBuff(buff, buffLimitSheet); Assert.Equal(atk * 1.5m, modifier.GetModifiedAll(atk)); Assert.Equal(atk * 1.5m, stats.ATK); } + + [Fact] + public void BuffLimitAdd() + { + const long buffDrrValue = 80; + const long buffLimitValue = 50; + + var stats = + new CharacterStats( + _tableSheets.CharacterSheet[GameConfig.DefaultAvatarCharacterId], + 1); + var statBuffSheet = new StatBuffSheet(); + statBuffSheet.Set($"id,group,_name,chance,duration,target_type,stat_type,modify_type,modify_value,is_enhanceable,max_stack\n702001,702001,DRR (커스텀 장비),100,12,Self,DRR,Add,{buffDrrValue},true,"); + var buffLimitSheet = new BuffLimitSheet(); + // +60% atk with no limit + var buff = new StatBuff(statBuffSheet[702001]); + var groupId = buff.RowData.GroupId; + var drr = stats.DRR; + stats.AddBuff(buff, buffLimitSheet); + Assert.Equal(drr + buffDrrValue, stats.DRR); + + // reset stats + stats.RemoveBuff(buff); + Assert.Equal(drr, stats.DRR); + // +60% atk but limit 50% stats + buffLimitSheet.Set($"group_id,modify_type,percentage\n{groupId},Add,{buffLimitValue}"); + var modifier = buffLimitSheet[groupId].GetModifier(buff.RowData.StatType); + stats.AddBuff(buff, buffLimitSheet); + Assert.Equal(drr + buffLimitValue, modifier.GetModifiedAll(drr)); + Assert.Equal(drr + buffLimitValue, stats.DRR); + } } } diff --git a/Lib9c/TableCSV/Skill/BuffLimitSheet.csv b/Lib9c/TableCSV/Skill/BuffLimitSheet.csv index 863782f7e1..9fa8380829 100644 --- a/Lib9c/TableCSV/Skill/BuffLimitSheet.csv +++ b/Lib9c/TableCSV/Skill/BuffLimitSheet.csv @@ -1,5 +1,5 @@ -group_id,percentage -206000,-20 -709000,-50 -206003,-20 -206009,-50 \ No newline at end of file +group_id,modify_type1,percentage +206000,Percentage,-20 +709000,Percentage,-50 +206003,Percentage,-20 +206009,Percentage,-50 \ No newline at end of file diff --git a/Lib9c/TableData/Skill/BuffLimitSheet.cs b/Lib9c/TableData/Skill/BuffLimitSheet.cs index 55c51b7958..f837a5892a 100644 --- a/Lib9c/TableData/Skill/BuffLimitSheet.cs +++ b/Lib9c/TableData/Skill/BuffLimitSheet.cs @@ -1,9 +1,12 @@ +using System; using System.Collections.Generic; using Nekoyume.Model.Stat; using static Nekoyume.TableData.TableExtensions; namespace Nekoyume.TableData { + using OperationType = StatModifier.OperationType; + public class BuffLimitSheet : Sheet { public class Row : SheetRow @@ -12,17 +15,20 @@ public class Row : SheetRow public int GroupId { get; set; } + public StatModifier.OperationType StatModifier { get; private set; } + public int Value { get; set; } public override void Set(IReadOnlyList fields) { GroupId = ParseInt(fields[0]); - Value = ParseInt(fields[1]); + StatModifier = (OperationType)Enum.Parse(typeof(OperationType), fields[1]); + Value = ParseInt(fields[2]); } public StatModifier GetModifier(StatType statType) { - return new StatModifier(statType, StatModifier.OperationType.Percentage, Value); + return new StatModifier(statType, StatModifier, Value); } } From a0dd247d88f9a683b22afbac1a83057ae1fbc9dd Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:02:05 +0900 Subject: [PATCH 187/206] fix comment --- .Lib9c.Tests/Model/CharacterStatsTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.Lib9c.Tests/Model/CharacterStatsTest.cs b/.Lib9c.Tests/Model/CharacterStatsTest.cs index f53fafa65c..a761fdef9a 100644 --- a/.Lib9c.Tests/Model/CharacterStatsTest.cs +++ b/.Lib9c.Tests/Model/CharacterStatsTest.cs @@ -78,7 +78,7 @@ public void BuffLimitAdd() var statBuffSheet = new StatBuffSheet(); statBuffSheet.Set($"id,group,_name,chance,duration,target_type,stat_type,modify_type,modify_value,is_enhanceable,max_stack\n702001,702001,DRR (커스텀 장비),100,12,Self,DRR,Add,{buffDrrValue},true,"); var buffLimitSheet = new BuffLimitSheet(); - // +60% atk with no limit + // +80 drr with no limit var buff = new StatBuff(statBuffSheet[702001]); var groupId = buff.RowData.GroupId; var drr = stats.DRR; @@ -88,7 +88,7 @@ public void BuffLimitAdd() // reset stats stats.RemoveBuff(buff); Assert.Equal(drr, stats.DRR); - // +60% atk but limit 50% stats + // +80 drr but limit 50 stats buffLimitSheet.Set($"group_id,modify_type,percentage\n{groupId},Add,{buffLimitValue}"); var modifier = buffLimitSheet[groupId].GetModifier(buff.RowData.StatType); stats.AddBuff(buff, buffLimitSheet); From a8b6a669f630aa32199a87ffbedabc0a97d45f7e Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:10:46 +0900 Subject: [PATCH 188/206] change drr limit value 5000 -> 8100 --- Lib9c/Battle/DamageHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib9c/Battle/DamageHelper.cs b/Lib9c/Battle/DamageHelper.cs index c93e4d680e..384f777782 100644 --- a/Lib9c/Battle/DamageHelper.cs +++ b/Lib9c/Battle/DamageHelper.cs @@ -11,8 +11,8 @@ public static decimal GetDamageReductionRate(long drr) { switch (drr) { - case > 5000L: - drr = 5000L; + case > 8100L: + drr = 8100L; break; case < 0L: return 0m; From 25bdd3bbb65bec7de2f08b1925c3ef9f79b4c061 Mon Sep 17 00:00:00 2001 From: eugene-hong <58686228+eugene-doobu@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:32:06 +0900 Subject: [PATCH 189/206] fix unit test --- .Lib9c.Tests/Action/DamageHelperTest.cs | 2 +- .Lib9c.Tests/Model/Skill/Adventure/CombatTest.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.Lib9c.Tests/Action/DamageHelperTest.cs b/.Lib9c.Tests/Action/DamageHelperTest.cs index 505766b578..fba4871771 100644 --- a/.Lib9c.Tests/Action/DamageHelperTest.cs +++ b/.Lib9c.Tests/Action/DamageHelperTest.cs @@ -5,7 +5,7 @@ namespace Lib9c.Tests.Action public class DamageHelperTest { [Theory] - [InlineData(long.MaxValue, 0.5)] + [InlineData(long.MaxValue, 0.19)] [InlineData(4000L, 0.6)] [InlineData(long.MinValue, 0)] public void GetDamageReductionRate(long drr, decimal expected) diff --git a/.Lib9c.Tests/Model/Skill/Adventure/CombatTest.cs b/.Lib9c.Tests/Model/Skill/Adventure/CombatTest.cs index 99cfa8fd7a..6de4b18626 100644 --- a/.Lib9c.Tests/Model/Skill/Adventure/CombatTest.cs +++ b/.Lib9c.Tests/Model/Skill/Adventure/CombatTest.cs @@ -61,7 +61,7 @@ public CombatTest() [InlineData(10, 10, 5000, 120, 50)] [InlineData(0, 1000, 0, 999, 1)] [InlineData(1000, 0, 0, 999, 1)] - [InlineData(0, 0, 10000, 300, 150)] + [InlineData(0, 0, 10000, 300, 57)] [InlineData(0, 0, 10000, int.MaxValue, 300)] public void CalculateDEFAndDamageReduction(int def, int drv, int drr, int enemyATK, int expectedDamage) { From 86056a0449ddaca4858f1cee42ed27efc3c5d377 Mon Sep 17 00:00:00 2001 From: Hyun Seungmin Date: Thu, 21 Nov 2024 16:31:03 +0900 Subject: [PATCH 190/206] configure project versions to 1.20.0 --- Lib9c.Abstractions/Lib9c.Abstractions.csproj | 2 +- Lib9c.MessagePack/Lib9c.MessagePack.csproj | 2 +- Lib9c.Renderers/Lib9c.Renderers.csproj | 2 +- Lib9c/Lib9c.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib9c.Abstractions/Lib9c.Abstractions.csproj b/Lib9c.Abstractions/Lib9c.Abstractions.csproj index b52b617288..d1eeb2b29a 100644 --- a/Lib9c.Abstractions/Lib9c.Abstractions.csproj +++ b/Lib9c.Abstractions/Lib9c.Abstractions.csproj @@ -8,7 +8,7 @@ .obj 9 ..\Lib9c.Common.ruleset - 1.19.0 + 1.20.0 diff --git a/Lib9c.MessagePack/Lib9c.MessagePack.csproj b/Lib9c.MessagePack/Lib9c.MessagePack.csproj index 6b5a8ee0e3..f588c9d3ff 100644 --- a/Lib9c.MessagePack/Lib9c.MessagePack.csproj +++ b/Lib9c.MessagePack/Lib9c.MessagePack.csproj @@ -8,7 +8,7 @@ AnyCPU .bin .obj - 1.19.0 + 1.20.0 diff --git a/Lib9c.Renderers/Lib9c.Renderers.csproj b/Lib9c.Renderers/Lib9c.Renderers.csproj index 4333aa1094..0b6e08a83f 100644 --- a/Lib9c.Renderers/Lib9c.Renderers.csproj +++ b/Lib9c.Renderers/Lib9c.Renderers.csproj @@ -6,7 +6,7 @@ enable .bin .obj - 1.19.0 + 1.20.0 Lib9c diff --git a/Lib9c/Lib9c.csproj b/Lib9c/Lib9c.csproj index 9409204a84..783bb66bb4 100644 --- a/Lib9c/Lib9c.csproj +++ b/Lib9c/Lib9c.csproj @@ -9,7 +9,7 @@ .obj Nekoyume 9 - 1.19.0 + 1.20.0 true Debug;Release AnyCPU From 03dfa8949c6e97e122471634a978bb3fbcdaa52b Mon Sep 17 00:00:00 2001 From: Atralupus Date: Thu, 21 Nov 2024 17:57:04 +0900 Subject: [PATCH 191/206] Add release publish trigger --- .github/workflows/publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 880d8e0835..310536ba20 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,6 +5,7 @@ on: branches: - development - main + - release/* tags: - "*.*.*" From 682e96b3561eb1135f3b9b2e1b93d798d678a92b Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 22 Nov 2024 09:52:58 +0900 Subject: [PATCH 192/206] fix: Fix debug-related features --- Lib9c.Policy/Policy/DebugPolicy.cs | 21 ++++++++++++++++++++- Lib9c.Proposer/Proposer.cs | 13 ++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Lib9c.Policy/Policy/DebugPolicy.cs b/Lib9c.Policy/Policy/DebugPolicy.cs index e10765da2f..8a1f5d1fbd 100644 --- a/Lib9c.Policy/Policy/DebugPolicy.cs +++ b/Lib9c.Policy/Policy/DebugPolicy.cs @@ -5,6 +5,7 @@ using Libplanet.Types.Blocks; using Libplanet.Types.Tx; using Nekoyume.Action; +using Nekoyume.Action.ValidatorDelegation; namespace Nekoyume.Blockchain.Policy { @@ -14,7 +15,25 @@ public DebugPolicy() { } - public IPolicyActionsRegistry PolicyActionsRegistry { get; } = new PolicyActionsRegistry(); + public IPolicyActionsRegistry PolicyActionsRegistry { get; } = + new PolicyActionsRegistry( + beginBlockActions: new IAction[] { + new SlashValidator(), + new AllocateGuildReward(), + new AllocateReward(), + }.ToImmutableArray(), + endBlockActions: new IAction[] { + new UpdateValidators(), + new RecordProposer(), + new RewardGold(), + new ReleaseValidatorUnbondings(), + }.ToImmutableArray(), + beginTxActions: new IAction[] { + new Mortgage(), + }.ToImmutableArray(), + endTxActions: new IAction[] { + new Reward(), new Refund(), + }.ToImmutableArray()); public TxPolicyViolationException ValidateNextBlockTx( BlockChain blockChain, Transaction transaction) diff --git a/Lib9c.Proposer/Proposer.cs b/Lib9c.Proposer/Proposer.cs index 65abac0838..0810c19713 100644 --- a/Lib9c.Proposer/Proposer.cs +++ b/Lib9c.Proposer/Proposer.cs @@ -3,6 +3,7 @@ using System.Collections.Immutable; using System.Threading; using Libplanet.Action; +using Libplanet.Action.State; using Libplanet.Blockchain; using Libplanet.Crypto; using Libplanet.Types.Blocks; @@ -33,6 +34,16 @@ public class Proposer block = _chain.ProposeBlock( _privateKey, lastCommit: lastCommit); + + if (!(_chain.GetNextWorldState() is IWorldState worldState)) + { + throw new InvalidOperationException( + "Failed to get next world state. Appending is not completed."); + } + + var proposerPower = worldState.GetValidatorSet().GetValidatorsPower( + new List { _privateKey.PublicKey }); + BlockCommit? commit = block.Index > 0 ? new BlockCommit( block.Index, @@ -45,7 +56,7 @@ public class Proposer block.Hash, DateTimeOffset.UtcNow, _privateKey.PublicKey, - null, + proposerPower, VoteFlag.PreCommit).Sign(_privateKey))) : null; _chain.Append(block, commit); From c73a7fcf6fd7080fafef92d3ef8163338b3263f0 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 22 Nov 2024 17:44:11 +0900 Subject: [PATCH 193/206] fix: Fix slashing, activate --- .../Migration/MigratePlanetariumValidator.cs | 83 +++++++++++++++++++ .../ValidatorDelegation/SlashValidator.cs | 5 ++ Lib9c/Delegation/Delegatee.cs | 2 +- Lib9c/Model/Guild/GuildDelegatee.cs | 18 ++++ .../ValidatorDelegation/ValidatorDelegatee.cs | 10 +++ 5 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 Lib9c/Action/Guild/Migration/MigratePlanetariumValidator.cs diff --git a/Lib9c/Action/Guild/Migration/MigratePlanetariumValidator.cs b/Lib9c/Action/Guild/Migration/MigratePlanetariumValidator.cs new file mode 100644 index 0000000000..0a91936bf2 --- /dev/null +++ b/Lib9c/Action/Guild/Migration/MigratePlanetariumValidator.cs @@ -0,0 +1,83 @@ +using System; +using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.State; +using Nekoyume.Model.Guild; +using Nekoyume.Module.Guild; +using Nekoyume.ValidatorDelegation; + +namespace Nekoyume.Action.Guild.Migration +{ + // TODO: [GuildMigration] Remove this class when the migration is done. + /// + /// An action to migrate the planetarium guild. + /// + [ActionType(TypeIdentifier)] + public class MigratePlanetariumValidator : ActionBase + { + public const string TypeIdentifier = "migrate_planetarium_validator"; + + public MigratePlanetariumValidator() + { + } + + public override IValue PlainValue => Dictionary.Empty + .Add("type_id", TypeIdentifier) + .Add("values", Null.Value); + + public override void LoadPlainValue(IValue plainValue) + { + if (plainValue is not Dictionary root || + !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 guildRepository = new GuildRepository(world, context); + var guildAddress = guildRepository.GetJoinedGuild(GuildConfig.PlanetariumGuildOwner); + if (guildAddress is not { } planetariumGuildAddress) + { + throw new InvalidOperationException("The planetarium guild not exists"); + } + + var planetariumGuild = guildRepository.GetGuild( + planetariumGuildAddress); + + var validatorRepository = new ValidatorRepository(guildRepository); + var validatorDelegatee = validatorRepository.GetValidatorDelegatee( + planetariumGuild.ValidatorAddress); + + var validatorSet = world.GetValidatorSet(); + + if (!validatorSet.ContainsPublicKey(validatorDelegatee.PublicKey)) + { + throw new InvalidOperationException( + "The planetarium validator is not in the validator set."); + } + + if (validatorDelegatee.IsActive) + { + throw new InvalidOperationException( + "The planetarium validator is already active."); + } + + validatorDelegatee.Activate(); + validatorRepository.SetValidatorDelegatee(validatorDelegatee); + + guildRepository.UpdateWorld(validatorRepository.World); + var guildDelegatee = guildRepository.GetGuildDelegatee( + planetariumGuild.ValidatorAddress); + guildDelegatee.Activate(); + guildRepository.SetGuildDelgatee(guildDelegatee); + + return guildRepository.World; + } + } +} diff --git a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs index 5d9fc74550..e97424efc0 100644 --- a/Lib9c/Action/ValidatorDelegation/SlashValidator.cs +++ b/Lib9c/Action/ValidatorDelegation/SlashValidator.cs @@ -52,6 +52,11 @@ public override IWorld Execute(IActionContext context) foreach (var abstain in abstainsToSlash) { var validatorDelegatee = repository.GetValidatorDelegatee(abstain.Address); + if (validatorDelegatee.Jailed) + { + continue; + } + validatorDelegatee.Slash(LivenessSlashFactor, context.BlockIndex, context.BlockIndex); validatorDelegatee.Jail(context.BlockIndex + AbstainJailTime); diff --git a/Lib9c/Delegation/Delegatee.cs b/Lib9c/Delegation/Delegatee.cs index e18ae6d3e8..52db964657 100644 --- a/Lib9c/Delegation/Delegatee.cs +++ b/Lib9c/Delegation/Delegatee.cs @@ -295,7 +295,7 @@ record = record.AddLumpSumRewards(rewards); Repository.SetLumpSumRewardsRecord(record); } - public void Slash(BigInteger slashFactor, long infractionHeight, long height) + public virtual void Slash(BigInteger slashFactor, long infractionHeight, long height) { FungibleAssetValue slashed = TotalDelegated.DivRem(slashFactor, out var rem); if (rem.Sign > 0) diff --git a/Lib9c/Model/Guild/GuildDelegatee.cs b/Lib9c/Model/Guild/GuildDelegatee.cs index 079fe52377..1ffedb2514 100644 --- a/Lib9c/Model/Guild/GuildDelegatee.cs +++ b/Lib9c/Model/Guild/GuildDelegatee.cs @@ -1,6 +1,7 @@ #nullable enable using System; using System.Collections.Generic; +using System.Numerics; using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Delegation; @@ -40,6 +41,23 @@ public GuildDelegatee( { } + public override void Slash(BigInteger slashFactor, long infractionHeight, long height) + { + FungibleAssetValue slashed = TotalDelegated.DivRem(slashFactor, out var rem); + if (rem.Sign > 0) + { + slashed += FungibleAssetValue.FromRawValue(rem.Currency, 1); + } + + if (slashed > Metadata.TotalDelegatedFAV) + { + slashed = Metadata.TotalDelegatedFAV; + } + + Metadata.RemoveDelegatedFAV(slashed); + Repository.SetDelegateeMetadata(Metadata); + } + public void Activate() { Metadata.DelegationPoolAddress = ValidatorDelegatee.ActiveDelegationPoolAddress; diff --git a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs index c3fa0d537b..7ebe20bea4 100644 --- a/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs +++ b/Lib9c/ValidatorDelegation/ValidatorDelegatee.cs @@ -199,6 +199,11 @@ public void SetCommissionPercentage(BigInteger percentage, long height) public void Activate() { + if (IsActive) + { + throw new InvalidOperationException("The validator is already active."); + } + ValidatorRepository repository = (ValidatorRepository)Repository; IsActive = true; Metadata.DelegationPoolAddress = ActiveDelegationPoolAddress; @@ -214,6 +219,11 @@ public void Activate() public void Deactivate() { + if (!IsActive) + { + throw new InvalidOperationException("The validator is already inactive."); + } + ValidatorRepository repository = (ValidatorRepository)Repository; IsActive = false; Metadata.DelegationPoolAddress = InactiveDelegationPoolAddress; From 657fe1ec93b605eebb5142743d7d3215adbb223b Mon Sep 17 00:00:00 2001 From: jonny Date: Fri, 22 Nov 2024 18:58:51 +0900 Subject: [PATCH 194/206] fix floor error --- Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs b/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs index c1025ef6f4..ff1cb02f75 100644 --- a/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs +++ b/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs @@ -251,8 +251,8 @@ public override IWorld Execute(IActionContext context) for (var fl = explorer.Floor + 1; fl < explorer.MaxFloor + 1; fl++) { // Get Data for simulator - var floorRow = floorRows.First(row => row.Floor == fl); - if (!floorSheet.TryGetValue(fl, out var flRow)) + var floorRow = floorRows.FirstOrDefault(row => row.Floor == fl); + if (floorRow == null) { throw new SheetRowNotFoundException(addressesHex, nameof(floorSheet), fl); } @@ -263,7 +263,7 @@ public override IWorld Execute(IActionContext context) } var rewards = - AdventureBossSimulator.GetWaveRewards(random, flRow, materialItemSheet); + AdventureBossSimulator.GetWaveRewards(random, floorRow, materialItemSheet); // Use AP Potion if (!avatarState.inventory.RemoveFungibleItem(material.ItemId, context.BlockIndex, @@ -292,7 +292,7 @@ public override IWorld Execute(IActionContext context) collectionModifiers, buffLimitSheet, buffLinkSheet, - false, + true, gameConfigState.ShatterStrikeMaxDamage ); From 7db907417570dba57dfa154e47ae96083be1c595 Mon Sep 17 00:00:00 2001 From: jonny Date: Fri, 22 Nov 2024 19:02:52 +0900 Subject: [PATCH 195/206] disable battle log --- Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs b/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs index ff1cb02f75..88d2ad2e47 100644 --- a/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs +++ b/Lib9c/Action/AdventureBoss/ExploreAdventureBoss.cs @@ -292,7 +292,7 @@ public override IWorld Execute(IActionContext context) collectionModifiers, buffLimitSheet, buffLinkSheet, - true, + false, gameConfigState.ShatterStrikeMaxDamage ); From 5836a23c956194e26711603fe6fdad8db2301aaf Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 22 Nov 2024 19:07:32 +0900 Subject: [PATCH 196/206] Update Libplanet 5.4.1 --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index af6e2c7447..f18e8496d7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 5.4.0 + 5.4.1 From 385d7ffab705dc89611a9141784a5a321e517c99 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 25 Nov 2024 14:24:54 +0900 Subject: [PATCH 197/206] chore: Fix typo --- Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs index 13f792cbef..7581ed37f6 100644 --- a/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs +++ b/Lib9c/Module/ValidatorDelegation/ValidatorDelegateeModule.cs @@ -35,7 +35,7 @@ public static ValidatorDelegatee CreateValidatorDelegatee( if (repository.TryGetValidatorDelegatee(publicKey.Address, out _)) { - throw new InvalidOperationException("The signer already has a validator delegatee."); + throw new InvalidOperationException("The public key already has a validator delegatee."); } var validatorDelegatee = new ValidatorDelegatee( From da6a53489f183f52cbbc9093e45a8b7d032ce14d Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 25 Nov 2024 14:25:36 +0900 Subject: [PATCH 198/206] test: Fix lacked test cases --- .../Lib9c.Proposer.Tests.csproj | 1 + .Lib9c.Miner.Tests/ProposerTest.cs | 145 ++++++++++++++++++ .../MigratePlanetariumValidatorTest.cs | 116 ++++++++++++++ .../ValidatorDelegation/SlashValidatorTest.cs | 46 ++++++ 4 files changed, 308 insertions(+) create mode 100644 .Lib9c.Miner.Tests/ProposerTest.cs create mode 100644 .Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumValidatorTest.cs diff --git a/.Lib9c.Miner.Tests/Lib9c.Proposer.Tests.csproj b/.Lib9c.Miner.Tests/Lib9c.Proposer.Tests.csproj index 059f555c09..e5b1dba72c 100644 --- a/.Lib9c.Miner.Tests/Lib9c.Proposer.Tests.csproj +++ b/.Lib9c.Miner.Tests/Lib9c.Proposer.Tests.csproj @@ -22,6 +22,7 @@ + diff --git a/.Lib9c.Miner.Tests/ProposerTest.cs b/.Lib9c.Miner.Tests/ProposerTest.cs new file mode 100644 index 0000000000..aae4ba2857 --- /dev/null +++ b/.Lib9c.Miner.Tests/ProposerTest.cs @@ -0,0 +1,145 @@ +using System.Collections.Immutable; +using Lib9c.Renderers; +using Libplanet.Action; +using Libplanet.Blockchain.Policies; +using Libplanet.Blockchain; +using Libplanet.Crypto; +using Libplanet.Store; +using Libplanet.Store.Trie; +using Libplanet.Types.Assets; +using Libplanet.Types.Blocks; +using Libplanet.Types.Consensus; +using Libplanet.Types.Tx; +using Nekoyume.Action; +using Nekoyume.Action.Loader; +using Nekoyume.Blockchain.Policy; +using Nekoyume.Model.State; +using System.Numerics; + +namespace Lib9c.Proposer.Tests +{ + public class ProposerTest + { + private readonly PrivateKey _admin; + private readonly PrivateKey _proposer; + private readonly BlockChain _blockChain; + + public ProposerTest() + { + _admin = new PrivateKey(); + _proposer = new PrivateKey(); + var ncg = Currency.Uncapped("ncg", 2, null); + var policy = new DebugPolicy(); + var actionTypeLoader = new NCActionLoader(); + IStagePolicy stagePolicy = new VolatileStagePolicy(); + var mint = new PrepareRewardAssets + { + RewardPoolAddress = _proposer.Address, + Assets = new List + { + 1 * Currencies.Mead, + }, + }; + + var validatorSet = new ValidatorSet( + new List { new(_proposer.PublicKey, 10_000_000_000_000_000_000) }); + + var initializeStates = new InitializeStates( + validatorSet, + new RankingState0(), + new ShopState(), + new Dictionary(), + new GameConfigState(), + new RedeemCodeState(new Dictionary()), + new ActivatedAccountsState(), + new GoldCurrencyState(ncg), + new GoldDistribution[] { }, + new PendingActivationState[] { }); + + List actions = new List + { + initializeStates, + }; + + var genesis = BlockChain.ProposeGenesisBlock( + privateKey: _proposer, + transactions: ImmutableList.Empty + .Add(Transaction.Create( + 0, _proposer, null, actions.ToPlainValues())), + timestamp: DateTimeOffset.MinValue); + + var store = new MemoryStore(); + var stateStore = new TrieStateStore(new MemoryKeyValueStore()); + + _blockChain = BlockChain.Create( + policy, + stagePolicy, + store, + stateStore, + genesis, + new ActionEvaluator( + policy.PolicyActionsRegistry, + stateStore, + new NCActionLoader() + ), + new[] { new BlockRenderer(), } + ); + } + + [Fact] + public void ProposeBlock() + { + Block block = _blockChain.ProposeBlock(_proposer); + _blockChain.Append( + block, + GenerateBlockCommit( + block, + _proposer, + 10_000_000_000_000_000_000)); + } + + [Fact] + public void AssertInvalidProposer() + { + Block block = _blockChain.ProposeBlock(_proposer); + Assert.Throws(() => _blockChain.Append( + block, + GenerateBlockCommit( + block, + new PrivateKey(), + 10_000_000_000_000_000_000))); + } + + [Fact] + public void AssertInvalidPower() + { + Block block = _blockChain.ProposeBlock(_proposer); + Assert.Throws(() => _blockChain.Append( + block, + GenerateBlockCommit( + block, + _proposer, + 10_000_000_000_000_000))); + } + + private BlockCommit? GenerateBlockCommit( + Block block, PrivateKey privateKey, BigInteger power) + { + return block.Index != 0 + ? new BlockCommit( + block.Index, + 0, + block.Hash, + ImmutableArray.Create( + new VoteMetadata( + block.Index, + 0, + block.Hash, + DateTimeOffset.UtcNow, + privateKey.PublicKey, + power, + VoteFlag.PreCommit).Sign(privateKey))) + : null; + } + } +} diff --git a/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumValidatorTest.cs b/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumValidatorTest.cs new file mode 100644 index 0000000000..cd15015166 --- /dev/null +++ b/.Lib9c.Tests/Action/Guild/Migration/MigratePlanetariumValidatorTest.cs @@ -0,0 +1,116 @@ +namespace Lib9c.Tests.Action.Guild.Migration +{ + using System; + using System.Collections.Generic; + using System.Numerics; + using Lib9c.Tests.Util; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Types.Assets; + using Libplanet.Types.Consensus; + using Nekoyume.Action.Guild; + using Nekoyume.Action.Guild.Migration; + using Nekoyume.Action.Guild.Migration.LegacyModels; + using Nekoyume.Action.ValidatorDelegation; + using Nekoyume.Model.Guild; + using Nekoyume.Model.Stake; + using Nekoyume.TypedAddress; + using Nekoyume.ValidatorDelegation; + using Xunit; + + // TODO: Remove this test class after the migration is completed. + public class MigratePlanetariumValidatorTest : GuildTestBase + { + [Fact] + public void Execute() + { + var guildAddress = AddressUtil.CreateGuildAddress(); + var validatorKey = new PrivateKey().PublicKey; + var validatorAddress = validatorKey.Address; + var power = 10_000_000_000_000_000_000; + var guildGold = Currencies.GuildGold; + var delegated = FungibleAssetValue.FromRawValue(guildGold, power); + + var world = EnsureLegacyPlanetariumValidator( + World, guildAddress, validatorKey, power); + + var guildRepository = new GuildRepository(world, new ActionContext { }); + var validatorRepository = new ValidatorRepository(world, new ActionContext { }); + var guildDelegatee = guildRepository.GetGuildDelegatee(validatorAddress); + var validatorDelegatee = validatorRepository.GetValidatorDelegatee(validatorAddress); + + Assert.False(validatorDelegatee.IsActive); + Assert.Equal(ValidatorDelegatee.InactiveDelegationPoolAddress, guildDelegatee.DelegationPoolAddress); + Assert.Equal(ValidatorDelegatee.InactiveDelegationPoolAddress, validatorDelegatee.DelegationPoolAddress); + Assert.Equal(delegated, world.GetBalance(ValidatorDelegatee.InactiveDelegationPoolAddress, Currencies.GuildGold)); + Assert.Equal(guildGold * 0, world.GetBalance(ValidatorDelegatee.ActiveDelegationPoolAddress, Currencies.GuildGold)); + + var action = new MigratePlanetariumValidator(); + var actionContext = new ActionContext + { + PreviousState = world, + Signer = new PrivateKey().Address, + }; + world = action.Execute(actionContext); + + guildRepository = new GuildRepository(world, new ActionContext { }); + validatorRepository = new ValidatorRepository(world, new ActionContext { }); + guildDelegatee = guildRepository.GetGuildDelegatee(validatorAddress); + validatorDelegatee = validatorRepository.GetValidatorDelegatee(validatorAddress); + + Assert.True(validatorRepository.GetValidatorDelegatee(validatorAddress).IsActive); + Assert.Equal(ValidatorDelegatee.ActiveDelegationPoolAddress, guildDelegatee.DelegationPoolAddress); + Assert.Equal(ValidatorDelegatee.ActiveDelegationPoolAddress, validatorDelegatee.DelegationPoolAddress); + Assert.Equal(delegated, world.GetBalance(ValidatorDelegatee.ActiveDelegationPoolAddress, Currencies.GuildGold)); + Assert.Equal(guildGold * 0, world.GetBalance(ValidatorDelegatee.InactiveDelegationPoolAddress, Currencies.GuildGold)); + + Assert.Throws(() => + { + var actionContext = new ActionContext + { + PreviousState = world, + Signer = new PrivateKey().Address, + }; + + world = action.Execute(actionContext); + }); + } + + private static IWorld EnsureLegacyPlanetariumValidator( + IWorld world, GuildAddress guildAddress, PublicKey validatorKey, BigInteger power) + { + world = world.SetDelegationMigrationHeight(0L); + + var toDelegate = FungibleAssetValue.FromRawValue(Currencies.GuildGold, power); + world = world + .MintAsset( + new ActionContext { }, + StakeState.DeriveAddress(validatorKey.Address), + toDelegate) + .MintAsset( + new ActionContext { }, + validatorKey.Address, + Currencies.Mead * 1); + + world = new PromoteValidator(validatorKey, toDelegate).Execute(new ActionContext + { + PreviousState = world, + Signer = validatorKey.Address, + }); + + world = new MakeGuild(validatorKey.Address).Execute(new ActionContext + { + PreviousState = world, + Signer = GuildConfig.PlanetariumGuildOwner, + }); + + world = world.SetValidatorSet(new ValidatorSet( + new List + { + new Validator(validatorKey, power), + })); + + return world; + } + } +} diff --git a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs index 3173039718..27699ba63a 100644 --- a/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs +++ b/.Lib9c.Tests/Action/ValidatorDelegation/SlashValidatorTest.cs @@ -210,4 +210,50 @@ public void Execute_ToJailedValidator_ThenNothingHappens() Assert.Equal(expectedJailed, actualJailed); } + + [Fact] + public void Execute_ByAbstain_ToJailedValidator_ThenNothingHappens() + { + // Given + var world = World; + var validatorKey = new PrivateKey(); + var height = 1L; + var actionContext = new ActionContext(); + world = EnsureToMintAsset(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsurePromotedValidator(world, validatorKey, DelegationCurrency * 10, height++); + world = EnsureJailedValidator(world, validatorKey, ref height); + + var expectedRepository = new ValidatorRepository(world, actionContext); + var expectedDelegatee = expectedRepository.GetValidatorDelegatee(validatorKey.Address); + var expectedTotalDelegated = expectedDelegatee.TotalDelegated; + + // When + for (var i = 0L; i <= AbstainHistory.MaxAbstainAllowance; i++) + { + var vote = CreateNullVote(validatorKey, height - 1); + var lastCommit = new BlockCommit( + height: height - 1, + round: 0, + blockHash: vote.BlockHash, + ImmutableArray.Create(vote)); + var slashValidator = new SlashValidator(); + actionContext = new ActionContext + { + PreviousState = world, + Signer = validatorKey.Address, + BlockIndex = height++, + LastCommit = lastCommit, + }; + world = slashValidator.Execute(actionContext); + } + + // Then + var actualRepisitory = new ValidatorRepository(world, actionContext); + var actualDelegatee = actualRepisitory.GetValidatorDelegatee(validatorKey.Address); + var actualTotalDelegated = actualDelegatee.TotalDelegated; + + Assert.True(actualDelegatee.Jailed); + Assert.False(actualDelegatee.Tombstoned); + Assert.Equal(expectedTotalDelegated, actualTotalDelegated); + } } From 04445280563cfd34cc3b94adee2831c0f45e8954 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 25 Nov 2024 15:51:30 +0900 Subject: [PATCH 199/206] fix: Fix MigrateDelegation action --- .../Guild/Migration/MigrateDelegation.cs | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/Lib9c/Action/Guild/Migration/MigrateDelegation.cs b/Lib9c/Action/Guild/Migration/MigrateDelegation.cs index 65dbec9dde..14e77621ac 100644 --- a/Lib9c/Action/Guild/Migration/MigrateDelegation.cs +++ b/Lib9c/Action/Guild/Migration/MigrateDelegation.cs @@ -119,23 +119,28 @@ public override IWorld Execute(IActionContext context) return repository.World; } - catch (FailedLoadStateException) + catch (Exception e) { - var pledgeAddress = ((Address)Target).GetPledgeAddress(); - - // Patron contract structure: - // [0] = PatronAddress - // [1] = IsApproved - // [2] = Mead amount to refill. - if (!world.TryGetLegacyState(pledgeAddress, out List list) || list.Count < 3 || - list[0] is not Binary || list[0].ToAddress() != MeadConfig.PatronAddress || - list[1] is not Bencodex.Types.Boolean approved || !approved) + if (e is FailedLoadStateException || e is NullReferenceException) { - throw new GuildMigrationFailedException("Unexpected pledge structure."); + var pledgeAddress = ((Address)Target).GetPledgeAddress(); + + // Patron contract structure: + // [0] = PatronAddress + // [1] = IsApproved + // [2] = Mead amount to refill. + if (!world.TryGetLegacyState(pledgeAddress, out List list) || list.Count < 3 || + list[0] is not Binary || list[0].ToAddress() != MeadConfig.PatronAddress || + list[1] is not Bencodex.Types.Boolean approved || !approved) + { + throw new GuildMigrationFailedException("Unexpected pledge structure."); + } + + repository.JoinGuild(planetariumGuildAddress, Target); + return repository.World; } - repository.JoinGuild(planetariumGuildAddress, Target); - return repository.World; + throw; } } } From df449d9ab9a3503cc3742f0ff0f47e134c319a65 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Tue, 26 Nov 2024 00:23:18 +0900 Subject: [PATCH 200/206] chore: Change MigrateDelegationHeight as admin action --- .../Guild/Migration/LegacyModels/MigrationModule.cs | 6 ------ .../Action/Guild/Migration/MigrateDelegationHeight.cs | 11 +++++++++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs b/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs index 4d2724c6f5..493282e363 100644 --- a/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs +++ b/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs @@ -1,4 +1,3 @@ -using System; using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; @@ -23,11 +22,6 @@ public static readonly Address DelegationMigrationHeight public static IWorld SetDelegationMigrationHeight(this IWorld world, long height) { - if (world.GetDelegationMigrationHeight() is long) - { - throw new InvalidOperationException("Cannot overwrite delegation migration index."); - } - return world .MutateAccount( Addresses.Migration, diff --git a/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs b/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs index 48d1ad2b9d..dfd5aaa531 100644 --- a/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs +++ b/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs @@ -3,6 +3,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Nekoyume.Action.Guild.Migration.LegacyModels; +using Nekoyume.Model.State; namespace Nekoyume.Action.Guild.Migration { @@ -53,6 +54,16 @@ public override IWorld Execute(IActionContext context) var world = context.PreviousState; + if (!TryGetAdminState(context, out AdminState adminState)) + { + throw new InvalidOperationException("Couldn't find admin state"); + } + + if (context.Signer != adminState.AdminAddress) + { + throw new PermissionDeniedException(adminState, context.Signer); + } + return world.SetDelegationMigrationHeight(Height); } } From 31c7167c7556df4fa4846553ac642f0fb852d73a Mon Sep 17 00:00:00 2001 From: s2quake Date: Thu, 21 Nov 2024 10:57:24 +0900 Subject: [PATCH 201/206] Fix: Collection was modified issue when slash executes --- Lib9c/ValidatorDelegation/AbstainHistory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/ValidatorDelegation/AbstainHistory.cs b/Lib9c/ValidatorDelegation/AbstainHistory.cs index c90a987361..430d2c78cd 100644 --- a/Lib9c/ValidatorDelegation/AbstainHistory.cs +++ b/Lib9c/ValidatorDelegation/AbstainHistory.cs @@ -78,7 +78,7 @@ public List FindToSlashAndAdd(IEnumerable abstainList, lon } } - foreach (var history in History) + foreach (var history in History.ToArray()) { history.Value.RemoveAll(abstainHeight => abstainHeight < lowerBound); if (history.Value.Count == 0) From 8650fc71e1dc736b2d5ce45c8ad8dd391090cdc2 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Tue, 26 Nov 2024 12:47:30 +0900 Subject: [PATCH 202/206] chore: Minor fixes and add docstrings for new actions --- Lib9c/Action/Guild/Migration/GuildMigrationFailedException.cs | 3 +++ Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuild.cs | 3 +++ .../Guild/Migration/LegacyModels/LegacyGuildParticipant.cs | 3 +++ Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs | 4 ++++ Lib9c/Action/Guild/Migration/MigrateDelegation.cs | 2 ++ Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs | 1 + Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs | 1 + Lib9c/Action/Guild/Migration/MigratePlanetariumValidator.cs | 4 +++- 8 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Lib9c/Action/Guild/Migration/GuildMigrationFailedException.cs b/Lib9c/Action/Guild/Migration/GuildMigrationFailedException.cs index 322ce9213c..118fd7e7f9 100644 --- a/Lib9c/Action/Guild/Migration/GuildMigrationFailedException.cs +++ b/Lib9c/Action/Guild/Migration/GuildMigrationFailedException.cs @@ -2,6 +2,9 @@ namespace Nekoyume.Action.Guild.Migration { + /// + /// An exception to be thrown when guild migration failed. + /// public class GuildMigrationFailedException : InvalidOperationException { public GuildMigrationFailedException(string message) : base(message) diff --git a/Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuild.cs b/Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuild.cs index b494b3d33e..da7e83b982 100644 --- a/Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuild.cs +++ b/Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuild.cs @@ -6,6 +6,9 @@ namespace Nekoyume.Action.Guild.Migration.LegacyModels { // TODO: [GuildMigration] Remove this class when the migration is done. + /// + /// The legacy model for Guild. + /// public class LegacyGuild : IEquatable, IBencodable { private const string StateTypeName = "guild"; diff --git a/Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuildParticipant.cs b/Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuildParticipant.cs index e69b6297aa..f3724c86a6 100644 --- a/Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuildParticipant.cs +++ b/Lib9c/Action/Guild/Migration/LegacyModels/LegacyGuildParticipant.cs @@ -6,6 +6,9 @@ namespace Nekoyume.Action.Guild.Migration.LegacyModels { // TODO: [GuildMigration] Remove this class when the migration is done. + /// + /// The legacy model for GuildParticipant. + /// public class LegacyGuildParticipant : IBencodable, IEquatable { private const string StateTypeName = "guild_participant"; diff --git a/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs b/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs index 493282e363..ce4d807185 100644 --- a/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs +++ b/Lib9c/Action/Guild/Migration/LegacyModels/MigrationModule.cs @@ -5,6 +5,10 @@ namespace Nekoyume.Action.Guild.Migration.LegacyModels { + // TODO: [GuildMigration] Remove this class when the migration is done. + /// + /// The module for delegation height for migration. + /// public static class MigrationModule { /// diff --git a/Lib9c/Action/Guild/Migration/MigrateDelegation.cs b/Lib9c/Action/Guild/Migration/MigrateDelegation.cs index 14e77621ac..1f383fd908 100644 --- a/Lib9c/Action/Guild/Migration/MigrateDelegation.cs +++ b/Lib9c/Action/Guild/Migration/MigrateDelegation.cs @@ -17,6 +17,8 @@ namespace Nekoyume.Action.Guild.Migration // TODO: [GuildMigration] Remove this class when the migration is done. /// /// An action to migrate guild delegation. + /// After migration is done, guild participant now have delegation with + /// validator. /// [ActionType(TypeIdentifier)] public class MigrateDelegation : ActionBase diff --git a/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs b/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs index dfd5aaa531..2574b9e2a4 100644 --- a/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs +++ b/Lib9c/Action/Guild/Migration/MigrateDelegationHeight.cs @@ -10,6 +10,7 @@ namespace Nekoyume.Action.Guild.Migration // TODO: [GuildMigration] Remove this class when the migration is done. /// /// An action to migrate the delegation height. + /// This action triggers the migration of the delegation height. /// [ActionType(TypeIdentifier)] public class MigrateDelegationHeight : ActionBase diff --git a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs index 216a39ded5..57e1b72de0 100644 --- a/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs +++ b/Lib9c/Action/Guild/Migration/MigratePlanetariumGuild.cs @@ -12,6 +12,7 @@ namespace Nekoyume.Action.Guild.Migration // TODO: [GuildMigration] Remove this class when the migration is done. /// /// An action to migrate the planetarium guild. + /// After migration, the planetarium guild now has a validator to delegate. /// [ActionType(TypeIdentifier)] public class MigratePlanetariumGuild : ActionBase diff --git a/Lib9c/Action/Guild/Migration/MigratePlanetariumValidator.cs b/Lib9c/Action/Guild/Migration/MigratePlanetariumValidator.cs index 0a91936bf2..98ef4ee0f4 100644 --- a/Lib9c/Action/Guild/Migration/MigratePlanetariumValidator.cs +++ b/Lib9c/Action/Guild/Migration/MigratePlanetariumValidator.cs @@ -10,7 +10,9 @@ namespace Nekoyume.Action.Guild.Migration { // TODO: [GuildMigration] Remove this class when the migration is done. /// - /// An action to migrate the planetarium guild. + /// An action to migrate the planetarium validator. + /// With this migration, planetarium validator is now active, + /// and bonded FAVs are moved to active address. /// [ActionType(TypeIdentifier)] public class MigratePlanetariumValidator : ActionBase From f900e40534ad514a912245e4c03e689028302586 Mon Sep 17 00:00:00 2001 From: tyrosine1153 Date: Tue, 26 Nov 2024 17:25:15 +0900 Subject: [PATCH 203/206] fix duplicate guid NonFungibleItem in Claimitems --- Lib9c/Action/ClaimItems.cs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/Lib9c/Action/ClaimItems.cs b/Lib9c/Action/ClaimItems.cs index 4a9cf52196..990e2b2e90 100644 --- a/Lib9c/Action/ClaimItems.cs +++ b/Lib9c/Action/ClaimItems.cs @@ -127,31 +127,30 @@ public override IWorld Execute(IActionContext context) { (bool tradable, int itemId) = Currencies.ParseItemCurrency(tokenCurrency); states = states.BurnAsset(context, context.Signer, fungibleAssetValue); - var item = itemSheet[itemId] switch - { - MaterialItemSheet.Row materialRow => tradable - ? ItemFactory.CreateTradableMaterial(materialRow) - : ItemFactory.CreateMaterial(materialRow), - var itemRow => ItemFactory.CreateItem(itemRow, random) - }; // FIXME: This is an implementation bug in the Inventory class, // but we'll deal with it temporarily here. // If Pluggable AEV ever becomes a reality, // it's only right that this is fixed in Inventory. + var itemRow = itemSheet[itemId]; var itemCount = (int)fungibleAssetValue.RawValue; - if (item is INonFungibleItem) + if (itemRow is MaterialItemSheet.Row materialRow) { - foreach (var _ in Enumerable.Range(0, itemCount)) - { - avatarState.inventory.AddItem(item, 1); - } + var item = tradable + ? ItemFactory.CreateTradableMaterial(materialRow) + : ItemFactory.CreateMaterial(materialRow); + avatarState.inventory.AddItem(item, itemCount); } else { - avatarState.inventory.AddItem(item, itemCount); + foreach (var _ in Enumerable.Range(0, itemCount)) + { + var item = ItemFactory.CreateItem(itemRow, random); + avatarState.inventory.AddItem(item); + } } - items.Add((item.Id, itemCount)); + + items.Add((itemRow.Id, itemCount)); } } From b775730826c33005400f49d2c19f224ae085b990 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 27 Nov 2024 15:20:22 +0900 Subject: [PATCH 204/206] prepare: 1.19.4 --- Lib9c.Abstractions/Lib9c.Abstractions.csproj | 2 +- Lib9c.MessagePack/Lib9c.MessagePack.csproj | 2 +- Lib9c.Renderers/Lib9c.Renderers.csproj | 2 +- Lib9c/Lib9c.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib9c.Abstractions/Lib9c.Abstractions.csproj b/Lib9c.Abstractions/Lib9c.Abstractions.csproj index 672b543412..1dbb1400b0 100644 --- a/Lib9c.Abstractions/Lib9c.Abstractions.csproj +++ b/Lib9c.Abstractions/Lib9c.Abstractions.csproj @@ -6,7 +6,7 @@ true .bin .obj - 1.19.0 + 1.19.4 diff --git a/Lib9c.MessagePack/Lib9c.MessagePack.csproj b/Lib9c.MessagePack/Lib9c.MessagePack.csproj index 6b5a8ee0e3..a7a0ae4068 100644 --- a/Lib9c.MessagePack/Lib9c.MessagePack.csproj +++ b/Lib9c.MessagePack/Lib9c.MessagePack.csproj @@ -8,7 +8,7 @@ AnyCPU .bin .obj - 1.19.0 + 1.19.4 diff --git a/Lib9c.Renderers/Lib9c.Renderers.csproj b/Lib9c.Renderers/Lib9c.Renderers.csproj index 4333aa1094..46e41affde 100644 --- a/Lib9c.Renderers/Lib9c.Renderers.csproj +++ b/Lib9c.Renderers/Lib9c.Renderers.csproj @@ -6,7 +6,7 @@ enable .bin .obj - 1.19.0 + 1.19.4 Lib9c diff --git a/Lib9c/Lib9c.csproj b/Lib9c/Lib9c.csproj index 9c745a7114..a3a93f5922 100644 --- a/Lib9c/Lib9c.csproj +++ b/Lib9c/Lib9c.csproj @@ -9,7 +9,7 @@ .obj Nekoyume 9 - 1.19.0 + 1.19.4 true Debug;Release AnyCPU From 0544f6f7793aac67e71ca55ed471e3a815195eda Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 25 Nov 2024 15:51:30 +0900 Subject: [PATCH 205/206] fix: Fix MigrateDelegation action --- .../Guild/Migration/MigrateDelegation.cs | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/Lib9c/Action/Guild/Migration/MigrateDelegation.cs b/Lib9c/Action/Guild/Migration/MigrateDelegation.cs index 65dbec9dde..14e77621ac 100644 --- a/Lib9c/Action/Guild/Migration/MigrateDelegation.cs +++ b/Lib9c/Action/Guild/Migration/MigrateDelegation.cs @@ -119,23 +119,28 @@ public override IWorld Execute(IActionContext context) return repository.World; } - catch (FailedLoadStateException) + catch (Exception e) { - var pledgeAddress = ((Address)Target).GetPledgeAddress(); - - // Patron contract structure: - // [0] = PatronAddress - // [1] = IsApproved - // [2] = Mead amount to refill. - if (!world.TryGetLegacyState(pledgeAddress, out List list) || list.Count < 3 || - list[0] is not Binary || list[0].ToAddress() != MeadConfig.PatronAddress || - list[1] is not Bencodex.Types.Boolean approved || !approved) + if (e is FailedLoadStateException || e is NullReferenceException) { - throw new GuildMigrationFailedException("Unexpected pledge structure."); + var pledgeAddress = ((Address)Target).GetPledgeAddress(); + + // Patron contract structure: + // [0] = PatronAddress + // [1] = IsApproved + // [2] = Mead amount to refill. + if (!world.TryGetLegacyState(pledgeAddress, out List list) || list.Count < 3 || + list[0] is not Binary || list[0].ToAddress() != MeadConfig.PatronAddress || + list[1] is not Bencodex.Types.Boolean approved || !approved) + { + throw new GuildMigrationFailedException("Unexpected pledge structure."); + } + + repository.JoinGuild(planetariumGuildAddress, Target); + return repository.World; } - repository.JoinGuild(planetariumGuildAddress, Target); - return repository.World; + throw; } } } From 491ec2a6596a0e10ddcc66ea44d76b98b081ac9f Mon Sep 17 00:00:00 2001 From: s2quake Date: Wed, 27 Nov 2024 16:23:46 +0900 Subject: [PATCH 206/206] fix: Add block actions to PluginActionEvaluator --- .Lib9c.Plugin/PluginActionEvaluator.cs | 22 ++++++++++++++++++---- Lib9c.Policy/Policy/BlockPolicySource.cs | 2 ++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.Lib9c.Plugin/PluginActionEvaluator.cs b/.Lib9c.Plugin/PluginActionEvaluator.cs index 961d1cab36..3f92335f0b 100644 --- a/.Lib9c.Plugin/PluginActionEvaluator.cs +++ b/.Lib9c.Plugin/PluginActionEvaluator.cs @@ -7,6 +7,7 @@ using Libplanet.Store; using Nekoyume.Action; using Nekoyume.Action.Loader; +using Nekoyume.Action.ValidatorDelegation; namespace Lib9c.Plugin { @@ -19,10 +20,23 @@ public PluginActionEvaluator(IPluginKeyValueStore keyValueStore) var stateStore = new TrieStateStore(new WrappedKeyValueStore(keyValueStore)); _actionEvaluator = new ActionEvaluator( new PolicyActionsRegistry( - beginBlockActions: ImmutableArray.Empty, - endBlockActions: new IAction[] { new RewardGold() }.ToImmutableArray(), - beginTxActions: ImmutableArray.Empty, - endTxActions: ImmutableArray.Empty), + beginBlockActions: new IAction[] { + new SlashValidator(), + new AllocateGuildReward(), + new AllocateReward(), + }.ToImmutableArray(), + endBlockActions: new IAction[] { + new UpdateValidators(), + new RecordProposer(), + new RewardGold(), + new ReleaseValidatorUnbondings(), + }.ToImmutableArray(), + beginTxActions: new IAction[] { + new Mortgage(), + }.ToImmutableArray(), + endTxActions: new IAction[] { + new Reward(), new Refund(), + }.ToImmutableArray()), stateStore, new NCActionLoader()); } diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index 17cb854389..5fead8e122 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -143,6 +143,8 @@ internal IBlockPolicy GetPolicy( maxTransactionsPerSignerPerBlockPolicy); // FIXME: Slight inconsistency due to pre-existing delegate. + // WARNING: If the block actions in the policyActionsRegistry have been modified, + // the constructor of the PluginActionEvaluator must be modified as well. return new BlockPolicy( policyActionsRegistry: new PolicyActionsRegistry( beginBlockActions: new IAction[] {