From fc636cf94b28efe9157f2e1046d432114aff6435 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 25 Oct 2024 22:06:42 +0800 Subject: [PATCH 1/7] add tree points --- contract/Forest/ForestContractState.cs | 9 ++ contract/Forest/ForestContract_Helpers.cs | 8 ++ contract/Forest/ForestContract_Tree.cs | 122 ++++++++++++++++++++++ contract/Forest/ForestContract_Views.cs | 5 + protobuf/forest_contract.proto | 71 +++++++++++++ 5 files changed, 215 insertions(+) create mode 100644 contract/Forest/ForestContract_Tree.cs diff --git a/contract/Forest/ForestContractState.cs b/contract/Forest/ForestContractState.cs index 7564e55..2d89bfd 100755 --- a/contract/Forest/ForestContractState.cs +++ b/contract/Forest/ForestContractState.cs @@ -66,6 +66,15 @@ public partial class ForestContractState : ContractState public SingletonState MaxBatchCancelOfferCount { get; set; } public SingletonState MaxBatchCancelListCount { get; set; } + + public SingletonState TreePointsHashVerifyKey { get; set; } + /// Address -> TreePointsInfo + public MappedState TreePointsMap { get; set; } + /// Address -> timestamp 13 + public MappedState TreePointsAddTimeMap { get; set; } + /// Address -> activityId -> timestamp 13 + public MappedState TreePointsActivityClaimTimeMap { get; set; } + } } \ No newline at end of file diff --git a/contract/Forest/ForestContract_Helpers.cs b/contract/Forest/ForestContract_Helpers.cs index ca65991..a70999d 100644 --- a/contract/Forest/ForestContract_Helpers.cs +++ b/contract/Forest/ForestContract_Helpers.cs @@ -452,4 +452,12 @@ private void AssertBalanceEnoughForList(string symbol, Address address, long cur var listedAmount = GetEffectiveNFTListedTotalAmount(address, symbol); Assert(currentBalance >= (listedAmount + amount), $"The balance is not enough. Please reset it."); } + + private void CheckPointsRequestHash(string request, string hash) + { + var key = State.TreePointsHashVerifyKey.Value; + Assert(!string.IsNullOrEmpty(key), "Need SetTreePointsHashVerifyKey"); + var requestHash = HashHelper.ComputeFrom(string.Concat(request, key)); + Assert(hash.Equals(requestHash.ToHex()), "Unverified requests"); + } } diff --git a/contract/Forest/ForestContract_Tree.cs b/contract/Forest/ForestContract_Tree.cs new file mode 100644 index 0000000..00347d8 --- /dev/null +++ b/contract/Forest/ForestContract_Tree.cs @@ -0,0 +1,122 @@ +using AElf; +using AElf.Contracts.MultiToken; +using AElf.Sdk.CSharp; +using Google.Protobuf.WellKnownTypes; + +namespace Forest; + +public partial class ForestContract +{ + public override Empty AddTreePoints(AddTreePointsInput input) + { + AssertContractInitialized(); + Assert(input != null, "Invalid param"); + Assert(!input.Address.Value.IsNullOrEmpty(), "Invalid param Address"); + Assert(input.Points > 0, "Invalid param Points"); + Assert(!string.IsNullOrEmpty(input.RequestHash), "Invalid param RequestHash"); + Assert(input.OpTime != null && input.OpTime > 0, "Invalid param OpTime"); + Assert(Context.Sender == input.Address, "Param Address is not Sender"); + + var requestStr = string.Concat(input.Address, input.Points, input.PointsType, input.OpTime); + CheckPointsRequestHash(requestStr, input.RequestHash); + + var lastAddTime = State.TreePointsAddTimeMap[input.Address]; + Assert(input.OpTime > lastAddTime, "Invalid param OpTime"); + + var treePointsInfo = State.TreePointsMap[input.Address]; + if (treePointsInfo != null) + { + treePointsInfo.Points += input.Points; + } + else + { + treePointsInfo = new TreePointsInfo() + { + Owner = input.Address, + Points = input.PointsType + }; + } + + State.TreePointsMap[input.Address] = treePointsInfo; + State.TreePointsAddTimeMap[input.Address] = input.OpTime; + + //event + Context.Fire(new TreePointsAdded() + { + Owner = input.Address, + Points = input.Points, + PointsType = input.PointsType, + OpTime = input.OpTime, + TotalPoints = treePointsInfo.Points, + RequestHash = input.RequestHash + }); + return new Empty(); + } + public override Empty ClaimTreePoints(ClaimTreePointsInput input) + { + AssertContractInitialized(); + Assert(input != null, "Invalid param"); + Assert(!input.Address.Value.IsNullOrEmpty(), "Invalid param Address"); + Assert(input.Points > 0, "Invalid param Points"); + Assert(!string.IsNullOrEmpty(input.ActivityId), "Invalid param ActivityId"); + Assert(!string.IsNullOrEmpty(input.RequestHash), "Invalid param RequestHash"); + Assert(input.OpTime != null && input.OpTime > 0, "Invalid param OpTime"); + Assert(input.Reward != null && !string.IsNullOrEmpty(input.Reward.Symbol) && input.Reward.Amount > 0, "Param Address is not Sender"); + Assert(Context.Sender == input.Address, "Param Address is not Sender"); + + var requestStr = string.Concat(input.Address, input.Points, input.PointsType, input.OpTime, input.ActivityId, input.Reward.Symbol, input.Reward.Amount); + CheckPointsRequestHash(requestStr, input.RequestHash); + + var lastOpTime = State.TreePointsActivityClaimTimeMap[input.Address][input.ActivityId]; + Assert(input.OpTime > lastOpTime, "Invalid param OpTime"); + + var treePointsInfo = State.TreePointsMap[input.Address]; + Assert(treePointsInfo != null, "your points is zero"); + Assert(treePointsInfo.Points >= input.Points, "You don't have enough points"); + treePointsInfo.Points -= input.Points; + State.TreePointsMap[input.Address] = treePointsInfo; + State.TreePointsActivityClaimTimeMap[input.Address][input.ActivityId] = input.OpTime; + + //transfer award + var balance = State.TokenContract.GetBalance.Call(new GetBalanceInput + { + Symbol = input.Reward.Symbol, + Owner = Context.Self + }); + Assert(balance.Balance >= input.Reward.Amount,$"The platform does not have enough {input.Reward.Symbol}"); + State.TokenContract.Transfer.Send(new TransferInput + { + To = input.Address, + Symbol = input.Reward.Symbol, + Amount = input.Reward.Amount + }); + //event + Context.Fire(new TreePointsRemoved() + { + Owner = input.Address, + Points = input.Points, + PointsType = input.PointsType, + ActivityId = input.ActivityId, + OpTime = input.OpTime, + RewardSymbol = input.Reward.Symbol, + RewardAmount = input.Reward.Amount, + TotalPoints = treePointsInfo.Points, + RequestHash = input.RequestHash + }); + return new Empty(); + } + public override Empty SetTreePointsHashVerifyKey(StringValue input) + { + AssertContractInitialized(); + Assert(input != null && !string.IsNullOrEmpty(input.Value), "Invalid param key"); + var key = State.TreePointsHashVerifyKey.Value; + if (string.IsNullOrEmpty(key)) + { + State.TreePointsHashVerifyKey.Value = input.Value; + return new Empty(); + } + AssertSenderIsAdmin(); + State.TreePointsHashVerifyKey.Value = input.Value; + return new Empty(); + } +} \ No newline at end of file diff --git a/contract/Forest/ForestContract_Views.cs b/contract/Forest/ForestContract_Views.cs index 64315c2..00843df 100644 --- a/contract/Forest/ForestContract_Views.cs +++ b/contract/Forest/ForestContract_Views.cs @@ -156,4 +156,9 @@ public override Int32Value GetMaxBatchCancelListCount(Empty input) { return new Int32Value { Value = State.MaxBatchCancelListCount.Value }; } + + public override TreePointsInfo GetTreePoints(Address input) + { + return State.TreePointsMap[input]; + } } \ No newline at end of file diff --git a/protobuf/forest_contract.proto b/protobuf/forest_contract.proto index fbb3026..034f3dd 100755 --- a/protobuf/forest_contract.proto +++ b/protobuf/forest_contract.proto @@ -146,6 +146,19 @@ service ForestContract { rpc GetMaxBatchCancelListCount(google.protobuf.Empty) returns (google.protobuf.Int32Value){ option (aelf.is_view) = true; } + + rpc AddTreePoints(AddTreePointsInput) returns (google.protobuf.Empty){ + } + + rpc ClaimTreePoints(ClaimTreePointsInput) returns (google.protobuf.Empty){ + } + + rpc GetTreePoints(aelf.Address) returns (TreePointsInfo){ + option (aelf.is_view) = true; + } + + rpc SetTreePointsHashVerifyKey(google.protobuf.StringValue)returns (google.protobuf.Empty){ + } } // Structs. @@ -234,6 +247,11 @@ message WhitelistInfoList{ repeated WhitelistInfo whitelists = 1; } +message TreePointsInfo { + aelf.Address owner = 1; + int64 points = 2; + +} // Inputs. message InitializeInput { @@ -460,6 +478,35 @@ message CreateArtInfo { Price cost_price = 8; string painting_style = 9; } +message AddTreePointsInput { + aelf.Address address = 1; + int64 points = 2; + int32 points_type =3; + int64 op_time = 4; + string request_hash = 5; +} + +message ClaimTreePointsInput { + aelf.Address address = 1; + int64 points = 2; + int32 points_type =3; + string activity_id = 4; + int64 op_time = 5; + TreeReward reward = 6; + string request_hash = 7; +} + +message TreeReward { + string symbol = 1; + int64 amount = 2; +} + +enum TreePointsType { + NORMALONE = 0; + NORMALTWO = 1; + INVITE = 2; +} + // Events @@ -631,3 +678,27 @@ message ArtCreated { string painting_style = 9; } +message TreePointsAdded { + option (aelf.is_event) = true; + aelf.Address owner = 1; + int64 points = 2; + int32 points_type = 3; + int64 op_time = 4; + int64 total_points = 5; + string request_hash = 6; +} + +message TreePointsRemoved { + option (aelf.is_event) = true; + aelf.Address owner = 1; + int64 points = 2; + int32 points_type = 3; + string activity_id = 4; + int64 op_time = 5; + string reward_symbol = 6; + int64 reward_amount = 7; + int64 total_points = 8; + string request_hash = 9; +} + + From 7181e43d17a6744b9996091cfc91ef8f5c68148a Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 29 Oct 2024 10:17:24 +0800 Subject: [PATCH 2/7] add rpc TreeLevelUpgrade --- contract/Forest/ForestContractState.cs | 2 ++ contract/Forest/ForestContract_Tree.cs | 40 ++++++++++++++++++++++++-- protobuf/forest_contract.proto | 37 ++++++++++++++++++------ 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/contract/Forest/ForestContractState.cs b/contract/Forest/ForestContractState.cs index 2d89bfd..134ebcf 100755 --- a/contract/Forest/ForestContractState.cs +++ b/contract/Forest/ForestContractState.cs @@ -74,6 +74,8 @@ public partial class ForestContractState : ContractState public MappedState TreePointsAddTimeMap { get; set; } /// Address -> activityId -> timestamp 13 public MappedState TreePointsActivityClaimTimeMap { get; set; } + /// Address -> timestamp 13 + public MappedState TreePointsLevelUpgradeTimeMap { get; set; } } diff --git a/contract/Forest/ForestContract_Tree.cs b/contract/Forest/ForestContract_Tree.cs index 00347d8..72b0518 100644 --- a/contract/Forest/ForestContract_Tree.cs +++ b/contract/Forest/ForestContract_Tree.cs @@ -91,11 +91,10 @@ public override Empty ClaimTreePoints(ClaimTreePointsInput input) Amount = input.Reward.Amount }); //event - Context.Fire(new TreePointsRemoved() + Context.Fire(new TreePointsClaimed() { Owner = input.Address, Points = input.Points, - PointsType = input.PointsType, ActivityId = input.ActivityId, OpTime = input.OpTime, RewardSymbol = input.Reward.Symbol, @@ -119,4 +118,41 @@ public override Empty SetTreePointsHashVerifyKey(StringValue input) State.TreePointsHashVerifyKey.Value = input.Value; return new Empty(); } + + public override Empty TreeLevelUpgrade(TreeLevelUpgradeInput input) + { + AssertContractInitialized(); + Assert(input != null, "Invalid param"); + Assert(!input.Address.Value.IsNullOrEmpty(), "Invalid param Address"); + Assert(input.Points > 0, "Invalid param Points"); + Assert(!string.IsNullOrEmpty(input.RequestHash), "Invalid param RequestHash"); + Assert(input.OpTime != null && input.OpTime > 0, "Invalid param OpTime"); + Assert(input.UpgradeLevel > 0, $"Invalid UpgradeLevel, Should be greater than 0"); + Assert(Context.Sender == input.Address, "Param Address is not Sender"); + + var requestStr = string.Concat(input.Address, input.Points, input.OpTime, input.UpgradeLevel); + CheckPointsRequestHash(requestStr, input.RequestHash); + + var lastOpTime = State.TreePointsLevelUpgradeTimeMap[input.Address]; + Assert(input.OpTime > lastOpTime, "Invalid param OpTime"); + + var treePointsInfo = State.TreePointsMap[input.Address]; + Assert(treePointsInfo != null, "your points is zero"); + Assert(treePointsInfo.Points >= input.Points, "You don't have enough points"); + treePointsInfo.Points -= input.Points; + State.TreePointsMap[input.Address] = treePointsInfo; + State.TreePointsLevelUpgradeTimeMap[input.Address] = input.OpTime; + + //event + Context.Fire(new TreeLevelUpgraded() + { + Owner = input.Address, + Points = input.Points, + OpTime = input.OpTime, + UpgradeLevel = input.UpgradeLevel, + TotalPoints = treePointsInfo.Points, + RequestHash = input.RequestHash + }); + return new Empty(); + } } \ No newline at end of file diff --git a/protobuf/forest_contract.proto b/protobuf/forest_contract.proto index 034f3dd..014c4a6 100755 --- a/protobuf/forest_contract.proto +++ b/protobuf/forest_contract.proto @@ -153,6 +153,9 @@ service ForestContract { rpc ClaimTreePoints(ClaimTreePointsInput) returns (google.protobuf.Empty){ } + rpc TreeLevelUpgrade(TreeLevelUpgradeInput) returns (google.protobuf.Empty){ + } + rpc GetTreePoints(aelf.Address) returns (TreePointsInfo){ option (aelf.is_view) = true; } @@ -496,6 +499,14 @@ message ClaimTreePointsInput { string request_hash = 7; } +message TreeLevelUpgradeInput { + aelf.Address address = 1; + int64 points = 2; + int64 op_time = 3; + int32 upgrade_level = 4; + string request_hash = 5; +} + message TreeReward { string symbol = 1; int64 amount = 2; @@ -688,17 +699,27 @@ message TreePointsAdded { string request_hash = 6; } -message TreePointsRemoved { +message TreePointsClaimed { option (aelf.is_event) = true; aelf.Address owner = 1; int64 points = 2; - int32 points_type = 3; - string activity_id = 4; - int64 op_time = 5; - string reward_symbol = 6; - int64 reward_amount = 7; - int64 total_points = 8; - string request_hash = 9; + string activity_id = 3; + int64 op_time = 4; + string reward_symbol = 5; + int64 reward_amount = 6; + int64 total_points = 7; + string request_hash = 8; +} + +message TreeLevelUpgraded { + option (aelf.is_event) = true; + aelf.Address owner = 1; + int64 points = 2; + int32 upgrade_level = 3; + int64 op_time = 4; + int64 total_points = 5; + string request_hash = 6; } + From 11fa03f873eb5eccbf4d192de9be28c9bb380cf6 Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 31 Oct 2024 10:17:56 +0800 Subject: [PATCH 3/7] fix build issue --- contract/Forest/ForestContract_Tree.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contract/Forest/ForestContract_Tree.cs b/contract/Forest/ForestContract_Tree.cs index 72b0518..1e3b4f9 100644 --- a/contract/Forest/ForestContract_Tree.cs +++ b/contract/Forest/ForestContract_Tree.cs @@ -64,7 +64,8 @@ public override Empty ClaimTreePoints(ClaimTreePointsInput input) Assert(input.Reward != null && !string.IsNullOrEmpty(input.Reward.Symbol) && input.Reward.Amount > 0, "Param Address is not Sender"); Assert(Context.Sender == input.Address, "Param Address is not Sender"); - var requestStr = string.Concat(input.Address, input.Points, input.PointsType, input.OpTime, input.ActivityId, input.Reward.Symbol, input.Reward.Amount); + var requestStr = string.Concat(input.Address, input.Points, input.PointsType, input.OpTime); + requestStr = string.Concat(requestStr, input.ActivityId, input.Reward.Symbol, input.Reward.Amount); CheckPointsRequestHash(requestStr, input.RequestHash); var lastOpTime = State.TreePointsActivityClaimTimeMap[input.Address][input.ActivityId]; From bfad3a127b6b69040c6dd2c55084e34ded0706ff Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 1 Nov 2024 16:23:22 +0800 Subject: [PATCH 4/7] add test --- .../ForestContractTests_TreePoints.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 test/Forest.Tests/ForestContractTests_TreePoints.cs diff --git a/test/Forest.Tests/ForestContractTests_TreePoints.cs b/test/Forest.Tests/ForestContractTests_TreePoints.cs new file mode 100644 index 0000000..0b83287 --- /dev/null +++ b/test/Forest.Tests/ForestContractTests_TreePoints.cs @@ -0,0 +1,47 @@ +using System.Threading.Tasks; +using Xunit; + +namespace Forest; + +public class ForestContractTests_TreePoints : ForestContractTestBase +{ + private const string NftSymbol = "TESTNFT-1"; + private const string NftSymbol2 = "TESTNFT-2"; + private const string ElfSymbol = "ELF"; + private const int ServiceFeeRate = 1000; // 10% + private const int AIServiceFee = 10000000; + private const string DefaultAIImageSize1024 = "1024x1024"; + private const string DefaultAIImageSize512 = "512x512"; + private const string DefaultAIImageSize256 = "256x256"; + + private async Task InitializeForestContract() + { + await AdminForestContractStub.Initialize.SendAsync(new InitializeInput + { + ServiceFeeReceiver = MarketServiceFeeReceiverAddress, + ServiceFeeRate = ServiceFeeRate, + WhitelistContractAddress = WhitelistContractAddress + }); + + // await AdminForestContractStub.SetWhitelistContract.SendAsync(WhitelistContractAddress); + } + + private static Price Elf(long amunt) + { + return new Price() + { + Symbol = ElfSymbol, + Amount = amunt + }; + } + + [Fact] + //cancel offer + public async void SetAIServiceFee_Test() + { + await InitializeForestContract(); + + + } + +} \ No newline at end of file From a725801c3a1d130fccc4e0156a3f9bdc420e7037 Mon Sep 17 00:00:00 2001 From: suerlych <19871208@lhcyf> Date: Fri, 1 Nov 2024 22:44:38 +0800 Subject: [PATCH 5/7] add ut test --- contract/Forest/ForestContract_Tree.cs | 14 +- protobuf/forest_contract.proto | 9 +- .../ForestContractTests_TreePoints.cs | 166 +++++++++++++++++- 3 files changed, 173 insertions(+), 16 deletions(-) diff --git a/contract/Forest/ForestContract_Tree.cs b/contract/Forest/ForestContract_Tree.cs index 1e3b4f9..6a6290b 100644 --- a/contract/Forest/ForestContract_Tree.cs +++ b/contract/Forest/ForestContract_Tree.cs @@ -17,7 +17,7 @@ public override Empty AddTreePoints(AddTreePointsInput input) Assert(input.OpTime != null && input.OpTime > 0, "Invalid param OpTime"); Assert(Context.Sender == input.Address, "Param Address is not Sender"); - var requestStr = string.Concat(input.Address, input.Points, input.PointsType, input.OpTime); + var requestStr = string.Concat(input.Address.ToBase58(), input.Points, input.PointsType, input.OpTime); CheckPointsRequestHash(requestStr, input.RequestHash); var lastAddTime = State.TreePointsAddTimeMap[input.Address]; @@ -33,7 +33,7 @@ public override Empty AddTreePoints(AddTreePointsInput input) treePointsInfo = new TreePointsInfo() { Owner = input.Address, - Points = input.PointsType + Points = input.Points }; } @@ -57,15 +57,15 @@ public override Empty ClaimTreePoints(ClaimTreePointsInput input) AssertContractInitialized(); Assert(input != null, "Invalid param"); Assert(!input.Address.Value.IsNullOrEmpty(), "Invalid param Address"); - Assert(input.Points > 0, "Invalid param Points"); + Assert(input.Points >= 0, "Invalid param Points"); Assert(!string.IsNullOrEmpty(input.ActivityId), "Invalid param ActivityId"); Assert(!string.IsNullOrEmpty(input.RequestHash), "Invalid param RequestHash"); Assert(input.OpTime != null && input.OpTime > 0, "Invalid param OpTime"); - Assert(input.Reward != null && !string.IsNullOrEmpty(input.Reward.Symbol) && input.Reward.Amount > 0, "Param Address is not Sender"); + Assert(input.Reward != null && !string.IsNullOrEmpty(input.Reward.Symbol) && input.Reward.Amount > 0, "Invalid param Reward"); Assert(Context.Sender == input.Address, "Param Address is not Sender"); - var requestStr = string.Concat(input.Address, input.Points, input.PointsType, input.OpTime); - requestStr = string.Concat(requestStr, input.ActivityId, input.Reward.Symbol, input.Reward.Amount); + var requestStr = string.Concat(input.Address.ToBase58(), input.ActivityId,input.Points, input.OpTime); + requestStr = string.Concat(requestStr, input.Reward.Symbol, input.Reward.Amount); CheckPointsRequestHash(requestStr, input.RequestHash); var lastOpTime = State.TreePointsActivityClaimTimeMap[input.Address][input.ActivityId]; @@ -131,7 +131,7 @@ public override Empty TreeLevelUpgrade(TreeLevelUpgradeInput input) Assert(input.UpgradeLevel > 0, $"Invalid UpgradeLevel, Should be greater than 0"); Assert(Context.Sender == input.Address, "Param Address is not Sender"); - var requestStr = string.Concat(input.Address, input.Points, input.OpTime, input.UpgradeLevel); + var requestStr = string.Concat(input.Address.ToBase58(), input.Points, input.OpTime, input.UpgradeLevel); CheckPointsRequestHash(requestStr, input.RequestHash); var lastOpTime = State.TreePointsLevelUpgradeTimeMap[input.Address]; diff --git a/protobuf/forest_contract.proto b/protobuf/forest_contract.proto index 014c4a6..f932c50 100755 --- a/protobuf/forest_contract.proto +++ b/protobuf/forest_contract.proto @@ -492,11 +492,10 @@ message AddTreePointsInput { message ClaimTreePointsInput { aelf.Address address = 1; int64 points = 2; - int32 points_type =3; - string activity_id = 4; - int64 op_time = 5; - TreeReward reward = 6; - string request_hash = 7; + string activity_id = 3; + int64 op_time = 4; + TreeReward reward = 5; + string request_hash = 6; } message TreeLevelUpgradeInput { diff --git a/test/Forest.Tests/ForestContractTests_TreePoints.cs b/test/Forest.Tests/ForestContractTests_TreePoints.cs index 0b83287..6c24acb 100644 --- a/test/Forest.Tests/ForestContractTests_TreePoints.cs +++ b/test/Forest.Tests/ForestContractTests_TreePoints.cs @@ -1,4 +1,8 @@ using System.Threading.Tasks; +using AElf.Contracts.MultiToken; +using AElf.Types; +using Google.Protobuf.WellKnownTypes; +using Shouldly; using Xunit; namespace Forest; @@ -23,7 +27,7 @@ await AdminForestContractStub.Initialize.SendAsync(new InitializeInput WhitelistContractAddress = WhitelistContractAddress }); - // await AdminForestContractStub.SetWhitelistContract.SendAsync(WhitelistContractAddress); + await AdminForestContractStub.SetWhitelistContract.SendAsync(WhitelistContractAddress); } private static Price Elf(long amunt) @@ -36,12 +40,166 @@ private static Price Elf(long amunt) } [Fact] - //cancel offer - public async void SetAIServiceFee_Test() + public async void AddTreePoints_Test() { await InitializeForestContract(); + await ForestContractStub.SetTreePointsHashVerifyKey.SendAsync(new StringValue(){Value = "1a2b3c"}); + var points = await ForestContractStub.GetTreePoints.CallAsync(DefaultAddress); + points.Points.ShouldBe(0); + await ForestContractStub.AddTreePoints.SendAsync(new AddTreePointsInput() + { + Address = DefaultAddress, + Points = 10, + PointsType = 0, + OpTime = 1730454324136, + RequestHash = "e1320b97e10afc3a37ff3df54ef811ba97b77da39972ba383b2c351bd656e4cc" + }); + points = await ForestContractStub.GetTreePoints.CallAsync(DefaultAddress); + points.Points.ShouldBe(10); + var addResult = await ForestContractStub.AddTreePoints.SendWithExceptionAsync(new AddTreePointsInput() + { + Address = DefaultAddress, + Points = 10, + PointsType = 0, + OpTime = 1730454324136, + RequestHash = "e1320b97e10afc3a37ff3df54ef811ba97b77da39972ba383b2c351bd656e4cc" + }); + addResult.TransactionResult.Error.ShouldContain("Invalid param OpTime"); + } + [Fact] + public async void TreeLevelUpgrade_Test() + { + await InitializeForestContract(); + await ForestContractStub.SetTreePointsHashVerifyKey.SendAsync(new StringValue(){Value = "1a2b3c"}); + var points = await ForestContractStub.GetTreePoints.CallAsync(DefaultAddress); + points.Points.ShouldBe(0); + var result = await ForestContractStub.TreeLevelUpgrade.SendWithExceptionAsync(new TreeLevelUpgradeInput() + { + Address = DefaultAddress, + Points = 100, + UpgradeLevel = 2, + OpTime = 1730462882565, + RequestHash = "d3a274f226217fc6a18c250df41f10ae8fadc30d5e933dcdad1a75a51e1d26b7" + }); + result.TransactionResult.Error.ShouldContain("your points is zero"); + + await ForestContractStub.AddTreePoints.SendAsync(new AddTreePointsInput() + { + Address = DefaultAddress, + Points = 110, + PointsType = 0, + OpTime = 1730454324136, + RequestHash = "5494bedf4cb1d69920b17d89fbc1d6c5f18e46476b347ff8aa7c843d7faf7183" + }); + + points = await ForestContractStub.GetTreePoints.CallAsync(DefaultAddress); + points.Points.ShouldBe(110); + + result = await ForestContractStub.TreeLevelUpgrade.SendAsync(new TreeLevelUpgradeInput() + { + Address = DefaultAddress, + Points = 100, + UpgradeLevel = 2, + OpTime = 1730462882565, + RequestHash = "d3a274f226217fc6a18c250df41f10ae8fadc30d5e933dcdad1a75a51e1d26b7" + }); + points = await ForestContractStub.GetTreePoints.CallAsync(DefaultAddress); + points.Points.ShouldBe(10); + + result = await ForestContractStub.TreeLevelUpgrade.SendWithExceptionAsync(new TreeLevelUpgradeInput() + { + Address = DefaultAddress, + Points = 100, + UpgradeLevel = 2, + OpTime = 1730462882565, + RequestHash = "d3a274f226217fc6a18c250df41f10ae8fadc30d5e933dcdad1a75a51e1d26b7" + }); + result.TransactionResult.Error.ShouldContain("Invalid param OpTime"); + } + + [Fact] + public async void ClaimTreePoints_Test() + { + await InitializeForestContract(); + await ForestContractStub.SetTreePointsHashVerifyKey.SendAsync(new StringValue(){Value = "1a2b3c"}); + { + //transfer balance + await TokenContractStub.Transfer.SendAsync(new TransferInput() + { + To = ForestContractAddress, + Symbol = ElfSymbol, + Amount = 10000000000 + }); + + var nftBalance = await UserTokenContractStub.GetBalance.CallAsync(new GetBalanceInput() + { + Symbol = ElfSymbol, + Owner = ForestContractAddress + }); + nftBalance.Balance.ShouldBe(10000000000); + } + + + var points = await ForestContractStub.GetTreePoints.CallAsync(DefaultAddress); + points.Points.ShouldBe(0); + var result = await ForestContractStub.ClaimTreePoints.SendWithExceptionAsync(new ClaimTreePointsInput() + { + Address = DefaultAddress, + Points = 0, + ActivityId = "f9235130fda7230d9084374562248961d21e18e8445071b56e0630488623202e", + OpTime = 1730471642155, + Reward = new TreeReward() + { + Symbol = "ELF", + Amount = 1000000000 + }, + RequestHash = "41f1cd1ad97ea5bc7cf2513b805b899750b2d879cdf6fa9d8802b39e62f95093" + }); + result.TransactionResult.Error.ShouldContain("your points is zero"); + + await ForestContractStub.AddTreePoints.SendAsync(new AddTreePointsInput() + { + Address = DefaultAddress, + Points = 110, + PointsType = 0, + OpTime = 1730454324136, + RequestHash = "5494bedf4cb1d69920b17d89fbc1d6c5f18e46476b347ff8aa7c843d7faf7183" + }); + + points = await ForestContractStub.GetTreePoints.CallAsync(DefaultAddress); + points.Points.ShouldBe(110); + + result = await ForestContractStub.ClaimTreePoints.SendAsync(new ClaimTreePointsInput() + { + Address = DefaultAddress, + Points = 0, + ActivityId = "f9235130fda7230d9084374562248961d21e18e8445071b56e0630488623202e", + Reward = new TreeReward() + { + Symbol = "ELF", + Amount = 1000000000 + }, + OpTime = 1730471642155, + RequestHash = "41f1cd1ad97ea5bc7cf2513b805b899750b2d879cdf6fa9d8802b39e62f95093" + }); + points = await ForestContractStub.GetTreePoints.CallAsync(DefaultAddress); + points.Points.ShouldBe(110); + + result = await ForestContractStub.ClaimTreePoints.SendWithExceptionAsync(new ClaimTreePointsInput() + { + Address = DefaultAddress, + Points = 0, + ActivityId = "f9235130fda7230d9084374562248961d21e18e8445071b56e0630488623202e", + Reward = new TreeReward() + { + Symbol = "ELF", + Amount = 1000000000 + }, + OpTime = 1730471642155, + RequestHash = "41f1cd1ad97ea5bc7cf2513b805b899750b2d879cdf6fa9d8802b39e62f95093" + }); + result.TransactionResult.Error.ShouldContain("Invalid param OpTime"); } - } \ No newline at end of file From e7c0e1ef1c0496cd94216526c20b8c488db36ff9 Mon Sep 17 00:00:00 2001 From: suerlych <19871208@lhcyf> Date: Tue, 5 Nov 2024 16:56:39 +0800 Subject: [PATCH 6/7] add activityID Assert --- contract/Forest/ForestContract_Tree.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/contract/Forest/ForestContract_Tree.cs b/contract/Forest/ForestContract_Tree.cs index 6a6290b..12d1b46 100644 --- a/contract/Forest/ForestContract_Tree.cs +++ b/contract/Forest/ForestContract_Tree.cs @@ -69,6 +69,7 @@ public override Empty ClaimTreePoints(ClaimTreePointsInput input) CheckPointsRequestHash(requestStr, input.RequestHash); var lastOpTime = State.TreePointsActivityClaimTimeMap[input.Address][input.ActivityId]; + Assert(lastOpTime == 0, "You have already participated in this activity"); Assert(input.OpTime > lastOpTime, "Invalid param OpTime"); var treePointsInfo = State.TreePointsMap[input.Address]; From 8a90e893f5fb333d84f5eec36441bd54ae87e5ec Mon Sep 17 00:00:00 2001 From: suerlych <19871208@lhcyf> Date: Tue, 5 Nov 2024 18:57:46 +0800 Subject: [PATCH 7/7] limit UpgradeLevel --- contract/Forest/ForestContract_Tree.cs | 7 +++++-- protobuf/forest_contract.proto | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/contract/Forest/ForestContract_Tree.cs b/contract/Forest/ForestContract_Tree.cs index 12d1b46..af5ac3b 100644 --- a/contract/Forest/ForestContract_Tree.cs +++ b/contract/Forest/ForestContract_Tree.cs @@ -33,7 +33,8 @@ public override Empty AddTreePoints(AddTreePointsInput input) treePointsInfo = new TreePointsInfo() { Owner = input.Address, - Points = input.Points + Points = input.Points, + Level = 1 }; } @@ -129,7 +130,7 @@ public override Empty TreeLevelUpgrade(TreeLevelUpgradeInput input) Assert(input.Points > 0, "Invalid param Points"); Assert(!string.IsNullOrEmpty(input.RequestHash), "Invalid param RequestHash"); Assert(input.OpTime != null && input.OpTime > 0, "Invalid param OpTime"); - Assert(input.UpgradeLevel > 0, $"Invalid UpgradeLevel, Should be greater than 0"); + Assert(input.UpgradeLevel > 0, "Invalid UpgradeLevel, Should be greater than 0"); Assert(Context.Sender == input.Address, "Param Address is not Sender"); var requestStr = string.Concat(input.Address.ToBase58(), input.Points, input.OpTime, input.UpgradeLevel); @@ -141,6 +142,8 @@ public override Empty TreeLevelUpgrade(TreeLevelUpgradeInput input) var treePointsInfo = State.TreePointsMap[input.Address]; Assert(treePointsInfo != null, "your points is zero"); Assert(treePointsInfo.Points >= input.Points, "You don't have enough points"); + Assert(treePointsInfo.Level < input.UpgradeLevel, $"You are already level{input.UpgradeLevel}"); + treePointsInfo.Points -= input.Points; State.TreePointsMap[input.Address] = treePointsInfo; State.TreePointsLevelUpgradeTimeMap[input.Address] = input.OpTime; diff --git a/protobuf/forest_contract.proto b/protobuf/forest_contract.proto index f932c50..4e6ba47 100755 --- a/protobuf/forest_contract.proto +++ b/protobuf/forest_contract.proto @@ -253,7 +253,7 @@ message WhitelistInfoList{ message TreePointsInfo { aelf.Address owner = 1; int64 points = 2; - + int64 level = 3; } // Inputs.