Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PDX-19: Add UnloadFromGarages Action #2197

Merged
merged 7 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions .Lib9c.Tests/Action/Garages/UnloadFromGaragesTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#nullable enable

namespace Lib9c.Tests.Action.Garages
{
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using Libplanet.Common;
using Libplanet.Crypto;
using Libplanet.Types.Assets;
using Nekoyume;
using Nekoyume.Action.Garages;
using Xunit;

public class UnloadFromGaragesTest
{
private const int AvatarIndex = 0;

private static readonly Address AgentAddress = new PrivateKey().ToAddress();

public static IEnumerable<object[]> Get_Sample_PlainValue()
{
var avatarAddress = Addresses.GetAvatarAddress(AgentAddress, AvatarIndex);
var fungibleAssetValues = GetFungibleAssetValues(AgentAddress, avatarAddress);
var hex = string.Join(
string.Empty,
Enumerable.Range(0, 64).Select(i => (i % 10).ToString()));
IEnumerable<(HashDigest<SHA256> fungibleId, int count)> fungibleIdAndCounts = new[]
{
(HashDigest<SHA256>.FromString(hex), 1),
(HashDigest<SHA256>.FromString(hex), int.MaxValue),
};

yield return new object[]
{
(avatarAddress, fungibleAssetValues, fungibleIdAndCounts, memo: "memo"),
};
}

[Theory]
[MemberData(nameof(Get_Sample_PlainValue))]
public void Serialize(
(
Address recipientAvatarAddress,
IEnumerable<(Address balanceAddress, FungibleAssetValue value)>?
fungibleAssetValues,
IEnumerable<(HashDigest<SHA256> fungibleId, int count)>? fungibleIdAndCounts,
string? memo) unloadData)
{
var actions = new[]
{
new UnloadFromGarages(),
new UnloadFromGarages(new[] { unloadData }),
};

foreach (var action in actions)
{
var serialized = action.PlainValue;
var deserialized = new UnloadFromGarages();
deserialized.LoadPlainValue(serialized);

Assert.Equal(action.UnloadData.Count, deserialized.UnloadData.Count);
Assert.Equal(serialized, deserialized.PlainValue);

for (var i = 0; i < action.UnloadData.Count; i++)
{
var deserializedData = deserialized.UnloadData[i];
var actionData = action.UnloadData[i];

Assert.Equal(
actionData.recipientAvatarAddress,
deserializedData.recipientAvatarAddress);
Assert.True(
actionData.fungibleAssetValues?.SequenceEqual(deserializedData
.fungibleAssetValues!)
?? deserializedData.fungibleAssetValues is null);
Assert.True(
actionData.fungibleIdAndCounts?.SequenceEqual(deserializedData
.fungibleIdAndCounts!)
?? deserializedData.fungibleIdAndCounts is null);
Assert.Equal(actionData.memo, deserializedData.memo);
}
}
}

private static IEnumerable<(Address balanceAddr, FungibleAssetValue value)>
GetFungibleAssetValues(
Address agentAddr,
Address avatarAddr)
{
return CurrenciesTest.GetSampleCurrencies()
.Select(objects => (FungibleAssetValue)objects[0])
.Where(fav => fav.Sign > 0)
.Select(fav =>
{
if (Currencies.IsRuneTicker(fav.Currency.Ticker) ||
Currencies.IsSoulstoneTicker(fav.Currency.Ticker))
{
return (avatarAddr, fav);
}

return (agentAddr, fav);
})
.ToArray();
}
}
}
19 changes: 19 additions & 0 deletions Lib9c.Abstractions/IUnloadFromGaragesV1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using Libplanet.Common;
using Libplanet.Crypto;
using Libplanet.Types.Assets;

namespace Lib9c.Abstractions
{
public interface IUnloadFromGaragesV1
{
public IReadOnlyList<(
Address recipientAvatarAddress,
IOrderedEnumerable<(Address balanceAddress, FungibleAssetValue value)>?
fungibleAssetValues,
IOrderedEnumerable<(HashDigest<SHA256> fungibleId, int count)>? fungibleIdAndCounts,
string? memo)> UnloadData { get; }
}
}
143 changes: 143 additions & 0 deletions Lib9c/Action/Garages/UnloadFromGarages.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Security.Cryptography;
using Bencodex.Types;
using Lib9c.Abstractions;
using Libplanet.Action;
using Libplanet.Action.State;
using Libplanet.Common;
using Libplanet.Crypto;
using Libplanet.Types.Assets;
using Nekoyume.Model.State;

namespace Nekoyume.Action.Garages
{
[ActionType("unload_from_garages")]
public class UnloadFromGarages : GameAction, IUnloadFromGaragesV1, IAction
{
public IReadOnlyList<(
Address recipientAvatarAddress,
IOrderedEnumerable<(Address balanceAddress, FungibleAssetValue value)>?
fungibleAssetValues,
IOrderedEnumerable<(HashDigest<SHA256> fungibleId, int count)>? fungibleIdAndCounts,
string? memo)>
UnloadData { get; private set; }

public UnloadFromGarages()
{
UnloadData = new List<(
Address recipientAvatarAddress,
IOrderedEnumerable<(Address balanceAddress, FungibleAssetValue value)>?
fungibleAssetValues,
IOrderedEnumerable<(HashDigest<SHA256> fungibleId, int count)>?
fungibleIdAndCounts, string? memo)>();
}

public UnloadFromGarages(
IReadOnlyList<(
Address recipientAvatarAddress,
IEnumerable<(Address balanceAddress, FungibleAssetValue value)>?
fungibleAssetValues,
IEnumerable<(HashDigest<SHA256> fungibleId, int count)>? fungibleIdAndCounts,
string? memo)> unloadData)
Comment on lines +41 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is too sad to duplicate codes for type because of csharplang's limitation. 😢

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type Foo = (...)

😢

{
UnloadData = unloadData.Select(data => (
data.recipientAvatarAddress,
GarageUtils.MergeAndSort(data.fungibleAssetValues),
GarageUtils.MergeAndSort(data.fungibleIdAndCounts),
data.memo)).ToImmutableList();
}

protected override IImmutableDictionary<string, IValue> PlainValueInternal =>
new Dictionary<string, IValue>
{
{
"l",
new List(UnloadData.Select(data =>
new List(data.recipientAvatarAddress.Serialize(),
SerializeFungibleAssetValues(data.fungibleAssetValues),
SerializeFungibleIdAndCounts(data.fungibleIdAndCounts),
string.IsNullOrEmpty(data.memo) ? Null.Value : (Text)data.memo)))
}
}.ToImmutableDictionary();

protected override void LoadPlainValueInternal(
IImmutableDictionary<string, IValue> plainValue)
{
var serialized = plainValue["l"];
if (serialized is null or Null)
{
throw new ArgumentNullException(nameof(serialized));
}

if (serialized is not List list)
{
throw new ArgumentException(
$"The type of {nameof(serialized)} must be bencodex list.");
}

UnloadData = list.Select(rawValue =>
{
var value = rawValue as List
?? throw new ArgumentException(
$"The type of {nameof(rawValue)} must be bencodex list.");
var recipientAvatarAddress = value[0].ToAddress();
var fungibleAssetValues =
GarageUtils.MergeAndSort(
(value[1] as List)?.Select(raw =>
{
var fungibleAssetValue = (List)raw;
return (fungibleAssetValue[0].ToAddress(),
fungibleAssetValue[1].ToFungibleAssetValue());
}));
Comment on lines +88 to +95
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be treated as null instead of throwing an error, even if invalid items like Dictionary became.

var fungibleIdAndCounts =
GarageUtils.MergeAndSort(
(value[2] as List)?.Select(raw =>
{
var fungibleIdAndCount = (List)raw;
return (
fungibleIdAndCount[0].ToItemId(),
(int)((Integer)fungibleIdAndCount[1]).Value);
}));
var memo = value[3].Kind == ValueKind.Null
? null
: (string)(Text)value[3];

return (recipientAvatarAddress, fungibleAssetValues, fungibleIdAndCounts, memo);
}).ToList();
}

public override IAccount Execute(IActionContext context)
{
context.UseGas(1);

throw new System.NotImplementedException();
}

private static IValue SerializeFungibleAssetValues(
IOrderedEnumerable<(Address balanceAddress, FungibleAssetValue value)>?
fungibleAssetValues)
{
if (fungibleAssetValues is null) return Null.Value;

return new List(
fungibleAssetValues.Select(tuple => new List(
tuple.balanceAddress.Serialize(),
tuple.value.Serialize())));
}

private static IValue SerializeFungibleIdAndCounts(
IOrderedEnumerable<(HashDigest<SHA256> fungibleId, int count)>? fungibleIdAndCounts)
{
if (fungibleIdAndCounts is null) return Null.Value;

return new List(
fungibleIdAndCounts.Select(tuple => new List(
tuple.fungibleId.Serialize(),
(Integer)tuple.count)));
}
}
}
Loading