Skip to content

Commit

Permalink
Merge pull request #3018 from planetarium/release/1.19.2
Browse files Browse the repository at this point in the history
Release 1.19.2
  • Loading branch information
OnedgeLee authored Nov 18, 2024
2 parents 9398667 + 61cf9f9 commit 7b3f640
Show file tree
Hide file tree
Showing 2 changed files with 247 additions and 0 deletions.
135 changes: 135 additions & 0 deletions .Lib9c.Tests/Action/Guild/Migration/FixToRefundFromNonValidatorTest.cs
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 Lib9c/Action/Guild/Migration/FixToRefundFromNonValidator.cs
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));
}
}
}

0 comments on commit 7b3f640

Please sign in to comment.