diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 638b5c27f1..159b4982c3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -40,9 +40,9 @@ jobs: parts: 3 n: 2 codecoverage: true - # - template: templates/build-template-macos.yml - # parameters: - # parts: 3 - # n: 3 - # codecoverage: true + - template: templates/build-template-macos.yml + parameters: + parts: 3 + n: 3 + codecoverage: true \ No newline at end of file diff --git a/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs b/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs index e2a60ab703..199bf1810b 100644 --- a/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs +++ b/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs @@ -13,6 +13,8 @@ public partial class TokenHolderContract : TokenHolderContractImplContainer.Toke { public override Empty CreateScheme(CreateTokenHolderProfitSchemeInput input) { + Assert(State.TokenHolderProfitSchemes[Context.Sender] == null, "Token holder profit scheme already exists."); + if (State.ProfitContract.Value == null) State.ProfitContract.Value = Context.GetContractAddressByName(SmartContractConstants.ProfitContractSystemName); @@ -52,7 +54,7 @@ public override Empty AddBeneficiary(AddTokenHolderBeneficiaryInput input) SchemeId = scheme.SchemeId, Beneficiary = input.Beneficiary }); - shares.Add(detail.Details.Single().Shares); + shares = shares.Add(detail.Details.Single().Shares); } State.ProfitContract.AddBeneficiary.Send(new AddBeneficiaryInput @@ -295,6 +297,6 @@ private void UpdateTokenHolderProfitScheme(ref TokenHolderProfitScheme scheme, A var originScheme = State.ProfitContract.GetScheme.Call(originSchemeId); scheme.SchemeId = originScheme.SchemeId; scheme.Period = originScheme.CurrentPeriod; - State.TokenHolderProfitSchemes[Context.Sender] = scheme; + State.TokenHolderProfitSchemes[manager] = scheme; } } \ No newline at end of file diff --git a/nuget.config b/nuget.config index e75cd52656..c87e46df58 100644 --- a/nuget.config +++ b/nuget.config @@ -4,6 +4,5 @@ - \ No newline at end of file diff --git a/templates/build-template-linux.yml b/templates/build-template-linux.yml index 829eae430a..d93cf28f9b 100644 --- a/templates/build-template-linux.yml +++ b/templates/build-template-linux.yml @@ -18,6 +18,14 @@ jobs: variables: CI_TEST: true steps: + - script: | + echo "=== Disk space before cleanup ===" + df -h / + # Remove pre-installed android sdk to free disk space + sudo rm -rf /usr/local/lib/android || true + echo "=== Disk space after cleanup ===" + df -h / + displayName: 'Free disk space' - task: UseDotNet@2 displayName: 'Install .NET Core SDK' inputs: diff --git a/test/AElf.Contracts.TestContract.Tests/AElf.Contracts.TestContract.Tests.csproj b/test/AElf.Contracts.TestContract.Tests/AElf.Contracts.TestContract.Tests.csproj index b38736f190..bc7f0073e6 100644 --- a/test/AElf.Contracts.TestContract.Tests/AElf.Contracts.TestContract.Tests.csproj +++ b/test/AElf.Contracts.TestContract.Tests/AElf.Contracts.TestContract.Tests.csproj @@ -97,6 +97,7 @@ Contract PreserveNewest + diff --git a/test/AElf.Contracts.TestContract.Tests/PatchedContractSecurityTests.cs b/test/AElf.Contracts.TestContract.Tests/PatchedContractSecurityTests.cs index b2c8df1424..03dcae550d 100644 --- a/test/AElf.Contracts.TestContract.Tests/PatchedContractSecurityTests.cs +++ b/test/AElf.Contracts.TestContract.Tests/PatchedContractSecurityTests.cs @@ -1,10 +1,16 @@ using System.Text; using System.Threading.Tasks; using AElf.Contracts.TestContract.BasicSecurity; +using AElf.Kernel; +using AElf.Kernel.Blockchain.Application; +using AElf.Kernel.SmartContract; +using AElf.Kernel.SmartContract.Application; using AElf.Sdk.CSharp; +using AElf.TestBase; using AElf.Types; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; +using Microsoft.Extensions.DependencyInjection; using Shouldly; using Xunit; using SmartContractConstants = AElf.Kernel.SmartContract.SmartContractConstants; @@ -385,7 +391,7 @@ await TestBasicSecurityContractStub.TestMapped2State.SendAsync(new ProtobufInput } } - [Fact] + [IgnoreOnCIFact] public async Task TestBranchCount() { { @@ -434,7 +440,7 @@ await TestBasicSecurityContractStub.TestForeachInfiniteLoop.SendWithExceptionAsy } } - [Fact] + [IgnoreOnCIFact] public async Task TestMethodCallCount() { { @@ -453,4 +459,130 @@ await TestBasicSecurityContractStub.TestInfiniteRecursiveCallInSeparateClass.Sen txResult.TransactionResult.Error.ShouldContain(nameof(RuntimeCallThresholdExceededException)); } } + + [Fact] + public async Task TestBranchCountWithReducedThreshold() + { + // Get required services + var blockchainService = Application.ServiceProvider.GetRequiredService(); + var executionObserverThresholdProvider = + Application.ServiceProvider.GetRequiredService(); + + // Get current best chain block + var bestChainBlock = await blockchainService.GetBestChainLastBlockHeaderAsync(); + var blockIndex = new BlockIndex + { + BlockHash = bestChainBlock.GetHash(), + BlockHeight = bestChainBlock.Height + }; + + // Set reduced threshold to 5000 + const int reducedThreshold = 5000; + var newThreshold = new ExecutionObserverThreshold + { + ExecutionBranchThreshold = reducedThreshold, + ExecutionCallThreshold = reducedThreshold + }; + await executionObserverThresholdProvider.SetExecutionObserverThresholdAsync(blockIndex, newThreshold); + + // Verify threshold was set correctly + var currentThreshold = executionObserverThresholdProvider.GetExecutionObserverThreshold(blockIndex); + currentThreshold.ExecutionBranchThreshold.ShouldBe(reducedThreshold); + currentThreshold.ExecutionCallThreshold.ShouldBe(reducedThreshold); + + { + await TestBasicSecurityContractStub.TestWhileInfiniteLoop.SendAsync(new Int32Input + { Int32Value = reducedThreshold -1 }); + var txResult = await TestBasicSecurityContractStub.TestWhileInfiniteLoop.SendWithExceptionAsync( + new Int32Input + { Int32Value = reducedThreshold }); + txResult.TransactionResult.Error.ShouldContain(nameof(RuntimeBranchThresholdExceededException)); + } + + { + await TestBasicSecurityContractStub.TestForInfiniteLoop.SendAsync(new Int32Input { Int32Value = reducedThreshold - 1 }); + var txResult = await TestBasicSecurityContractStub.TestForInfiniteLoop.SendWithExceptionAsync( + new Int32Input + { Int32Value = reducedThreshold }); + txResult.TransactionResult.Error.ShouldContain(nameof(RuntimeBranchThresholdExceededException)); + } + + { + await TestBasicSecurityContractStub.TestForInfiniteLoopInSeparateClass.SendAsync(new Int32Input + { Int32Value = reducedThreshold - 1 }); + var txResult = await TestBasicSecurityContractStub.TestForInfiniteLoop.SendWithExceptionAsync( + new Int32Input + { Int32Value = reducedThreshold }); + txResult.TransactionResult.Error.ShouldContain(nameof(RuntimeBranchThresholdExceededException)); + } + + { + await TestBasicSecurityContractStub.TestWhileInfiniteLoopWithState.SendAsync(new Int32Input + { Int32Value = reducedThreshold - 1 }); + var txResult = + await TestBasicSecurityContractStub.TestWhileInfiniteLoopWithState.SendWithExceptionAsync( + new Int32Input + { Int32Value = reducedThreshold }); + txResult.TransactionResult.Error.ShouldContain(nameof(RuntimeBranchThresholdExceededException)); + } + + { + await TestBasicSecurityContractStub.TestForeachInfiniteLoop.SendAsync(new ListInput + { List = { new int[reducedThreshold - 1] } }); + var txResult = + await TestBasicSecurityContractStub.TestForeachInfiniteLoop.SendWithExceptionAsync( + new ListInput { List = { new int[reducedThreshold] } }); + txResult.TransactionResult.Error.ShouldContain(nameof(RuntimeBranchThresholdExceededException)); + } + } + + [Fact] + public async Task TestMethodCallCountWithReducedThreshold() + { + // Get required services + var blockchainService = Application.ServiceProvider.GetRequiredService(); + var executionObserverThresholdProvider = + Application.ServiceProvider.GetRequiredService(); + + // Get current best chain block + var bestChainBlock = await blockchainService.GetBestChainLastBlockHeaderAsync(); + var blockIndex = new BlockIndex + { + BlockHash = bestChainBlock.GetHash(), + BlockHeight = bestChainBlock.Height + }; + + // Set reduced threshold to 5000 + const int reducedThreshold = 5000; + var newThreshold = new ExecutionObserverThreshold + { + ExecutionBranchThreshold = reducedThreshold, + ExecutionCallThreshold = reducedThreshold + }; + await executionObserverThresholdProvider.SetExecutionObserverThresholdAsync(blockIndex, newThreshold); + + // Verify threshold was set correctly + var currentThreshold = executionObserverThresholdProvider.GetExecutionObserverThreshold(blockIndex); + currentThreshold.ExecutionBranchThreshold.ShouldBe(reducedThreshold); + currentThreshold.ExecutionCallThreshold.ShouldBe(reducedThreshold); + + // Test recursive call with reduced threshold + { + await TestBasicSecurityContractStub.TestInfiniteRecursiveCall.SendAsync(new Int32Input + { Int32Value = reducedThreshold - 100 }); + var txResult = await TestBasicSecurityContractStub.TestInfiniteRecursiveCall.SendWithExceptionAsync( + new Int32Input { Int32Value = reducedThreshold }); + txResult.TransactionResult.Error.ShouldContain(nameof(RuntimeCallThresholdExceededException)); + } + + // Test recursive call in separate class with reduced threshold + { + await TestBasicSecurityContractStub.TestInfiniteRecursiveCallInSeparateClass.SendAsync(new Int32Input + { Int32Value = reducedThreshold - 100 }); + var txResult = + await TestBasicSecurityContractStub.TestInfiniteRecursiveCallInSeparateClass.SendWithExceptionAsync( + new Int32Input { Int32Value = reducedThreshold }); + txResult.TransactionResult.Error.ShouldContain(nameof(RuntimeCallThresholdExceededException)); + } + } } \ No newline at end of file diff --git a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderContractTestBase.cs b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderContractTestBase.cs index e127c3a78b..075588845d 100644 --- a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderContractTestBase.cs +++ b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderContractTestBase.cs @@ -35,6 +35,9 @@ public class TokenHolderContractTestBase : ContractTestBase Accounts[1].Address; protected List UserKeyPairs => Accounts.Skip(2).Take(3).Select(a => a.KeyPair).ToList(); + + protected ECKeyPair OtherKeyPair => Accounts[2].KeyPair; + protected Address Other => Accounts[2].Address; protected List
UserAddresses => UserKeyPairs.Select(k => Address.FromPublicKey(k.PublicKey)).ToList(); @@ -58,6 +61,8 @@ public class TokenHolderContractTestBase : ContractTestBase GetContractZeroTester(StarterKeyPair) diff --git a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs index 68698e8177..2673adde1a 100644 --- a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs +++ b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs @@ -54,10 +54,71 @@ await TokenHolderContractStub.ContributeProfits.SendAsync(new ContributeProfitsI { var tokenHolderProfitScheme = await TokenHolderContractStub.GetScheme.CallAsync(Starter); - tokenHolderProfitScheme.SchemeId.ShouldNotBeNull(); + var schemeId = tokenHolderProfitScheme.SchemeId; + schemeId.ShouldNotBeNull(); + + var result = await TokenHolderContractStub.CreateScheme.SendWithExceptionAsync(new CreateTokenHolderProfitSchemeInput + { + Symbol = "AUG" + }); + + result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + result.TransactionResult.Error.ShouldContain("Token holder profit scheme already exists"); } } + [Fact] + public async Task AddBeneficiaryWithWrongManagerTest() + { + var symbol = "AUG"; + var totalSupply = 10000000; + var transferAmount = 1000; + await StarterCreateIssueAndApproveTokenAsync(symbol, totalSupply, totalSupply); + await TokenHolderContractStub.CreateScheme.SendAsync(new CreateTokenHolderProfitSchemeInput + { + Symbol = symbol + }); + + await TokenContractStub.Transfer.SendAsync(new TransferInput + { + To = Other, + Amount = transferAmount, + Symbol = symbol + }); + + await OtherTokenHolderContractStub.RegisterForProfits.SendAsync(new RegisterForProfitsInput + { + SchemeManager = Starter, + Amount = 10 + }); + + var tokenHolderProfitScheme = await TokenHolderContractStub.GetScheme.CallAsync(Other); + tokenHolderProfitScheme.SchemeId.ShouldBeNull(); + tokenHolderProfitScheme.Symbol.ShouldBeEmpty(); + + tokenHolderProfitScheme = await TokenHolderContractStub.GetScheme.CallAsync(Starter); + + var scheme = await ProfitContractStub.GetScheme.CallAsync(tokenHolderProfitScheme.SchemeId); + scheme.TotalShares.ShouldBe(10); + + var result = await OtherTokenHolderContractStub.AddBeneficiary.SendWithExceptionAsync(new AddTokenHolderBeneficiaryInput + { + Beneficiary = Other, + Shares = 100 + }); + result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + result.TransactionResult.Error.ShouldContain("Token holder profit scheme not found"); + + await TokenHolderContractStub.AddBeneficiary.SendAsync(new AddTokenHolderBeneficiaryInput + { + Beneficiary = Other, + Shares = 100 + }); + + scheme = await ProfitContractStub.GetScheme.CallAsync(tokenHolderProfitScheme.SchemeId); + scheme.TotalShares.ShouldBe(110); + } + [Fact] public async Task ContributeProfitsTest() { @@ -130,7 +191,7 @@ await TokenHolderContractStub.AddBeneficiary.SendAsync(new AddTokenHolderBenefic { var originScheme = await ProfitContractStub.GetScheme.CallAsync(tokenHolderProfitScheme.SchemeId); - originScheme.TotalShares.ShouldBe(newShare); + originScheme.TotalShares.ShouldBe(newShare + 1); } }