-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3018 from planetarium/release/1.19.2
Release 1.19.2
- Loading branch information
Showing
2 changed files
with
247 additions
and
0 deletions.
There are no files selected for viewing
135 changes: 135 additions & 0 deletions
135
.Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<string, string> | ||
{ | ||
{ | ||
"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<StakePolicySheet>(); | ||
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(new Address[] { _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<PermissionDeniedException>(() => | ||
{ | ||
new FixToRefundFromNonValidator(new Address[] { _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<InvalidOperationException>(() => | ||
{ | ||
new FixToRefundFromNonValidator(new Address[] { _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<InvalidOperationException>(() => | ||
{ | ||
new FixToRefundFromNonValidator(new Address[] { _agentAddress }).Execute(new ActionContext | ||
{ | ||
PreviousState = World, | ||
Signer = _adminAddress, | ||
BlockIndex = 2L, | ||
}); | ||
}); | ||
} | ||
} | ||
} |
112 changes: 112 additions & 0 deletions
112
Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
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; | ||
using System.Linq; | ||
|
||
namespace Nekoyume.Action.Guild.Migration | ||
{ | ||
// TODO: [GuildMigration] Remove this class when the migration is done. | ||
/// <summary> | ||
/// An action to fix refund from non-validator. | ||
/// </summary> | ||
[ActionType(TypeIdentifier)] | ||
public class FixToRefundFromNonValidator : ActionBase | ||
{ | ||
public const string TypeIdentifier = "fix_to_refund_from_non_validator"; | ||
|
||
private const string TargetsKey = "t"; | ||
|
||
public List<Address> Targets { get; private set; } | ||
|
||
public FixToRefundFromNonValidator() | ||
{ | ||
} | ||
|
||
public FixToRefundFromNonValidator(IEnumerable<Address> targets) | ||
{ | ||
Targets = targets.ToList(); | ||
} | ||
|
||
public override IValue PlainValue => Dictionary.Empty | ||
.Add("type_id", TypeIdentifier) | ||
.Add("values", Dictionary.Empty | ||
.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)TargetsKey, out var rawTarget) || | ||
rawTarget is not List targets) | ||
{ | ||
throw new InvalidCastException(); | ||
} | ||
|
||
Targets = targets.Select(t => new Address(t)).ToList(); | ||
} | ||
|
||
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); | ||
} | ||
|
||
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) | ||
|| 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)); | ||
} | ||
} | ||
} |