Example #1
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            var sellData       = TestbedHelper.LoadData <TestbedSell>("TestbedSell");
            var addedItemInfos = sellData.Items
                                 .Select(item => new TestbedHelper.AddedItemInfo(
                                             context.Random.GenerateRandomGuid(),
                                             context.Random.GenerateRandomGuid()))
                                 .ToList();

            var agentAddress = _privateKey.PublicKey.ToAddress();
            var states       = context.PreviousStates;

            var avatarAddress = agentAddress.Derive(
                string.Format(
                    CultureInfo.InvariantCulture,
                    CreateAvatar.DeriveFormat,
                    _slotIndex
                    )
                );
            var inventoryAddress        = avatarAddress.Derive(LegacyInventoryKey);
            var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey);
            var questListAddress        = avatarAddress.Derive(LegacyQuestListKey);
            var orderReceiptAddress     = OrderDigestListState.DeriveAddress(avatarAddress);

            var weeklyArenaData        = TestbedHelper.LoadData <TestbedWeeklyArena>("TestbedWeeklyArena");
            var weeklyArenaAvatarNames = weeklyArenaData.Avatars
                                         .Distinct()
                                         .Select(avatar => avatar.Name)
                                         .ToArray();
            var weeklyArenaAgentAndAvatarAddresses = weeklyArenaAvatarNames
                                                     .Select(name => _privateKey.ToAddress().Derive(name))
                                                     .Select(e => (agentAddress: e, avatarAddress: e.Derive(
                                                                       string.Format(
                                                                           CultureInfo.InvariantCulture,
                                                                           CreateAvatar.DeriveFormat,
                                                                           0
                                                                           )
                                                                       )))
                                                     .ToArray();

            if (context.Rehearsal)
            {
                states = states.SetState(agentAddress, MarkChanged);
                for (var i = 0; i < AvatarState.CombinationSlotCapacity; i++)
                {
                    var slotAddress = avatarAddress.Derive(
                        string.Format(CultureInfo.InvariantCulture,
                                      CombinationSlotState.DeriveFormat, i));
                    states = states.SetState(slotAddress, MarkChanged);
                }

                states = states.SetState(avatarAddress, MarkChanged)
                         .SetState(Addresses.Ranking, MarkChanged)
                         .SetState(worldInformationAddress, MarkChanged)
                         .SetState(questListAddress, MarkChanged)
                         .SetState(inventoryAddress, MarkChanged);

                for (var i = 0; i < sellData.Items.Length; i++)
                {
                    var itemAddress  = Addresses.GetItemAddress(addedItemInfos[i].TradableId);
                    var orderAddress = Order.DeriveAddress(addedItemInfos[i].OrderId);
                    var shopAddress  = ShardedShopStateV2.DeriveAddress(
                        sellData.Items[i].ItemSubType,
                        addedItemInfos[i].OrderId);

                    states = states.SetState(avatarAddress, MarkChanged)
                             .SetState(inventoryAddress, MarkChanged)
                             .MarkBalanceChanged(GoldCurrencyMock, agentAddress,
                                                 GoldCurrencyState.Address)
                             .SetState(orderReceiptAddress, MarkChanged)
                             .SetState(itemAddress, MarkChanged)
                             .SetState(orderAddress, MarkChanged)
                             .SetState(shopAddress, MarkChanged);
                }

                states = states.SetState(weeklyArenaAddress, MarkChanged);
                foreach (var tuple in weeklyArenaAgentAndAvatarAddresses)
                {
                    states = states
                             .SetState(tuple.agentAddress, MarkChanged)
                             .SetState(tuple.avatarAddress, MarkChanged);
                }

                return(states);
            }

            // Create Agent and avatar
            var existingAgentState = states.GetAgentState(agentAddress);
            var agentState         = existingAgentState ?? new AgentState(agentAddress);
            var avatarState        = states.GetAvatarState(avatarAddress);

            if (!(avatarState is null))
            {
                throw new InvalidAddressException(
                          $"Aborted as there is already an avatar at {avatarAddress}.");
            }

            if (agentState.avatarAddresses.ContainsKey(_slotIndex))
            {
                throw new AvatarIndexAlreadyUsedException(
                          $"Aborted as the signer already has an avatar at index #{_slotIndex}.");
            }

            agentState.avatarAddresses.Add(_slotIndex, avatarAddress);

            var rankingState      = context.PreviousStates.GetRankingState();
            var rankingMapAddress = rankingState.UpdateRankingMap(avatarAddress);

            avatarState = TestbedHelper.CreateAvatarState(sellData.Avatar.Name,
                                                          agentAddress,
                                                          avatarAddress,
                                                          context.BlockIndex,
                                                          context.PreviousStates.GetAvatarSheets(),
                                                          context.PreviousStates.GetSheet <WorldSheet>(),
                                                          context.PreviousStates.GetGameConfigState(),
                                                          rankingMapAddress);

            // Add item
            var costumeItemSheet    = context.PreviousStates.GetSheet <CostumeItemSheet>();
            var equipmentItemSheet  = context.PreviousStates.GetSheet <EquipmentItemSheet>();
            var optionSheet         = context.PreviousStates.GetSheet <EquipmentItemOptionSheet>();
            var skillSheet          = context.PreviousStates.GetSheet <SkillSheet>();
            var materialItemSheet   = context.PreviousStates.GetSheet <MaterialItemSheet>();
            var consumableItemSheet = context.PreviousStates.GetSheet <ConsumableItemSheet>();

            for (var i = 0; i < sellData.Items.Length; i++)
            {
                TestbedHelper.AddItem(costumeItemSheet,
                                      equipmentItemSheet,
                                      optionSheet,
                                      skillSheet,
                                      materialItemSheet,
                                      consumableItemSheet,
                                      context.Random,
                                      sellData.Items[i], addedItemInfos[i], avatarState);
            }

            avatarState.Customize(0, 0, 0, 0);

            foreach (var address in avatarState.combinationSlotAddresses)
            {
                var slotState =
                    new CombinationSlotState(address,
                                             GameConfig.RequireClearedStageLevel.CombinationEquipmentAction);
                states = states.SetState(address, slotState.Serialize());
            }

            avatarState.UpdateQuestRewards(materialItemSheet);
            states = states.SetState(agentAddress, agentState.Serialize())
                     .SetState(Addresses.Ranking, rankingState.Serialize())
                     .SetState(inventoryAddress, avatarState.inventory.Serialize())
                     .SetState(worldInformationAddress, avatarState.worldInformation.Serialize())
                     .SetState(questListAddress, avatarState.questList.Serialize())
                     .SetState(avatarAddress, avatarState.SerializeV2());
            // ~Create Agent and avatar && ~Add item

            // for sell
            var costumeStatSheet = states.GetSheet <CostumeStatSheet>();

            for (var i = 0; i < sellData.Items.Length; i++)
            {
                var itemAddress  = Addresses.GetItemAddress(addedItemInfos[i].TradableId);
                var orderAddress = Order.DeriveAddress(addedItemInfos[i].OrderId);
                var shopAddress  = ShardedShopStateV2.DeriveAddress(
                    sellData.Items[i].ItemSubType,
                    addedItemInfos[i].OrderId);

                var balance =
                    context.PreviousStates.GetBalance(agentAddress, states.GetGoldCurrency());
                var price = new FungibleAssetValue(balance.Currency, sellData.Items[i].Price, 0);
                var order = OrderFactory.Create(agentAddress, avatarAddress,
                                                addedItemInfos[i].OrderId,
                                                price,
                                                addedItemInfos[i].TradableId,
                                                context.BlockIndex,
                                                sellData.Items[i].ItemSubType,
                                                sellData.Items[i].Count);

                Orders.Add(order);
                order.Validate(avatarState, sellData.Items[i].Count);
                var tradableItem = order.Sell(avatarState);

                var shardedShopState =
                    states.TryGetState(shopAddress, out Dictionary serializedState)
                        ? new ShardedShopStateV2(serializedState)
                        : new ShardedShopStateV2(shopAddress);
                var orderDigest = order.Digest(avatarState, costumeStatSheet);
                shardedShopState.Add(orderDigest, context.BlockIndex);
                var orderReceiptList =
                    states.TryGetState(orderReceiptAddress, out Dictionary receiptDict)
                        ? new OrderDigestListState(receiptDict)
                        : new OrderDigestListState(orderReceiptAddress);
                orderReceiptList.Add(orderDigest);

                states = states.SetState(orderReceiptAddress, orderReceiptList.Serialize())
                         .SetState(inventoryAddress, avatarState.inventory.Serialize())
                         .SetState(avatarAddress, avatarState.SerializeV2())
                         .SetState(itemAddress, tradableItem.Serialize())
                         .SetState(orderAddress, order.Serialize())
                         .SetState(shopAddress, shardedShopState.Serialize());
            }

            result.SellerAgentAddress  = agentAddress;
            result.SellerAvatarAddress = avatarAddress;
            result.ItemInfos           = new List <ItemInfos>();
            for (var i = 0; i < sellData.Items.Length; i++)
            {
                result.ItemInfos.Add(new ItemInfos(
                                         addedItemInfos[i].OrderId,
                                         addedItemInfos[i].TradableId,
                                         sellData.Items[i].ItemSubType,
                                         sellData.Items[i].Price,
                                         sellData.Items[i].Count));
            }

            // NOTE: Update WeeklyArenaState
            var gameConfigState  = states.GetGameConfigState();
            var weeklyArenaState = states.GetWeeklyArenaState(weeklyArenaAddress);
            var characterSheet   = states.GetSheet <CharacterSheet>();

            // loop
            for (var i = 0; i < weeklyArenaAvatarNames.Length; i++)
            {
                var name  = weeklyArenaAvatarNames[i];
                var tuple = weeklyArenaAgentAndAvatarAddresses[i];
                rankingMapAddress = rankingState.UpdateRankingMap(tuple.avatarAddress);
                avatarState       = new AvatarState(
                    tuple.avatarAddress,
                    tuple.agentAddress,
                    context.BlockIndex,
                    states.GetAvatarSheets(),
                    gameConfigState,
                    rankingMapAddress,
                    name);
                weeklyArenaState.SetV2(avatarState, characterSheet, costumeStatSheet);
                states = states
                         .SetState(tuple.avatarAddress, new AgentState(tuple.agentAddress).Serialize())
                         .SetState(tuple.avatarAddress, avatarState.Serialize());
            }
            // ~loop
            states = states.SetState(weeklyArenaAddress, weeklyArenaState.Serialize());
            // ~Update WeeklyArenaState

            return(states);
        }
Example #2
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx    = context;
            var            states = ctx.PreviousStates;

            if (ctx.Rehearsal)
            {
                states = states
                         .SetState(buyerAvatarAddress, MarkChanged)
                         .SetState(ctx.Signer, MarkChanged)
                         .SetState(sellerAvatarAddress, MarkChanged)
                         .MarkBalanceChanged(
                    GoldCurrencyMock,
                    ctx.Signer,
                    sellerAgentAddress,
                    GoldCurrencyState.Address);
                return(states.SetState(ShopState.Address, MarkChanged));
            }

            CheckObsolete(BlockChain.Policy.BlockPolicySource.V100080ObsoleteIndex, context);

            var addressesHex = GetSignerAndOtherAddressesHex(context, buyerAvatarAddress, sellerAvatarAddress);

            if (ctx.Signer.Equals(sellerAgentAddress))
            {
                throw new InvalidAddressException($"{addressesHex}Aborted as the signer is the seller.");
            }

            var sw = new Stopwatch();

            sw.Start();
            var started = DateTimeOffset.UtcNow;

            Log.Verbose("{AddressesHex}Buy exec started", addressesHex);

            if (!states.TryGetAvatarState(ctx.Signer, buyerAvatarAddress, out var buyerAvatarState))
            {
                throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the buyer was failed to load.");
            }
            sw.Stop();
            Log.Verbose("{AddressesHex}Buy Get Buyer AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            if (!buyerAvatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInShop))
            {
                buyerAvatarState.worldInformation.TryGetLastClearedStageId(out var current);
                throw new NotEnoughClearedStageLevelException(addressesHex, GameConfig.RequireClearedStageLevel.ActionsInShop, current);
            }

            if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary shopStateDict))
            {
                throw new FailedLoadStateException($"{addressesHex}Aborted as the shop state was failed to load.");
            }

            sw.Stop();
            Log.Verbose("{AddressesHex}Buy Get ShopState: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            Log.Verbose(
                "{AddressesHex}Execute Buy; buyer: {Buyer} seller: {Seller}",
                addressesHex,
                buyerAvatarAddress,
                sellerAvatarAddress);
            // 상점에서 구매할 아이템을 찾는다.
            Dictionary products = (Dictionary)shopStateDict["products"];

            IKey productIdSerialized = (IKey)productId.Serialize();

            if (!products.ContainsKey(productIdSerialized))
            {
                throw new ItemDoesNotExistException(
                          $"{addressesHex}Aborted as the shop item ({productId}) was failed to get from the shop."
                          );
            }

            ShopItem shopItem = new ShopItem((Dictionary)products[productIdSerialized]);

            if (!shopItem.SellerAgentAddress.Equals(sellerAgentAddress))
            {
                throw new ItemDoesNotExistException(
                          $"{addressesHex}Aborted as the shop item ({productId}) of seller ({shopItem.SellerAgentAddress}) is different from ({sellerAgentAddress})."
                          );
            }
            sw.Stop();
            Log.Verbose("{AddressesHex}Buy Get Item: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            if (!states.TryGetAvatarState(sellerAgentAddress, sellerAvatarAddress, out var sellerAvatarState))
            {
                throw new FailedLoadStateException(
                          $"{addressesHex}Aborted as the seller agent/avatar was failed to load from {sellerAgentAddress}/{sellerAvatarAddress}."
                          );
            }
            sw.Stop();
            Log.Verbose("{AddressesHex}Buy Get Seller AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            // 돈은 있냐?
            FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency());

            if (buyerBalance < shopItem.Price)
            {
                throw new InsufficientBalanceException(
                          ctx.Signer,
                          buyerBalance,
                          $"{addressesHex}Aborted as the buyer ({ctx.Signer}) has no sufficient gold: {buyerBalance} < {shopItem.Price}"
                          );
            }

            var tax        = shopItem.Price.DivRem(100, out _) * Buy7.TaxRate;
            var taxedPrice = shopItem.Price - tax;

            // 세금을 송금한다.
            states = states.TransferAsset(
                context.Signer,
                GoldCurrencyState.Address,
                tax);

            // 구매자의 돈을 판매자에게 송금한다.
            states = states.TransferAsset(
                context.Signer,
                sellerAgentAddress,
                taxedPrice
                );

            products      = (Dictionary)products.Remove(productIdSerialized);
            shopStateDict = shopStateDict.SetItem("products", products);

            // 구매자, 판매자에게 결과 메일 전송
            buyerResult = new Buy7.BuyerResult
            {
                shopItem   = shopItem,
                itemUsable = shopItem.ItemUsable,
                costume    = shopItem.Costume
            };
            var buyerMail = new BuyerMail(buyerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex);

            buyerResult.id = buyerMail.id;

            sellerResult = new Buy7.SellerResult
            {
                shopItem   = shopItem,
                itemUsable = shopItem.ItemUsable,
                costume    = shopItem.Costume,
                gold       = taxedPrice
            };
            var sellerMail = new SellerMail(sellerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(),
                                            ctx.BlockIndex);

            sellerResult.id = sellerMail.id;

            buyerAvatarState.Update(buyerMail);
            if (buyerResult.itemUsable != null)
            {
                buyerAvatarState.UpdateFromAddItem2(buyerResult.itemUsable, false);
            }

            if (buyerResult.costume != null)
            {
                buyerAvatarState.UpdateFromAddCostume(buyerResult.costume, false);
            }
            sellerAvatarState.Update(sellerMail);

            // 퀘스트 업데이트
            buyerAvatarState.questList.UpdateTradeQuest(TradeType.Buy, shopItem.Price);
            sellerAvatarState.questList.UpdateTradeQuest(TradeType.Sell, shopItem.Price);

            buyerAvatarState.updatedAt   = ctx.BlockIndex;
            buyerAvatarState.blockIndex  = ctx.BlockIndex;
            sellerAvatarState.updatedAt  = ctx.BlockIndex;
            sellerAvatarState.blockIndex = ctx.BlockIndex;

            var materialSheet = states.GetSheet <MaterialItemSheet>();

            buyerAvatarState.UpdateQuestRewards2(materialSheet);
            sellerAvatarState.UpdateQuestRewards2(materialSheet);

            states = states.SetState(sellerAvatarAddress, sellerAvatarState.Serialize());
            sw.Stop();
            Log.Verbose("{AddressesHex}Buy Set Seller AvatarState: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            states = states.SetState(buyerAvatarAddress, buyerAvatarState.Serialize());
            sw.Stop();
            Log.Verbose("{AddressesHex}Buy Set Buyer AvatarState: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            states = states.SetState(ShopState.Address, shopStateDict);
            sw.Stop();
            var ended = DateTimeOffset.UtcNow;

            Log.Verbose("{AddressesHex}Buy Set ShopState: {Elapsed}", addressesHex, sw.Elapsed);
            Log.Verbose("{AddressesHex}Buy Total Executed Time: {Elapsed}", addressesHex, ended - started);

            return(states);
        }
Example #3
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx         = context;
            var            states      = ctx.PreviousStates;
            var            slotAddress = AvatarAddress.Derive(
                string.Format(
                    CultureInfo.InvariantCulture,
                    CombinationSlotState.DeriveFormat,
                    SlotIndex
                    )
                );

            if (ctx.Rehearsal)
            {
                return(states
                       .SetState(AvatarAddress, MarkChanged)
                       .SetState(slotAddress, MarkChanged)
                       .SetState(ctx.Signer, MarkChanged)
                       .MarkBalanceChanged(GoldCurrencyMock, ctx.Signer, BlacksmithAddress));
            }

            if (!states.TryGetAgentAvatarStates(ctx.Signer, AvatarAddress, out var agentState,
                                                out var avatarState))
            {
                return(LogError(context, "Aborted as the avatar state of the signer was failed to load."));
            }

            var slotState = states.GetCombinationSlotState(AvatarAddress, SlotIndex);

            if (slotState is null || !(slotState.Validate(avatarState, ctx.BlockIndex)))
            {
                return(LogError(
                           context,
                           "Aborted as the slot state is failed to load or invalid: {@SlotState} @ {SlotIndex}",
                           slotState,
                           SlotIndex
                           ));
            }

            var tableSheets   = TableSheets.FromActionContext(ctx);
            var recipeSheet   = tableSheets.EquipmentItemRecipeSheet;
            var materialSheet = tableSheets.MaterialItemSheet;
            var materials     = new Dictionary <Material, int>();

            // 레시피 검증
            if (!recipeSheet.TryGetValue(RecipeId, out var recipe))
            {
                return(LogError(
                           context,
                           "Aborted as the recipe {RecipeId} was failed to load from the sheet.",
                           RecipeId
                           ));
            }

            if (!(SubRecipeId is null))
            {
                if (!recipe.SubRecipeIds.Contains((int)SubRecipeId))
                {
                    return(LogError(
                               context,
                               "Aborted as the subrecipe {SubRecipeId} was failed to load from the sheet.",
                               SubRecipeId
                               ));
                }
            }

            // 메인 레시피 해금 검사.
            if (!avatarState.worldInformation.IsStageCleared(recipe.UnlockStage))
            {
                return(LogError(
                           context,
                           "Aborted as the signer is not cleared the minimum stage level required to use the recipe {@Recipe} yet.",
                           recipe
                           ));
            }

            if (!materialSheet.TryGetValue(recipe.MaterialId, out var material))
            {
                return(LogError(
                           context,
                           "Aborted as the material {MaterialId} was failed to load from the sheet.",
                           recipe.MaterialId
                           ));
            }

            if (!avatarState.inventory.RemoveMaterial(material.ItemId, recipe.MaterialCount))
            {
                return(LogError(
                           context,
                           "Aborted as the player has no enough material ({Material} * {Quantity})",
                           material,
                           recipe.MaterialCount
                           ));
            }

            var equipmentMaterial = ItemFactory.CreateMaterial(materialSheet, material.Id);

            materials[equipmentMaterial] = recipe.MaterialCount;

            BigInteger requiredGold        = recipe.RequiredGold;
            var        requiredActionPoint = recipe.RequiredActionPoint;

            // 장비 제작
            if (!tableSheets.EquipmentItemSheet.TryGetValue(recipe.ResultEquipmentId, out var equipRow))
            {
                return(LogError(
                           context,
                           "Aborted as the equipment item {EquipmentId} was failed to load from the sheet.",
                           recipe.ResultEquipmentId
                           ));
            }

            var requiredBlockIndex = ctx.BlockIndex + recipe.RequiredBlockIndex;
            var equipment          = (Equipment)ItemFactory.CreateItemUsable(
                equipRow,
                ctx.Random.GenerateRandomGuid(),
                requiredBlockIndex
                );

            // 서브 레시피 검증
            HashSet <int> optionIds = null;

            if (SubRecipeId.HasValue)
            {
                var subSheet = tableSheets.EquipmentItemSubRecipeSheet;
                if (!subSheet.TryGetValue((int)SubRecipeId, out var subRecipe))
                {
                    return(LogError(
                               context,
                               "Aborted as the subrecipe {SubRecipeId} was failed to load from the subsheet.",
                               SubRecipeId
                               ));
                }

                requiredBlockIndex  += subRecipe.RequiredBlockIndex;
                requiredGold        += subRecipe.RequiredGold;
                requiredActionPoint += subRecipe.RequiredActionPoint;

                foreach (var materialInfo in subRecipe.Materials)
                {
                    if (!materialSheet.TryGetValue(materialInfo.Id, out var subMaterialRow))
                    {
                        return(LogError(
                                   context,
                                   "Aborted as the meterial info {MaterialInfoId} was failed to load from the submaterial sheet.",
                                   materialInfo.Id
                                   ));
                    }

                    if (!avatarState.inventory.RemoveMaterial(subMaterialRow.ItemId,
                                                              materialInfo.Count))
                    {
                        return(LogError(
                                   context,
                                   "Aborted as the player has no enough material ({Material} * {Quantity})",
                                   subMaterialRow,
                                   materialInfo.Count
                                   ));
                    }

                    var subMaterial = ItemFactory.CreateMaterial(materialSheet, materialInfo.Id);
                    materials[subMaterial] = materialInfo.Count;
                }

                optionIds = SelectOption(tableSheets, subRecipe, ctx.Random, equipment);
                equipment.Update(requiredBlockIndex);
            }

            // 자원 검증
            FungibleAssetValue agentBalance = states.GetBalance(ctx.Signer, states.GetGoldCurrency());

            if (agentBalance < (states.GetGoldCurrency() * requiredGold) || avatarState.actionPoint < requiredActionPoint)
            {
                return(LogError(
                           context,
                           "Aborted due to insufficient action point: {ActionPointBalance} < {ActionCost}",
                           avatarState.actionPoint,
                           requiredActionPoint
                           ));
            }

            avatarState.actionPoint -= requiredActionPoint;
            if (!(optionIds is null))
            {
                foreach (var id in optionIds)
                {
                    agentState.unlockedOptions.Add(id);
                }
            }

            // FIXME: BlacksmithAddress 계좌로 돈이 쌓이기만 하는데 이걸 어떻게 순환시킬지 기획이 필요.
            if (requiredGold > 0)
            {
                states = states.TransferAsset(
                    ctx.Signer,
                    BlacksmithAddress,
                    states.GetGoldCurrency() * requiredGold
                    );
            }

            var result = new CombinationConsumable.ResultModel
            {
                actionPoint = requiredActionPoint,
                gold        = requiredGold,
                materials   = materials,
                itemUsable  = equipment,
                recipeId    = RecipeId,
                subRecipeId = SubRecipeId,
                itemType    = ItemType.Equipment,
            };

            slotState.Update(result, ctx.BlockIndex, requiredBlockIndex);
            var mail = new CombinationMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(),
                                           requiredBlockIndex);

            result.id = mail.id;
            avatarState.Update(mail);
            avatarState.questList.UpdateCombinationEquipmentQuest(RecipeId);
            avatarState.UpdateFromCombination(equipment);
            avatarState.UpdateQuestRewards(ctx);
            return(states
                   .SetState(AvatarAddress, avatarState.Serialize())
                   .SetState(slotAddress, slotState.Serialize())
                   .SetState(ctx.Signer, agentState.Serialize()));
        }
Example #4
0
 public PriceData(Currency currency)
 {
     TaxSum        = new FungibleAssetValue(currency, 0, 0);
     TaxedPriceSum = new Dictionary <Address, FungibleAssetValue>();
     PriceSum      = new FungibleAssetValue(currency, 0, 0);
 }
Example #5
0
        public void Execute()
        {
            var shopState = _initialState.GetShopState();

            Assert.Empty(shopState.Products);

            var avatarState = _initialState.GetAvatarState(_avatarAddress);

            Assert.Single(avatarState.inventory.Equipments);

            var equipment = avatarState.inventory.Equipments.FirstOrDefault();

            Assert.NotNull(equipment);

            var consumable = avatarState.inventory.Consumables.FirstOrDefault();

            Assert.NotNull(equipment);

            var costume = avatarState.inventory.Costumes.FirstOrDefault();

            Assert.NotNull(costume);

            var items = new INonFungibleItem[] { equipment, consumable, costume };

            var previousStates = _initialState;
            var currencyState  = previousStates.GetGoldCurrency();
            var price          = new FungibleAssetValue(currencyState, ProductPrice, 0);

            var productCount = 0;
            var random       = new TestRandom();

            foreach (var nonFungibleItem in items)
            {
                var sellAction = new Sell3
                {
                    itemId = nonFungibleItem.NonFungibleId,
                    price  = price,
                    sellerAvatarAddress = _avatarAddress,
                };

                var nextState = sellAction.Execute(new ActionContext
                {
                    BlockIndex     = 0,
                    PreviousStates = previousStates,
                    Rehearsal      = false,
                    Signer         = _agentAddress,
                    Random         = random,
                });

                productCount++;

                var nextAvatarState = nextState.GetAvatarState(_avatarAddress);
                Assert.Empty(nextAvatarState.inventory.Equipments);

                var nextShopState = nextState.GetShopState();

                Assert.Equal(productCount, nextShopState.Products.Count);

                var products = nextShopState.Products.Values;
                Assert.NotNull(products);

                var shopItem = nonFungibleItem is Costume?
                               products.First(x => x.Costume != null) :
                                   products.First(x => x.ItemUsable != null);

                Assert.Equal(price, shopItem.Price);
                Assert.Equal(_agentAddress, shopItem.SellerAgentAddress);
                Assert.Equal(_avatarAddress, shopItem.SellerAvatarAddress);

                previousStates = nextState;
            }
        }
Example #6
0
 public TransferAsset(Address sender, Address recipient, FungibleAssetValue amount)
 {
     Sender    = sender;
     Recipient = recipient;
     Amount    = amount;
 }
Example #7
0
 private void SetGold(FungibleAssetValue gold)
 {
     text.text = gold.GetQuantityString();
 }
Example #8
0
File: Buy7.cs Project: dahlia/lib9c
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx    = context;
            var            states = ctx.PreviousStates;

            if (ctx.Rehearsal)
            {
                foreach (var purchaseInfo in purchaseInfos)
                {
                    Address shardedShopAddress =
                        ShardedShopState.DeriveAddress(purchaseInfo.itemSubType, purchaseInfo.productId);
                    states = states
                             .SetState(shardedShopAddress, MarkChanged)
                             .SetState(purchaseInfo.sellerAvatarAddress, MarkChanged)
                             .MarkBalanceChanged(
                        GoldCurrencyMock,
                        ctx.Signer,
                        purchaseInfo.sellerAgentAddress,
                        GoldCurrencyState.Address);
                }
                return(states
                       .SetState(buyerAvatarAddress, MarkChanged)
                       .SetState(ctx.Signer, MarkChanged)
                       .SetState(Addresses.Shop, MarkChanged));
            }

            CheckObsolete(BlockChain.Policy.BlockPolicySource.V100080ObsoleteIndex, context);

            var addressesHex = GetSignerAndOtherAddressesHex(context, buyerAvatarAddress);

            var sw = new Stopwatch();

            sw.Start();
            var started = DateTimeOffset.UtcNow;

            Log.Verbose("{AddressesHex}Buy exec started", addressesHex);

            if (!states.TryGetAvatarState(ctx.Signer, buyerAvatarAddress, out var buyerAvatarState))
            {
                throw new FailedLoadStateException(
                          $"{addressesHex}Aborted as the avatar state of the buyer was failed to load.");
            }

            sw.Stop();
            Log.Verbose("{AddressesHex}Buy Get Buyer AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            if (!buyerAvatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInShop))
            {
                buyerAvatarState.worldInformation.TryGetLastClearedStageId(out var current);
                throw new NotEnoughClearedStageLevelException(addressesHex,
                                                              GameConfig.RequireClearedStageLevel.ActionsInShop, current);
            }

            List <PurchaseResult> purchaseResults = new List <PurchaseResult>();
            List <SellerResult>   sellerResults   = new List <SellerResult>();
            MaterialItemSheet     materialSheet   = states.GetSheet <MaterialItemSheet>();

            buyerMultipleResult  = new BuyerMultipleResult();
            sellerMultipleResult = new SellerMultipleResult();

            foreach (var purchaseInfo in purchaseInfos)
            {
                PurchaseResult purchaseResult     = new PurchaseResult(purchaseInfo.productId);
                Address        shardedShopAddress =
                    ShardedShopState.DeriveAddress(purchaseInfo.itemSubType, purchaseInfo.productId);
                Address sellerAgentAddress  = purchaseInfo.sellerAgentAddress;
                Address sellerAvatarAddress = purchaseInfo.sellerAvatarAddress;
                Guid    productId           = purchaseInfo.productId;

                purchaseResults.Add(purchaseResult);

                if (purchaseInfo.sellerAgentAddress == ctx.Signer)
                {
                    purchaseResult.errorCode = ErrorCodeInvalidAddress;
                    continue;
                }

                if (!states.TryGetState(shardedShopAddress, out Bencodex.Types.Dictionary shopStateDict))
                {
                    ShardedShopState shardedShopState = new ShardedShopState(shardedShopAddress);
                    shopStateDict = (Dictionary)shardedShopState.Serialize();
                }

                sw.Stop();
                Log.Verbose("{AddressesHex}Buy Get ShopState: {Elapsed}", addressesHex, sw.Elapsed);
                sw.Restart();

                Log.Verbose(
                    "{AddressesHex}Execute Buy; buyer: {Buyer} seller: {Seller}",
                    addressesHex,
                    buyerAvatarAddress,
                    sellerAvatarAddress);
                // Find product from ShardedShopState.
                List       products               = (List)shopStateDict[ProductsKey];
                IValue     productIdSerialized    = productId.Serialize();
                IValue     sellerAgentSerialized  = purchaseInfo.sellerAgentAddress.Serialize();
                IValue     sellerAvatarSerialized = purchaseInfo.sellerAvatarAddress.Serialize();
                Dictionary productSerialized      = products
                                                    .Select(p => (Dictionary)p)
                                                    .FirstOrDefault(p =>
                                                                    p[LegacyProductIdKey].Equals(productIdSerialized) &&
                                                                    p[LegacySellerAvatarAddressKey].Equals(sellerAvatarSerialized) &&
                                                                    p[LegacySellerAgentAddressKey].Equals(sellerAgentSerialized));

                bool fromLegacy = false;
                if (productSerialized.Equals(Dictionary.Empty))
                {
                    if (purchaseInfo.itemSubType == ItemSubType.Hourglass ||
                        purchaseInfo.itemSubType == ItemSubType.ApStone)
                    {
                        purchaseResult.errorCode = ErrorCodeItemDoesNotExist;
                        continue;
                    }
                    // Backward compatibility.
                    IValue rawShop = states.GetState(Addresses.Shop);
                    if (!(rawShop is null))
                    {
                        Dictionary legacyShopDict = (Dictionary)rawShop;
                        Dictionary legacyProducts = (Dictionary)legacyShopDict[LegacyProductsKey];
                        IKey       productKey     = (IKey)productId.Serialize();
                        // SoldOut
                        if (!legacyProducts.ContainsKey(productKey))
                        {
                            purchaseResult.errorCode = ErrorCodeItemDoesNotExist;
                            continue;
                        }

                        productSerialized = (Dictionary)legacyProducts[productKey];
                        legacyProducts    = (Dictionary)legacyProducts.Remove(productKey);
                        legacyShopDict    = legacyShopDict.SetItem(LegacyProductsKey, legacyProducts);
                        states            = states.SetState(Addresses.Shop, legacyShopDict);
                        fromLegacy        = true;
                    }
                }

                ShopItem shopItem = new ShopItem(productSerialized);
                if (!shopItem.SellerAgentAddress.Equals(sellerAgentAddress))
                {
                    purchaseResult.errorCode = ErrorCodeItemDoesNotExist;
                    continue;
                }

                sw.Stop();
                Log.Verbose("{AddressesHex}Buy Get Item: {Elapsed}", addressesHex, sw.Elapsed);
                sw.Restart();

                if (0 < shopItem.ExpiredBlockIndex && shopItem.ExpiredBlockIndex < context.BlockIndex)
                {
                    purchaseResult.errorCode = ErrorCodeShopItemExpired;
                    continue;
                }

                if (!shopItem.Price.Equals(purchaseInfo.price))
                {
                    purchaseResult.errorCode = ErrorCodeInvalidPrice;
                    continue;
                }

                if (!states.TryGetAvatarState(sellerAgentAddress, sellerAvatarAddress, out var sellerAvatarState))
                {
                    purchaseResult.errorCode = ErrorCodeFailedLoadingState;
                    continue;
                }

                sw.Stop();
                Log.Verbose("{AddressesHex}Buy Get Seller AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed);
                sw.Restart();

                // Check Balance.
                FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency());
                if (buyerBalance < shopItem.Price)
                {
                    purchaseResult.errorCode = ErrorCodeInsufficientBalance;
                    continue;
                }

                // Check Seller inventory.
                ITradableItem tradableItem;
                int           count = 1;
                if (!(shopItem.ItemUsable is null))
                {
                    tradableItem = shopItem.ItemUsable;
                }
 public IAccountStateDelta MintAsset(Address recipient, FungibleAssetValue value) =>
 this;
Example #10
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx    = context;
            var            states = ctx.PreviousStates;

            if (ctx.Rehearsal)
            {
                return(states.SetState(ctx.Signer, MarkChanged)
                       .SetState(AvatarAddress, MarkChanged)
                       .SetState(WeeklyArenaAddress, MarkChanged)
                       .SetState(ctx.Signer, MarkChanged)
                       .MarkBalanceChanged(GoldCurrencyMock, ctx.Signer, WeeklyArenaAddress));
            }

            if (AvatarAddress.Equals(EnemyAddress))
            {
                return(LogError(context, "Aborted as the signer tried to battle for themselves."));
            }

            if (!states.TryGetAgentAvatarStates(ctx.Signer, AvatarAddress, out var agentState,
                                                out var avatarState))
            {
                return(LogError(context, "Aborted as the avatar state of the signer was failed to load."));
            }

            // 도전자의 장비가 유효한지 검사한다.
            // 피도전자의 장비도 검사해야 하는가는 모르겠다. 이후에 필요하다면 추가하는 것으로 한다.
            if (!avatarState.ValidateEquipments(equipmentIds, context.BlockIndex))
            {
                // 장비가 유효하지 않은 에러.
                return(LogError(context, "Aborted as the equipment is invalid."));
            }

            if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world))
            {
                return(LogError(context, "Aborted as the WorldInformation was failed to load or not cleared yet."));
            }

            if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard)
            {
                // 스테이지 클리어 부족 에러.
                return(LogError(
                           context,
                           "Aborted as the signer is not cleared the minimum stage level required to battle with other players yet: {ClearedLevel} < {RequiredLevel}.",
                           world.StageClearedId,
                           GameConfig.RequireClearedStageLevel.ActionsInRankingBoard
                           ));
            }

            avatarState.EquipCostumes(costumeIds);
            avatarState.EquipEquipments(equipmentIds);

            var enemyAvatarState = states.GetAvatarState(EnemyAddress);

            if (enemyAvatarState is null)
            {
                return(LogError(
                           context,
                           "Aborted as the avatar state of the opponent ({OpponentAddress}) was failed to load.",
                           EnemyAddress
                           ));
            }

            var weeklyArenaState = states.GetWeeklyArenaState(WeeklyArenaAddress);

            //FIXME 오류던지게 고쳐야함
            if (weeklyArenaState.Ended)
            {
                return(LogError(context, "Aborted as the weekly arena state already ended."));
            }

            if (!weeklyArenaState.ContainsKey(AvatarAddress))
            {
                return(LogError(context, "Aborted as the weekly arena state was failed to load."));
            }

            var arenaInfo = weeklyArenaState[AvatarAddress];

            if (arenaInfo.DailyChallengeCount <= 0)
            {
                return(LogError(context, "Aborted as the arena state reached the daily limit."));
            }

            if (!arenaInfo.Active)
            {
                FungibleAssetValue agentBalance = states.GetBalance(ctx.Signer, states.GetGoldCurrency());

                if (agentBalance >= new FungibleAssetValue(agentBalance.Currency, EntranceFee, 0))
                {
                    states = states.TransferAsset(
                        ctx.Signer,
                        WeeklyArenaAddress,
                        new FungibleAssetValue(
                            states.GetGoldCurrency(),
                            EntranceFee,
                            0
                            )
                        );
                    arenaInfo.Activate();
                }
                else
                {
                    return(LogError(
                               context,
                               "Aborted as the signer's balance ({Balance}) is insufficient to pay entrance fee/stake ({EntranceFee}).",
                               agentBalance,
                               EntranceFee
                               ));
                }
            }

            if (!weeklyArenaState.ContainsKey(EnemyAddress))
            {
                return(LogError(
                           context,
                           "Aborted as the opponent ({OpponentAddress}) is not registered in the weekly arena state.",
                           EnemyAddress
                           ));
            }

            Log.Debug(weeklyArenaState.address.ToHex());

            var tableSheetState = TableSheetsState.FromActionContext(ctx);
            var tableSheets     = TableSheets.FromTableSheetsState(tableSheetState);

            var simulator = new RankingSimulator(
                ctx.Random,
                avatarState,
                enemyAvatarState,
                consumableIds,
                tableSheets,
                StageId,
                arenaInfo,
                weeklyArenaState[EnemyAddress]);

            simulator.Simulate();

            Result = simulator.Log;

            foreach (var itemBase in simulator.Reward)
            {
                avatarState.inventory.AddItem(itemBase);
            }

            return(states
                   .SetState(ctx.Signer, agentState.Serialize())
                   .SetState(WeeklyArenaAddress, weeklyArenaState.Serialize())
                   .SetState(AvatarAddress, avatarState.Serialize()));
        }
Example #11
0
        public void Execute(ItemType itemType, bool shopItemExist, int blockIndex)
        {
            var avatarState = _initialState.GetAvatarState(_avatarAddress);
            List <Inventory.Item> inventoryItem = avatarState.inventory.Items.Where(i => i.item.ItemType == itemType).ToList();

            Assert.Single(inventoryItem);
            var previousStates = _initialState;
            var currencyState  = previousStates.GetGoldCurrency();
            var price          = new FungibleAssetValue(currencyState, ProductPrice, 0);
            INonFungibleItem nonFungibleItem = (INonFungibleItem)inventoryItem.First().item;

            nonFungibleItem.RequiredBlockIndex = blockIndex;
            Assert.Equal(blockIndex, nonFungibleItem.RequiredBlockIndex);
            ItemSubType itemSubType = ItemSubType.Food;
            Guid        productId   = new Guid("6f460c1a-755d-48e4-ad67-65d5f519dbc8");

            if (nonFungibleItem is ItemUsable itemUsable)
            {
                itemSubType = itemUsable.ItemSubType;
            }
            else if (nonFungibleItem is Costume costume)
            {
                itemSubType = costume.ItemSubType;
            }

            Address shopAddress = ShardedShopState.DeriveAddress(itemSubType, productId);

            if (shopItemExist)
            {
                var si = new ShopItem(
                    _agentAddress,
                    _avatarAddress,
                    productId,
                    new FungibleAssetValue(currencyState, 100, 0),
                    blockIndex,
                    nonFungibleItem);
                ShardedShopState shardedShopState =
                    new ShardedShopState(shopAddress);
                shardedShopState.Register(si);
                Assert.Single(shardedShopState.Products);
                previousStates = previousStates.SetState(shopAddress, shardedShopState.Serialize());
            }
            else
            {
                Assert.Null(previousStates.GetState(shopAddress));
            }

            var sellAction = new Sell4
            {
                itemId = nonFungibleItem.NonFungibleId,
                price  = price,
                sellerAvatarAddress = _avatarAddress,
                itemSubType         = itemSubType,
            };

            var nextState = sellAction.Execute(new ActionContext
            {
                BlockIndex     = 1,
                PreviousStates = previousStates,
                Rehearsal      = false,
                Signer         = _agentAddress,
                Random         = new TestRandom(),
            });

            const long expiredBlockIndex = Sell6.ExpiredBlockIndex + 1;
            var        nextAvatarState   = nextState.GetAvatarState(_avatarAddress);

            Assert.True(nextAvatarState.inventory.TryGetNonFungibleItem(nonFungibleItem.NonFungibleId, out var nextItem));
            INonFungibleItem nextNonFungibleItem = (INonFungibleItem)nextItem.item;

            Assert.Equal(expiredBlockIndex, nextNonFungibleItem.RequiredBlockIndex);

            var nextShopState = new ShardedShopState((Dictionary)nextState.GetState(shopAddress));

            Assert.Single(nextShopState.Products);

            var products = nextShopState.Products.Values;

            var shopItem          = products.First();
            INonFungibleItem item = itemType == ItemType.Costume ? (INonFungibleItem)shopItem.Costume : shopItem.ItemUsable;

            Assert.Equal(price, shopItem.Price);
            Assert.Equal(expiredBlockIndex, shopItem.ExpiredBlockIndex);
            Assert.Equal(expiredBlockIndex, item.RequiredBlockIndex);
            Assert.Equal(_agentAddress, shopItem.SellerAgentAddress);
            Assert.Equal(_avatarAddress, shopItem.SellerAvatarAddress);

            var mailList = nextAvatarState.mailBox.Where(m => m is SellCancelMail).ToList();

            Assert.Single(mailList);

            Assert.Equal(expiredBlockIndex, mailList.First().requiredBlockIndex);
        }
Example #12
0
        public override int ValidateTransfer2(AvatarState avatarState, Guid tradableId, FungibleAssetValue price, long blockIndex)
        {
            var errorCode = base.ValidateTransfer2(avatarState, tradableId, price, blockIndex);

            if (errorCode != 0)
            {
                return(errorCode);
            }

            if (!avatarState.inventory.TryGetNonFungibleItem(TradableId, out INonFungibleItem nonFungibleItem))
            {
                return(Buy.ErrorCodeItemDoesNotExist);
            }

            return(!nonFungibleItem.ItemSubType.Equals(ItemSubType) ? Buy.ErrorCodeInvalidItemType : errorCode);
        }
Example #13
0
        public override int ValidateTransfer(AvatarState avatarState, Guid tradableId, FungibleAssetValue price, long blockIndex)
        {
            var errorCode = base.ValidateTransfer(avatarState, tradableId, price, blockIndex);

            if (errorCode != 0)
            {
                return(errorCode);
            }

            if (!avatarState.inventory.TryGetLockedItem(new OrderLock(OrderId), out var inventoryItem))
            {
                return(Buy.ErrorCodeItemDoesNotExist);
            }

            if (inventoryItem.item is INonFungibleItem nonFungibleItem)
            {
                return(nonFungibleItem.ItemSubType.Equals(ItemSubType) ? errorCode : Buy.ErrorCodeInvalidItemType);
            }

            return(Buy.ErrorCodeItemDoesNotExist);
        }
Example #14
0
        public void Balance(
            [Option('v', Description = "Print more logs.")]
            bool verbose,
            [Option('s', Description = "Path to the chain store.")]
            string storePath,
            [Option(
                 'b',
                 Description = "Optional block hash/index offset to query balances at.  " +
                               "Tip by default.")]
            string block = null,
            [Option('c', Description = "Optional chain ID.  Default is the canonical chain ID.")]
            Guid?chainId = null,
            [Argument(Description = "Account address.")]
            string address = null
            )
        {
            using Logger logger = Utils.ConfigureLogger(verbose);
            TextWriter stderr = Console.Error;

            (BlockChain <NCAction> chain, IStore store, _, _) =
                Utils.GetBlockChain(logger, storePath, chainId);

            Block <NCAction> offset = Utils.ParseBlockOffset(chain, block);

            stderr.WriteLine("The offset block: #{0} {1}.", offset.Index, offset.Hash);

            Bencodex.Types.Dictionary goldCurrencyStateDict = (Bencodex.Types.Dictionary)
                                                              chain.GetState(GoldCurrencyState.Address);
            GoldCurrencyState goldCurrencyState = new GoldCurrencyState(goldCurrencyStateDict);
            Currency          gold = goldCurrencyState.Currency;

            if (address is {} addrStr)
            {
                Address            addr    = Utils.ParseAddress(addrStr);
                FungibleAssetValue balance = chain.GetBalance(addr, gold, offset.Hash);
                Console.WriteLine("{0}\t{1}", addr, balance);
                return;
            }

            var printed = new HashSet <Address>();

            foreach (BlockHash blockHash in chain.BlockHashes)
            {
                BlockDigest digest = GetBlockDigest(store, blockHash);
                stderr.WriteLine("Scanning block #{0} {1}...", digest.Index, digest.Hash);
                stderr.Flush();
                IEnumerable <Address> addrs = digest.TxIds
                                              .Select(txId => store.GetTransaction <NCAction>(new TxId(txId.ToArray())))
                                              .SelectMany(tx => tx.Actions
                                                          .Select(a => a.InnerAction)
                                                          .SelectMany(a => a is TransferAsset t
                            ? new[] { t.Sender, t.Recipient }
                            : a is InitializeStates i &&
                                                                      i.GoldDistributions is Bencodex.Types.List l
                            ? l.OfType <Bencodex.Types.Dictionary>()
                                                                      .Select(d => new GoldDistribution(d).Address)
                            : new Address[0]))
                                              .Append(digest.Miner);
                foreach (Address addr in addrs)
                {
                    if (!printed.Contains(addr))
                    {
                        FungibleAssetValue balance = chain.GetBalance(addr, gold, offset.Hash);
                        Console.WriteLine("{0}\t{1}", addr, balance);
                        printed.Add(addr);
                    }
                }
            }
        }
Example #15
0
 public static IValue Serialize(this FungibleAssetValue value) =>
 new Bencodex.Types.List(new IValue[]
 {
     CurrencyExtensions.Serialize(value.Currency),
     value.RawValue.Serialize(),
 });
 public IAccountStateDelta TransferAsset(
     Address sender,
     Address recipient,
     FungibleAssetValue value,
     bool allowNegativeBalance = false
     ) => this;
Example #17
0
        public void Execute(
            ItemType itemType,
            bool orderExist,
            int itemCount,
            bool backward
            )
        {
            var avatarState = _initialState.GetAvatarState(_avatarAddress);

            ITradableItem tradableItem;

            switch (itemType)
            {
            case ItemType.Consumable:
                tradableItem = ItemFactory.CreateItemUsable(
                    _tableSheets.ConsumableItemSheet.First,
                    Guid.NewGuid(),
                    0);
                break;

            case ItemType.Costume:
                tradableItem = ItemFactory.CreateCostume(
                    _tableSheets.CostumeItemSheet.First,
                    Guid.NewGuid());
                break;

            case ItemType.Equipment:
                tradableItem = ItemFactory.CreateItemUsable(
                    _tableSheets.EquipmentItemSheet.First,
                    Guid.NewGuid(),
                    0);
                break;

            case ItemType.Material:
                var tradableMaterialRow = _tableSheets.MaterialItemSheet.OrderedList
                                          .First(row => row.ItemSubType == ItemSubType.Hourglass);
                tradableItem = ItemFactory.CreateTradableMaterial(tradableMaterialRow);
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null);
            }

            Assert.Equal(0, tradableItem.RequiredBlockIndex);
            avatarState.inventory.AddItem2((ItemBase)tradableItem, itemCount);

            var previousStates = _initialState;

            if (backward)
            {
                previousStates = previousStates.SetState(_avatarAddress, avatarState.Serialize());
            }
            else
            {
                previousStates = previousStates
                                 .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize())
                                 .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize())
                                 .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize())
                                 .SetState(_avatarAddress, avatarState.SerializeV2());
            }

            var currencyState      = previousStates.GetGoldCurrency();
            var price              = new FungibleAssetValue(currencyState, ProductPrice, 0);
            var orderId            = new Guid("6f460c1a755d48e4ad6765d5f519dbc8");
            var existOrderId       = new Guid("229e5f8c-fabe-4c04-bab9-45325cfa69a4");
            var orderAddress       = Order.DeriveAddress(orderId);
            var shardedShopAddress = ShardedShopStateV2.DeriveAddress(
                tradableItem.ItemSubType,
                orderId);
            long blockIndex = 1;

            if (orderExist)
            {
                var shardedShopState = new ShardedShopStateV2(shardedShopAddress);
                var existOrder       = OrderFactory.Create(
                    _agentAddress,
                    _avatarAddress,
                    existOrderId,
                    price,
                    tradableItem.TradableId,
                    0,
                    tradableItem.ItemSubType,
                    1
                    );
                existOrder.Sell2(avatarState);
                blockIndex = existOrder.ExpiredBlockIndex;
                shardedShopState.Add(existOrder.Digest2(avatarState, _tableSheets.CostumeStatSheet), blockIndex);
                Assert.Single(shardedShopState.OrderDigestList);
                previousStates = previousStates.SetState(
                    shardedShopAddress,
                    shardedShopState.Serialize());
            }
            else
            {
                Assert.Null(previousStates.GetState(shardedShopAddress));
            }

            var sellAction = new Sell8
            {
                sellerAvatarAddress = _avatarAddress,
                tradableId          = tradableItem.TradableId,
                count       = itemCount,
                price       = price,
                itemSubType = tradableItem.ItemSubType,
                orderId     = orderId,
            };
            var nextState = sellAction.Execute(new ActionContext
            {
                BlockIndex     = blockIndex,
                PreviousStates = previousStates,
                Rehearsal      = false,
                Signer         = _agentAddress,
                Random         = new TestRandom(),
            });

            long expiredBlockIndex = Order.ExpirationInterval + blockIndex;

            // Check AvatarState and Inventory
            var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress);

            Assert.Single(nextAvatarState.inventory.Items);
            Assert.True(nextAvatarState.inventory.TryGetTradableItems(
                            tradableItem.TradableId,
                            expiredBlockIndex,
                            1,
                            out var inventoryItems));
            Assert.Single(inventoryItems);
            ITradableItem nextTradableItem = (ITradableItem)inventoryItems.First().item;

            Assert.Equal(expiredBlockIndex, nextTradableItem.RequiredBlockIndex);

            // Check ShardedShopState
            var nextSerializedShardedShopState = nextState.GetState(shardedShopAddress);

            Assert.NotNull(nextSerializedShardedShopState);
            var nextShardedShopState =
                new ShardedShopStateV2((Dictionary)nextSerializedShardedShopState);
            var expectedCount = orderExist ? 2 : 1;

            Assert.Equal(expectedCount, nextShardedShopState.OrderDigestList.Count);
            var orderDigest = nextShardedShopState.OrderDigestList.First(o => o.OrderId.Equals(orderId));

            Assert.Equal(price, orderDigest.Price);
            Assert.Equal(blockIndex, orderDigest.StartedBlockIndex);
            Assert.Equal(expiredBlockIndex, orderDigest.ExpiredBlockIndex);
            Assert.Equal(((ItemBase)tradableItem).Id, orderDigest.ItemId);
            Assert.Equal(tradableItem.TradableId, orderDigest.TradableId);

            var serializedOrder = nextState.GetState(orderAddress);

            Assert.NotNull(serializedOrder);
            var serializedItem = nextState.GetState(Addresses.GetItemAddress(tradableItem.TradableId));

            Assert.NotNull(serializedItem);

            var           order     = OrderFactory.Deserialize((Dictionary)serializedOrder);
            ITradableItem orderItem = (ITradableItem)ItemFactory.Deserialize((Dictionary)serializedItem);

            Assert.Equal(price, order.Price);
            Assert.Equal(orderId, order.OrderId);
            Assert.Equal(tradableItem.TradableId, order.TradableId);
            Assert.Equal(blockIndex, order.StartedBlockIndex);
            Assert.Equal(expiredBlockIndex, order.ExpiredBlockIndex);
            Assert.Equal(_agentAddress, order.SellerAgentAddress);
            Assert.Equal(_avatarAddress, order.SellerAvatarAddress);
            Assert.Equal(expiredBlockIndex, orderItem.RequiredBlockIndex);

            var receiptDict = nextState.GetState(OrderDigestListState.DeriveAddress(_avatarAddress));

            Assert.NotNull(receiptDict);
            var orderDigestList = new OrderDigestListState((Dictionary)receiptDict);

            Assert.Single(orderDigestList.OrderDigestList);
            OrderDigest orderDigest2 = orderDigestList.OrderDigestList.First();

            Assert.Equal(orderDigest, orderDigest2);
        }
 public IAccountStateDelta BurnAsset(Address owner, FungibleAssetValue value) => this;
Example #19
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx         = context;
            var            states      = ctx.PreviousStates;
            var            slotAddress = AvatarAddress.Derive(
                string.Format(
                    CultureInfo.InvariantCulture,
                    CombinationSlotState.DeriveFormat,
                    SlotIndex
                    )
                );

            if (ctx.Rehearsal)
            {
                return(states
                       .SetState(AvatarAddress, MarkChanged)
                       .SetState(slotAddress, MarkChanged)
                       .SetState(ctx.Signer, MarkChanged)
                       .MarkBalanceChanged(GoldCurrencyMock, ctx.Signer, BlacksmithAddress));
            }

            CheckObsolete(BlockChain.Policy.BlockPolicySource.V100080ObsoleteIndex, context);

            var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress);

            if (!states.TryGetAgentAvatarStates(ctx.Signer, AvatarAddress, out var agentState,
                                                out var avatarState))
            {
                throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load.");
            }

            var slotState = states.GetCombinationSlotState(AvatarAddress, SlotIndex);

            if (slotState is null)
            {
                throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state is failed to load");
            }

            if (!slotState.Validate(avatarState, ctx.BlockIndex))
            {
                throw new CombinationSlotUnlockException(
                          $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {SlotIndex}");
            }

            var recipeSheet   = states.GetSheet <EquipmentItemRecipeSheet>();
            var materialSheet = states.GetSheet <MaterialItemSheet>();
            var materials     = new Dictionary <Material, int>();

            // Validate recipe.
            if (!recipeSheet.TryGetValue(RecipeId, out var recipe))
            {
                throw new SheetRowNotFoundException(addressesHex, nameof(EquipmentItemRecipeSheet), RecipeId);
            }

            if (!(SubRecipeId is null))
            {
                if (!recipe.SubRecipeIds.Contains((int)SubRecipeId))
                {
                    throw new SheetRowColumnException(
                              $"{addressesHex}Aborted as the sub recipe {SubRecipeId} was failed to load from the sheet."
                              );
                }
            }

            // Validate main recipe is unlocked.
            if (!avatarState.worldInformation.IsStageCleared(recipe.UnlockStage))
            {
                avatarState.worldInformation.TryGetLastClearedStageId(out var current);
                throw new NotEnoughClearedStageLevelException(addressesHex, recipe.UnlockStage, current);
            }

            if (!materialSheet.TryGetValue(recipe.MaterialId, out var material))
            {
                throw new SheetRowNotFoundException(addressesHex, nameof(MaterialItemSheet), recipe.MaterialId);
            }

            if (!avatarState.inventory.RemoveFungibleItem2(material.ItemId, recipe.MaterialCount))
            {
                throw new NotEnoughMaterialException(
                          $"{addressesHex}Aborted as the player has no enough material ({material} * {recipe.MaterialCount})"
                          );
            }

            var equipmentMaterial = ItemFactory.CreateMaterial(materialSheet, material.Id);

            materials[equipmentMaterial] = recipe.MaterialCount;

            BigInteger requiredGold        = recipe.RequiredGold;
            var        requiredActionPoint = recipe.RequiredActionPoint;
            var        equipmentItemSheet  = states.GetSheet <EquipmentItemSheet>();

            // Validate equipment id.
            if (!equipmentItemSheet.TryGetValue(recipe.ResultEquipmentId, out var equipRow))
            {
                throw new SheetRowNotFoundException(addressesHex, nameof(equipmentItemSheet), recipe.ResultEquipmentId);
            }

            var requiredBlockIndex = ctx.BlockIndex + recipe.RequiredBlockIndex;
            var equipment          = (Equipment)ItemFactory.CreateItemUsable(
                equipRow,
                ctx.Random.GenerateRandomGuid(),
                requiredBlockIndex
                );

            // Validate sub recipe.
            HashSet <int> optionIds = null;

            if (SubRecipeId.HasValue)
            {
                var subSheet = states.GetSheet <EquipmentItemSubRecipeSheet>();
                var subId    = (int)SubRecipeId;
                if (!subSheet.TryGetValue(subId, out var subRecipe))
                {
                    throw new SheetRowNotFoundException(addressesHex, nameof(EquipmentItemSubRecipeSheet), subId);
                }

                requiredBlockIndex  += subRecipe.RequiredBlockIndex;
                requiredGold        += subRecipe.RequiredGold;
                requiredActionPoint += subRecipe.RequiredActionPoint;

                foreach (var materialInfo in subRecipe.Materials)
                {
                    if (!materialSheet.TryGetValue(materialInfo.Id, out var subMaterialRow))
                    {
                        throw new SheetRowNotFoundException(addressesHex, nameof(MaterialItemSheet), materialInfo.Id);
                    }

                    if (!avatarState.inventory.RemoveFungibleItem2(subMaterialRow.ItemId,
                                                                   materialInfo.Count))
                    {
                        throw new NotEnoughMaterialException(
                                  $"{addressesHex}Aborted as the player has no enough material ({subMaterialRow} * {materialInfo.Count})"
                                  );
                    }

                    var subMaterial = ItemFactory.CreateMaterial(materialSheet, materialInfo.Id);
                    materials[subMaterial] = materialInfo.Count;
                }

                optionIds = SelectOption(states.GetSheet <EquipmentItemOptionSheet>(), states.GetSheet <SkillSheet>(),
                                         subRecipe, ctx.Random, equipment);
                equipment.Update(requiredBlockIndex);
            }

            // Validate NCG.
            FungibleAssetValue agentBalance = states.GetBalance(ctx.Signer, states.GetGoldCurrency());

            if (agentBalance < states.GetGoldCurrency() * requiredGold)
            {
                throw new InsufficientBalanceException(
                          ctx.Signer,
                          agentBalance,
                          $"{addressesHex}Aborted as the agent ({ctx.Signer}) has no sufficient gold: {agentBalance} < {requiredGold}"
                          );
            }

            if (avatarState.actionPoint < requiredActionPoint)
            {
                throw new NotEnoughActionPointException(
                          $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredActionPoint}"
                          );
            }

            avatarState.actionPoint -= requiredActionPoint;
            if (!(optionIds is null))
            {
                foreach (var id in optionIds.OrderBy(id => id))
                {
                    agentState.unlockedOptions.Add(id);
                }
            }

            // FIXME: BlacksmithAddress just accumulate NCG. we need plan how to circulate this.
            if (requiredGold > 0)
            {
                states = states.TransferAsset(
                    ctx.Signer,
                    BlacksmithAddress,
                    states.GetGoldCurrency() * requiredGold
                    );
            }

            var result = new CombinationConsumable5.ResultModel
            {
                actionPoint = requiredActionPoint,
                gold        = requiredGold,
                materials   = materials,
                itemUsable  = equipment,
                recipeId    = RecipeId,
                subRecipeId = SubRecipeId,
                itemType    = ItemType.Equipment,
            };

            slotState.Update(result, ctx.BlockIndex, requiredBlockIndex);
            var mail = new CombinationMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(),
                                           requiredBlockIndex);

            result.id = mail.id;
            avatarState.Update3(mail);
            avatarState.questList.UpdateCombinationEquipmentQuest(RecipeId);
            avatarState.UpdateFromCombination2(equipment);
            avatarState.UpdateQuestRewards2(materialSheet);
            return(states
                   .SetState(AvatarAddress, avatarState.Serialize())
                   .SetState(slotAddress, slotState.Serialize())
                   .SetState(ctx.Signer, agentState.Serialize()));
        }
Example #20
0
 public FungibleAssetValue GetBalance(Address address, Currency currency) =>
 _rawStates.TryGetValue(ToBalanceKey(address, currency), out IValue value)
         ? FungibleAssetValue.FromRawValue(currency, value is Bencodex.Types.Integer i ? i.Value : 0)
         : currency * 0;
Example #21
0
        public void Execute(params ShopItemData[] productDatas)
        {
            var buyerAvatarState = _initialState.GetAvatarState(_buyerAvatarAddress);

            var goldCurrency = _initialState.GetGoldCurrency();
            var shopState    = _initialState.GetShopState();
            var buyCount     = 0;
            var itemsToBuy   = new List <BuyMultiple.PurchaseInfo>();

            foreach (var product in productDatas)
            {
                var(sellerAvatarState, sellerAgentState) =
                    CreateAvatarState(product.SellerAgentAddress, product.SellerAvatarAddress);

                INonFungibleItem nonFungibleItem;
                if (product.ItemType == ItemType.Equipment)
                {
                    var itemUsable = ItemFactory.CreateItemUsable(
                        _tableSheets.EquipmentItemSheet.First,
                        product.ItemId,
                        product.RequiredBlockIndex);
                    nonFungibleItem = itemUsable;
                }
                else
                {
                    var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, product.ItemId);
                    costume.Update(product.RequiredBlockIndex);
                    nonFungibleItem = costume;
                }

                // Case for backward compatibility of `Buy`
                if (product.ContainsInInventory)
                {
                    sellerAvatarState.inventory.AddItem2((ItemBase)nonFungibleItem);
                }

                var shopItemId = Guid.NewGuid();

                var shopItem = new ShopItem(
                    sellerAgentState.address,
                    sellerAvatarState.address,
                    shopItemId,
                    new FungibleAssetValue(_goldCurrencyState.Currency, product.Price, 0),
                    product.RequiredBlockIndex,
                    nonFungibleItem);
                shopState.Register(shopItem);

                if (product.Buy)
                {
                    ++buyCount;
                    var purchaseInfo = new BuyMultiple.PurchaseInfo(
                        shopItem.ProductId,
                        shopItem.SellerAgentAddress,
                        shopItem.SellerAvatarAddress);
                    itemsToBuy.Add(purchaseInfo);
                }
            }

            Assert.NotNull(shopState.Products);
            Assert.Equal(3, shopState.Products.Count);

            _initialState = _initialState
                            .SetState(_buyerAvatarAddress, buyerAvatarState.Serialize())
                            .SetState(Addresses.Shop, shopState.Serialize());

            var priceData = new PriceData(goldCurrency);

            foreach (var avatarState in _sellerAgentStateMap.Keys)
            {
                var agentState = _sellerAgentStateMap[avatarState];
                priceData.TaxedPriceSum[agentState.address] = new FungibleAssetValue(goldCurrency, 0, 0);

                _initialState = _initialState
                                .SetState(avatarState.address, avatarState.Serialize());
            }

            IAccountStateDelta previousStates = _initialState;

            var buyerGold    = previousStates.GetBalance(_buyerAgentAddress, goldCurrency);
            var priceSumData = productDatas
                               .Where(i => i.Buy)
                               .Aggregate(priceData, (priceSum, next) =>
            {
                var price         = new FungibleAssetValue(goldCurrency, next.Price, 0);
                var tax           = price.DivRem(100, out _) * Buy.TaxRate;
                var taxedPrice    = price - tax;
                priceData.TaxSum += tax;

                var prevSum = priceData.TaxedPriceSum[next.SellerAgentAddress];
                priceData.TaxedPriceSum[next.SellerAgentAddress] = prevSum + taxedPrice;
                priceData.PriceSum += price;
                return(priceData);
            });

            var buyMultipleAction = new BuyMultiple
            {
                buyerAvatarAddress = _buyerAvatarAddress,
                purchaseInfos      = itemsToBuy,
            };
            var nextState = buyMultipleAction.Execute(new ActionContext()
            {
                BlockIndex     = 1,
                PreviousStates = previousStates,
                Random         = new TestRandom(),
                Rehearsal      = false,
                Signer         = _buyerAgentAddress,
            });

            var nextShopState = nextState.GetShopState();

            Assert.Equal(productDatas.Length - buyCount, nextShopState.Products.Count);

            var nextBuyerAvatarState = nextState.GetAvatarState(_buyerAvatarAddress);

            foreach (var product in productDatas.Where(i => i.Buy))
            {
                Assert.True(
                    nextBuyerAvatarState.inventory.TryGetNonFungibleItem(
                        product.ItemId,
                        out INonFungibleItem outNonFungibleItem)
                    );
                Assert.Equal(product.RequiredBlockIndex, outNonFungibleItem.RequiredBlockIndex);
            }

            Assert.Equal(buyCount, nextBuyerAvatarState.mailBox.Count);

            goldCurrency = nextState.GetGoldCurrency();
            var nextGoldCurrencyGold = nextState.GetBalance(Addresses.GoldCurrency, goldCurrency);

            Assert.Equal(priceSumData.TaxSum, nextGoldCurrencyGold);

            foreach (var product in productDatas)
            {
                var nextSellerGold = nextState.GetBalance(product.SellerAgentAddress, goldCurrency);
                Assert.Equal(priceSumData.TaxedPriceSum[product.SellerAgentAddress], nextSellerGold);
            }

            var nextBuyerGold = nextState.GetBalance(_buyerAgentAddress, goldCurrency);

            Assert.Equal(buyerGold - priceSumData.PriceSum, nextBuyerGold);

            previousStates = nextState;
        }
Example #22
0
 .ToImmutableDictionary(
     pair => pair.Item1,
     pair => FungibleAssetValue.FromRawValue(pair.Item1, (BigInteger)pair.Item2)
     )
 );
Example #23
0
        public void Execute(
            ItemType itemType,
            string guid,
            int itemCount,
            int inventoryCount,
            int expectedCount,
            bool fromPreviousAction
            )
        {
            var           avatarState = _initialState.GetAvatarState(_avatarAddress);
            ITradableItem tradableItem;
            var           itemId            = new Guid(guid);
            var           orderId           = Guid.NewGuid();
            var           updateSellOrderId = Guid.NewGuid();
            ItemSubType   itemSubType;
            const long    requiredBlockIndex = Order.ExpirationInterval;

            switch (itemType)
            {
            case ItemType.Equipment:
            {
                var itemUsable = ItemFactory.CreateItemUsable(
                    _tableSheets.EquipmentItemSheet.First,
                    itemId,
                    requiredBlockIndex);
                tradableItem = itemUsable;
                itemSubType  = itemUsable.ItemSubType;
                break;
            }

            case ItemType.Costume:
            {
                var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, itemId);
                costume.Update(requiredBlockIndex);
                tradableItem = costume;
                itemSubType  = costume.ItemSubType;
                break;
            }

            default:
            {
                var material = ItemFactory.CreateTradableMaterial(
                    _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass));
                itemSubType = material.ItemSubType;
                material.RequiredBlockIndex = requiredBlockIndex;
                tradableItem = material;
                break;
            }
            }

            var shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId);
            var shopState          = new ShardedShopStateV2(shardedShopAddress);
            var order = OrderFactory.Create(
                _agentAddress,
                _avatarAddress,
                orderId,
                new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0),
                tradableItem.TradableId,
                requiredBlockIndex,
                itemSubType,
                itemCount
                );

            var orderDigestList = new OrderDigestListState(OrderDigestListState.DeriveAddress(_avatarAddress));
            var prevState       = _initialState;

            if (inventoryCount > 1)
            {
                for (int i = 0; i < inventoryCount; i++)
                {
                    // Different RequiredBlockIndex for divide inventory slot.
                    if (tradableItem is ITradableFungibleItem tradableFungibleItem)
                    {
                        var tradable = (TradableMaterial)tradableFungibleItem.Clone();
                        tradable.RequiredBlockIndex = tradableItem.RequiredBlockIndex - i;
                        avatarState.inventory.AddItem(tradable, 2 - i);
                    }
                }
            }
            else
            {
                avatarState.inventory.AddItem((ItemBase)tradableItem, itemCount);
            }

            var sellItem    = order.Sell(avatarState);
            var orderDigest = order.Digest(avatarState, _tableSheets.CostumeStatSheet);

            shopState.Add(orderDigest, requiredBlockIndex);
            orderDigestList.Add(orderDigest);

            Assert.True(avatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out _));

            Assert.Equal(inventoryCount, avatarState.inventory.Items.Count);
            Assert.Equal(expectedCount, avatarState.inventory.Items.Sum(i => i.count));

            Assert.Single(shopState.OrderDigestList);
            Assert.Single(orderDigestList.OrderDigestList);

            Assert.Equal(requiredBlockIndex * 2, sellItem.RequiredBlockIndex);

            if (fromPreviousAction)
            {
                prevState = prevState.SetState(_avatarAddress, avatarState.Serialize());
            }
            else
            {
                prevState = prevState
                            .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize())
                            .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize())
                            .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize())
                            .SetState(_avatarAddress, avatarState.SerializeV2());
            }

            prevState = prevState
                        .SetState(Addresses.GetItemAddress(itemId), sellItem.Serialize())
                        .SetState(Order.DeriveAddress(order.OrderId), order.Serialize())
                        .SetState(orderDigestList.Address, orderDigestList.Serialize())
                        .SetState(shardedShopAddress, shopState.Serialize());

            var currencyState = prevState.GetGoldCurrency();
            var price         = new FungibleAssetValue(currencyState, ProductPrice, 0);
            var action        = new UpdateSell
            {
                orderId             = orderId,
                updateSellOrderId   = updateSellOrderId,
                tradableId          = itemId,
                sellerAvatarAddress = _avatarAddress,
                itemSubType         = itemSubType,
                price = price,
                count = itemCount,
            };
            var nextState = action.Execute(new ActionContext
            {
                BlockIndex     = 101,
                PreviousStates = prevState,
                Random         = new TestRandom(),
                Rehearsal      = false,
                Signer         = _agentAddress,
            });

            var updateSellShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, updateSellOrderId);
            var nextShopState         = new ShardedShopStateV2((Dictionary)nextState.GetState(updateSellShopAddress));

            Assert.Equal(1, nextShopState.OrderDigestList.Count);
            Assert.NotEqual(orderId, nextShopState.OrderDigestList.First().OrderId);
            Assert.Equal(updateSellOrderId, nextShopState.OrderDigestList.First().OrderId);
            Assert.Equal(itemId, nextShopState.OrderDigestList.First().TradableId);
            Assert.Equal(requiredBlockIndex + 101, nextShopState.OrderDigestList.First().ExpiredBlockIndex);
        }
Example #24
0
        public async Task Transfer(string?memo, bool error)
        {
            NineChroniclesNodeService service = StandaloneContextFx.NineChroniclesNodeService !;
            Currency goldCurrency             = new GoldCurrencyState(
                (Dictionary)BlockChain.GetState(GoldCurrencyState.Address)
                ).Currency;

            Address senderAddress = service.MinerPrivateKey !.ToAddress();
            var     store         = service.Store;
            await BlockChain.MineBlock(service.MinerPrivateKey);

            await BlockChain.MineBlock(service.MinerPrivateKey);

            // 10 + 10 (mining rewards)
            Assert.Equal(
                20 * goldCurrency,
                BlockChain.GetBalance(senderAddress, goldCurrency)
                );

            var     recipientKey = new PrivateKey();
            Address recipient    = recipientKey.ToAddress();
            long    txNonce      = BlockChain.GetNextTxNonce(senderAddress);

            var args = $"recipient: \"{recipient}\", txNonce: {txNonce}, amount: \"17.5\"";

            if (!(memo is null))
            {
                args += $"memo: \"{memo}\"";
            }

            var             query  = $"mutation {{ transfer({args}) }}";
            ExecutionResult result = await ExecuteQueryAsync(query);

            if (error)
            {
                Assert.NotNull(result.Errors);
            }
            else
            {
                Assert.Null(result.Errors);

                var stagedTxIds = BlockChain.GetStagedTransactionIds().ToImmutableList();
                Assert.Single(stagedTxIds);
                string transferTxIdString = stagedTxIds.Single().ToString();
                TxId   transferTxId       = new TxId(ByteUtil.ParseHex(transferTxIdString));

                Transaction <NCAction>?tx = BlockChain.StagePolicy.Get(BlockChain, transferTxId, false);
                Assert.NotNull(tx);
                Assert.IsType <TransferAsset>(tx !.Actions.Single().InnerAction);
                TransferAsset transferAsset = (TransferAsset)tx.Actions.Single().InnerAction;
                Assert.Equal(memo, transferAsset.Memo);

                var expectedResult = new Dictionary <string, object>
                {
                    ["transfer"] = transferTxIdString,
                };

                Assert.Equal(expectedResult, result.Data);

                await BlockChain.MineBlock(recipientKey);

                // 10 + 10 - 17.5(transfer)
                Assert.Equal(
                    FungibleAssetValue.Parse(goldCurrency, "2.5"),
                    BlockChain.GetBalance(senderAddress, goldCurrency)
                    );

                // 0 + 17.5(transfer) + 10(mining reward)
                Assert.Equal(
                    FungibleAssetValue.Parse(goldCurrency, "27.5"),
                    BlockChain.GetBalance(recipient, goldCurrency)
                    );
            }
        }
Example #25
0
 public SellerResult(Bencodex.Types.Dictionary serialized) : base(serialized)
 {
     shopItem = new ShopItem((Bencodex.Types.Dictionary)serialized["shopItem"]);
     id       = serialized["id"].ToGuid();
     gold     = serialized["gold"].ToFungibleAssetValue();
 }
Example #26
0
File: Buy.cs Project: x86chi/lib9c
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx    = context;
            var            states = ctx.PreviousStates;

            if (ctx.Rehearsal)
            {
                states = states
                         .SetState(buyerAvatarAddress, MarkChanged)
                         .SetState(ctx.Signer, MarkChanged)
                         .SetState(sellerAvatarAddress, MarkChanged)
                         .MarkBalanceChanged(
                    GoldCurrencyMock,
                    ctx.Signer,
                    sellerAgentAddress,
                    GoldCurrencyState.Address);
                return(states.SetState(ShopState.Address, MarkChanged));
            }

            if (ctx.Signer.Equals(sellerAgentAddress))
            {
                return(LogError(context, "Aborted as the signer is the seller."));
            }

            var sw = new Stopwatch();

            sw.Start();
            var started = DateTimeOffset.UtcNow;

            Log.Debug("Buy exec started.");

            if (!states.TryGetAgentAvatarStates(ctx.Signer, buyerAvatarAddress, out var buyerAgentState, out var buyerAvatarState))
            {
                return(LogError(context, "Aborted as the avatar state of the buyer was failed to load."));
            }
            sw.Stop();
            Log.Debug("Buy Get Buyer AgentAvatarStates: {Elapsed}", sw.Elapsed);
            sw.Restart();

            if (!buyerAvatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world))
            {
                return(LogError(context, "Aborted as the WorldInformation was failed to load."));
            }

            if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInShop)
            {
                // 스테이지 클리어 부족 에러.
                return(LogError(
                           context,
                           "Aborted as the signer is not cleared the minimum stage level required to use shop yet: {ClearedLevel} < {RequiredLevel}.",
                           world.StageClearedId,
                           GameConfig.RequireClearedStageLevel.ActionsInShop
                           ));
            }

            if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary d))
            {
                return(LogError(context, "Aborted as the shop state was failed to load."));
            }

            var shopState = new ShopState(d);

            sw.Stop();
            Log.Debug("Buy Get ShopState: {Elapsed}", sw.Elapsed);
            sw.Restart();

            Log.Debug("Execute Buy; buyer: {Buyer} seller: {Seller}", buyerAvatarAddress, sellerAvatarAddress);
            // 상점에서 구매할 아이템을 찾는다.
            if (!shopState.TryGet(sellerAgentAddress, productId, out var shopItem))
            {
                return(LogError(
                           context,
                           "Aborted as the shop item ({ProductId}) was failed to get from the seller agent ({SellerAgent}).",
                           productId,
                           sellerAgentAddress
                           ));
            }
            sw.Stop();
            Log.Debug($"Buy Get Item: {sw.Elapsed}");
            sw.Restart();

            if (!states.TryGetAgentAvatarStates(sellerAgentAddress, sellerAvatarAddress, out var sellerAgentState, out var sellerAvatarState))
            {
                return(LogError(
                           context,
                           "Aborted as the seller agent/avatar was filed to load from {SellerAgent}/{SellerAvatar}.",
                           sellerAgentAddress,
                           sellerAvatarAddress
                           ));
            }
            sw.Stop();
            Log.Debug($"Buy Get Seller AgentAvatarStates: {sw.Elapsed}");
            sw.Restart();

            // 돈은 있냐?
            FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency());

            if (buyerBalance < shopItem.Price)
            {
                return(LogError(
                           context,
                           "Aborted as the buyer ({Buyer}) has no sufficient gold: {BuyerBalance} < {ItemPrice}",
                           ctx.Signer,
                           buyerBalance,
                           shopItem.Price
                           ));
            }

            var tax        = shopItem.Price.DivRem(100, out _) * 8;
            var taxedPrice = shopItem.Price - tax;

            // 세금을 송금한다.
            states = states.TransferAsset(
                context.Signer,
                GoldCurrencyState.Address,
                tax);

            // 구매자의 돈을 판매자에게 송금한다.
            states = states.TransferAsset(
                context.Signer,
                sellerAgentAddress,
                taxedPrice
                );

            // 상점에서 구매할 아이템을 제거한다.
            try
            {
                shopState.Unregister(sellerAgentAddress, shopItem);
            }
            catch (FailedToUnregisterInShopStateException)
            {
                return(LogError(
                           context,
                           "Aborted as the item ({@ProductId}) was failed to unregister from seller ({Seller})'s inventory.",
                           shopItem.ProductId,
                           sellerAgentAddress
                           ));
            }

            // 구매자, 판매자에게 결과 메일 전송
            buyerResult = new BuyerResult
            {
                shopItem   = shopItem,
                itemUsable = shopItem.ItemUsable
            };
            var buyerMail = new BuyerMail(buyerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex);

            buyerResult.id = buyerMail.id;

            sellerResult = new SellerResult
            {
                shopItem   = shopItem,
                itemUsable = shopItem.ItemUsable,
                gold       = taxedPrice
            };
            var sellerMail = new SellerMail(sellerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(),
                                            ctx.BlockIndex);

            sellerResult.id = sellerMail.id;

            buyerAvatarState.Update(buyerMail);
            buyerAvatarState.UpdateFromAddItem(buyerResult.itemUsable, false);
            sellerAvatarState.Update(sellerMail);

            // 퀘스트 업데이트
            buyerAvatarState.questList.UpdateTradeQuest(TradeType.Buy, shopItem.Price);
            sellerAvatarState.questList.UpdateTradeQuest(TradeType.Sell, shopItem.Price);

            var timestamp = DateTimeOffset.UtcNow;

            buyerAvatarState.updatedAt   = timestamp;
            buyerAvatarState.blockIndex  = ctx.BlockIndex;
            sellerAvatarState.updatedAt  = timestamp;
            sellerAvatarState.blockIndex = ctx.BlockIndex;

            buyerAvatarState.UpdateQuestRewards(ctx);
            sellerAvatarState.UpdateQuestRewards(ctx);

            states = states.SetState(sellerAvatarAddress, sellerAvatarState.Serialize());
            sw.Stop();
            Log.Debug("Buy Set Seller AvatarState: {Elapsed}", sw.Elapsed);
            sw.Restart();

            states = states.SetState(buyerAvatarAddress, buyerAvatarState.Serialize());
            sw.Stop();
            Log.Debug("Buy Set Buyer AvatarState: {Elapsed}", sw.Elapsed);
            sw.Restart();

            states = states.SetState(ShopState.Address, shopState.Serialize());
            sw.Stop();
            var ended = DateTimeOffset.UtcNow;

            Log.Debug("Buy Set ShopState: {Elapsed}", sw.Elapsed);
            Log.Debug("Buy Total Executed Time: {Elapsed}", ended - started);

            return(states.SetState(ctx.Signer, buyerAgentState.Serialize()));
        }
Example #27
0
 protected override void LoadPlainValueInternal(IImmutableDictionary <string, IValue> plainValue)
 {
     sellerAvatarAddress = plainValue["sellerAvatarAddress"].ToAddress();
     itemId = plainValue["itemId"].ToGuid();
     price  = plainValue["price"].ToFungibleAssetValue();
 }
Example #28
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx    = context;
            var            states = ctx.PreviousStates;

            if (ctx.Rehearsal)
            {
                return(states.SetState(ctx.Signer, MarkChanged)
                       .SetState(AvatarAddress, MarkChanged)
                       .SetState(WeeklyArenaAddress, MarkChanged)
                       .SetState(ctx.Signer, MarkChanged)
                       .MarkBalanceChanged(GoldCurrencyMock, ctx.Signer, WeeklyArenaAddress));
            }

            CheckObsolete(BlockChain.Policy.BlockPolicySource.V100080ObsoleteIndex, context);

            var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress, EnemyAddress);

            Log.Warning("ranking_battle is deprecated. Please use ranking_battle2");
            if (AvatarAddress.Equals(EnemyAddress))
            {
                throw new InvalidAddressException($"{addressesHex}Aborted as the signer tried to battle for themselves.");
            }

            if (!states.TryGetAgentAvatarStates(
                    ctx.Signer,
                    AvatarAddress,
                    out var agentState,
                    out var avatarState))
            {
                throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load.");
            }

            var costumes = new HashSet <int>(costumeIds);

            avatarState.ValidateEquipments(equipmentIds, context.BlockIndex);
            avatarState.ValidateConsumable(consumableIds, context.BlockIndex);
            avatarState.ValidateCostume(costumes);

            if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world) ||
                world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard)
            {
                throw new NotEnoughClearedStageLevelException(
                          addressesHex,
                          GameConfig.RequireClearedStageLevel.ActionsInRankingBoard,
                          world.StageClearedId);
            }

            avatarState.EquipCostumes(costumes);
            avatarState.EquipEquipments(equipmentIds);

            var enemyAvatarState = states.GetAvatarState(EnemyAddress);

            if (enemyAvatarState is null)
            {
                throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the opponent ({EnemyAddress}) was failed to load.");
            }

            var weeklyArenaState = states.GetWeeklyArenaState(WeeklyArenaAddress);

            if (weeklyArenaState.Ended)
            {
                throw new WeeklyArenaStateAlreadyEndedException(
                          addressesHex + WeeklyArenaStateAlreadyEndedException.BaseMessage);
            }

            if (!weeklyArenaState.ContainsKey(AvatarAddress))
            {
                throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, AvatarAddress);
            }

            var arenaInfo = weeklyArenaState[AvatarAddress];

            if (arenaInfo.DailyChallengeCount <= 0)
            {
                throw new NotEnoughWeeklyArenaChallengeCountException(
                          addressesHex + NotEnoughWeeklyArenaChallengeCountException.BaseMessage);
            }

            if (!arenaInfo.Active)
            {
                FungibleAssetValue agentBalance = default;
                try
                {
                    agentBalance = states.GetBalance(ctx.Signer, states.GetGoldCurrency());
                }
                catch (InvalidOperationException)
                {
                    throw new NotEnoughFungibleAssetValueException(addressesHex, EntranceFee, agentBalance);
                }

                if (agentBalance >= new FungibleAssetValue(agentBalance.Currency, EntranceFee, 0))
                {
                    states = states.TransferAsset(
                        ctx.Signer,
                        WeeklyArenaAddress,
                        new FungibleAssetValue(
                            states.GetGoldCurrency(),
                            EntranceFee,
                            0
                            )
                        );
                    arenaInfo.Activate();
                }
                else
                {
                    throw new NotEnoughFungibleAssetValueException(addressesHex, EntranceFee, agentBalance);
                }
            }

            if (!weeklyArenaState.ContainsKey(EnemyAddress))
            {
                throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, EnemyAddress);
            }

            Log.Verbose("{WeeklyArenaStateAddress}", weeklyArenaState.address.ToHex());

            var simulator = new RankingSimulator(
                ctx.Random,
                avatarState,
                enemyAvatarState,
                consumableIds,
                states.GetRankingSimulatorSheets(),
                StageId,
                arenaInfo,
                weeklyArenaState[EnemyAddress]);

            simulator.SimulateV1();

            Result = simulator.Log;

            foreach (var itemBase in simulator.Reward.OrderBy(i => i.Id))
            {
                avatarState.inventory.AddItem2(itemBase);
            }

            return(states
                   .SetState(ctx.Signer, agentState.Serialize())
                   .SetState(WeeklyArenaAddress, weeklyArenaState.Serialize())
                   .SetState(AvatarAddress, avatarState.Serialize()));
        }