Skip to content

Commit

Permalink
Merge pull request #3023 from planetarium/release/1.19.3
Browse files Browse the repository at this point in the history
Fix Refund admin action
  • Loading branch information
OnedgeLee authored Nov 20, 2024
2 parents 7b3f640 + 3b06f84 commit 51c8b73
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 121 deletions.
114 changes: 30 additions & 84 deletions .Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs
Original file line number Diff line number Diff line change
@@ -1,135 +1,81 @@
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<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);
.MintAsset(new ActionContext { }, Addresses.NonValidatorDelegatee, Currencies.GuildGold * 500);
}

[Fact]
public void 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(targets, recon.Targets);
}

[Fact]
public void Execute()
{
var world = new FixToRefundFromNonValidator(new Address[] { _agentAddress }).Execute(new ActionContext
var targets = Enumerable.Range(0, 5).Select(i => (new PrivateKey().Address, (i + 1) * 10)).ToList();

var world = new FixToRefundFromNonValidator(targets).Execute(new ActionContext
{
PreviousState = _world,
Signer = _adminAddress,
BlockIndex = 2L,
});

Assert.Equal(_gg * 10, world.GetBalance(_stakeAddress, _gg));
Assert.Equal(_gg * (10000 - 5), world.GetBalance(Addresses.NonValidatorDelegatee, _gg));
foreach (var item in targets)
{
Assert.Equal(
Currencies.GuildGold * item.Item2,
world.GetBalance(StakeState.DeriveAddress(item.Item1), Currencies.GuildGold));
}

Assert.Equal(
Currencies.GuildGold * (500 - targets.Select(t => t.Item2).Sum()),
world.GetBalance(Addresses.NonValidatorDelegatee, Currencies.GuildGold));
}

[Fact]
public void AssertWhenExecutedByNonAdmin()
{
var targets = Enumerable.Range(0, 5).Select(i => (new PrivateKey().Address, (i + 1) * 10)).ToList();

Assert.Throws<PermissionDeniedException>(() =>
{
new FixToRefundFromNonValidator(new Address[] { _agentAddress }).Execute(new ActionContext
new FixToRefundFromNonValidator(targets).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,
});
});
}
}
}
60 changes: 23 additions & 37 deletions Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -24,21 +22,25 @@ public class FixToRefundFromNonValidator : ActionBase

private const string TargetsKey = "t";

public List<Address> Targets { get; private set; }
public List<(Address Address, int Amount)> Targets { get; private set; }

public FixToRefundFromNonValidator()
{
}

public FixToRefundFromNonValidator(IEnumerable<Address> targets)
public FixToRefundFromNonValidator(
IEnumerable<(Address, int)> 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))));
.Add(
TargetsKey,
new List(Targets.Select(t =>
new List(t.Item1.Bencoded, (Integer)t.Item2)))));

public override void LoadPlainValue(IValue plainValue)
{
Expand All @@ -51,7 +53,10 @@ rawValues is not Dictionary values ||
throw new InvalidCastException();
}

Targets = targets.Select(t => new Address(t)).ToList();
Targets = targets.Select(
t => (
new Address(((List)t)[0]),
(int)(Integer)((List)t)[1])).ToList();
}

public override IWorld Execute(IActionContext context)
Expand All @@ -70,43 +75,24 @@ public override IWorld Execute(IActionContext context)
throw new PermissionDeniedException(adminState, context.Signer);
}

foreach (var target in Targets)
foreach (var ta in Targets)
{
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);
}
}
}

0 comments on commit 51c8b73

Please sign in to comment.