diff --git a/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs index b281a5661d..0ab4a1c619 100644 --- a/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/StakeAndClaimScenarioTest.cs @@ -107,7 +107,7 @@ public void Test() "StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", LegacyStakeState.RewardInterval, - LegacyStakeState.LockupInterval); + 0); var validatorKey = new PrivateKey().PublicKey; state = DelegationUtil.EnsureValidatorPromotionReady(state, validatorKey, 0L); @@ -139,7 +139,7 @@ public void Test() "StakeRegularFixedRewardSheet_V2", "StakeRegularRewardSheet_V2", 50_400, - 201_600); + 75_600); // Patch StakePolicySheet and so on. state = state.SetLegacyState( @@ -163,7 +163,7 @@ public void Test() "StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", 40, - 150); + 75_600); } private static IWorld MintAsset( @@ -253,7 +253,7 @@ private static void ValidateStakedStateV2( string expectStakeRegularFixedRewardSheetName, string expectStakeRegularRewardSheetName, long expectRewardInterval, - long expectLockupInterval) + long expectUnstakingInterval) { var stakeAddr = LegacyStakeState.DeriveAddress(agentAddr); var actualStakedAmount = state.GetBalance(stakeAddr, expectStakedAmount.Currency); @@ -267,7 +267,7 @@ private static void ValidateStakedStateV2( expectStakeRegularRewardSheetName, stakeState.Contract.StakeRegularRewardSheetTableName); Assert.Equal(expectRewardInterval, stakeState.Contract.RewardInterval); - Assert.Equal(expectLockupInterval, stakeState.Contract.LockupInterval); + Assert.Equal(expectUnstakingInterval, stakeState.Contract.UnstakingInterval); } } } diff --git a/.Lib9c.Tests/Action/Stake2Test.cs b/.Lib9c.Tests/Action/Stake2Test.cs index 41e6b6c6c4..76b0be34b4 100644 --- a/.Lib9c.Tests/Action/Stake2Test.cs +++ b/.Lib9c.Tests/Action/Stake2Test.cs @@ -274,7 +274,7 @@ public void RestrictForStakeStateV2() "StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", 50400, - 201600), + 75600), 0).Serialize()), Signer = _signerAddress, BlockIndex = 0, diff --git a/.Lib9c.Tests/Action/StakeTest.cs b/.Lib9c.Tests/Action/StakeTest.cs index f116515859..4c6f8f45ec 100644 --- a/.Lib9c.Tests/Action/StakeTest.cs +++ b/.Lib9c.Tests/Action/StakeTest.cs @@ -586,8 +586,8 @@ private IWorld Execute( _stakePolicySheet.RewardIntervalValue, stakeStateV2.Contract.RewardInterval); Assert.Equal( - _stakePolicySheet.LockupIntervalValue, - stakeStateV2.Contract.LockupInterval); + _stakePolicySheet.UnstakingIntervalValue, + stakeStateV2.Contract.UnstakingInterval); Assert.Equal(blockIndex, stakeStateV2.StartedBlockIndex); Assert.Equal(0, stakeStateV2.ReceivedBlockIndex); Assert.Equal(blockIndex, stakeStateV2.ClaimedBlockIndex); diff --git a/.Lib9c.Tests/Action/TransferAssetTest.cs b/.Lib9c.Tests/Action/TransferAssetTest.cs index b46576e983..c292e3d03f 100644 --- a/.Lib9c.Tests/Action/TransferAssetTest.cs +++ b/.Lib9c.Tests/Action/TransferAssetTest.cs @@ -328,7 +328,7 @@ public void Execute_Throw_ArgumentException() "StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", 50400, - 201600), + 75600), 0).Serialize()), Signer = _sender, BlockIndex = 1, diff --git a/.Lib9c.Tests/Action/TransferAssetsTest.cs b/.Lib9c.Tests/Action/TransferAssetsTest.cs index 0de06fda84..1847c9170d 100644 --- a/.Lib9c.Tests/Action/TransferAssetsTest.cs +++ b/.Lib9c.Tests/Action/TransferAssetsTest.cs @@ -414,7 +414,7 @@ public void Execute_Throw_ArgumentException() "StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", 50400, - 201600), + 75600), 0).Serialize()), Signer = _sender, BlockIndex = 1, diff --git a/.Lib9c.Tests/Fixtures/TableCSV/Stake/StakePolicySheetFixtures.cs b/.Lib9c.Tests/Fixtures/TableCSV/Stake/StakePolicySheetFixtures.cs index f12447fbd1..b5217ec80f 100644 --- a/.Lib9c.Tests/Fixtures/TableCSV/Stake/StakePolicySheetFixtures.cs +++ b/.Lib9c.Tests/Fixtures/TableCSV/Stake/StakePolicySheetFixtures.cs @@ -6,24 +6,28 @@ public static class StakePolicySheetFixtures StakeRegularFixedRewardSheet,StakeRegularFixedRewardSheet_V1 StakeRegularRewardSheet,StakeRegularRewardSheet_V1 RewardInterval,50400 -LockupInterval,201600"; +LockupInterval,201600 +UnstakingInterval,75600"; public const string V2 = @"attr_name,value StakeRegularFixedRewardSheet,StakeRegularFixedRewardSheet_V2 StakeRegularRewardSheet,StakeRegularRewardSheet_V2 RewardInterval,50400 -LockupInterval,201600"; +LockupInterval,201600 +UnstakingInterval,75600"; public const string V3 = @"attr_name,value StakeRegularFixedRewardSheet,StakeRegularFixedRewardSheet_V1 StakeRegularRewardSheet,StakeRegularRewardSheet_V1 RewardInterval,40 -LockupInterval,150"; +LockupInterval,150 +UnstakingInterval,75600"; public const string V6 = @"attr_name,value StakeRegularFixedRewardSheet,StakeRegularFixedRewardSheet_V3 StakeRegularRewardSheet,StakeRegularRewardSheet_V6 RewardInterval,75600 -LockupInterval,302400"; +LockupInterval,302400 +UnstakingInterval,75600"; } } diff --git a/.Lib9c.Tests/Model/Stake/ContractTest.cs b/.Lib9c.Tests/Model/Stake/ContractTest.cs index 8e32e684b4..28d57cfe67 100644 --- a/.Lib9c.Tests/Model/Stake/ContractTest.cs +++ b/.Lib9c.Tests/Model/Stake/ContractTest.cs @@ -23,13 +23,13 @@ public void Constructor( string stakeRegularFixedRewardSheetTableName, string stakeRegularRewardSheetTableName, long rewardInterval, - long lockupInterval) + long unstakingInterval) { var contract = new Contract( stakeRegularFixedRewardSheetTableName, stakeRegularRewardSheetTableName, rewardInterval, - lockupInterval); + unstakingInterval); Assert.Equal( stakeRegularFixedRewardSheetTableName, contract.StakeRegularFixedRewardSheetTableName); @@ -37,7 +37,7 @@ public void Constructor( stakeRegularRewardSheetTableName, contract.StakeRegularRewardSheetTableName); Assert.Equal(rewardInterval, contract.RewardInterval); - Assert.Equal(lockupInterval, contract.LockupInterval); + Assert.Equal(unstakingInterval, contract.UnstakingInterval); } [Theory] @@ -75,14 +75,14 @@ public void Constructor_Throws_ArgumentException( string stakeRegularFixedRewardSheetTableName, string stakeRegularRewardSheetTableName, long rewardInterval, - long lockupInterval) + long unstakingInterval) { Assert.Throws( () => new Contract( stakeRegularFixedRewardSheetTableName, stakeRegularRewardSheetTableName, rewardInterval, - lockupInterval)); + unstakingInterval)); } [Theory] @@ -110,14 +110,14 @@ public void Constructor_Throws_ArgumentOutOfRangeException( string stakeRegularFixedRewardSheetTableName, string stakeRegularRewardSheetTableName, long rewardInterval, - long lockupInterval) + long unstakingInterval) { Assert.Throws( () => new Contract( stakeRegularFixedRewardSheetTableName, stakeRegularRewardSheetTableName, rewardInterval, - lockupInterval)); + unstakingInterval)); } [Fact] @@ -141,7 +141,7 @@ public void Constructor_With_StakePolicySheet(string stakePolicySheetCsv) sheet.StakeRegularRewardSheetValue, contract.StakeRegularRewardSheetTableName); Assert.Equal(sheet.RewardIntervalValue, contract.RewardInterval); - Assert.Equal(sheet.LockupIntervalValue, contract.LockupInterval); + Assert.Equal(sheet.UnstakingIntervalValue, contract.UnstakingInterval); } [Fact] @@ -161,7 +161,7 @@ public void Serde() contract.StakeRegularRewardSheetTableName, des.StakeRegularRewardSheetTableName); Assert.Equal(contract.RewardInterval, des.RewardInterval); - Assert.Equal(contract.LockupInterval, des.LockupInterval); + Assert.Equal(contract.UnstakingInterval, des.UnstakingInterval); var ser2 = des.Serialize(); Assert.Equal(ser, ser2); } diff --git a/.Lib9c.Tests/TableData/Stake/StakePolicySheetTest.cs b/.Lib9c.Tests/TableData/Stake/StakePolicySheetTest.cs index 410b274f3a..a7acd6d05c 100644 --- a/.Lib9c.Tests/TableData/Stake/StakePolicySheetTest.cs +++ b/.Lib9c.Tests/TableData/Stake/StakePolicySheetTest.cs @@ -14,7 +14,7 @@ public void Set_Success() { var sheet = new StakePolicySheet(); sheet.Set(StakePolicySheetFixtures.V1); - Assert.Equal(4, sheet.Count); + Assert.Equal(5, sheet.Count); var row = sheet["StakeRegularFixedRewardSheet"]; Assert.Equal("StakeRegularFixedRewardSheet", row.AttrName); Assert.Equal("StakeRegularFixedRewardSheet_V1", row.Value); @@ -27,6 +27,9 @@ public void Set_Success() row = sheet["LockupInterval"]; Assert.Equal("LockupInterval", row.AttrName); Assert.Equal("201600", row.Value); + row = sheet["UnstakingInterval"]; + Assert.Equal("UnstakingInterval", row.AttrName); + Assert.Equal("75600", row.Value); } [Theory] diff --git a/Lib9c/Action/Stake.cs b/Lib9c/Action/Stake.cs index 2e02ca9bf8..360ed42337 100644 --- a/Lib9c/Action/Stake.cs +++ b/Lib9c/Action/Stake.cs @@ -204,7 +204,7 @@ public override IWorld Execute(IActionContext context) // NOTE: When the staking state is locked up. // TODO: Remove this condition after the migration is done. - if (stakeStateV2.CancellableBlockIndex > context.BlockIndex) + if (stakeStateV2.UnstakableBlockIndex > context.BlockIndex) { // NOTE: Cannot re-contract with less balance. if (targetStakeBalance < stakedBalance) @@ -246,8 +246,8 @@ private static IWorld ContractNewStake( FungibleAssetValue targetStakeBalance, Contract latestStakeContract) { - var stakeStateValue = new StakeState(latestStakeContract, context.BlockIndex).Serialize(); var additionalBalance = targetStakeBalance - stakedBalance; + var stakeStateValue = new StakeState(latestStakeContract, context.BlockIndex, unstaked: additionalBalance.Sign < 0).Serialize(); var height = context.BlockIndex; var agentAddress = new AgentAddress(context.Signer); diff --git a/Lib9c/Model/Stake/Contract.cs b/Lib9c/Model/Stake/Contract.cs index 6a5464712b..1e5be9df43 100644 --- a/Lib9c/Model/Stake/Contract.cs +++ b/Lib9c/Model/Stake/Contract.cs @@ -7,7 +7,7 @@ namespace Nekoyume.Model.Stake public class Contract { public const string StateTypeName = "stake_contract"; - public const long StateTypeVersion = 1; + public const long StateTypeVersion = 2; public const string StakeRegularFixedRewardSheetPrefix = "StakeRegularFixedRewardSheet_"; @@ -18,8 +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 long UnstakingInterval { get; } public Contract(StakePolicySheet stakePolicySheet) : this( stakePolicySheet?.StakeRegularFixedRewardSheetValue ?? throw new ArgumentNullException( @@ -27,7 +26,7 @@ public Contract(StakePolicySheet stakePolicySheet) : this( $"{nameof(stakePolicySheet)} is null"), stakePolicySheet.StakeRegularRewardSheetValue, stakePolicySheet.RewardIntervalValue, - stakePolicySheet.LockupIntervalValue) + stakePolicySheet.UnstakingIntervalValue) { } @@ -35,7 +34,7 @@ public Contract( string stakeRegularFixedRewardSheetTableName, string stakeRegularRewardSheetTableName, long rewardInterval, - long lockupInterval) + long unstakingInterval) { if (string.IsNullOrEmpty(stakeRegularFixedRewardSheetTableName)) { @@ -67,17 +66,10 @@ public Contract( $"{nameof(rewardInterval)} must be greater than 0"); } - if (lockupInterval <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(lockupInterval), - $"{nameof(lockupInterval)} must be greater than 0"); - } - StakeRegularFixedRewardSheetTableName = stakeRegularFixedRewardSheetTableName; StakeRegularRewardSheetTableName = stakeRegularRewardSheetTableName; RewardInterval = rewardInterval; - LockupInterval = lockupInterval; + UnstakingInterval = unstakingInterval; } public Contract(IValue serialized) @@ -109,7 +101,7 @@ public Contract(IValue serialized) StakeRegularFixedRewardSheetTableName = (Text)list[reservedCount]; StakeRegularRewardSheetTableName = (Text)list[reservedCount + 1]; RewardInterval = (Integer)list[reservedCount + 2]; - LockupInterval = (Integer)list[reservedCount + 3]; + UnstakingInterval = (Integer)list[reservedCount + 3]; } public List Serialize() @@ -120,7 +112,7 @@ public List Serialize() (Text)StakeRegularFixedRewardSheetTableName, (Text)StakeRegularRewardSheetTableName, (Integer)RewardInterval, - (Integer)LockupInterval + (Integer)UnstakingInterval ); } @@ -130,7 +122,7 @@ protected bool Equals(Contract other) other.StakeRegularFixedRewardSheetTableName && StakeRegularRewardSheetTableName == other.StakeRegularRewardSheetTableName && RewardInterval == other.RewardInterval && - LockupInterval == other.LockupInterval; + UnstakingInterval == other.UnstakingInterval; } public override bool Equals(object obj) @@ -152,7 +144,7 @@ public override int GetHashCode() ? StakeRegularRewardSheetTableName.GetHashCode() : 0); hashCode = (hashCode * 397) ^ RewardInterval.GetHashCode(); - hashCode = (hashCode * 397) ^ LockupInterval.GetHashCode(); + hashCode = (hashCode * 397) ^ UnstakingInterval.GetHashCode(); return hashCode; } } diff --git a/Lib9c/Model/Stake/StakeState.cs b/Lib9c/Model/Stake/StakeState.cs index 6ac1e628c7..eb716ea19f 100644 --- a/Lib9c/Model/Stake/StakeState.cs +++ b/Lib9c/Model/Stake/StakeState.cs @@ -17,9 +17,11 @@ public static Address DeriveAddress(Address address) => public readonly Contract Contract; public readonly long StartedBlockIndex; public readonly long ReceivedBlockIndex; + public readonly bool Unstaked; - public long CancellableBlockIndex => - StartedBlockIndex + Contract.LockupInterval; + public long UnstakableBlockIndex => Unstaked + ? StartedBlockIndex + Contract.UnstakingInterval + : StartedBlockIndex; public long ClaimedBlockIndex => ReceivedBlockIndex == 0 ? StartedBlockIndex @@ -36,6 +38,7 @@ public StakeState( Contract contract, long startedBlockIndex, long receivedBlockIndex = 0, + bool unstaked = false, int stateVersion = LatestStateTypeVersion) { if (startedBlockIndex < 0) @@ -57,6 +60,7 @@ public StakeState( Contract = contract ?? throw new ArgumentNullException(nameof(contract)); StartedBlockIndex = startedBlockIndex; ReceivedBlockIndex = receivedBlockIndex; + Unstaked = unstaked; StateVersion = stateVersion; } @@ -68,6 +72,7 @@ Contract contract contract, legacyStakeState?.StartedBlockIndex ?? throw new ArgumentNullException(nameof(legacyStakeState)), legacyStakeState.ReceivedBlockIndex, + false, stateVersion: 2 ) { @@ -99,6 +104,15 @@ list[1] is not Integer stateTypeVersionValue || Contract = new Contract(list[reservedCount]); StartedBlockIndex = (Integer)list[reservedCount + 1]; ReceivedBlockIndex = (Integer)list[reservedCount + 2]; + + try + { + Unstaked = (Bencodex.Types.Boolean)list[reservedCount + 3]; + } + catch (IndexOutOfRangeException) + { + Unstaked = false; + } } public IValue Serialize() => new List( @@ -106,7 +120,8 @@ list[1] is not Integer stateTypeVersionValue || (Integer)StateVersion, Contract.Serialize(), (Integer)StartedBlockIndex, - (Integer)ReceivedBlockIndex + (Integer)ReceivedBlockIndex, + (Bencodex.Types.Boolean)Unstaked ); public bool Equals(StakeState other) @@ -114,6 +129,7 @@ public bool Equals(StakeState other) return Equals(Contract, other.Contract) && StartedBlockIndex == other.StartedBlockIndex && ReceivedBlockIndex == other.ReceivedBlockIndex && + Unstaked == other.Unstaked && StateVersion == other.StateVersion; } @@ -129,6 +145,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) ^ Unstaked.GetHashCode(); hashCode = (hashCode * 397) ^ StateVersion.GetHashCode(); return hashCode; } diff --git a/Lib9c/Model/Stake/StakeStateUtils.cs b/Lib9c/Model/Stake/StakeStateUtils.cs index 92a4b383c6..f953236421 100644 --- a/Lib9c/Model/Stake/StakeStateUtils.cs +++ b/Lib9c/Model/Stake/StakeStateUtils.cs @@ -129,7 +129,7 @@ public static bool TryMigrateV1ToV2( stakeRegularFixedRewardSheetTableName: stakeRegularFixedRewardSheetTableName, stakeRegularRewardSheetTableName: stakeRegularRewardSheetTableName, rewardInterval: LegacyStakeState.RewardInterval, - lockupInterval: LegacyStakeState.LockupInterval)); + unstakingInterval: 0)); } public static bool TryMigrateV2ToV3( diff --git a/Lib9c/TableCSV/Stake/StakePolicySheet.csv b/Lib9c/TableCSV/Stake/StakePolicySheet.csv index 8c773981fd..33dc008e2f 100644 --- a/Lib9c/TableCSV/Stake/StakePolicySheet.csv +++ b/Lib9c/TableCSV/Stake/StakePolicySheet.csv @@ -2,4 +2,5 @@ attr_name,value StakeRegularFixedRewardSheet,StakeRegularFixedRewardSheet_V3 StakeRegularRewardSheet,StakeRegularRewardSheet_V9 RewardInterval,75600 -LockupInterval,302400 \ No newline at end of file +LockupInterval,302400 +UnstakingInterval, 75600 diff --git a/Lib9c/TableData/Stake/StakePolicySheet.cs b/Lib9c/TableData/Stake/StakePolicySheet.cs index 26262f553e..16dc7c48a8 100644 --- a/Lib9c/TableData/Stake/StakePolicySheet.cs +++ b/Lib9c/TableData/Stake/StakePolicySheet.cs @@ -50,6 +50,7 @@ public override void Validate() "StakeRegularRewardSheet", "RewardInterval", "LockupInterval", + "UnstakingInterval" }; public static readonly (string attrName, string value)[] SheetPrefixRules = @@ -70,6 +71,9 @@ public static readonly (string attrName, string value)[] SheetPrefixRules = public long LockupIntervalValue => long.Parse(this["LockupInterval"].Value, CultureInfo.InvariantCulture); + public long UnstakingIntervalValue => + long.Parse(this["UnstakingInterval"].Value, CultureInfo.InvariantCulture); + public StakePolicySheet() : base(nameof(StakePolicySheet)) { }