From ec0afbb67d60846ef20d1149e2a8dc4946332fee Mon Sep 17 00:00:00 2001 From: "zecong.nian" Date: Wed, 22 Nov 2023 20:35:04 +0800 Subject: [PATCH 001/128] feat: save fail tx to storage --- .../Application/ITransactionResultService.cs | 31 ++++++++++++++++- .../Domain/ITransactionResultManager.cs | 29 +++++++++++++++- ...tionValidationStatusChangedEventHandler.cs | 22 ++++++++++++- .../Services/TransactionResultAppService.cs | 33 ++++++++++++------- 4 files changed, 101 insertions(+), 14 deletions(-) diff --git a/src/AElf.Kernel.Core/Blockchain/Application/ITransactionResultService.cs b/src/AElf.Kernel.Core/Blockchain/Application/ITransactionResultService.cs index c2d6e0b2de..f660f3b7ec 100644 --- a/src/AElf.Kernel.Core/Blockchain/Application/ITransactionResultService.cs +++ b/src/AElf.Kernel.Core/Blockchain/Application/ITransactionResultService.cs @@ -1,3 +1,4 @@ +using System.Linq; using AElf.Kernel.Blockchain.Domain; namespace AElf.Kernel.Blockchain.Application; @@ -6,17 +7,23 @@ public interface ITransactionResultQueryService { Task GetTransactionResultAsync(Hash transactionId); Task GetTransactionResultAsync(Hash transactionId, Hash blockHash); + Task GetFailedTransactionResultAsync(Hash transactionId); } public interface ITransactionResultService : ITransactionResultQueryService { Task AddTransactionResultsAsync(IList transactionResult, BlockHeader blockHeader); - Task ProcessTransactionResultAfterExecutionAsync(BlockHeader blockHeader, List transactionIds); + Task AddFailedTransactionResultsAsync(TransactionResult transactionResult); } public class TransactionResultService : ITransactionResultService, ITransientDependency { + private static readonly IEnumerable FailStatus = new List + { + TransactionResultStatus.Failed, TransactionResultStatus.NodeValidationFailed, TransactionResultStatus.Conflict + }; + private readonly ITransactionBlockIndexService _transactionBlockIndexService; private readonly ITransactionResultManager _transactionResultManager; @@ -27,6 +34,28 @@ public TransactionResultService(ITransactionResultManager transactionResultManag _transactionBlockIndexService = transactionBlockIndexService; } + public async Task AddFailedTransactionResultsAsync(TransactionResult transactionResult) + { + if (!FailStatus.Contains(transactionResult.Status)) return; + await _transactionResultManager.AddFailedTransactionResultAsync(transactionResult); + } + + public async Task AddFailedTransactionResultsAsync(IList transactionResults) + { + var failTransactionResult = transactionResults + .Where(r => FailStatus.Contains(r.Status)).ToList(); + if (failTransactionResult.IsNullOrEmpty()) return; + + await _transactionResultManager.AddFailedTransactionResultsAsync(failTransactionResult); + } + + public async Task GetFailedTransactionResultAsync(Hash transactionId) + { + var transactionResult = await _transactionResultManager.GetFailedTransactionResultAsync(transactionId); + transactionResult.TransactionId = transactionId; + return transactionResult; + } + public async Task AddTransactionResultsAsync(IList transactionResults, BlockHeader blockHeader) { diff --git a/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionResultManager.cs b/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionResultManager.cs index e2f2176886..964841541c 100644 --- a/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionResultManager.cs +++ b/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionResultManager.cs @@ -7,15 +7,19 @@ namespace AElf.Kernel.Blockchain.Domain; public interface ITransactionResultManager { Task AddTransactionResultAsync(TransactionResult transactionResult, Hash disambiguationHash); - Task AddTransactionResultsAsync(IList transactionResults, Hash disambiguationHash); Task GetTransactionResultAsync(Hash txId, Hash disambiguationHash); Task> GetTransactionResultsAsync(IList txIds, Hash disambiguationHash); Task HasTransactionResultAsync(Hash transactionId, Hash disambiguationHash); + Task AddFailedTransactionResultAsync(TransactionResult transactionResult); + Task AddFailedTransactionResultsAsync(IList transactionResults); + Task GetFailedTransactionResultAsync(Hash transactionId); + Task> GetFailedTransactionResultsAsync(IList txIds); } public class TransactionResultManager : ITransactionResultManager { + private const string FailStorageKeyPrefix = "FAIL:"; private readonly IBlockchainStore _transactionResultStore; public TransactionResultManager(IBlockchainStore transactionResultStore) @@ -23,6 +27,29 @@ public TransactionResultManager(IBlockchainStore transactionR _transactionResultStore = transactionResultStore; } + public async Task AddFailedTransactionResultAsync(TransactionResult transactionResult) + { + await _transactionResultStore.SetAsync(FailStorageKeyPrefix + transactionResult.TransactionId.ToStorageKey(), transactionResult); + } + + public async Task AddFailedTransactionResultsAsync(IList transactionResults) + { + await _transactionResultStore.SetAllAsync( + transactionResults.ToDictionary(tx => FailStorageKeyPrefix + tx.TransactionId.ToStorageKey(), t => t) + ); + } + + public async Task GetFailedTransactionResultAsync(Hash transactionId) + { + return await _transactionResultStore.GetAsync(FailStorageKeyPrefix + transactionId.ToStorageKey()); + } + + public async Task> GetFailedTransactionResultsAsync(IList txIds) + { + return await _transactionResultStore.GetAllAsync(txIds.Select(id => FailStorageKeyPrefix + id.ToStorageKey()).ToList()); + } + + public async Task AddTransactionResultAsync(TransactionResult transactionResult, Hash disambiguationHash) { await _transactionResultStore.SetAsync( diff --git a/src/AElf.WebApp.Application.Chain/Application/TransactionValidationStatusChangedEventHandler.cs b/src/AElf.WebApp.Application.Chain/Application/TransactionValidationStatusChangedEventHandler.cs index 890740441c..ef9edb620f 100644 --- a/src/AElf.WebApp.Application.Chain/Application/TransactionValidationStatusChangedEventHandler.cs +++ b/src/AElf.WebApp.Application.Chain/Application/TransactionValidationStatusChangedEventHandler.cs @@ -1,6 +1,9 @@ using System.Threading.Tasks; using AElf.Kernel; +using AElf.Kernel.Blockchain.Application; +using AElf.Types; using AElf.WebApp.Application.Chain.Infrastructure; +using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus; @@ -11,21 +14,38 @@ public class TransactionValidationStatusChangedEventHandler : ITransientDependency { private readonly ITransactionResultStatusCacheProvider _transactionResultStatusCacheProvider; + private readonly ITransactionResultService _transactionResultService; + private readonly WebAppOptions _webAppOptions; public TransactionValidationStatusChangedEventHandler( - ITransactionResultStatusCacheProvider transactionResultStatusCacheProvider) + ITransactionResultStatusCacheProvider transactionResultStatusCacheProvider, + ITransactionResultService transactionResultService, + IOptionsMonitor optionsSnapshot) { _transactionResultStatusCacheProvider = transactionResultStatusCacheProvider; + _transactionResultService = transactionResultService; + _webAppOptions = optionsSnapshot.CurrentValue; } public Task HandleEventAsync(TransactionValidationStatusChangedEvent eventData) { + + // save to local cache _transactionResultStatusCacheProvider.ChangeTransactionResultStatus(eventData.TransactionId, new TransactionValidateStatus { TransactionResultStatus = eventData.TransactionResultStatus, Error = eventData.Error }); + + // save to storage + _transactionResultService.AddFailedTransactionResultsAsync(new TransactionResult + { + TransactionId = eventData.TransactionId, + Status = eventData.TransactionResultStatus, + Error = TransactionErrorResolver.TakeErrorMessage(eventData.Error, _webAppOptions.IsDebugMode) + }); + return Task.CompletedTask; } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs index 08552f42e5..c52fae025b 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs @@ -88,23 +88,34 @@ public async Task GetTransactionResultAsync(string transac output.Transaction = _objectMapper.Map(transaction); output.TransactionSize = transaction?.CalculateSize() ?? 0; - if (transactionResult.Status == TransactionResultStatus.NotExisted) + if (transactionResult.Status != TransactionResultStatus.NotExisted) { - var validationStatus = - _transactionResultStatusCacheProvider.GetTransactionResultStatus(transactionIdHash); - if (validationStatus != null) - { - output.Status = validationStatus.TransactionResultStatus.ToString().ToUpper(); - output.Error = - TransactionErrorResolver.TakeErrorMessage(validationStatus.Error, _webAppOptions.IsDebugMode); - } + await FormatTransactionParamsAsync(output.Transaction, transaction.Params); + return output; + } + var validationStatus = _transactionResultStatusCacheProvider.GetTransactionResultStatus(transactionIdHash); + if (validationStatus != null) + { + output.Status = validationStatus.TransactionResultStatus.ToString().ToUpper(); + output.Error = + TransactionErrorResolver.TakeErrorMessage(validationStatus.Error, _webAppOptions.IsDebugMode); return output; } - await FormatTransactionParamsAsync(output.Transaction, transaction.Params); - + var failedTransactionResult = + await _transactionResultProxyService.TransactionResultQueryService.GetFailedTransactionResultAsync( + transactionIdHash); + if (failedTransactionResult != null) + { + output.Status = failedTransactionResult.Status.ToString().ToUpper(); + output.Error = + TransactionErrorResolver.TakeErrorMessage(failedTransactionResult.Error, _webAppOptions.IsDebugMode); + return output; + } + return output; + } /// From 3a05484ab0f6110b1754a4543d196fb5a4336971 Mon Sep 17 00:00:00 2001 From: "zecong.nian" Date: Fri, 1 Dec 2023 19:56:02 +0800 Subject: [PATCH 002/128] feat: save failed tx result to tf storage --- protobuf/aelf/core.proto | 9 +++++ .../Application/ITransactionResultService.cs | 31 +-------------- .../Domain/ITransactionFailedResultManager.cs | 38 +++++++++++++++++++ .../TransactionOptions.cs | 7 ++++ ...tionValidationStatusChangedEventHandler.cs | 29 +++++++++----- .../ChainApplicationWebAppAElfModule.cs | 18 +++++++++ .../TransactionFailedResultService.cs | 34 +++++++++++++++++ .../Services/TransactionResultAppService.cs | 2 +- .../Services/TransactionResultProxyService.cs | 7 +++- 9 files changed, 134 insertions(+), 41 deletions(-) create mode 100644 src/AElf.Kernel.Core/Blockchain/Domain/ITransactionFailedResultManager.cs create mode 100644 src/AElf.WebApp.Application.Chain/Services/TransactionFailedResultService.cs diff --git a/protobuf/aelf/core.proto b/protobuf/aelf/core.proto index c990aa9df6..cbf739892c 100644 --- a/protobuf/aelf/core.proto +++ b/protobuf/aelf/core.proto @@ -72,6 +72,15 @@ message TransactionResult { string error = 10; } +message TransactionFailedResult { + // The transaction id. + Hash transaction_id = 1; + // The transaction result status. + TransactionResultStatus status = 2; + // Failed execution error message. + string error = 3; +} + message LogEvent { // The contract address. Address address = 1; diff --git a/src/AElf.Kernel.Core/Blockchain/Application/ITransactionResultService.cs b/src/AElf.Kernel.Core/Blockchain/Application/ITransactionResultService.cs index f660f3b7ec..c2d6e0b2de 100644 --- a/src/AElf.Kernel.Core/Blockchain/Application/ITransactionResultService.cs +++ b/src/AElf.Kernel.Core/Blockchain/Application/ITransactionResultService.cs @@ -1,4 +1,3 @@ -using System.Linq; using AElf.Kernel.Blockchain.Domain; namespace AElf.Kernel.Blockchain.Application; @@ -7,23 +6,17 @@ public interface ITransactionResultQueryService { Task GetTransactionResultAsync(Hash transactionId); Task GetTransactionResultAsync(Hash transactionId, Hash blockHash); - Task GetFailedTransactionResultAsync(Hash transactionId); } public interface ITransactionResultService : ITransactionResultQueryService { Task AddTransactionResultsAsync(IList transactionResult, BlockHeader blockHeader); + Task ProcessTransactionResultAfterExecutionAsync(BlockHeader blockHeader, List transactionIds); - Task AddFailedTransactionResultsAsync(TransactionResult transactionResult); } public class TransactionResultService : ITransactionResultService, ITransientDependency { - private static readonly IEnumerable FailStatus = new List - { - TransactionResultStatus.Failed, TransactionResultStatus.NodeValidationFailed, TransactionResultStatus.Conflict - }; - private readonly ITransactionBlockIndexService _transactionBlockIndexService; private readonly ITransactionResultManager _transactionResultManager; @@ -34,28 +27,6 @@ public TransactionResultService(ITransactionResultManager transactionResultManag _transactionBlockIndexService = transactionBlockIndexService; } - public async Task AddFailedTransactionResultsAsync(TransactionResult transactionResult) - { - if (!FailStatus.Contains(transactionResult.Status)) return; - await _transactionResultManager.AddFailedTransactionResultAsync(transactionResult); - } - - public async Task AddFailedTransactionResultsAsync(IList transactionResults) - { - var failTransactionResult = transactionResults - .Where(r => FailStatus.Contains(r.Status)).ToList(); - if (failTransactionResult.IsNullOrEmpty()) return; - - await _transactionResultManager.AddFailedTransactionResultsAsync(failTransactionResult); - } - - public async Task GetFailedTransactionResultAsync(Hash transactionId) - { - var transactionResult = await _transactionResultManager.GetFailedTransactionResultAsync(transactionId); - transactionResult.TransactionId = transactionId; - return transactionResult; - } - public async Task AddTransactionResultsAsync(IList transactionResults, BlockHeader blockHeader) { diff --git a/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionFailedResultManager.cs b/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionFailedResultManager.cs new file mode 100644 index 0000000000..1d9b05c13f --- /dev/null +++ b/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionFailedResultManager.cs @@ -0,0 +1,38 @@ +using System.Linq; +using AElf.Kernel.Blockchain.Infrastructure; +using AElf.Kernel.Infrastructure; + +namespace AElf.Kernel.Blockchain.Domain; + +public interface ITransactionFailedResultManager +{ + Task AddFailedTransactionResultAsync(Hash transactionId, TransactionFailedResult transactionResult); + Task GetFailedTransactionResultAsync(Hash transactionId); +} + +public class TransactionFailedResultManager : ITransactionFailedResultManager +{ + private readonly IBlockchainStore _transactionFailedResultStore; + + public TransactionFailedResultManager(IBlockchainStore transactionFailedResultStore) + { + _transactionFailedResultStore = transactionFailedResultStore; + } + + public async Task AddFailedTransactionResultAsync(Hash transactionId, TransactionFailedResult transactionResult) + { + await _transactionFailedResultStore.SetAsync(transactionId.ToStorageKey(), transactionResult); + } + + + public async Task GetFailedTransactionResultAsync(Hash transactionId) + { + var failedResult = await _transactionFailedResultStore.GetAsync(transactionId.ToStorageKey()); + if (failedResult != null) + { + failedResult.TransactionId = transactionId; + } + return failedResult; + } + +} \ No newline at end of file diff --git a/src/AElf.Kernel.TransactionPool/TransactionOptions.cs b/src/AElf.Kernel.TransactionPool/TransactionOptions.cs index 7847da5094..b99b43d10c 100644 --- a/src/AElf.Kernel.TransactionPool/TransactionOptions.cs +++ b/src/AElf.Kernel.TransactionPool/TransactionOptions.cs @@ -17,4 +17,11 @@ public class TransactionOptions /// But common node needs to enable it to prevent transaction flood attack /// public bool EnableTransactionExecutionValidation { get; set; } = true; + + + /// + /// Configuration whether to save failed transaction results + /// + public bool SaveFailedResult { get; set; } + } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Application/TransactionValidationStatusChangedEventHandler.cs b/src/AElf.WebApp.Application.Chain/Application/TransactionValidationStatusChangedEventHandler.cs index ef9edb620f..a5ca046188 100644 --- a/src/AElf.WebApp.Application.Chain/Application/TransactionValidationStatusChangedEventHandler.cs +++ b/src/AElf.WebApp.Application.Chain/Application/TransactionValidationStatusChangedEventHandler.cs @@ -1,8 +1,10 @@ using System.Threading.Tasks; using AElf.Kernel; using AElf.Kernel.Blockchain.Application; +using AElf.Kernel.TransactionPool; using AElf.Types; using AElf.WebApp.Application.Chain.Infrastructure; +using AElf.WebApp.Application.Chain.Services; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus; @@ -15,16 +17,22 @@ public class TransactionValidationStatusChangedEventHandler : { private readonly ITransactionResultStatusCacheProvider _transactionResultStatusCacheProvider; private readonly ITransactionResultService _transactionResultService; + private readonly ITransactionFailedResultService _transactionFailedResultService; private readonly WebAppOptions _webAppOptions; + private readonly TransactionOptions _transactionOptions; public TransactionValidationStatusChangedEventHandler( ITransactionResultStatusCacheProvider transactionResultStatusCacheProvider, ITransactionResultService transactionResultService, - IOptionsMonitor optionsSnapshot) + IOptionsMonitor optionsSnapshot, + IOptionsMonitor transactionOptions, + ITransactionFailedResultService transactionFailedResultService) { _transactionResultStatusCacheProvider = transactionResultStatusCacheProvider; _transactionResultService = transactionResultService; + _transactionFailedResultService = transactionFailedResultService; _webAppOptions = optionsSnapshot.CurrentValue; + _transactionOptions = transactionOptions.CurrentValue; } public Task HandleEventAsync(TransactionValidationStatusChangedEvent eventData) @@ -37,15 +45,18 @@ public Task HandleEventAsync(TransactionValidationStatusChangedEvent eventData) TransactionResultStatus = eventData.TransactionResultStatus, Error = eventData.Error }); - - // save to storage - _transactionResultService.AddFailedTransactionResultsAsync(new TransactionResult + + if (_transactionOptions.SaveFailedResult) { - TransactionId = eventData.TransactionId, - Status = eventData.TransactionResultStatus, - Error = TransactionErrorResolver.TakeErrorMessage(eventData.Error, _webAppOptions.IsDebugMode) - }); - + // save to storage + _transactionFailedResultService.AddFailedTransactionResultsAsync(eventData.TransactionId, + new TransactionFailedResult + { + Status = eventData.TransactionResultStatus, + Error = TransactionErrorResolver.TakeErrorMessage(eventData.Error, _webAppOptions.IsDebugMode) + }); + } + return Task.CompletedTask; } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs b/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs index d5820e1828..b21fc539ee 100644 --- a/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs +++ b/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs @@ -1,6 +1,9 @@ using AElf.Kernel; +using AElf.Kernel.Infrastructure; using AElf.Modularity; +using AElf.Types; using AElf.WebApp.Application.Chain.Infrastructure; +using Google.Protobuf; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AutoMapper; using Volo.Abp.Modularity; @@ -22,5 +25,20 @@ public override void ConfigureServices(ServiceConfigurationContext context) context.Services .AddSingleton(); + + context.Services.AddStoreKeyPrefixProvide("tf"); + } +} + +public static class StoreKeyPrefixProviderServiceCollectionExtensions +{ + public static IServiceCollection AddStoreKeyPrefixProvide( + this IServiceCollection serviceCollection, string prefix) + where T : IMessage, new() + { + serviceCollection.AddTransient>(c => + new FastStoreKeyPrefixProvider(prefix)); + + return serviceCollection; } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionFailedResultService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionFailedResultService.cs new file mode 100644 index 0000000000..66be6301ed --- /dev/null +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionFailedResultService.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; +using AElf.Kernel.Blockchain.Domain; +using AElf.Types; +using Volo.Abp.DependencyInjection; + +namespace AElf.WebApp.Application.Chain.Services; + + +public interface ITransactionFailedResultService +{ + Task AddFailedTransactionResultsAsync(Hash transactionId, TransactionFailedResult transactionResult); + Task GetFailedTransactionResultAsync(Hash transactionId); +} + +public class TransactionFailedResultService : ITransactionFailedResultService, ITransientDependency +{ + private readonly ITransactionFailedResultManager _transactionFailedResultManager; + + public TransactionFailedResultService(ITransactionFailedResultManager transactionFailedResultManager) + { + _transactionFailedResultManager = transactionFailedResultManager; + } + + + public async Task AddFailedTransactionResultsAsync(Hash transactionId, TransactionFailedResult transactionResult) + { + await _transactionFailedResultManager.AddFailedTransactionResultAsync(transactionId, transactionResult); + } + + public async Task GetFailedTransactionResultAsync(Hash transactionId) + { + return await _transactionFailedResultManager.GetFailedTransactionResultAsync(transactionId); + } +} \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs index c52fae025b..fc690541ba 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs @@ -104,7 +104,7 @@ public async Task GetTransactionResultAsync(string transac } var failedTransactionResult = - await _transactionResultProxyService.TransactionResultQueryService.GetFailedTransactionResultAsync( + await _transactionResultProxyService.TransactionFailedResultService.GetFailedTransactionResultAsync( transactionIdHash); if (failedTransactionResult != null) { diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs index 5ebfcc8598..99f088818e 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs @@ -1,5 +1,6 @@ using AElf.Kernel.Blockchain.Application; using AElf.Kernel.TransactionPool.Application; +using AElf.WebApp.Application.Chain.Services; namespace AElf.WebApp.Application.Chain; @@ -7,17 +8,21 @@ public interface ITransactionResultProxyService { ITransactionPoolService TransactionPoolService { get; } ITransactionResultQueryService TransactionResultQueryService { get; } + ITransactionFailedResultService TransactionFailedResultService { get; } } public class TransactionResultProxyService : ITransactionResultProxyService { public TransactionResultProxyService(ITransactionPoolService transactionPoolService, - ITransactionResultQueryService transactionResultQueryService) + ITransactionResultQueryService transactionResultQueryService, + ITransactionFailedResultService transactionFailedResultService) { TransactionPoolService = transactionPoolService; TransactionResultQueryService = transactionResultQueryService; + TransactionFailedResultService = transactionFailedResultService; } public ITransactionPoolService TransactionPoolService { get; set; } public ITransactionResultQueryService TransactionResultQueryService { get; set; } + public ITransactionFailedResultService TransactionFailedResultService { get; } } \ No newline at end of file From 15e5093484a78d27e284637ad09d314604d1e6f2 Mon Sep 17 00:00:00 2001 From: "zecong.nian" Date: Wed, 6 Dec 2023 23:14:30 +0800 Subject: [PATCH 003/128] feat: save invalid transaction result --- protobuf/aelf/core.proto | 2 +- .../Domain/ITransactionFailedResultManager.cs | 21 +++---- src/AElf.Kernel.Core/CoreKernelAElfModule.cs | 2 +- .../TransactionFailedResultService.cs | 15 +++-- ...ctionValidationStatusFailedEventHandler.cs | 58 +++++++++++++++++++ .../TransactionOptions.cs | 3 +- src/AElf.Launcher/appsettings.json | 1 + ...tionValidationStatusChangedEventHandler.cs | 33 +---------- .../ChainApplicationWebAppAElfModule.cs | 17 ------ .../Services/TransactionResultProxyService.cs | 1 - 10 files changed, 79 insertions(+), 74 deletions(-) rename src/{AElf.WebApp.Application.Chain/Services => AElf.Kernel.TransactionPool/Application}/TransactionFailedResultService.cs (59%) create mode 100644 src/AElf.Kernel.TransactionPool/Handler/TransactionValidationStatusFailedEventHandler.cs diff --git a/protobuf/aelf/core.proto b/protobuf/aelf/core.proto index cbf739892c..c0ca910f0d 100644 --- a/protobuf/aelf/core.proto +++ b/protobuf/aelf/core.proto @@ -72,7 +72,7 @@ message TransactionResult { string error = 10; } -message TransactionFailedResult { +message TransactionValidationFailure{ // The transaction id. Hash transaction_id = 1; // The transaction result status. diff --git a/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionFailedResultManager.cs b/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionFailedResultManager.cs index 1d9b05c13f..b2a96da421 100644 --- a/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionFailedResultManager.cs +++ b/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionFailedResultManager.cs @@ -6,33 +6,28 @@ namespace AElf.Kernel.Blockchain.Domain; public interface ITransactionFailedResultManager { - Task AddFailedTransactionResultAsync(Hash transactionId, TransactionFailedResult transactionResult); - Task GetFailedTransactionResultAsync(Hash transactionId); + Task AddFailedTransactionResultAsync(TransactionValidationFailure transactionResult); + Task GetFailedTransactionResultAsync(Hash transactionId); } public class TransactionFailedResultManager : ITransactionFailedResultManager { - private readonly IBlockchainStore _transactionFailedResultStore; + private readonly IBlockchainStore _transactionFailedResultStore; - public TransactionFailedResultManager(IBlockchainStore transactionFailedResultStore) + public TransactionFailedResultManager(IBlockchainStore transactionFailedResultStore) { _transactionFailedResultStore = transactionFailedResultStore; } - public async Task AddFailedTransactionResultAsync(Hash transactionId, TransactionFailedResult transactionResult) + public async Task AddFailedTransactionResultAsync(TransactionValidationFailure transactionResult) { - await _transactionFailedResultStore.SetAsync(transactionId.ToStorageKey(), transactionResult); + await _transactionFailedResultStore.SetAsync(transactionResult.TransactionId.ToStorageKey(), transactionResult); } - public async Task GetFailedTransactionResultAsync(Hash transactionId) + public async Task GetFailedTransactionResultAsync(Hash transactionId) { - var failedResult = await _transactionFailedResultStore.GetAsync(transactionId.ToStorageKey()); - if (failedResult != null) - { - failedResult.TransactionId = transactionId; - } - return failedResult; + return await _transactionFailedResultStore.GetAsync(transactionId.ToStorageKey()); } } \ No newline at end of file diff --git a/src/AElf.Kernel.Core/CoreKernelAElfModule.cs b/src/AElf.Kernel.Core/CoreKernelAElfModule.cs index 86df74b631..f40455f686 100644 --- a/src/AElf.Kernel.Core/CoreKernelAElfModule.cs +++ b/src/AElf.Kernel.Core/CoreKernelAElfModule.cs @@ -39,7 +39,7 @@ public override void ConfigureServices(ServiceConfigurationContext context) services.AddStoreKeyPrefixProvide("ti"); services.AddStoreKeyPrefixProvide("tr"); services.AddStoreKeyPrefixProvide("vs"); - + services.AddStoreKeyPrefixProvide("tf"); services.AddTransient(typeof(IStateStore<>), typeof(StateStore<>)); services.AddSingleton(typeof(INotModifiedCachedStateStore<>), typeof(NotModifiedCachedStateStore<>)); diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionFailedResultService.cs b/src/AElf.Kernel.TransactionPool/Application/TransactionFailedResultService.cs similarity index 59% rename from src/AElf.WebApp.Application.Chain/Services/TransactionFailedResultService.cs rename to src/AElf.Kernel.TransactionPool/Application/TransactionFailedResultService.cs index 66be6301ed..c9b6a23e3c 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionFailedResultService.cs +++ b/src/AElf.Kernel.TransactionPool/Application/TransactionFailedResultService.cs @@ -3,13 +3,13 @@ using AElf.Types; using Volo.Abp.DependencyInjection; -namespace AElf.WebApp.Application.Chain.Services; +namespace AElf.Kernel.TransactionPool.Application; public interface ITransactionFailedResultService { - Task AddFailedTransactionResultsAsync(Hash transactionId, TransactionFailedResult transactionResult); - Task GetFailedTransactionResultAsync(Hash transactionId); + Task AddFailedTransactionResultsAsync(TransactionValidationFailure transactionResult); + Task GetFailedTransactionResultAsync(Hash transactionId); } public class TransactionFailedResultService : ITransactionFailedResultService, ITransientDependency @@ -20,14 +20,13 @@ public TransactionFailedResultService(ITransactionFailedResultManager transactio { _transactionFailedResultManager = transactionFailedResultManager; } - - - public async Task AddFailedTransactionResultsAsync(Hash transactionId, TransactionFailedResult transactionResult) + + public async Task AddFailedTransactionResultsAsync(TransactionValidationFailure transactionResult) { - await _transactionFailedResultManager.AddFailedTransactionResultAsync(transactionId, transactionResult); + await _transactionFailedResultManager.AddFailedTransactionResultAsync(transactionResult); } - public async Task GetFailedTransactionResultAsync(Hash transactionId) + public async Task GetFailedTransactionResultAsync(Hash transactionId) { return await _transactionFailedResultManager.GetFailedTransactionResultAsync(transactionId); } diff --git a/src/AElf.Kernel.TransactionPool/Handler/TransactionValidationStatusFailedEventHandler.cs b/src/AElf.Kernel.TransactionPool/Handler/TransactionValidationStatusFailedEventHandler.cs new file mode 100644 index 0000000000..ec6b2ff26d --- /dev/null +++ b/src/AElf.Kernel.TransactionPool/Handler/TransactionValidationStatusFailedEventHandler.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using AElf.Kernel.TransactionPool.Application; +using AElf.Types; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus; + +namespace AElf.Kernel.TransactionPool.Handler; + +public class TransactionValidationStatusFailedEventHandler : + ILocalEventHandler, + ITransientDependency +{ + private static readonly IEnumerable FailStatus = new List + { + TransactionResultStatus.Failed, TransactionResultStatus.NodeValidationFailed, TransactionResultStatus.Conflict + }; + + private readonly ITransactionFailedResultService _transactionFailedResultService; + private readonly TransactionOptions _transactionOptions; + + public TransactionValidationStatusFailedEventHandler( + IOptionsMonitor transactionOptionsMonitor, + ITransactionFailedResultService transactionFailedResultService) + { + _transactionFailedResultService = transactionFailedResultService; + _transactionOptions = transactionOptionsMonitor.CurrentValue; + } + + public Task HandleEventAsync(TransactionValidationStatusChangedEvent eventData) + { + if (!FailStatus.Contains(eventData.TransactionResultStatus)) return Task.CompletedTask; + if (!_transactionOptions.SaveInvalidTransactionResult) return Task.CompletedTask; + + // save to storage + _transactionFailedResultService.AddFailedTransactionResultsAsync( + new TransactionValidationFailure + { + TransactionId = eventData.TransactionId, + Status = eventData.TransactionResultStatus, + Error = TakeErrorMessage(eventData.Error) + }); + + return Task.CompletedTask; + } + + public static string TakeErrorMessage(string transactionResultError) + { + if (string.IsNullOrWhiteSpace(transactionResultError)) + return null; + using var stringReader = new StringReader(transactionResultError); + return stringReader.ReadLine(); + } + +} \ No newline at end of file diff --git a/src/AElf.Kernel.TransactionPool/TransactionOptions.cs b/src/AElf.Kernel.TransactionPool/TransactionOptions.cs index b99b43d10c..4170415511 100644 --- a/src/AElf.Kernel.TransactionPool/TransactionOptions.cs +++ b/src/AElf.Kernel.TransactionPool/TransactionOptions.cs @@ -22,6 +22,7 @@ public class TransactionOptions /// /// Configuration whether to save failed transaction results /// - public bool SaveFailedResult { get; set; } + public bool SaveInvalidTransactionResult { get; set; } + } \ No newline at end of file diff --git a/src/AElf.Launcher/appsettings.json b/src/AElf.Launcher/appsettings.json index 80b0e93f8f..a7a524721a 100644 --- a/src/AElf.Launcher/appsettings.json +++ b/src/AElf.Launcher/appsettings.json @@ -31,6 +31,7 @@ "MinerIncreaseInterval": 31536000 }, "Transaction": { + "SaveInvalidTransactionResult" : true, "PoolLimit": 10240 }, "BasicAuth": { diff --git a/src/AElf.WebApp.Application.Chain/Application/TransactionValidationStatusChangedEventHandler.cs b/src/AElf.WebApp.Application.Chain/Application/TransactionValidationStatusChangedEventHandler.cs index a5ca046188..890740441c 100644 --- a/src/AElf.WebApp.Application.Chain/Application/TransactionValidationStatusChangedEventHandler.cs +++ b/src/AElf.WebApp.Application.Chain/Application/TransactionValidationStatusChangedEventHandler.cs @@ -1,11 +1,6 @@ using System.Threading.Tasks; using AElf.Kernel; -using AElf.Kernel.Blockchain.Application; -using AElf.Kernel.TransactionPool; -using AElf.Types; using AElf.WebApp.Application.Chain.Infrastructure; -using AElf.WebApp.Application.Chain.Services; -using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus; @@ -16,47 +11,21 @@ public class TransactionValidationStatusChangedEventHandler : ITransientDependency { private readonly ITransactionResultStatusCacheProvider _transactionResultStatusCacheProvider; - private readonly ITransactionResultService _transactionResultService; - private readonly ITransactionFailedResultService _transactionFailedResultService; - private readonly WebAppOptions _webAppOptions; - private readonly TransactionOptions _transactionOptions; public TransactionValidationStatusChangedEventHandler( - ITransactionResultStatusCacheProvider transactionResultStatusCacheProvider, - ITransactionResultService transactionResultService, - IOptionsMonitor optionsSnapshot, - IOptionsMonitor transactionOptions, - ITransactionFailedResultService transactionFailedResultService) + ITransactionResultStatusCacheProvider transactionResultStatusCacheProvider) { _transactionResultStatusCacheProvider = transactionResultStatusCacheProvider; - _transactionResultService = transactionResultService; - _transactionFailedResultService = transactionFailedResultService; - _webAppOptions = optionsSnapshot.CurrentValue; - _transactionOptions = transactionOptions.CurrentValue; } public Task HandleEventAsync(TransactionValidationStatusChangedEvent eventData) { - - // save to local cache _transactionResultStatusCacheProvider.ChangeTransactionResultStatus(eventData.TransactionId, new TransactionValidateStatus { TransactionResultStatus = eventData.TransactionResultStatus, Error = eventData.Error }); - - if (_transactionOptions.SaveFailedResult) - { - // save to storage - _transactionFailedResultService.AddFailedTransactionResultsAsync(eventData.TransactionId, - new TransactionFailedResult - { - Status = eventData.TransactionResultStatus, - Error = TransactionErrorResolver.TakeErrorMessage(eventData.Error, _webAppOptions.IsDebugMode) - }); - } - return Task.CompletedTask; } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs b/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs index b21fc539ee..d6e09b36f8 100644 --- a/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs +++ b/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs @@ -1,9 +1,6 @@ using AElf.Kernel; -using AElf.Kernel.Infrastructure; using AElf.Modularity; -using AElf.Types; using AElf.WebApp.Application.Chain.Infrastructure; -using Google.Protobuf; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AutoMapper; using Volo.Abp.Modularity; @@ -26,19 +23,5 @@ public override void ConfigureServices(ServiceConfigurationContext context) context.Services .AddSingleton(); - context.Services.AddStoreKeyPrefixProvide("tf"); - } -} - -public static class StoreKeyPrefixProviderServiceCollectionExtensions -{ - public static IServiceCollection AddStoreKeyPrefixProvide( - this IServiceCollection serviceCollection, string prefix) - where T : IMessage, new() - { - serviceCollection.AddTransient>(c => - new FastStoreKeyPrefixProvider(prefix)); - - return serviceCollection; } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs index 99f088818e..cf02dcf037 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs @@ -1,6 +1,5 @@ using AElf.Kernel.Blockchain.Application; using AElf.Kernel.TransactionPool.Application; -using AElf.WebApp.Application.Chain.Services; namespace AElf.WebApp.Application.Chain; From 5c6c669e315c5cc58218e99d0d250ede6db6cc49 Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 7 Dec 2023 15:16:06 +0800 Subject: [PATCH 004/128] feat: add ModifyTokenIssuerAndOwner, SetTokenIssuerAndOwnerModificationEnabled and GetTokenIssuerAndOwnerModificationEnabled --- .../TokenContractState.cs | 2 ++ .../TokenContract_Actions.cs | 34 +++++++++++++++++++ protobuf/token_contract_impl.proto | 16 +++++++++ 3 files changed, 52 insertions(+) diff --git a/contract/AElf.Contracts.MultiToken/TokenContractState.cs b/contract/AElf.Contracts.MultiToken/TokenContractState.cs index 92da423908..c32b0ea62f 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractState.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractState.cs @@ -63,4 +63,6 @@ public partial class TokenContractState : ContractState public SingletonState
ElectionContractAddress { get; set; } public SingletonState
VoteContractAddress { get; set; } + + public SingletonState TokenIssuerAndOwnerModificationDisabled { get; set; } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 483f073b54..073d2fe457 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -589,4 +589,38 @@ public override Empty CrossChainReceiveToken(CrossChainReceiveTokenInput input) } #endregion + + public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput input) + { + Assert(!State.TokenIssuerAndOwnerModificationDisabled.Value, "Set token issuer and owner disabled."); + Assert(string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); + Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "Invalid input issuer."); + Assert(input.Owner != null && !input.Owner.Value.IsNullOrEmpty(), "Invalid input owner."); + + var tokenInfo = State.TokenInfos[input.Symbol]; + + Assert(tokenInfo != null, "Token is not found."); + Assert(tokenInfo.Owner == null, "Only set token without owner."); + Assert(tokenInfo.Issuer == Context.Sender, "Only token issuer can set token issuer and owner."); + + tokenInfo.Issuer = input.Issuer; + tokenInfo.Owner = input.Owner; + + return new Empty(); + } + + public override Empty SetTokenIssuerAndOwnerModificationEnabled(BoolValue input) + { + AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); + Assert(input != null, "Invalid input."); + + State.TokenIssuerAndOwnerModificationDisabled.Value = input.Value; + + return new Empty(); + } + + public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) + { + return new BoolValue{ Value = State.TokenIssuerAndOwnerModificationDisabled.Value}; + } } \ No newline at end of file diff --git a/protobuf/token_contract_impl.proto b/protobuf/token_contract_impl.proto index 97acd5b439..b0689af301 100644 --- a/protobuf/token_contract_impl.proto +++ b/protobuf/token_contract_impl.proto @@ -163,6 +163,16 @@ service TokenContractImpl { rpc GetTransactionFeeDelegateInfo(GetTransactionFeeDelegateInfoInput) returns (token.TransactionFeeDelegations){ option (aelf.is_view) = true; } + + rpc ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput) returns (google.protobuf.Empty) { + } + + rpc SetTokenIssuerAndOwnerModificationEnabled(google.protobuf.BoolValue) returns (google.protobuf.Empty) { + } + + rpc GetTokenIssuerAndOwnerModificationEnabled(google.protobuf.Empty) returns (google.protobuf.BoolValue) { + option (aelf.is_view) = true; + } } message AdvanceResourceTokenInput { @@ -416,3 +426,9 @@ message TransactionFeeDelegateInfoCancelled { aelf.Address caller = 3 ; DelegateTransactionList delegate_transaction_list = 4; } + +message ModifyTokenIssuerAndOwnerInput { + string symbol = 1; + aelf.Address issuer = 2; + aelf.Address owner = 3; +} From a1cb698502129a5daf88c40571667a3e99098d98 Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 7 Dec 2023 15:39:12 +0800 Subject: [PATCH 005/128] fix: format code --- .../AElf.Contracts.MultiToken/TokenContract_Actions.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 073d2fe457..6f9b499f91 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -596,9 +596,9 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i Assert(string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "Invalid input issuer."); Assert(input.Owner != null && !input.Owner.Value.IsNullOrEmpty(), "Invalid input owner."); - + var tokenInfo = State.TokenInfos[input.Symbol]; - + Assert(tokenInfo != null, "Token is not found."); Assert(tokenInfo.Owner == null, "Only set token without owner."); Assert(tokenInfo.Issuer == Context.Sender, "Only token issuer can set token issuer and owner."); @@ -613,14 +613,14 @@ public override Empty SetTokenIssuerAndOwnerModificationEnabled(BoolValue input) { AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); Assert(input != null, "Invalid input."); - + State.TokenIssuerAndOwnerModificationDisabled.Value = input.Value; - + return new Empty(); } public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) { - return new BoolValue{ Value = State.TokenIssuerAndOwnerModificationDisabled.Value}; + return new BoolValue { Value = State.TokenIssuerAndOwnerModificationDisabled.Value }; } } \ No newline at end of file From 82b284a3b30d0105ef203617590ebcbf98c81bdd Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 7 Dec 2023 22:32:36 +0800 Subject: [PATCH 006/128] fix: ModifyTokenIssuerAndOwner --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 6f9b499f91..08c151f096 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -593,7 +593,7 @@ public override Empty CrossChainReceiveToken(CrossChainReceiveTokenInput input) public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput input) { Assert(!State.TokenIssuerAndOwnerModificationDisabled.Value, "Set token issuer and owner disabled."); - Assert(string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); + Assert(!string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "Invalid input issuer."); Assert(input.Owner != null && !input.Owner.Value.IsNullOrEmpty(), "Invalid input owner."); From 242638e12e1c82bb8fbe5b17976dd7933cbfbf33 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 8 Dec 2023 10:16:59 +0800 Subject: [PATCH 007/128] fix: assert message --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 08c151f096..9b945f4da3 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -600,7 +600,7 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i var tokenInfo = State.TokenInfos[input.Symbol]; Assert(tokenInfo != null, "Token is not found."); - Assert(tokenInfo.Owner == null, "Only set token without owner."); + Assert(tokenInfo.Owner == null, "Can only set token which does not have owner."); Assert(tokenInfo.Issuer == Context.Sender, "Only token issuer can set token issuer and owner."); tokenInfo.Issuer = input.Issuer; From 913ed5bae1f5981307282c0efc633abd98c3b6cf Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 8 Dec 2023 21:29:31 +0800 Subject: [PATCH 008/128] =?UTF-8?q?fix=EF=BC=9Achange=20SetTokenIssuerAndO?= =?UTF-8?q?wnerModificationEnabled=20to=20SetTokenIssuerAndOwnerModificati?= =?UTF-8?q?onDisabled=20and=20adjust=20GetTokenIssuerAndOwnerModificationE?= =?UTF-8?q?nabled=20return=20value?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TokenContract_Actions.cs | 11 +++++++---- protobuf/token_contract_impl.proto | 6 +++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 9b945f4da3..24e44a6560 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -594,7 +594,7 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i { Assert(!State.TokenIssuerAndOwnerModificationDisabled.Value, "Set token issuer and owner disabled."); Assert(!string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); - Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "Invalid input issuer."); + Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "InBvalid input issuer."); Assert(input.Owner != null && !input.Owner.Value.IsNullOrEmpty(), "Invalid input owner."); var tokenInfo = State.TokenInfos[input.Symbol]; @@ -609,18 +609,21 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i return new Empty(); } - public override Empty SetTokenIssuerAndOwnerModificationEnabled(BoolValue input) + public override Empty SetTokenIssuerAndOwnerModificationDisabled(SetTokenIssuerAndOwnerModificationDisabledInput input) { AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); Assert(input != null, "Invalid input."); - State.TokenIssuerAndOwnerModificationDisabled.Value = input.Value; + State.TokenIssuerAndOwnerModificationDisabled.Value = input.Disabled; return new Empty(); } public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) { - return new BoolValue { Value = State.TokenIssuerAndOwnerModificationDisabled.Value }; + return new BoolValue + { + Value = !State.TokenIssuerAndOwnerModificationDisabled.Value + }; } } \ No newline at end of file diff --git a/protobuf/token_contract_impl.proto b/protobuf/token_contract_impl.proto index b0689af301..ac9840bce9 100644 --- a/protobuf/token_contract_impl.proto +++ b/protobuf/token_contract_impl.proto @@ -167,7 +167,7 @@ service TokenContractImpl { rpc ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput) returns (google.protobuf.Empty) { } - rpc SetTokenIssuerAndOwnerModificationEnabled(google.protobuf.BoolValue) returns (google.protobuf.Empty) { + rpc SetTokenIssuerAndOwnerModificationDisabled(SetTokenIssuerAndOwnerModificationDisabledInput) returns (google.protobuf.Empty) { } rpc GetTokenIssuerAndOwnerModificationEnabled(google.protobuf.Empty) returns (google.protobuf.BoolValue) { @@ -432,3 +432,7 @@ message ModifyTokenIssuerAndOwnerInput { aelf.Address issuer = 2; aelf.Address owner = 3; } + +message SetTokenIssuerAndOwnerModificationDisabledInput{ + bool disabled = 1; +} \ No newline at end of file From c521d69e8a8e4a7cd3b9bbd93b14c12b55e076be Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 8 Dec 2023 22:01:12 +0800 Subject: [PATCH 009/128] fix: improve semantic accuracy --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 4 ++-- protobuf/token_contract_impl.proto | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 24e44a6560..f44ba64bd8 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -609,12 +609,12 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i return new Empty(); } - public override Empty SetTokenIssuerAndOwnerModificationDisabled(SetTokenIssuerAndOwnerModificationDisabledInput input) + public override Empty SetTokenIssuerAndOwnerModificationEnabled(SetTokenIssuerAndOwnerModificationEnabledInput input) { AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); Assert(input != null, "Invalid input."); - State.TokenIssuerAndOwnerModificationDisabled.Value = input.Disabled; + State.TokenIssuerAndOwnerModificationDisabled.Value = !input.Enabled; return new Empty(); } diff --git a/protobuf/token_contract_impl.proto b/protobuf/token_contract_impl.proto index ac9840bce9..9a7779ec45 100644 --- a/protobuf/token_contract_impl.proto +++ b/protobuf/token_contract_impl.proto @@ -167,7 +167,7 @@ service TokenContractImpl { rpc ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput) returns (google.protobuf.Empty) { } - rpc SetTokenIssuerAndOwnerModificationDisabled(SetTokenIssuerAndOwnerModificationDisabledInput) returns (google.protobuf.Empty) { + rpc SetTokenIssuerAndOwnerModificationEnabled(SetTokenIssuerAndOwnerModificationEnabledInput) returns (google.protobuf.Empty) { } rpc GetTokenIssuerAndOwnerModificationEnabled(google.protobuf.Empty) returns (google.protobuf.BoolValue) { @@ -433,6 +433,6 @@ message ModifyTokenIssuerAndOwnerInput { aelf.Address owner = 3; } -message SetTokenIssuerAndOwnerModificationDisabledInput{ - bool disabled = 1; +message SetTokenIssuerAndOwnerModificationEnabledInput{ + bool enabled = 1; } \ No newline at end of file From 1864d2e3a5c11911b91c6fe5c2ea426935f4e804 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 8 Dec 2023 22:07:47 +0800 Subject: [PATCH 010/128] fix: typo --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index f44ba64bd8..5994b39bcf 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -594,7 +594,7 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i { Assert(!State.TokenIssuerAndOwnerModificationDisabled.Value, "Set token issuer and owner disabled."); Assert(!string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); - Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "InBvalid input issuer."); + Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "Invalid input issuer."); Assert(input.Owner != null && !input.Owner.Value.IsNullOrEmpty(), "Invalid input owner."); var tokenInfo = State.TokenInfos[input.Symbol]; From b403f2fcff6a8b8c7c6d3a274509896b352a3e9a Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 8 Dec 2023 22:38:26 +0800 Subject: [PATCH 011/128] fix: remove redundant message --- protobuf/token_contract.proto | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/protobuf/token_contract.proto b/protobuf/token_contract.proto index 5082990a99..d858bf5f1d 100644 --- a/protobuf/token_contract.proto +++ b/protobuf/token_contract.proto @@ -632,19 +632,6 @@ message TotalResourceTokensMap map value = 1; } -message ChangeTokenIssuerInput -{ - // The token symbol. - string symbol = 1; - // The new token issuer for change. - aelf.Address new_token_Issuer = 2; -} - -message ResetExternalInfoInput { - string symbol = 1; - ExternalInfo external_info = 2; -} - message StringList { repeated string value = 1; } @@ -853,12 +840,6 @@ message CrossChainReceived { aelf.Hash transfer_transaction_id =9; } -message ExternalInfoChanged { - option (aelf.is_event) = true; - string symbol = 1; - ExternalInfo external_info = 2; -} - message TransactionFeeDelegationAdded { option (aelf.is_event) = true; aelf.Address delegator = 1 [(aelf.is_indexed) = true]; From 3a0445d7c76329ee2166e738d5e6743cd52798b8 Mon Sep 17 00:00:00 2001 From: kevin Date: Sat, 9 Dec 2023 20:45:29 +0800 Subject: [PATCH 012/128] feat: add ut --- .../TokenContract_Actions.cs | 4 +- .../BVT/TokenApplicationTests.cs | 76 +++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 5994b39bcf..249afc33ea 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -600,9 +600,9 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i var tokenInfo = State.TokenInfos[input.Symbol]; Assert(tokenInfo != null, "Token is not found."); - Assert(tokenInfo.Owner == null, "Can only set token which does not have owner."); Assert(tokenInfo.Issuer == Context.Sender, "Only token issuer can set token issuer and owner."); - + Assert(tokenInfo.Owner == null, "Can only set token which does not have owner."); + tokenInfo.Issuer = input.Issuer; tokenInfo.Owner = input.Owner; diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index b4aa6e54d7..dba96e1023 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -1227,4 +1227,80 @@ await TokenContractStub.Issue.SendAsync(new IssueInput checkTokenInfo.IsBurnable.ShouldBe(createTokenInput.IsBurnable); checkTokenInfo.ExternalInfo.Value.ShouldBe(createTokenInput.ExternalInfo.Value); } + + [Fact] + public async Task TokenIssuerAndOwnerModification_Test() + { + var result = await TokenContractStub.ModifyTokenIssuerAndOwner.SendWithExceptionAsync(new ModifyTokenIssuerAndOwnerInput()); + result.TransactionResult.Error.ShouldContain("Invalid input symbol."); + + result = await TokenContractStub.ModifyTokenIssuerAndOwner.SendWithExceptionAsync(new ModifyTokenIssuerAndOwnerInput + { + Symbol = "TEST" + }); + result.TransactionResult.Error.ShouldContain("Invalid input issuer."); + + result = await TokenContractStub.ModifyTokenIssuerAndOwner.SendWithExceptionAsync(new ModifyTokenIssuerAndOwnerInput + { + Symbol = "TEST", + Issuer = DefaultAddress + }); + result.TransactionResult.Error.ShouldContain("Invalid input owner."); + + result = await TokenContractStub.ModifyTokenIssuerAndOwner.SendWithExceptionAsync(new ModifyTokenIssuerAndOwnerInput + { + Symbol = "TEST", + Issuer = DefaultAddress, + Owner = DefaultAddress + }); + result.TransactionResult.Error.ShouldContain("Token is not found."); + + result = await TokenContractStubUser.ModifyTokenIssuerAndOwner.SendWithExceptionAsync(new ModifyTokenIssuerAndOwnerInput + { + Symbol = DefaultSymbol, + Issuer = DefaultAddress, + Owner = DefaultAddress + }); + result.TransactionResult.Error.ShouldContain("Only token issuer can set token issuer and owner."); + + result = await TokenContractStub.ModifyTokenIssuerAndOwner.SendWithExceptionAsync(new ModifyTokenIssuerAndOwnerInput + { + Symbol = DefaultSymbol, + Issuer = DefaultAddress, + Owner = DefaultAddress + }); + result.TransactionResult.Error.ShouldContain("Can only set token which does not have owner."); + + var output = await TokenContractStub.GetTokenIssuerAndOwnerModificationEnabled.CallAsync(new Empty()); + output.Value.ShouldBeTrue(); + + result = await TokenContractStub.SetTokenIssuerAndOwnerModificationEnabled.SendWithExceptionAsync( + new SetTokenIssuerAndOwnerModificationEnabledInput + { + Enabled = false + }); + result.TransactionResult.Error.ShouldContain("Unauthorized behavior."); + + var defaultParliament = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + var proposalId = await CreateProposalAsync(TokenContractAddress, + defaultParliament, nameof(TokenContractStub.SetTokenIssuerAndOwnerModificationEnabled), + new SetTokenIssuerAndOwnerModificationEnabledInput + { + Enabled = false + }); + await ApproveWithMinersAsync(proposalId); + await ParliamentContractStub.Release.SendAsync(proposalId); + + output = await TokenContractStub.GetTokenIssuerAndOwnerModificationEnabled.CallAsync(new Empty()); + output.Value.ShouldBeFalse(); + + result = await TokenContractStub.ModifyTokenIssuerAndOwner.SendWithExceptionAsync(new ModifyTokenIssuerAndOwnerInput + { + Symbol = DefaultSymbol, + Issuer = DefaultAddress, + Owner = DefaultAddress + }); + result.TransactionResult.Error.ShouldContain("Set token issuer and owner disabled."); + + } } \ No newline at end of file From 1a520c37196d9b90963df781ec6c3f8fc943dbc8 Mon Sep 17 00:00:00 2001 From: "zecong.nian" Date: Wed, 13 Dec 2023 11:36:04 +0800 Subject: [PATCH 013/128] feat: rename --- protobuf/aelf/core.proto | 2 +- .../Domain/ITransactionFailedResultManager.cs | 33 ------------------- .../ITransactionInvalidResultManager.cs | 33 +++++++++++++++++++ src/AElf.Kernel.Core/CoreKernelAElfModule.cs | 2 +- .../ITransactionInvalidResultService.cs | 33 +++++++++++++++++++ .../TransactionFailedResultService.cs | 33 ------------------- ...ctionValidationStatusFailedEventHandler.cs | 12 +++---- .../TransactionOptions.cs | 2 +- src/AElf.Launcher/appsettings.json | 2 +- .../Services/TransactionResultAppService.cs | 2 +- .../Services/TransactionResultProxyService.cs | 8 ++--- .../BlockChainAppServiceTest.cs | 33 +++++++++++++++++++ .../WebAppTestAElfModule.cs | 5 +++ 13 files changed, 119 insertions(+), 81 deletions(-) delete mode 100644 src/AElf.Kernel.Core/Blockchain/Domain/ITransactionFailedResultManager.cs create mode 100644 src/AElf.Kernel.Core/Blockchain/Domain/ITransactionInvalidResultManager.cs create mode 100644 src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs delete mode 100644 src/AElf.Kernel.TransactionPool/Application/TransactionFailedResultService.cs diff --git a/protobuf/aelf/core.proto b/protobuf/aelf/core.proto index c0ca910f0d..e45d7e581d 100644 --- a/protobuf/aelf/core.proto +++ b/protobuf/aelf/core.proto @@ -72,7 +72,7 @@ message TransactionResult { string error = 10; } -message TransactionValidationFailure{ +message InvalidTransactionResult{ // The transaction id. Hash transaction_id = 1; // The transaction result status. diff --git a/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionFailedResultManager.cs b/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionFailedResultManager.cs deleted file mode 100644 index b2a96da421..0000000000 --- a/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionFailedResultManager.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Linq; -using AElf.Kernel.Blockchain.Infrastructure; -using AElf.Kernel.Infrastructure; - -namespace AElf.Kernel.Blockchain.Domain; - -public interface ITransactionFailedResultManager -{ - Task AddFailedTransactionResultAsync(TransactionValidationFailure transactionResult); - Task GetFailedTransactionResultAsync(Hash transactionId); -} - -public class TransactionFailedResultManager : ITransactionFailedResultManager -{ - private readonly IBlockchainStore _transactionFailedResultStore; - - public TransactionFailedResultManager(IBlockchainStore transactionFailedResultStore) - { - _transactionFailedResultStore = transactionFailedResultStore; - } - - public async Task AddFailedTransactionResultAsync(TransactionValidationFailure transactionResult) - { - await _transactionFailedResultStore.SetAsync(transactionResult.TransactionId.ToStorageKey(), transactionResult); - } - - - public async Task GetFailedTransactionResultAsync(Hash transactionId) - { - return await _transactionFailedResultStore.GetAsync(transactionId.ToStorageKey()); - } - -} \ No newline at end of file diff --git a/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionInvalidResultManager.cs b/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionInvalidResultManager.cs new file mode 100644 index 0000000000..bb98dbda40 --- /dev/null +++ b/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionInvalidResultManager.cs @@ -0,0 +1,33 @@ +using System.Linq; +using AElf.Kernel.Blockchain.Infrastructure; +using AElf.Kernel.Infrastructure; + +namespace AElf.Kernel.Blockchain.Domain; + +public interface ITransactionInvalidResultManager +{ + Task AddFailedTransactionResultAsync(InvalidTransactionResult transactionResult); + Task GetFailedTransactionResultAsync(Hash transactionId); +} + +public class TransactionInvalidResultManager : ITransactionInvalidResultManager +{ + private readonly IBlockchainStore _transactionFailedResultStore; + + public TransactionInvalidResultManager(IBlockchainStore transactionFailedResultStore) + { + _transactionFailedResultStore = transactionFailedResultStore; + } + + public async Task AddFailedTransactionResultAsync(InvalidTransactionResult transactionResult) + { + await _transactionFailedResultStore.SetAsync(transactionResult.TransactionId.ToStorageKey(), transactionResult); + } + + + public async Task GetFailedTransactionResultAsync(Hash transactionId) + { + return await _transactionFailedResultStore.GetAsync(transactionId.ToStorageKey()); + } + +} \ No newline at end of file diff --git a/src/AElf.Kernel.Core/CoreKernelAElfModule.cs b/src/AElf.Kernel.Core/CoreKernelAElfModule.cs index f40455f686..0dcf8b87bb 100644 --- a/src/AElf.Kernel.Core/CoreKernelAElfModule.cs +++ b/src/AElf.Kernel.Core/CoreKernelAElfModule.cs @@ -39,7 +39,7 @@ public override void ConfigureServices(ServiceConfigurationContext context) services.AddStoreKeyPrefixProvide("ti"); services.AddStoreKeyPrefixProvide("tr"); services.AddStoreKeyPrefixProvide("vs"); - services.AddStoreKeyPrefixProvide("tf"); + services.AddStoreKeyPrefixProvide("ir"); services.AddTransient(typeof(IStateStore<>), typeof(StateStore<>)); services.AddSingleton(typeof(INotModifiedCachedStateStore<>), typeof(NotModifiedCachedStateStore<>)); diff --git a/src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs b/src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs new file mode 100644 index 0000000000..2490e8fad4 --- /dev/null +++ b/src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using AElf.Kernel.Blockchain.Domain; +using AElf.Types; +using Volo.Abp.DependencyInjection; + +namespace AElf.Kernel.TransactionPool.Application; + + +public interface ITransactionInvalidResultService +{ + Task AddFailedTransactionResultsAsync(InvalidTransactionResult transactionResult); + Task GetFailedTransactionResultAsync(Hash transactionId); +} + +public class TransactionInvalidResultService : ITransactionInvalidResultService, ITransientDependency +{ + private readonly ITransactionInvalidResultManager _transactionInvalidResultManager; + + public TransactionInvalidResultService(ITransactionInvalidResultManager transactionInvalidResultManager) + { + _transactionInvalidResultManager = transactionInvalidResultManager; + } + + public async Task AddFailedTransactionResultsAsync(InvalidTransactionResult transactionResult) + { + await _transactionInvalidResultManager.AddFailedTransactionResultAsync(transactionResult); + } + + public async Task GetFailedTransactionResultAsync(Hash transactionId) + { + return await _transactionInvalidResultManager.GetFailedTransactionResultAsync(transactionId); + } +} \ No newline at end of file diff --git a/src/AElf.Kernel.TransactionPool/Application/TransactionFailedResultService.cs b/src/AElf.Kernel.TransactionPool/Application/TransactionFailedResultService.cs deleted file mode 100644 index c9b6a23e3c..0000000000 --- a/src/AElf.Kernel.TransactionPool/Application/TransactionFailedResultService.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Threading.Tasks; -using AElf.Kernel.Blockchain.Domain; -using AElf.Types; -using Volo.Abp.DependencyInjection; - -namespace AElf.Kernel.TransactionPool.Application; - - -public interface ITransactionFailedResultService -{ - Task AddFailedTransactionResultsAsync(TransactionValidationFailure transactionResult); - Task GetFailedTransactionResultAsync(Hash transactionId); -} - -public class TransactionFailedResultService : ITransactionFailedResultService, ITransientDependency -{ - private readonly ITransactionFailedResultManager _transactionFailedResultManager; - - public TransactionFailedResultService(ITransactionFailedResultManager transactionFailedResultManager) - { - _transactionFailedResultManager = transactionFailedResultManager; - } - - public async Task AddFailedTransactionResultsAsync(TransactionValidationFailure transactionResult) - { - await _transactionFailedResultManager.AddFailedTransactionResultAsync(transactionResult); - } - - public async Task GetFailedTransactionResultAsync(Hash transactionId) - { - return await _transactionFailedResultManager.GetFailedTransactionResultAsync(transactionId); - } -} \ No newline at end of file diff --git a/src/AElf.Kernel.TransactionPool/Handler/TransactionValidationStatusFailedEventHandler.cs b/src/AElf.Kernel.TransactionPool/Handler/TransactionValidationStatusFailedEventHandler.cs index ec6b2ff26d..efef84e542 100644 --- a/src/AElf.Kernel.TransactionPool/Handler/TransactionValidationStatusFailedEventHandler.cs +++ b/src/AElf.Kernel.TransactionPool/Handler/TransactionValidationStatusFailedEventHandler.cs @@ -19,25 +19,25 @@ public class TransactionValidationStatusFailedEventHandler : TransactionResultStatus.Failed, TransactionResultStatus.NodeValidationFailed, TransactionResultStatus.Conflict }; - private readonly ITransactionFailedResultService _transactionFailedResultService; + private readonly ITransactionInvalidResultService _transactionInvalidResultService; private readonly TransactionOptions _transactionOptions; public TransactionValidationStatusFailedEventHandler( IOptionsMonitor transactionOptionsMonitor, - ITransactionFailedResultService transactionFailedResultService) + ITransactionInvalidResultService transactionInvalidResultService) { - _transactionFailedResultService = transactionFailedResultService; + _transactionInvalidResultService = transactionInvalidResultService; _transactionOptions = transactionOptionsMonitor.CurrentValue; } public Task HandleEventAsync(TransactionValidationStatusChangedEvent eventData) { if (!FailStatus.Contains(eventData.TransactionResultStatus)) return Task.CompletedTask; - if (!_transactionOptions.SaveInvalidTransactionResult) return Task.CompletedTask; + if (!_transactionOptions.StoreInvalidTransactionResultEnabled) return Task.CompletedTask; // save to storage - _transactionFailedResultService.AddFailedTransactionResultsAsync( - new TransactionValidationFailure + _transactionInvalidResultService.AddFailedTransactionResultsAsync( + new InvalidTransactionResult { TransactionId = eventData.TransactionId, Status = eventData.TransactionResultStatus, diff --git a/src/AElf.Kernel.TransactionPool/TransactionOptions.cs b/src/AElf.Kernel.TransactionPool/TransactionOptions.cs index 4170415511..491ddd51be 100644 --- a/src/AElf.Kernel.TransactionPool/TransactionOptions.cs +++ b/src/AElf.Kernel.TransactionPool/TransactionOptions.cs @@ -22,7 +22,7 @@ public class TransactionOptions /// /// Configuration whether to save failed transaction results /// - public bool SaveInvalidTransactionResult { get; set; } + public bool StoreInvalidTransactionResultEnabled { get; set; } } \ No newline at end of file diff --git a/src/AElf.Launcher/appsettings.json b/src/AElf.Launcher/appsettings.json index a7a524721a..45c0923284 100644 --- a/src/AElf.Launcher/appsettings.json +++ b/src/AElf.Launcher/appsettings.json @@ -31,7 +31,7 @@ "MinerIncreaseInterval": 31536000 }, "Transaction": { - "SaveInvalidTransactionResult" : true, + "StoreInvalidTransactionResultEnabled" : false, "PoolLimit": 10240 }, "BasicAuth": { diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs index fc690541ba..5143de62e3 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs @@ -104,7 +104,7 @@ public async Task GetTransactionResultAsync(string transac } var failedTransactionResult = - await _transactionResultProxyService.TransactionFailedResultService.GetFailedTransactionResultAsync( + await _transactionResultProxyService.TransactionInvalidResultService.GetFailedTransactionResultAsync( transactionIdHash); if (failedTransactionResult != null) { diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs index cf02dcf037..8602bfa3ed 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs @@ -7,21 +7,21 @@ public interface ITransactionResultProxyService { ITransactionPoolService TransactionPoolService { get; } ITransactionResultQueryService TransactionResultQueryService { get; } - ITransactionFailedResultService TransactionFailedResultService { get; } + ITransactionInvalidResultService TransactionInvalidResultService { get; } } public class TransactionResultProxyService : ITransactionResultProxyService { public TransactionResultProxyService(ITransactionPoolService transactionPoolService, ITransactionResultQueryService transactionResultQueryService, - ITransactionFailedResultService transactionFailedResultService) + ITransactionInvalidResultService transactionInvalidResultService) { TransactionPoolService = transactionPoolService; TransactionResultQueryService = transactionResultQueryService; - TransactionFailedResultService = transactionFailedResultService; + TransactionInvalidResultService = transactionInvalidResultService; } public ITransactionPoolService TransactionPoolService { get; set; } public ITransactionResultQueryService TransactionResultQueryService { get; set; } - public ITransactionFailedResultService TransactionFailedResultService { get; } + public ITransactionInvalidResultService TransactionInvalidResultService { get; } } \ No newline at end of file diff --git a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs index b898977dc3..fadfe062eb 100644 --- a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs +++ b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs @@ -15,6 +15,7 @@ using AElf.Kernel.SmartContract.Application; using AElf.Kernel.SmartContract.Domain; using AElf.Kernel.Token; +using AElf.Kernel.TransactionPool.Handler; using AElf.Kernel.TransactionPool.Infrastructure; using AElf.OS; using AElf.Runtime.CSharp; @@ -44,6 +45,8 @@ public sealed class BlockChainAppServiceTest : WebAppTestBase private readonly ISmartContractAddressService _smartContractAddressService; private readonly ITransactionResultStatusCacheProvider _transactionResultStatusCacheProvider; private readonly TransactionValidationStatusChangedEventHandler _transactionValidationStatusChangedEventHandler; + private readonly TransactionValidationStatusFailedEventHandler _transactionExecutionValidationFailedEventHandler; + private readonly ITransactionResultProxyService _transactionResultProxyService; private readonly ITxHub _txHub; private IReadOnlyDictionary _codes; @@ -58,6 +61,9 @@ public BlockChainAppServiceTest(ITestOutputHelper outputHelper) : base(outputHel _osTestHelper = GetRequiredService(); _accountService = GetRequiredService(); _blockStateSetManger = GetRequiredService(); + _transactionResultProxyService = GetRequiredService(); + _transactionExecutionValidationFailedEventHandler = + GetRequiredService(); _transactionValidationStatusChangedEventHandler = GetRequiredService(); } @@ -1822,4 +1828,31 @@ await PostResponseAsObjectAsync("/api/blockChain/sendTran response.Transaction.Params.ShouldNotBe( AddOptionInput.Parser.ParseFrom(transaction.Params).ToString()); } + + [Fact] + public async Task TransactionInvalidResultTest() + { + + var txId = HashHelper.ComputeFrom("TransactionInvalidResultTest"); + await _transactionExecutionValidationFailedEventHandler.HandleEventAsync(new TransactionValidationStatusChangedEvent + { + TransactionId = txId, + TransactionResultStatus = TransactionResultStatus.NodeValidationFailed, + Error = "tx error" + }); + + var invalidResult = await _transactionResultProxyService.TransactionInvalidResultService + .GetFailedTransactionResultAsync(txId); + invalidResult.ShouldNotBeNull(); + invalidResult.Status.ShouldBe(TransactionResultStatus.NodeValidationFailed); + invalidResult.Error.ShouldBe("tx error"); + + var response = await GetResponseAsObjectAsync( + $"/api/blockChain/transactionResult?transactionId={txId.ToHex()}"); + response.ShouldNotBeNull(); + response.Status.ShouldBe(TransactionResultStatus.NodeValidationFailed.ToString().ToUpper()); + response.Error.ShouldBe("tx error"); + + } + } \ No newline at end of file diff --git a/test/AElf.WebApp.Application.TestBase/WebAppTestAElfModule.cs b/test/AElf.WebApp.Application.TestBase/WebAppTestAElfModule.cs index c2d6783b8f..c9dcf809ec 100644 --- a/test/AElf.WebApp.Application.TestBase/WebAppTestAElfModule.cs +++ b/test/AElf.WebApp.Application.TestBase/WebAppTestAElfModule.cs @@ -6,6 +6,7 @@ using AElf.Kernel.FeeCalculation; using AElf.Kernel.SmartContract.Application; using AElf.Kernel.SmartContract.ExecutionPluginForMethodFee; +using AElf.Kernel.TransactionPool; using AElf.Modularity; using AElf.OS; using AElf.OS.Network.Application; @@ -88,5 +89,9 @@ public override void ConfigureServices(ServiceConfigurationContext context) options.UserName = BasicAuth.DefaultUserName; options.Password = BasicAuth.DefaultPassword; }); + Configure(o => { + o.PoolLimit = 20; + o.StoreInvalidTransactionResultEnabled = true; + }); } } \ No newline at end of file From 84f76db373279310e7ab2ae3f3a26eb314f74158 Mon Sep 17 00:00:00 2001 From: "zecong.nian" Date: Thu, 14 Dec 2023 10:35:21 +0800 Subject: [PATCH 014/128] feat: rename and clear --- .../Domain/ITransactionResultManager.cs | 29 +------------------ .../ITransactionInvalidResultService.cs | 4 +-- .../ChainApplicationWebAppAElfModule.cs | 1 - .../Services/TransactionResultAppService.cs | 2 +- .../BlockChainAppServiceTest.cs | 2 +- 5 files changed, 5 insertions(+), 33 deletions(-) diff --git a/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionResultManager.cs b/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionResultManager.cs index 964841541c..e2f2176886 100644 --- a/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionResultManager.cs +++ b/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionResultManager.cs @@ -7,19 +7,15 @@ namespace AElf.Kernel.Blockchain.Domain; public interface ITransactionResultManager { Task AddTransactionResultAsync(TransactionResult transactionResult, Hash disambiguationHash); + Task AddTransactionResultsAsync(IList transactionResults, Hash disambiguationHash); Task GetTransactionResultAsync(Hash txId, Hash disambiguationHash); Task> GetTransactionResultsAsync(IList txIds, Hash disambiguationHash); Task HasTransactionResultAsync(Hash transactionId, Hash disambiguationHash); - Task AddFailedTransactionResultAsync(TransactionResult transactionResult); - Task AddFailedTransactionResultsAsync(IList transactionResults); - Task GetFailedTransactionResultAsync(Hash transactionId); - Task> GetFailedTransactionResultsAsync(IList txIds); } public class TransactionResultManager : ITransactionResultManager { - private const string FailStorageKeyPrefix = "FAIL:"; private readonly IBlockchainStore _transactionResultStore; public TransactionResultManager(IBlockchainStore transactionResultStore) @@ -27,29 +23,6 @@ public TransactionResultManager(IBlockchainStore transactionR _transactionResultStore = transactionResultStore; } - public async Task AddFailedTransactionResultAsync(TransactionResult transactionResult) - { - await _transactionResultStore.SetAsync(FailStorageKeyPrefix + transactionResult.TransactionId.ToStorageKey(), transactionResult); - } - - public async Task AddFailedTransactionResultsAsync(IList transactionResults) - { - await _transactionResultStore.SetAllAsync( - transactionResults.ToDictionary(tx => FailStorageKeyPrefix + tx.TransactionId.ToStorageKey(), t => t) - ); - } - - public async Task GetFailedTransactionResultAsync(Hash transactionId) - { - return await _transactionResultStore.GetAsync(FailStorageKeyPrefix + transactionId.ToStorageKey()); - } - - public async Task> GetFailedTransactionResultsAsync(IList txIds) - { - return await _transactionResultStore.GetAllAsync(txIds.Select(id => FailStorageKeyPrefix + id.ToStorageKey()).ToList()); - } - - public async Task AddTransactionResultAsync(TransactionResult transactionResult, Hash disambiguationHash) { await _transactionResultStore.SetAsync( diff --git a/src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs b/src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs index 2490e8fad4..fa3f912dc4 100644 --- a/src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs +++ b/src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs @@ -9,7 +9,7 @@ namespace AElf.Kernel.TransactionPool.Application; public interface ITransactionInvalidResultService { Task AddFailedTransactionResultsAsync(InvalidTransactionResult transactionResult); - Task GetFailedTransactionResultAsync(Hash transactionId); + Task GetTransactionInvalidResultAsync(Hash transactionId); } public class TransactionInvalidResultService : ITransactionInvalidResultService, ITransientDependency @@ -26,7 +26,7 @@ public async Task AddFailedTransactionResultsAsync(InvalidTransactionResult tran await _transactionInvalidResultManager.AddFailedTransactionResultAsync(transactionResult); } - public async Task GetFailedTransactionResultAsync(Hash transactionId) + public async Task GetTransactionInvalidResultAsync(Hash transactionId) { return await _transactionInvalidResultManager.GetFailedTransactionResultAsync(transactionId); } diff --git a/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs b/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs index d6e09b36f8..d5820e1828 100644 --- a/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs +++ b/src/AElf.WebApp.Application.Chain/ChainApplicationWebAppAElfModule.cs @@ -22,6 +22,5 @@ public override void ConfigureServices(ServiceConfigurationContext context) context.Services .AddSingleton(); - } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs index 5143de62e3..e187c47a22 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs @@ -104,7 +104,7 @@ public async Task GetTransactionResultAsync(string transac } var failedTransactionResult = - await _transactionResultProxyService.TransactionInvalidResultService.GetFailedTransactionResultAsync( + await _transactionResultProxyService.TransactionInvalidResultService.GetTransactionInvalidResultAsync( transactionIdHash); if (failedTransactionResult != null) { diff --git a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs index fadfe062eb..ce435f91ab 100644 --- a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs +++ b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs @@ -1842,7 +1842,7 @@ await _transactionExecutionValidationFailedEventHandler.HandleEventAsync(new Tra }); var invalidResult = await _transactionResultProxyService.TransactionInvalidResultService - .GetFailedTransactionResultAsync(txId); + .GetTransactionInvalidResultAsync(txId); invalidResult.ShouldNotBeNull(); invalidResult.Status.ShouldBe(TransactionResultStatus.NodeValidationFailed); invalidResult.Error.ShouldBe("tx error"); From dddae354668ca9ee24883ad2e7739e019b3ad38e Mon Sep 17 00:00:00 2001 From: "zecong.nian" Date: Thu, 14 Dec 2023 10:59:52 +0800 Subject: [PATCH 015/128] feat: rename --- .../Domain/ITransactionInvalidResultManager.cs | 18 +++++++++--------- .../ITransactionInvalidResultService.cs | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionInvalidResultManager.cs b/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionInvalidResultManager.cs index bb98dbda40..f06198bd6e 100644 --- a/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionInvalidResultManager.cs +++ b/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionInvalidResultManager.cs @@ -6,28 +6,28 @@ namespace AElf.Kernel.Blockchain.Domain; public interface ITransactionInvalidResultManager { - Task AddFailedTransactionResultAsync(InvalidTransactionResult transactionResult); - Task GetFailedTransactionResultAsync(Hash transactionId); + Task AddTransactionInvalidResultAsync(InvalidTransactionResult transactionResult); + Task GetTransactionInvalidResultAsync(Hash transactionId); } public class TransactionInvalidResultManager : ITransactionInvalidResultManager { - private readonly IBlockchainStore _transactionFailedResultStore; + private readonly IBlockchainStore _transactionInvalidResultStore; - public TransactionInvalidResultManager(IBlockchainStore transactionFailedResultStore) + public TransactionInvalidResultManager(IBlockchainStore transactionInvalidResultStore) { - _transactionFailedResultStore = transactionFailedResultStore; + _transactionInvalidResultStore = transactionInvalidResultStore; } - public async Task AddFailedTransactionResultAsync(InvalidTransactionResult transactionResult) + public async Task AddTransactionInvalidResultAsync(InvalidTransactionResult transactionResult) { - await _transactionFailedResultStore.SetAsync(transactionResult.TransactionId.ToStorageKey(), transactionResult); + await _transactionInvalidResultStore.SetAsync(transactionResult.TransactionId.ToStorageKey(), transactionResult); } - public async Task GetFailedTransactionResultAsync(Hash transactionId) + public async Task GetTransactionInvalidResultAsync(Hash transactionId) { - return await _transactionFailedResultStore.GetAsync(transactionId.ToStorageKey()); + return await _transactionInvalidResultStore.GetAsync(transactionId.ToStorageKey()); } } \ No newline at end of file diff --git a/src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs b/src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs index fa3f912dc4..63b51561af 100644 --- a/src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs +++ b/src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs @@ -23,11 +23,11 @@ public TransactionInvalidResultService(ITransactionInvalidResultManager transact public async Task AddFailedTransactionResultsAsync(InvalidTransactionResult transactionResult) { - await _transactionInvalidResultManager.AddFailedTransactionResultAsync(transactionResult); + await _transactionInvalidResultManager.AddTransactionInvalidResultAsync(transactionResult); } public async Task GetTransactionInvalidResultAsync(Hash transactionId) { - return await _transactionInvalidResultManager.GetFailedTransactionResultAsync(transactionId); + return await _transactionInvalidResultManager.GetTransactionInvalidResultAsync(transactionId); } } \ No newline at end of file From 4f6f2a55bf033007acf99b3074bf1d77b09aa837 Mon Sep 17 00:00:00 2001 From: "zecong.nian" Date: Thu, 14 Dec 2023 23:31:50 +0800 Subject: [PATCH 016/128] fix:cr issue --- .../IInvalidTransactionResultManager.cs | 33 +++++++++++++++++++ .../ITransactionInvalidResultManager.cs | 33 ------------------- .../IInvalidTransactionResultService.cs | 33 +++++++++++++++++++ .../ITransactionInvalidResultService.cs | 33 ------------------- ...ctionValidationStatusFailedEventHandler.cs | 20 +++++------ .../Services/TransactionResultAppService.cs | 25 ++++++++------ .../Services/TransactionResultProxyService.cs | 8 ++--- .../BlockChainAppServiceTest.cs | 8 ++--- 8 files changed, 98 insertions(+), 95 deletions(-) create mode 100644 src/AElf.Kernel.Core/Blockchain/Domain/IInvalidTransactionResultManager.cs delete mode 100644 src/AElf.Kernel.Core/Blockchain/Domain/ITransactionInvalidResultManager.cs create mode 100644 src/AElf.Kernel.TransactionPool/Application/IInvalidTransactionResultService.cs delete mode 100644 src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs diff --git a/src/AElf.Kernel.Core/Blockchain/Domain/IInvalidTransactionResultManager.cs b/src/AElf.Kernel.Core/Blockchain/Domain/IInvalidTransactionResultManager.cs new file mode 100644 index 0000000000..9c29556ad1 --- /dev/null +++ b/src/AElf.Kernel.Core/Blockchain/Domain/IInvalidTransactionResultManager.cs @@ -0,0 +1,33 @@ +using System.Linq; +using AElf.Kernel.Blockchain.Infrastructure; +using AElf.Kernel.Infrastructure; + +namespace AElf.Kernel.Blockchain.Domain; + +public interface IInvalidTransactionResultManager +{ + Task AddInvalidTransactionResultAsync(InvalidTransactionResult transactionResult); + Task GetInvalidTransactionResultAsync(Hash transactionId); +} + +public class InvalidTransactionResultManager : IInvalidTransactionResultManager +{ + private readonly IBlockchainStore _invalidTransactionResultStore; + + public InvalidTransactionResultManager(IBlockchainStore invalidTransactionResultStore) + { + _invalidTransactionResultStore = invalidTransactionResultStore; + } + + public async Task AddInvalidTransactionResultAsync(InvalidTransactionResult transactionResult) + { + await _invalidTransactionResultStore.SetAsync(transactionResult.TransactionId.ToStorageKey(), transactionResult); + } + + + public async Task GetInvalidTransactionResultAsync(Hash transactionId) + { + return await _invalidTransactionResultStore.GetAsync(transactionId.ToStorageKey()); + } + +} \ No newline at end of file diff --git a/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionInvalidResultManager.cs b/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionInvalidResultManager.cs deleted file mode 100644 index f06198bd6e..0000000000 --- a/src/AElf.Kernel.Core/Blockchain/Domain/ITransactionInvalidResultManager.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Linq; -using AElf.Kernel.Blockchain.Infrastructure; -using AElf.Kernel.Infrastructure; - -namespace AElf.Kernel.Blockchain.Domain; - -public interface ITransactionInvalidResultManager -{ - Task AddTransactionInvalidResultAsync(InvalidTransactionResult transactionResult); - Task GetTransactionInvalidResultAsync(Hash transactionId); -} - -public class TransactionInvalidResultManager : ITransactionInvalidResultManager -{ - private readonly IBlockchainStore _transactionInvalidResultStore; - - public TransactionInvalidResultManager(IBlockchainStore transactionInvalidResultStore) - { - _transactionInvalidResultStore = transactionInvalidResultStore; - } - - public async Task AddTransactionInvalidResultAsync(InvalidTransactionResult transactionResult) - { - await _transactionInvalidResultStore.SetAsync(transactionResult.TransactionId.ToStorageKey(), transactionResult); - } - - - public async Task GetTransactionInvalidResultAsync(Hash transactionId) - { - return await _transactionInvalidResultStore.GetAsync(transactionId.ToStorageKey()); - } - -} \ No newline at end of file diff --git a/src/AElf.Kernel.TransactionPool/Application/IInvalidTransactionResultService.cs b/src/AElf.Kernel.TransactionPool/Application/IInvalidTransactionResultService.cs new file mode 100644 index 0000000000..0a1d13c0bf --- /dev/null +++ b/src/AElf.Kernel.TransactionPool/Application/IInvalidTransactionResultService.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using AElf.Kernel.Blockchain.Domain; +using AElf.Types; +using Volo.Abp.DependencyInjection; + +namespace AElf.Kernel.TransactionPool.Application; + + +public interface IInvalidTransactionResultService +{ + Task AddInvalidTransactionResultsAsync(InvalidTransactionResult transactionResult); + Task GetInvalidTransactionResultAsync(Hash transactionId); +} + +public class InvalidTransactionResultService : IInvalidTransactionResultService, ITransientDependency +{ + private readonly IInvalidTransactionResultManager _invalidTransactionResultManager; + + public InvalidTransactionResultService(IInvalidTransactionResultManager invalidTransactionResultManager) + { + _invalidTransactionResultManager = invalidTransactionResultManager; + } + + public async Task AddInvalidTransactionResultsAsync(InvalidTransactionResult transactionResult) + { + await _invalidTransactionResultManager.AddInvalidTransactionResultAsync(transactionResult); + } + + public async Task GetInvalidTransactionResultAsync(Hash transactionId) + { + return await _invalidTransactionResultManager.GetInvalidTransactionResultAsync(transactionId); + } +} \ No newline at end of file diff --git a/src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs b/src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs deleted file mode 100644 index 63b51561af..0000000000 --- a/src/AElf.Kernel.TransactionPool/Application/ITransactionInvalidResultService.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Threading.Tasks; -using AElf.Kernel.Blockchain.Domain; -using AElf.Types; -using Volo.Abp.DependencyInjection; - -namespace AElf.Kernel.TransactionPool.Application; - - -public interface ITransactionInvalidResultService -{ - Task AddFailedTransactionResultsAsync(InvalidTransactionResult transactionResult); - Task GetTransactionInvalidResultAsync(Hash transactionId); -} - -public class TransactionInvalidResultService : ITransactionInvalidResultService, ITransientDependency -{ - private readonly ITransactionInvalidResultManager _transactionInvalidResultManager; - - public TransactionInvalidResultService(ITransactionInvalidResultManager transactionInvalidResultManager) - { - _transactionInvalidResultManager = transactionInvalidResultManager; - } - - public async Task AddFailedTransactionResultsAsync(InvalidTransactionResult transactionResult) - { - await _transactionInvalidResultManager.AddTransactionInvalidResultAsync(transactionResult); - } - - public async Task GetTransactionInvalidResultAsync(Hash transactionId) - { - return await _transactionInvalidResultManager.GetTransactionInvalidResultAsync(transactionId); - } -} \ No newline at end of file diff --git a/src/AElf.Kernel.TransactionPool/Handler/TransactionValidationStatusFailedEventHandler.cs b/src/AElf.Kernel.TransactionPool/Handler/TransactionValidationStatusFailedEventHandler.cs index efef84e542..414cc76036 100644 --- a/src/AElf.Kernel.TransactionPool/Handler/TransactionValidationStatusFailedEventHandler.cs +++ b/src/AElf.Kernel.TransactionPool/Handler/TransactionValidationStatusFailedEventHandler.cs @@ -14,40 +14,38 @@ public class TransactionValidationStatusFailedEventHandler : ILocalEventHandler, ITransientDependency { - private static readonly IEnumerable FailStatus = new List + private readonly IEnumerable _failStatus = new List { TransactionResultStatus.Failed, TransactionResultStatus.NodeValidationFailed, TransactionResultStatus.Conflict }; - private readonly ITransactionInvalidResultService _transactionInvalidResultService; + private readonly IInvalidTransactionResultService _invalidTransactionResultService; private readonly TransactionOptions _transactionOptions; public TransactionValidationStatusFailedEventHandler( IOptionsMonitor transactionOptionsMonitor, - ITransactionInvalidResultService transactionInvalidResultService) + IInvalidTransactionResultService invalidTransactionResultService) { - _transactionInvalidResultService = transactionInvalidResultService; + _invalidTransactionResultService = invalidTransactionResultService; _transactionOptions = transactionOptionsMonitor.CurrentValue; } - public Task HandleEventAsync(TransactionValidationStatusChangedEvent eventData) + public async Task HandleEventAsync(TransactionValidationStatusChangedEvent eventData) { - if (!FailStatus.Contains(eventData.TransactionResultStatus)) return Task.CompletedTask; - if (!_transactionOptions.StoreInvalidTransactionResultEnabled) return Task.CompletedTask; + if (!_failStatus.Contains(eventData.TransactionResultStatus)) return; + if (!_transactionOptions.StoreInvalidTransactionResultEnabled) return; // save to storage - _transactionInvalidResultService.AddFailedTransactionResultsAsync( + await _invalidTransactionResultService.AddInvalidTransactionResultsAsync( new InvalidTransactionResult { TransactionId = eventData.TransactionId, Status = eventData.TransactionResultStatus, Error = TakeErrorMessage(eventData.Error) }); - - return Task.CompletedTask; } - public static string TakeErrorMessage(string transactionResultError) + private string TakeErrorMessage(string transactionResultError) { if (string.IsNullOrWhiteSpace(transactionResultError)) return null; diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs index e187c47a22..483dbb4366 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionResultAppService.cs @@ -7,6 +7,7 @@ using AElf.Kernel.Blockchain.Application; using AElf.Kernel.Blockchain.Domain; using AElf.Kernel.SmartContract.Application; +using AElf.Kernel.TransactionPool; using AElf.Types; using AElf.WebApp.Application.Chain.Dto; using AElf.WebApp.Application.Chain.Infrastructure; @@ -39,6 +40,7 @@ public class TransactionResultAppService : AElfAppService, ITransactionResultApp private readonly ITransactionResultProxyService _transactionResultProxyService; private readonly ITransactionResultStatusCacheProvider _transactionResultStatusCacheProvider; private readonly WebAppOptions _webAppOptions; + private readonly TransactionOptions _transactionOptions; public TransactionResultAppService(ITransactionResultProxyService transactionResultProxyService, ITransactionManager transactionManager, @@ -46,7 +48,7 @@ public TransactionResultAppService(ITransactionResultProxyService transactionRes ITransactionReadOnlyExecutionService transactionReadOnlyExecutionService, IObjectMapper objectMapper, ITransactionResultStatusCacheProvider transactionResultStatusCacheProvider, - IOptionsMonitor optionsSnapshot) + IOptionsMonitor optionsSnapshot, IOptionsMonitor transactionOptions) { _transactionResultProxyService = transactionResultProxyService; _transactionManager = transactionManager; @@ -54,6 +56,7 @@ public TransactionResultAppService(ITransactionResultProxyService transactionRes _transactionReadOnlyExecutionService = transactionReadOnlyExecutionService; _objectMapper = objectMapper; _transactionResultStatusCacheProvider = transactionResultStatusCacheProvider; + _transactionOptions = transactionOptions.CurrentValue; _webAppOptions = optionsSnapshot.CurrentValue; Logger = NullLogger.Instance; @@ -102,16 +105,18 @@ public async Task GetTransactionResultAsync(string transac TransactionErrorResolver.TakeErrorMessage(validationStatus.Error, _webAppOptions.IsDebugMode); return output; } - - var failedTransactionResult = - await _transactionResultProxyService.TransactionInvalidResultService.GetTransactionInvalidResultAsync( - transactionIdHash); - if (failedTransactionResult != null) + + if (_transactionOptions.StoreInvalidTransactionResultEnabled) { - output.Status = failedTransactionResult.Status.ToString().ToUpper(); - output.Error = - TransactionErrorResolver.TakeErrorMessage(failedTransactionResult.Error, _webAppOptions.IsDebugMode); - return output; + var failedTransactionResult = + await _transactionResultProxyService.InvalidTransactionResultService.GetInvalidTransactionResultAsync( + transactionIdHash); + if (failedTransactionResult != null) + { + output.Status = failedTransactionResult.Status.ToString().ToUpper(); + output.Error = failedTransactionResult.Error; + return output; + } } return output; diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs index 8602bfa3ed..9d13296930 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionResultProxyService.cs @@ -7,21 +7,21 @@ public interface ITransactionResultProxyService { ITransactionPoolService TransactionPoolService { get; } ITransactionResultQueryService TransactionResultQueryService { get; } - ITransactionInvalidResultService TransactionInvalidResultService { get; } + IInvalidTransactionResultService InvalidTransactionResultService { get; } } public class TransactionResultProxyService : ITransactionResultProxyService { public TransactionResultProxyService(ITransactionPoolService transactionPoolService, ITransactionResultQueryService transactionResultQueryService, - ITransactionInvalidResultService transactionInvalidResultService) + IInvalidTransactionResultService invalidTransactionResultService) { TransactionPoolService = transactionPoolService; TransactionResultQueryService = transactionResultQueryService; - TransactionInvalidResultService = transactionInvalidResultService; + InvalidTransactionResultService = invalidTransactionResultService; } public ITransactionPoolService TransactionPoolService { get; set; } public ITransactionResultQueryService TransactionResultQueryService { get; set; } - public ITransactionInvalidResultService TransactionInvalidResultService { get; } + public IInvalidTransactionResultService InvalidTransactionResultService { get; } } \ No newline at end of file diff --git a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs index ce435f91ab..7aec5b5cfd 100644 --- a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs +++ b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs @@ -1830,10 +1830,10 @@ await PostResponseAsObjectAsync("/api/blockChain/sendTran } [Fact] - public async Task TransactionInvalidResultTest() + public async Task InvalidTransactionResultTest() { - var txId = HashHelper.ComputeFrom("TransactionInvalidResultTest"); + var txId = HashHelper.ComputeFrom("InvalidTransactionResultTest"); await _transactionExecutionValidationFailedEventHandler.HandleEventAsync(new TransactionValidationStatusChangedEvent { TransactionId = txId, @@ -1841,8 +1841,8 @@ await _transactionExecutionValidationFailedEventHandler.HandleEventAsync(new Tra Error = "tx error" }); - var invalidResult = await _transactionResultProxyService.TransactionInvalidResultService - .GetTransactionInvalidResultAsync(txId); + var invalidResult = await _transactionResultProxyService.InvalidTransactionResultService + .GetInvalidTransactionResultAsync(txId); invalidResult.ShouldNotBeNull(); invalidResult.Status.ShouldBe(TransactionResultStatus.NodeValidationFailed); invalidResult.Error.ShouldBe("tx error"); From 2bf43ff805b76c02fcef487b05a356558839e692 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Mon, 8 Jan 2024 23:42:52 +0800 Subject: [PATCH 017/128] Add ProfitContract state to limit the max profit receiving period count. --- .../AElf.Contracts.Profit/ProfitContract.cs | 32 ++++++++++---- .../ProfitContractConstants.cs | 2 +- .../ProfitContractState.cs | 2 + contract/AElf.Contracts.Profit/ViewMethods.cs | 8 ++++ protobuf/profit_contract.proto | 7 ++++ .../ProfitContractTestConstants.cs | 1 + .../ProfitTests.cs | 42 +++++++++++++++++++ 7 files changed, 86 insertions(+), 8 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ProfitContract.cs b/contract/AElf.Contracts.Profit/ProfitContract.cs index c3c09b3aba..76d75044a6 100644 --- a/contract/AElf.Contracts.Profit/ProfitContract.cs +++ b/contract/AElf.Contracts.Profit/ProfitContract.cs @@ -807,6 +807,16 @@ public override Empty ClaimProfits(ClaimProfitsInput input) return new Empty(); } + public override Empty SetMaximumProfitReceivingPeriodCount(Int32Value input) + { + ValidateContractState(State.ParliamentContract, SmartContractConstants.ParliamentContractSystemName); + Assert(Context.Sender == State.ParliamentContract.GetDefaultOrganizationAddress.Call(new Empty()), + "No permission."); + Assert(input.Value > 0, "Invalid maximum profit receiving period count."); + State.MaximumProfitReceivingPeriodCount.Value = input.Value; + return new Empty(); + } + private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail profitDetail, Address beneficiary, bool isView = false, string targetSymbol = null) { @@ -818,13 +828,12 @@ private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail pr foreach (var symbol in symbols) { var totalAmount = 0L; - for (var period = profitDetail.LastProfitPeriod; - period <= (profitDetail.EndPeriod == long.MaxValue - ? Math.Min(scheme.CurrentPeriod - 1, - profitDetail.LastProfitPeriod.Add(ProfitContractConstants - .MaximumProfitReceivingPeriodCountOfOneTime)) - : Math.Min(scheme.CurrentPeriod - 1, profitDetail.EndPeriod)); - period++) + var maximumProfitReceivingPeriodCount = GetMaximumProfitReceivingPeriodCount(); + var targetPeriod = Math.Min(scheme.CurrentPeriod - 1, profitDetail.EndPeriod); + var maxProfitPeriod = profitDetail.EndPeriod == long.MaxValue + ? Math.Min(scheme.CurrentPeriod - 1, profitDetail.LastProfitPeriod.Add(maximumProfitReceivingPeriodCount)) + : Math.Min(targetPeriod, profitDetail.LastProfitPeriod.Add(maximumProfitReceivingPeriodCount)); + for (var period = profitDetail.LastProfitPeriod; period <= maxProfitPeriod; period++) { var periodToPrint = period; var detailToPrint = profitDetail; @@ -885,6 +894,15 @@ private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail pr return profitsMap; } + + private int GetMaximumProfitReceivingPeriodCount() + { + var maxPeriodCount = State.MaximumProfitReceivingPeriodCount.Value; + maxPeriodCount = maxPeriodCount == 0 + ? ProfitContractConstants.DefaultMaximumProfitReceivingPeriodCountOfOneTime + : maxPeriodCount; + return maxPeriodCount; + } private void ValidateContractState(ContractReferenceState state, string contractSystemName) { diff --git a/contract/AElf.Contracts.Profit/ProfitContractConstants.cs b/contract/AElf.Contracts.Profit/ProfitContractConstants.cs index 59b80b971f..0aaf080aa5 100644 --- a/contract/AElf.Contracts.Profit/ProfitContractConstants.cs +++ b/contract/AElf.Contracts.Profit/ProfitContractConstants.cs @@ -6,5 +6,5 @@ public class ProfitContractConstants public const int DefaultProfitReceivingDuePeriodCount = 10; public const int MaximumProfitReceivingDuePeriodCount = 1024; public const int TokenAmountLimit = 5; - public const int MaximumProfitReceivingPeriodCountOfOneTime = 100; + public const int DefaultMaximumProfitReceivingPeriodCountOfOneTime = 100; } \ No newline at end of file diff --git a/contract/AElf.Contracts.Profit/ProfitContractState.cs b/contract/AElf.Contracts.Profit/ProfitContractState.cs index 7a525e3ff6..b0046830e0 100644 --- a/contract/AElf.Contracts.Profit/ProfitContractState.cs +++ b/contract/AElf.Contracts.Profit/ProfitContractState.cs @@ -17,4 +17,6 @@ public partial class ProfitContractState : ContractState public MappedState TransactionFees { get; set; } public SingletonState MethodFeeController { get; set; } + + public SingletonState MaximumProfitReceivingPeriodCount { get; set; } } \ No newline at end of file diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index 31d8c0d4d6..5780d28b2e 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -133,4 +133,12 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) Value = { profitsDict } }; } + + public override Int32Value GetMaximumProfitReceivingPeriodCount(Empty input) + { + return new Int32Value + { + Value = GetMaximumProfitReceivingPeriodCount() + }; + } } \ No newline at end of file diff --git a/protobuf/profit_contract.proto b/protobuf/profit_contract.proto index 1326da62ff..0f84a870e5 100644 --- a/protobuf/profit_contract.proto +++ b/protobuf/profit_contract.proto @@ -63,6 +63,9 @@ service ProfitContract { // Reset the manager of a scheme. rpc ResetManager (ResetManagerInput) returns (google.protobuf.Empty) { } + + rpc SetMaximumProfitReceivingPeriodCount(google.protobuf.Int32Value) returns (google.protobuf.Empty) { + } // Get all schemes managed by the specified manager. rpc GetManagingSchemeIds (GetManagingSchemeIdsInput) returns (CreatedSchemeIds) { @@ -98,6 +101,10 @@ service ProfitContract { rpc GetProfitsMap (ClaimProfitsInput) returns (ReceivedProfitsMap) { option (aelf.is_view) = true; } + + rpc GetMaximumProfitReceivingPeriodCount(google.protobuf.Empty) returns (google.protobuf.Int32Value) { + option (aelf.is_view) = true; + } } message CreateSchemeInput { diff --git a/test/AElf.Contracts.Profit.Tests/ProfitContractTestConstants.cs b/test/AElf.Contracts.Profit.Tests/ProfitContractTestConstants.cs index d4f937117a..bda82297c0 100644 --- a/test/AElf.Contracts.Profit.Tests/ProfitContractTestConstants.cs +++ b/test/AElf.Contracts.Profit.Tests/ProfitContractTestConstants.cs @@ -5,4 +5,5 @@ public class ProfitContractTestConstants public const string NativeTokenSymbol = "ELF"; public const long NativeTokenTotalSupply = 1_000_000_000_00000000; public const int MaximumProfitReceivingDuePeriodCount = 1024; + public const int DefaultMaximumProfitReceivingPeriodCountOfOneTime = 100; } \ No newline at end of file diff --git a/test/AElf.Contracts.Profit.Tests/ProfitTests.cs b/test/AElf.Contracts.Profit.Tests/ProfitTests.cs index 34979069da..d1a847f207 100644 --- a/test/AElf.Contracts.Profit.Tests/ProfitTests.cs +++ b/test/AElf.Contracts.Profit.Tests/ProfitTests.cs @@ -3,6 +3,7 @@ using AElf.Contracts.MultiToken; using AElf.CSharp.Core; using AElf.Types; +using Google.Protobuf.WellKnownTypes; using Shouldly; using Xunit; @@ -1680,6 +1681,47 @@ await ProfitContractStub.ClaimProfits.SendAsync(new ClaimProfitsInput } } + + [Fact] + public async Task MaximumProfitReceivingPeriodCount_Test() + { + var maximumProfitReceivingPeriodCount = + await ProfitContractStub.GetMaximumProfitReceivingPeriodCount.CallAsync(new Empty()); + maximumProfitReceivingPeriodCount.Value.ShouldBe(ProfitContractTestConstants + .DefaultMaximumProfitReceivingPeriodCountOfOneTime); + var maxPeriodCount = 10; + var result = await ProfitContractStub.SetMaximumProfitReceivingPeriodCount.SendWithExceptionAsync(new Int32Value + { + Value = maxPeriodCount + }); + result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + result.TransactionResult.Error.ShouldContain("No permission"); + + var defaultOrganizationAddress = + await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + var proposalId = await CreateProposalAsync(ProfitContractAddress, + defaultOrganizationAddress, nameof(ProfitContractStub.SetMaximumProfitReceivingPeriodCount), new Int32Value + { + Value = 0 + }); + await ApproveWithMinersAsync(proposalId); + result = await ParliamentContractStub.Release.SendWithExceptionAsync(proposalId); + result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + result.TransactionResult.Error.ShouldContain("Invalid maximum profit receiving period count"); + + proposalId = await CreateProposalAsync(ProfitContractAddress, + defaultOrganizationAddress, nameof(ProfitContractStub.SetMaximumProfitReceivingPeriodCount), new Int32Value + { + Value = maxPeriodCount + }); + await ApproveWithMinersAsync(proposalId); + await ParliamentContractStub.Release.SendAsync(proposalId); + + maximumProfitReceivingPeriodCount = + await ProfitContractStub.GetMaximumProfitReceivingPeriodCount.CallAsync(new Empty()); + maximumProfitReceivingPeriodCount.Value.ShouldBe(maxPeriodCount); + } + private async Task ContributeProfits(Hash schemeId, long amount = 100) { await ProfitContractStub.ContributeProfits.SendAsync(new ContributeProfitsInput From b3011d117eb663a0b4199b3d6ddb57f68a8886f5 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 10 Jan 2024 22:22:35 +0800 Subject: [PATCH 018/128] Change the logic of calculating max profit receiving period count --- .../AElf.Contracts.Profit/ProfitContract.cs | 24 ++++++++++++------- contract/AElf.Contracts.Profit/ViewMethods.cs | 20 +++++++++------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ProfitContract.cs b/contract/AElf.Contracts.Profit/ProfitContract.cs index 76d75044a6..a75bc7d909 100644 --- a/contract/AElf.Contracts.Profit/ProfitContract.cs +++ b/contract/AElf.Contracts.Profit/ProfitContract.cs @@ -769,18 +769,19 @@ public override Empty ClaimProfits(ClaimProfitsInput input) Context.LogDebug(() => $"Profitable details: {profitableDetails.Aggregate("\n", (profit1, profit2) => profit1.ToString() + "\n" + profit2)}"); + var profitableDetailCount = + Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, profitableDetails.Count); + var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); // Only can get profit from last profit period to actual last period (profit.CurrentPeriod - 1), // because current period not released yet. - for (var i = 0; - i < Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, profitableDetails.Count); - i++) + for (var i = 0; i < profitableDetailCount; i++) { var profitDetail = profitableDetails[i]; if (profitDetail.LastProfitPeriod == 0) // This detail never performed profit before. profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - ProfitAllPeriods(scheme, profitDetail, beneficiary); + ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount); } var profitDetailsToRemove = profitableDetails @@ -807,6 +808,14 @@ public override Empty ClaimProfits(ClaimProfitsInput input) return new Empty(); } + private int GetMaximumPeriodCountForProfitableDetail(int profitableDetailCount) + { + var maxPeriodCount = GetMaximumProfitReceivingPeriodCount(); + return maxPeriodCount > profitableDetailCount && profitableDetailCount > 0 + ? maxPeriodCount.Div(profitableDetailCount) + : 1; + } + public override Empty SetMaximumProfitReceivingPeriodCount(Int32Value input) { ValidateContractState(State.ParliamentContract, SmartContractConstants.ParliamentContractSystemName); @@ -817,7 +826,7 @@ public override Empty SetMaximumProfitReceivingPeriodCount(Int32Value input) return new Empty(); } - private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail profitDetail, Address beneficiary, + private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail profitDetail, Address beneficiary, int maxProfitReceivingPeriodCount, bool isView = false, string targetSymbol = null) { var profitsMap = new Dictionary(); @@ -828,11 +837,10 @@ private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail pr foreach (var symbol in symbols) { var totalAmount = 0L; - var maximumProfitReceivingPeriodCount = GetMaximumProfitReceivingPeriodCount(); var targetPeriod = Math.Min(scheme.CurrentPeriod - 1, profitDetail.EndPeriod); var maxProfitPeriod = profitDetail.EndPeriod == long.MaxValue - ? Math.Min(scheme.CurrentPeriod - 1, profitDetail.LastProfitPeriod.Add(maximumProfitReceivingPeriodCount)) - : Math.Min(targetPeriod, profitDetail.LastProfitPeriod.Add(maximumProfitReceivingPeriodCount)); + ? Math.Min(scheme.CurrentPeriod - 1, maxProfitReceivingPeriodCount) + : Math.Min(targetPeriod, profitDetail.LastProfitPeriod.Add(maxProfitReceivingPeriodCount)); for (var period = profitDetail.LastProfitPeriod; period <= maxProfitPeriod; period++) { var periodToPrint = period; diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index 5780d28b2e..154c9a49be 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -79,14 +79,16 @@ public override Int64Value GetProfitAmount(GetProfitAmountInput input) var amount = 0L; - for (var i = 0; - i < Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); - i++) + var profitableDetailCount = + Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); + var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); + + for (var i = 0;i < profitableDetailCount; i++) { var profitDetail = availableDetails[i]; if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - var profitsDict = ProfitAllPeriods(profitItem, profitDetail, beneficiary, true, + var profitsDict = ProfitAllPeriods(profitItem, profitDetail, beneficiary,maxProfitReceivingPeriodCount, true, input.Symbol); amount = amount.Add(profitsDict[input.Symbol]); } @@ -111,16 +113,18 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) ? d.EndPeriod >= d.StartPeriod : d.EndPeriod >= d.LastProfitPeriod) ).ToList(); + + var profitableDetailCount = + Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); + var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); var profitsDict = new Dictionary(); - for (var i = 0; - i < Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); - i++) + for (var i = 0; i < profitableDetailCount; i++) { var profitDetail = availableDetails[i]; if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - var profitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, true); + var profitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount,true); foreach (var kv in profitsDictForEachProfitDetail) if (profitsDict.ContainsKey(kv.Key)) profitsDict[kv.Key] = profitsDict[kv.Key].Add(kv.Value); From 4ed4215c5a27f2474de8a0ab0cc8c0f0f247178b Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 11 Jan 2024 16:19:40 +0800 Subject: [PATCH 019/128] Fix wrong period of profits. --- contract/AElf.Contracts.Profit/ProfitContract.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.Profit/ProfitContract.cs b/contract/AElf.Contracts.Profit/ProfitContract.cs index a75bc7d909..002fe8540b 100644 --- a/contract/AElf.Contracts.Profit/ProfitContract.cs +++ b/contract/AElf.Contracts.Profit/ProfitContract.cs @@ -839,7 +839,7 @@ private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail pr var totalAmount = 0L; var targetPeriod = Math.Min(scheme.CurrentPeriod - 1, profitDetail.EndPeriod); var maxProfitPeriod = profitDetail.EndPeriod == long.MaxValue - ? Math.Min(scheme.CurrentPeriod - 1, maxProfitReceivingPeriodCount) + ? Math.Min(scheme.CurrentPeriod - 1, profitDetail.LastProfitPeriod.Add(maxProfitReceivingPeriodCount)) : Math.Min(targetPeriod, profitDetail.LastProfitPeriod.Add(maxProfitReceivingPeriodCount)); for (var period = profitDetail.LastProfitPeriod; period <= maxProfitPeriod; period++) { From c05c2c331359bfabddc5d8c1947389280af1a35e Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 12 Jan 2024 18:30:15 +0800 Subject: [PATCH 020/128] Add comments for method 'GetMaximumPeriodCountForProfitableDetail' --- contract/AElf.Contracts.Profit/ProfitContract.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/contract/AElf.Contracts.Profit/ProfitContract.cs b/contract/AElf.Contracts.Profit/ProfitContract.cs index 002fe8540b..037e31ecb5 100644 --- a/contract/AElf.Contracts.Profit/ProfitContract.cs +++ b/contract/AElf.Contracts.Profit/ProfitContract.cs @@ -808,11 +808,27 @@ public override Empty ClaimProfits(ClaimProfitsInput input) return new Empty(); } + /// + /// This method calculates the maximum period count for a profitable detail + /// based on the number of profitable details and the maximum profit receiving period + /// For example: + /// If the number of profitable detail is 10 and the maximum profit receiving period is 100, + /// the maximum period count for a profitable detail will be 10. + /// If the number of profitable detail is 10 and the maximum profit receiving period is 5, + /// the maximum period count for a profitable detail will be 1. + /// + /// The number of profitable details + /// private int GetMaximumPeriodCountForProfitableDetail(int profitableDetailCount) { + // Get the maximum profit receiving period count var maxPeriodCount = GetMaximumProfitReceivingPeriodCount(); + // Check if the maximum period count is greater than the profitable detail count + // and if the profitable detail count is greater than 0 return maxPeriodCount > profitableDetailCount && profitableDetailCount > 0 + // Divide the maximum period count by the profitable detail count ? maxPeriodCount.Div(profitableDetailCount) + // If the conditions are not met, return 1 as the maximum period count : 1; } From 456d45e03b52782267609ff8421f85857da4fcfe Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 16 Jan 2024 17:25:06 +0800 Subject: [PATCH 021/128] Add one time claimable profit amount to return value --- .../AElf.Contracts.Profit/ProfitContract.cs | 2 +- contract/AElf.Contracts.Profit/ViewMethods.cs | 60 ++++++++++++++----- protobuf/profit_contract.proto | 13 +++- .../ProfitTests.cs | 18 ++++-- 4 files changed, 67 insertions(+), 26 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ProfitContract.cs b/contract/AElf.Contracts.Profit/ProfitContract.cs index 037e31ecb5..a1f43d1af4 100644 --- a/contract/AElf.Contracts.Profit/ProfitContract.cs +++ b/contract/AElf.Contracts.Profit/ProfitContract.cs @@ -842,7 +842,7 @@ public override Empty SetMaximumProfitReceivingPeriodCount(Int32Value input) return new Empty(); } - private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail profitDetail, Address beneficiary, int maxProfitReceivingPeriodCount, + private Dictionary ProfitAllPeriods(Scheme scheme, ProfitDetail profitDetail, Address beneficiary, long maxProfitReceivingPeriodCount, bool isView = false, string targetSymbol = null) { var profitsMap = new Dictionary(); diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index 154c9a49be..79de28d260 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -59,14 +59,14 @@ private Hash GeneratePeriodVirtualAddressFromHash(Hash schemeId, long period) return HashHelper.XorAndCompute(schemeId, HashHelper.ComputeFrom(period)); } - public override Int64Value GetProfitAmount(GetProfitAmountInput input) + public override GetProfitAmountOutput GetProfitAmount(GetProfitAmountInput input) { var profitItem = State.SchemeInfos[input.SchemeId]; Assert(profitItem != null, "Scheme not found."); var beneficiary = input.Beneficiary ?? Context.Sender; var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; - if (profitDetails == null) return new Int64Value { Value = 0 }; + if (profitDetails == null) return new GetProfitAmountOutput { AllProfitAmount = 0, OneTimeClaimableProfitAmount = 0 }; var profitVirtualAddress = Context.ConvertVirtualAddressToContractAddress(input.SchemeId); @@ -77,7 +77,8 @@ public override Int64Value GetProfitAmount(GetProfitAmountInput input) : d.EndPeriod >= d.LastProfitPeriod) ).ToList(); - var amount = 0L; + var allProfitAmount = 0L; + var claimableProfitAmount = 0L; var profitableDetailCount = Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); @@ -88,12 +89,29 @@ public override Int64Value GetProfitAmount(GetProfitAmountInput input) var profitDetail = availableDetails[i]; if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - var profitsDict = ProfitAllPeriods(profitItem, profitDetail, beneficiary,maxProfitReceivingPeriodCount, true, + var totalProfitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, + profitDetail.EndPeriod.Sub(profitDetail.LastProfitPeriod), true, input.Symbol); + allProfitAmount = + allProfitAmount.Add(totalProfitsDictForEachProfitDetail.TryGetValue(input.Symbol, out var value) + ? value + : 0); + if(i >= profitableDetailCount) continue; + var claimableProfitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, + maxProfitReceivingPeriodCount, true, input.Symbol); - amount = amount.Add(profitsDict[input.Symbol]); + + claimableProfitAmount = + claimableProfitAmount.Add( + claimableProfitsDictForEachProfitDetail.TryGetValue(input.Symbol, out var claimableValue) + ? claimableValue + : 0); } - return new Int64Value { Value = amount }; + return new GetProfitAmountOutput + { + AllProfitAmount = allProfitAmount, + OneTimeClaimableProfitAmount = claimableProfitAmount + }; } public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) @@ -118,25 +136,35 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); - var profitsDict = new Dictionary(); - for (var i = 0; i < profitableDetailCount; i++) + var allProfitsDict = new Dictionary(); + var claimableProfitsDict = new Dictionary(); + for (var i = 0; i < availableDetails.Count; i++) { var profitDetail = availableDetails[i]; if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - - var profitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount,true); - foreach (var kv in profitsDictForEachProfitDetail) - if (profitsDict.ContainsKey(kv.Key)) - profitsDict[kv.Key] = profitsDict[kv.Key].Add(kv.Value); - else - profitsDict[kv.Key] = kv.Value; + + var totalProfitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, profitDetail.EndPeriod.Sub(profitDetail.LastProfitPeriod),true); + AddProfitToDict(allProfitsDict, totalProfitsDictForEachProfitDetail); + if(i >= profitableDetailCount) continue; + var claimableProfitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount,true); + AddProfitToDict(claimableProfitsDict, claimableProfitsDictForEachProfitDetail); } return new ReceivedProfitsMap { - Value = { profitsDict } + AllProfitsMap = { allProfitsDict }, + OneTimeClaimableProfitsMap = { claimableProfitsDict } }; } + + private void AddProfitToDict(Dictionary profitsDict, Dictionary profitsToAdd) + { + foreach (var kv in profitsToAdd) + if (profitsDict.ContainsKey(kv.Key)) + profitsDict[kv.Key] = profitsDict[kv.Key].Add(kv.Value); + else + profitsDict[kv.Key] = kv.Value; + } public override Int32Value GetMaximumProfitReceivingPeriodCount(Empty input) { diff --git a/protobuf/profit_contract.proto b/protobuf/profit_contract.proto index 0f84a870e5..45b39a8886 100644 --- a/protobuf/profit_contract.proto +++ b/protobuf/profit_contract.proto @@ -93,7 +93,7 @@ service ProfitContract { } // Query the amount of profit according to token symbol. (up to 10 periods). - rpc GetProfitAmount (GetProfitAmountInput) returns (google.protobuf.Int64Value) { + rpc GetProfitAmount (GetProfitAmountInput) returns (GetProfitAmountOutput) { option (aelf.is_view) = true; } @@ -315,9 +315,16 @@ message GetProfitAmountInput { aelf.Address beneficiary = 3; } +message GetProfitAmountOutput{ + int64 all_profit_amount = 1; + int64 one_time_claimable_profit_amount = 2; +} + message ReceivedProfitsMap { - // The collection of profits received, token symbol -> amount. - map value = 1; + // The collection of all profits received, token symbol -> amount. + map all_profits_map = 1; + // The collection of claimable profits received, token symbol -> amount. + map one_time_claimable_profits_map = 2; } message SchemeCreated { diff --git a/test/AElf.Contracts.Profit.Tests/ProfitTests.cs b/test/AElf.Contracts.Profit.Tests/ProfitTests.cs index d1a847f207..69ce56e25f 100644 --- a/test/AElf.Contracts.Profit.Tests/ProfitTests.cs +++ b/test/AElf.Contracts.Profit.Tests/ProfitTests.cs @@ -1605,13 +1605,15 @@ await creator.DistributeProfits.SendAsync(new DistributeProfitsInput Symbol = tokenSymbol, SchemeId = schemeId }); - profitAmount.Value.ShouldBe(amount); + profitAmount.AllProfitAmount.ShouldBe(amount); + profitAmount.OneTimeClaimableProfitAmount.ShouldBe(amount); var profitMap = await ProfitContractStub.GetProfitsMap.CallAsync(new ClaimProfitsInput { SchemeId = schemeId, Beneficiary = receiver }); - profitMap.Value[tokenSymbol].ShouldBe(amount); + profitMap.AllProfitsMap[tokenSymbol].ShouldBe(amount); + profitMap.OneTimeClaimableProfitsMap[tokenSymbol].ShouldBe(amount); } // after claim @@ -1633,13 +1635,15 @@ await ProfitContractStub.ClaimProfits.SendAsync(new ClaimProfitsInput Symbol = tokenSymbol, SchemeId = schemeId }); - profitAmount.Value.ShouldBe(0); + profitAmount.AllProfitAmount.ShouldBe(0); + profitAmount.OneTimeClaimableProfitAmount.ShouldBe(0); var profitMap = await ProfitContractStub.GetProfitsMap.CallAsync(new ClaimProfitsInput { SchemeId = schemeId, Beneficiary = receiver }); - profitMap.Value.ShouldNotContainKey(tokenSymbol); + profitMap.AllProfitsMap.ShouldNotContainKey(tokenSymbol); + profitMap.OneTimeClaimableProfitsMap.ShouldNotContainKey(tokenSymbol); } //second time @@ -1659,13 +1663,15 @@ await creator.DistributeProfits.SendAsync(new DistributeProfitsInput Symbol = tokenSymbol, SchemeId = schemeId }); - profitAmount.Value.ShouldBe(amount); + profitAmount.AllProfitAmount.ShouldBe(amount); + profitAmount.OneTimeClaimableProfitAmount.ShouldBe(amount); var profitMap = await ProfitContractStub.GetProfitsMap.CallAsync(new ClaimProfitsInput { SchemeId = schemeId, Beneficiary = receiver }); - profitMap.Value[tokenSymbol].ShouldBe(amount); + profitMap.AllProfitsMap[tokenSymbol].ShouldBe(amount); + profitMap.OneTimeClaimableProfitsMap[tokenSymbol].ShouldBe(amount); await ProfitContractStub.ClaimProfits.SendAsync(new ClaimProfitsInput { From 89c8604a4d461d7edf6e77eb8ad593bbf9567b2b Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 17 Jan 2024 19:37:35 +0800 Subject: [PATCH 022/128] Fix build error --- contract/AElf.Contracts.Profit/ViewMethods.cs | 2 +- .../TokenHolderContract.cs | 2 +- .../BVT/ElectionTests.cs | 18 ++++++++--------- .../Full/MainChainMinerElection.cs | 2 +- .../Full/ReleaseProfitsFromTreasury.cs | 20 +++++++++---------- .../TokenHolderTests.cs | 6 +++--- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index 79de28d260..d5ba00e3a4 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -84,7 +84,7 @@ public override GetProfitAmountOutput GetProfitAmount(GetProfitAmountInput input Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); - for (var i = 0;i < profitableDetailCount; i++) + for (var i = 0;i < availableDetails.Count; i++) { var profitDetail = availableDetails[i]; if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; diff --git a/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs b/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs index e2a60ab703..1c5a4e976a 100644 --- a/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs +++ b/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs @@ -271,7 +271,7 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) }); return new ReceivedProfitsMap { - Value = { profitsMap.Value } + Value = { profitsMap.AllProfitsMap } }; } diff --git a/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs b/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs index 2ba363f220..50127529b8 100644 --- a/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs +++ b/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs @@ -653,7 +653,7 @@ await ElectionContractStub.GetElectorVoteWithRecords.CallAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitAmountList.Add(profitAmount.Value); + profitAmountList.Add(profitAmount.AllProfitAmount); } weightList.First().Div(weightList.Last()).ShouldBe(profitAmountList.First().Div(profitAmountList.Last())); @@ -737,7 +737,7 @@ await ElectionContractStub.GetElectorVoteWithRecords.CallAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitAmountList.Add(profitAmount.Value); + profitAmountList.Add(profitAmount.AllProfitAmount); } profitAmountList.First().ShouldBe(profitAmountList.Last()); @@ -851,8 +851,8 @@ await ProfitContractStub.GetProfitDetails.CallAsync(new GetProfitDetailsInput SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitList.Add(profitAmount.Value); - allProfitAmount += profitAmount.Value; + profitList.Add(profitAmount.AllProfitAmount); + allProfitAmount += profitAmount.AllProfitAmount; } profitList.First().ShouldBe(profitList.Last()); @@ -888,7 +888,7 @@ await originVoterElectionStub.Withdraw.SendAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - originProfitAmount.Value.ShouldBe(profitAmountList[term.Value - 1].Div(2)); + originProfitAmount.AllProfitAmount.ShouldBe(profitAmountList[term.Value - 1].Div(2)); var changeProfitAmount = await ProfitContractStub.GetProfitAmount.CallAsync(new GetProfitAmountInput { @@ -896,7 +896,7 @@ await originVoterElectionStub.Withdraw.SendAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - changeProfitAmount.Value.ShouldBe(profitAmountList[term.Value - 1].Div(2) + changeProfitAmount.AllProfitAmount.ShouldBe(profitAmountList[term.Value - 1].Div(2) .Add(citizenAmount.AmountsMap[EconomicContractsTestConstants.NativeTokenSymbol])); } @@ -993,7 +993,7 @@ await ProfitContractStub.GetProfitDetails.CallAsync(new GetProfitDetailsInput var profitsClaimedEvents = claimResult.TransactionResult.Logs.Where(l => l.Name.Equals("ProfitsClaimed")) .Select(l => l.NonIndexed); var logEvents = profitsClaimedEvents.Select(e => ProfitsClaimed.Parser.ParseFrom(e)); - logEvents.Sum(e => e.Amount).ShouldBe(profitAmount.Value); + logEvents.Sum(e => e.Amount).ShouldBe(profitAmount.AllProfitAmount); } [Fact] @@ -1177,14 +1177,14 @@ await ElectionContractStub.GetElectorVoteWithRecords.CallAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitAll += profitAmount.Value; + profitAll += profitAmount.AllProfitAmount; var claimResult = await voter.ClaimProfits.SendAsync(new ClaimProfitsInput { SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare] }); claimResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var afterBalance = await GetNativeTokenBalance(votersKeyPairs[i].PublicKey); - afterBalance.ShouldBe(balance.Add(profitAmount.Value)); + afterBalance.ShouldBe(balance.Add(profitAmount.AllProfitAmount)); } // diff --git a/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs b/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs index 6d51422854..f7585ea3b6 100644 --- a/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs +++ b/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs @@ -63,7 +63,7 @@ public async Task UserVote_And_GetProfitAmount_Test() { SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = "ELF" - })).Value; + })).AllProfitAmount; profitBalance.ShouldBeGreaterThan(24000000); } } \ No newline at end of file diff --git a/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs b/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs index cbe677415b..e0a2c78445 100644 --- a/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs +++ b/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs @@ -387,7 +387,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol - })).Value; + })).OneTimeClaimableProfitAmount; profitAmount.ShouldBeGreaterThan(0); var profitResult = await profitTester.ClaimProfits.SendAsync(new ClaimProfitsInput @@ -425,7 +425,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BasicMinerReward], Symbol = "ELF" - })).Value; + })).AllProfitAmount; basicMinerRewardAmount.ShouldBeGreaterThan(0); //flexible Shares - 10% @@ -433,7 +433,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.FlexibleReward], Symbol = "ELF" - })).Value; + })).AllProfitAmount; flexibleRewardWeight.ShouldBeGreaterThan(0); //welcome Shares - 10% @@ -441,7 +441,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.WelcomeReward], Symbol = "ELF" - })).Value; + })).AllProfitAmount; welcomeBalance.ShouldBe(0); //backup Shares - 20% @@ -449,7 +449,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BackupSubsidy], Symbol = "ELF" - })).Value; + })).AllProfitAmount; backupBalance.ShouldBeGreaterThan(0); //Profit all @@ -505,7 +505,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BasicMinerReward], Symbol = "ELF" - })).Value; + })).AllProfitAmount; basicMinerRewardAmount.ShouldBeGreaterThan(0); //flexible Shares - 75% @@ -513,7 +513,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.FlexibleReward], Symbol = "ELF" - })).Value; + })).AllProfitAmount; flexibleRewardWeight.ShouldBe(0); //welcome Shares - 5% @@ -521,7 +521,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.WelcomeReward], Symbol = "ELF" - })).Value; + })).AllProfitAmount; welcomeBalance.ShouldBe(0); //backup Shares - 5% @@ -529,7 +529,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BackupSubsidy], Symbol = "ELF" - })).Value; + })).AllProfitAmount; backupBalance.ShouldBeGreaterThan(0); //Profit all @@ -640,7 +640,7 @@ private async Task GetProfitAmount(ProfitType type) { SchemeId = ProfitItemsIds[type], Symbol = EconomicContractsTestConstants.NativeTokenSymbol - })).Value; + })).OneTimeClaimableProfitAmount; } private async Task GetReleasedAmount() diff --git a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs index 68698e8177..9f21a6098f 100644 --- a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs +++ b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs @@ -409,9 +409,9 @@ await TokenHolderContractStub.RegisterForProfits.SendAsync(new RegisterForProfit Beneficiary = Starter, SchemeId = schemeId }); - profitMap.Value.Count.ShouldBe(2); - profitMap.Value.ContainsKey(nativeTokenSymbol).ShouldBeTrue(); - profitMap.Value[nativeTokenSymbol].ShouldBe(amount); + profitMap.AllProfitsMap.Count.ShouldBe(2); + profitMap.AllProfitsMap.ContainsKey(nativeTokenSymbol).ShouldBeTrue(); + profitMap.AllProfitsMap[nativeTokenSymbol].ShouldBe(amount); var schemeInfoInProfit = await ProfitContractStub.GetScheme.CallAsync(schemeId); var schemeInfoInTokenHolder = await TokenHolderContractStub.GetScheme.CallAsync(Starter); schemeInfoInProfit.CurrentPeriod.ShouldBe(2); From 39d1120e8cb069affbe1d2aeaaab86b89cfe8fbf Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 23 Jan 2024 13:54:31 +0800 Subject: [PATCH 023/128] Enhance ProfitContract with methods for getting all profit amount --- contract/AElf.Contracts.Profit/ViewMethods.cs | 86 +++++++++++++++++-- .../TokenHolderContract.cs | 2 +- protobuf/profit_contract.proto | 35 +++++++- .../BVT/ElectionTests.cs | 18 ++-- .../Full/MainChainMinerElection.cs | 2 +- .../Full/ReleaseProfitsFromTreasury.cs | 20 ++--- .../ProfitTests.cs | 71 ++++++++++++--- .../TokenHolderTests.cs | 6 +- 8 files changed, 197 insertions(+), 43 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index d5ba00e3a4..c6f73db5f8 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -59,14 +59,56 @@ private Hash GeneratePeriodVirtualAddressFromHash(Hash schemeId, long period) return HashHelper.XorAndCompute(schemeId, HashHelper.ComputeFrom(period)); } - public override GetProfitAmountOutput GetProfitAmount(GetProfitAmountInput input) + public override Int64Value GetProfitAmount(GetProfitAmountInput input) { var profitItem = State.SchemeInfos[input.SchemeId]; Assert(profitItem != null, "Scheme not found."); var beneficiary = input.Beneficiary ?? Context.Sender; var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; - if (profitDetails == null) return new GetProfitAmountOutput { AllProfitAmount = 0, OneTimeClaimableProfitAmount = 0 }; + if (profitDetails == null) return new Int64Value { Value = 0 }; + + // ReSharper disable once PossibleNullReferenceException + var availableDetails = profitDetails.Details.Where(d => + d.LastProfitPeriod < profitItem.CurrentPeriod && (d.LastProfitPeriod == 0 + ? d.EndPeriod >= d.StartPeriod + : d.EndPeriod >= d.LastProfitPeriod) + ).ToList(); + + var amount = 0L; + + var profitableDetailCount = + Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); + var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); + + for (var i = 0;i < profitableDetailCount; i++) + { + var profitDetail = availableDetails[i]; + if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; + + var profitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, + maxProfitReceivingPeriodCount, true, + input.Symbol); + + amount = amount.Add(profitsDictForEachProfitDetail.TryGetValue(input.Symbol, out var value) + ? value + : 0); + } + + return new Int64Value + { + Value = amount + }; + } + + public override GetAllProfitAmountOutput GetAllProfitAmount(GetAllProfitAmountInput input) + { + var profitItem = State.SchemeInfos[input.SchemeId]; + Assert(profitItem != null, "Scheme not found."); + var beneficiary = input.Beneficiary ?? Context.Sender; + var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; + + if (profitDetails == null) return new GetAllProfitAmountOutput { AllProfitAmount = 0, OneTimeClaimableProfitAmount = 0 }; var profitVirtualAddress = Context.ConvertVirtualAddressToContractAddress(input.SchemeId); @@ -107,7 +149,7 @@ public override GetProfitAmountOutput GetProfitAmount(GetProfitAmountInput input : 0); } - return new GetProfitAmountOutput + return new GetAllProfitAmountOutput { AllProfitAmount = allProfitAmount, OneTimeClaimableProfitAmount = claimableProfitAmount @@ -123,7 +165,41 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) if (profitDetails == null) return new ReceivedProfitsMap(); - var profitVirtualAddress = Context.ConvertVirtualAddressToContractAddress(input.SchemeId); + // ReSharper disable once PossibleNullReferenceException + var availableDetails = profitDetails.Details.Where(d => + d.LastProfitPeriod < scheme.CurrentPeriod && (d.LastProfitPeriod == 0 + ? d.EndPeriod >= d.StartPeriod + : d.EndPeriod >= d.LastProfitPeriod) + ).ToList(); + + var profitableDetailCount = + Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); + var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); + + var profitsDict = new Dictionary(); + for (var i = 0; i < profitableDetailCount; i++) + { + var profitDetail = availableDetails[i]; + if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; + + var profitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount,true); + AddProfitToDict(profitsDict, profitsDictForEachProfitDetail); + } + + return new ReceivedProfitsMap + { + Value = { profitsDict } + }; + } + + public override GetAllProfitsMapOutput GetAllProfitsMap(GetAllProfitsMapInput input) + { + var scheme = State.SchemeInfos[input.SchemeId]; + Assert(scheme != null, "Scheme not found."); + var beneficiary = input.Beneficiary ?? Context.Sender; + var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; + + if (profitDetails == null) return new GetAllProfitsMapOutput(); // ReSharper disable once PossibleNullReferenceException var availableDetails = profitDetails.Details.Where(d => @@ -150,7 +226,7 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) AddProfitToDict(claimableProfitsDict, claimableProfitsDictForEachProfitDetail); } - return new ReceivedProfitsMap + return new GetAllProfitsMapOutput { AllProfitsMap = { allProfitsDict }, OneTimeClaimableProfitsMap = { claimableProfitsDict } diff --git a/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs b/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs index 1c5a4e976a..e2a60ab703 100644 --- a/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs +++ b/contract/AElf.Contracts.TokenHolder/TokenHolderContract.cs @@ -271,7 +271,7 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) }); return new ReceivedProfitsMap { - Value = { profitsMap.AllProfitsMap } + Value = { profitsMap.Value } }; } diff --git a/protobuf/profit_contract.proto b/protobuf/profit_contract.proto index 45b39a8886..aac44f2d6a 100644 --- a/protobuf/profit_contract.proto +++ b/protobuf/profit_contract.proto @@ -93,7 +93,12 @@ service ProfitContract { } // Query the amount of profit according to token symbol. (up to 10 periods). - rpc GetProfitAmount (GetProfitAmountInput) returns (GetProfitAmountOutput) { + rpc GetProfitAmount (GetProfitAmountInput) returns (google.protobuf.Int64Value) { + option (aelf.is_view) = true; + } + + // Query the amount of profit according to token symbol. + rpc GetAllProfitAmount (GetAllProfitAmountInput) returns (GetAllProfitAmountOutput) { option (aelf.is_view) = true; } @@ -102,6 +107,11 @@ service ProfitContract { option (aelf.is_view) = true; } + // Query all profit. + rpc GetAllProfitsMap (GetAllProfitsMapInput) returns (GetAllProfitsMapOutput) { + option (aelf.is_view) = true; + } + rpc GetMaximumProfitReceivingPeriodCount(google.protobuf.Empty) returns (google.protobuf.Int32Value) { option (aelf.is_view) = true; } @@ -315,12 +325,33 @@ message GetProfitAmountInput { aelf.Address beneficiary = 3; } -message GetProfitAmountOutput{ +message GetAllProfitAmountInput { + // The scheme id. + aelf.Hash scheme_id = 1; + // The token symbol. + string symbol = 2; + // The beneficiary's address. + aelf.Address beneficiary = 3; +} + +message GetAllProfitAmountOutput{ int64 all_profit_amount = 1; int64 one_time_claimable_profit_amount = 2; } message ReceivedProfitsMap { + // The collection of profits received, token symbol -> amount. + map value = 1; +} + +message GetAllProfitsMapInput { + // The scheme id. + aelf.Hash scheme_id = 1; + // The address of beneficiary. + aelf.Address beneficiary = 2; +} + +message GetAllProfitsMapOutput { // The collection of all profits received, token symbol -> amount. map all_profits_map = 1; // The collection of claimable profits received, token symbol -> amount. diff --git a/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs b/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs index 50127529b8..2ba363f220 100644 --- a/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs +++ b/test/AElf.Contracts.Election.Tests/BVT/ElectionTests.cs @@ -653,7 +653,7 @@ await ElectionContractStub.GetElectorVoteWithRecords.CallAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitAmountList.Add(profitAmount.AllProfitAmount); + profitAmountList.Add(profitAmount.Value); } weightList.First().Div(weightList.Last()).ShouldBe(profitAmountList.First().Div(profitAmountList.Last())); @@ -737,7 +737,7 @@ await ElectionContractStub.GetElectorVoteWithRecords.CallAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitAmountList.Add(profitAmount.AllProfitAmount); + profitAmountList.Add(profitAmount.Value); } profitAmountList.First().ShouldBe(profitAmountList.Last()); @@ -851,8 +851,8 @@ await ProfitContractStub.GetProfitDetails.CallAsync(new GetProfitDetailsInput SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitList.Add(profitAmount.AllProfitAmount); - allProfitAmount += profitAmount.AllProfitAmount; + profitList.Add(profitAmount.Value); + allProfitAmount += profitAmount.Value; } profitList.First().ShouldBe(profitList.Last()); @@ -888,7 +888,7 @@ await originVoterElectionStub.Withdraw.SendAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - originProfitAmount.AllProfitAmount.ShouldBe(profitAmountList[term.Value - 1].Div(2)); + originProfitAmount.Value.ShouldBe(profitAmountList[term.Value - 1].Div(2)); var changeProfitAmount = await ProfitContractStub.GetProfitAmount.CallAsync(new GetProfitAmountInput { @@ -896,7 +896,7 @@ await originVoterElectionStub.Withdraw.SendAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - changeProfitAmount.AllProfitAmount.ShouldBe(profitAmountList[term.Value - 1].Div(2) + changeProfitAmount.Value.ShouldBe(profitAmountList[term.Value - 1].Div(2) .Add(citizenAmount.AmountsMap[EconomicContractsTestConstants.NativeTokenSymbol])); } @@ -993,7 +993,7 @@ await ProfitContractStub.GetProfitDetails.CallAsync(new GetProfitDetailsInput var profitsClaimedEvents = claimResult.TransactionResult.Logs.Where(l => l.Name.Equals("ProfitsClaimed")) .Select(l => l.NonIndexed); var logEvents = profitsClaimedEvents.Select(e => ProfitsClaimed.Parser.ParseFrom(e)); - logEvents.Sum(e => e.Amount).ShouldBe(profitAmount.AllProfitAmount); + logEvents.Sum(e => e.Amount).ShouldBe(profitAmount.Value); } [Fact] @@ -1177,14 +1177,14 @@ await ElectionContractStub.GetElectorVoteWithRecords.CallAsync( SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol }); - profitAll += profitAmount.AllProfitAmount; + profitAll += profitAmount.Value; var claimResult = await voter.ClaimProfits.SendAsync(new ClaimProfitsInput { SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare] }); claimResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var afterBalance = await GetNativeTokenBalance(votersKeyPairs[i].PublicKey); - afterBalance.ShouldBe(balance.Add(profitAmount.AllProfitAmount)); + afterBalance.ShouldBe(balance.Add(profitAmount.Value)); } // diff --git a/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs b/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs index f7585ea3b6..6d51422854 100644 --- a/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs +++ b/test/AElf.Contracts.Election.Tests/Full/MainChainMinerElection.cs @@ -63,7 +63,7 @@ public async Task UserVote_And_GetProfitAmount_Test() { SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = "ELF" - })).AllProfitAmount; + })).Value; profitBalance.ShouldBeGreaterThan(24000000); } } \ No newline at end of file diff --git a/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs b/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs index e0a2c78445..cbe677415b 100644 --- a/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs +++ b/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs @@ -387,7 +387,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol - })).OneTimeClaimableProfitAmount; + })).Value; profitAmount.ShouldBeGreaterThan(0); var profitResult = await profitTester.ClaimProfits.SendAsync(new ClaimProfitsInput @@ -425,7 +425,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BasicMinerReward], Symbol = "ELF" - })).AllProfitAmount; + })).Value; basicMinerRewardAmount.ShouldBeGreaterThan(0); //flexible Shares - 10% @@ -433,7 +433,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.FlexibleReward], Symbol = "ELF" - })).AllProfitAmount; + })).Value; flexibleRewardWeight.ShouldBeGreaterThan(0); //welcome Shares - 10% @@ -441,7 +441,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.WelcomeReward], Symbol = "ELF" - })).AllProfitAmount; + })).Value; welcomeBalance.ShouldBe(0); //backup Shares - 20% @@ -449,7 +449,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BackupSubsidy], Symbol = "ELF" - })).AllProfitAmount; + })).Value; backupBalance.ShouldBeGreaterThan(0); //Profit all @@ -505,7 +505,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BasicMinerReward], Symbol = "ELF" - })).AllProfitAmount; + })).Value; basicMinerRewardAmount.ShouldBeGreaterThan(0); //flexible Shares - 75% @@ -513,7 +513,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.FlexibleReward], Symbol = "ELF" - })).AllProfitAmount; + })).Value; flexibleRewardWeight.ShouldBe(0); //welcome Shares - 5% @@ -521,7 +521,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.WelcomeReward], Symbol = "ELF" - })).AllProfitAmount; + })).Value; welcomeBalance.ShouldBe(0); //backup Shares - 5% @@ -529,7 +529,7 @@ public async Task CheckTreasuryProfitsDistribution_Test() { SchemeId = ProfitItemsIds[ProfitType.BackupSubsidy], Symbol = "ELF" - })).AllProfitAmount; + })).Value; backupBalance.ShouldBeGreaterThan(0); //Profit all @@ -640,7 +640,7 @@ private async Task GetProfitAmount(ProfitType type) { SchemeId = ProfitItemsIds[type], Symbol = EconomicContractsTestConstants.NativeTokenSymbol - })).OneTimeClaimableProfitAmount; + })).Value; } private async Task GetReleasedAmount() diff --git a/test/AElf.Contracts.Profit.Tests/ProfitTests.cs b/test/AElf.Contracts.Profit.Tests/ProfitTests.cs index 69ce56e25f..601fd7ccf6 100644 --- a/test/AElf.Contracts.Profit.Tests/ProfitTests.cs +++ b/test/AElf.Contracts.Profit.Tests/ProfitTests.cs @@ -1605,15 +1605,32 @@ await creator.DistributeProfits.SendAsync(new DistributeProfitsInput Symbol = tokenSymbol, SchemeId = schemeId }); - profitAmount.AllProfitAmount.ShouldBe(amount); - profitAmount.OneTimeClaimableProfitAmount.ShouldBe(amount); + + profitAmount.Value.ShouldBe(amount); + + var allProfitAmount = await ProfitContractStub.GetAllProfitAmount.CallAsync(new GetAllProfitAmountInput + { + Beneficiary = receiver, + Symbol = tokenSymbol, + SchemeId = schemeId + }); + allProfitAmount.AllProfitAmount.ShouldBe(amount); + allProfitAmount.OneTimeClaimableProfitAmount.ShouldBe(amount); var profitMap = await ProfitContractStub.GetProfitsMap.CallAsync(new ClaimProfitsInput { SchemeId = schemeId, Beneficiary = receiver }); - profitMap.AllProfitsMap[tokenSymbol].ShouldBe(amount); - profitMap.OneTimeClaimableProfitsMap[tokenSymbol].ShouldBe(amount); + + profitMap.Value[tokenSymbol].ShouldBe(amount); + + var allProfitMap = await ProfitContractStub.GetAllProfitsMap.CallAsync(new GetAllProfitsMapInput + { + SchemeId = schemeId, + Beneficiary = receiver + }); + allProfitMap.AllProfitsMap[tokenSymbol].ShouldBe(amount); + allProfitMap.OneTimeClaimableProfitsMap[tokenSymbol].ShouldBe(amount); } // after claim @@ -1635,15 +1652,31 @@ await ProfitContractStub.ClaimProfits.SendAsync(new ClaimProfitsInput Symbol = tokenSymbol, SchemeId = schemeId }); - profitAmount.AllProfitAmount.ShouldBe(0); - profitAmount.OneTimeClaimableProfitAmount.ShouldBe(0); + profitAmount.Value.ShouldBe(0); + + var allProfitAmount = await ProfitContractStub.GetAllProfitAmount.CallAsync(new GetAllProfitAmountInput + { + Beneficiary = receiver, + Symbol = tokenSymbol, + SchemeId = schemeId + }); + allProfitAmount.AllProfitAmount.ShouldBe(0); + allProfitAmount.OneTimeClaimableProfitAmount.ShouldBe(0); + var profitMap = await ProfitContractStub.GetProfitsMap.CallAsync(new ClaimProfitsInput { SchemeId = schemeId, Beneficiary = receiver }); - profitMap.AllProfitsMap.ShouldNotContainKey(tokenSymbol); - profitMap.OneTimeClaimableProfitsMap.ShouldNotContainKey(tokenSymbol); + profitMap.Value.ShouldNotContainKey(tokenSymbol); + + var allProfitMap = await ProfitContractStub.GetAllProfitsMap.CallAsync(new GetAllProfitsMapInput + { + SchemeId = schemeId, + Beneficiary = receiver + }); + allProfitMap.AllProfitsMap.ShouldNotContainKey(tokenSymbol); + allProfitMap.OneTimeClaimableProfitsMap.ShouldNotContainKey(tokenSymbol); } //second time @@ -1663,15 +1696,29 @@ await creator.DistributeProfits.SendAsync(new DistributeProfitsInput Symbol = tokenSymbol, SchemeId = schemeId }); - profitAmount.AllProfitAmount.ShouldBe(amount); - profitAmount.OneTimeClaimableProfitAmount.ShouldBe(amount); + profitAmount.Value.ShouldBe(amount); + var allProfitAmount = await ProfitContractStub.GetAllProfitAmount.CallAsync(new GetAllProfitAmountInput + { + Beneficiary = receiver, + Symbol = tokenSymbol, + SchemeId = schemeId + }); + allProfitAmount.AllProfitAmount.ShouldBe(amount); + allProfitAmount.OneTimeClaimableProfitAmount.ShouldBe(amount); var profitMap = await ProfitContractStub.GetProfitsMap.CallAsync(new ClaimProfitsInput { SchemeId = schemeId, Beneficiary = receiver }); - profitMap.AllProfitsMap[tokenSymbol].ShouldBe(amount); - profitMap.OneTimeClaimableProfitsMap[tokenSymbol].ShouldBe(amount); + profitMap.Value[tokenSymbol].ShouldBe(amount); + + var allProfitMap = await ProfitContractStub.GetAllProfitsMap.CallAsync(new GetAllProfitsMapInput + { + SchemeId = schemeId, + Beneficiary = receiver + }); + allProfitMap.AllProfitsMap[tokenSymbol].ShouldBe(amount); + allProfitMap.OneTimeClaimableProfitsMap[tokenSymbol].ShouldBe(amount); await ProfitContractStub.ClaimProfits.SendAsync(new ClaimProfitsInput { diff --git a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs index 9f21a6098f..68698e8177 100644 --- a/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs +++ b/test/AElf.Contracts.TokenHolder.Tests/TokenHolderTests.cs @@ -409,9 +409,9 @@ await TokenHolderContractStub.RegisterForProfits.SendAsync(new RegisterForProfit Beneficiary = Starter, SchemeId = schemeId }); - profitMap.AllProfitsMap.Count.ShouldBe(2); - profitMap.AllProfitsMap.ContainsKey(nativeTokenSymbol).ShouldBeTrue(); - profitMap.AllProfitsMap[nativeTokenSymbol].ShouldBe(amount); + profitMap.Value.Count.ShouldBe(2); + profitMap.Value.ContainsKey(nativeTokenSymbol).ShouldBeTrue(); + profitMap.Value[nativeTokenSymbol].ShouldBe(amount); var schemeInfoInProfit = await ProfitContractStub.GetScheme.CallAsync(schemeId); var schemeInfoInTokenHolder = await TokenHolderContractStub.GetScheme.CallAsync(Starter); schemeInfoInProfit.CurrentPeriod.ShouldBe(2); From 06d1e434371982fca963b6d89ddab00bf16bb743 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 23 Jan 2024 15:33:04 +0800 Subject: [PATCH 024/128] Remove duplicate codes --- contract/AElf.Contracts.Profit/ViewMethods.cs | 98 +++++-------------- 1 file changed, 24 insertions(+), 74 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index c6f73db5f8..99a4d666d7 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -61,57 +61,28 @@ private Hash GeneratePeriodVirtualAddressFromHash(Hash schemeId, long period) public override Int64Value GetProfitAmount(GetProfitAmountInput input) { - var profitItem = State.SchemeInfos[input.SchemeId]; - Assert(profitItem != null, "Scheme not found."); - var beneficiary = input.Beneficiary ?? Context.Sender; - var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; - - if (profitDetails == null) return new Int64Value { Value = 0 }; - - // ReSharper disable once PossibleNullReferenceException - var availableDetails = profitDetails.Details.Where(d => - d.LastProfitPeriod < profitItem.CurrentPeriod && (d.LastProfitPeriod == 0 - ? d.EndPeriod >= d.StartPeriod - : d.EndPeriod >= d.LastProfitPeriod) - ).ToList(); - - var amount = 0L; - - var profitableDetailCount = - Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); - var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); - - for (var i = 0;i < profitableDetailCount; i++) - { - var profitDetail = availableDetails[i]; - if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - - var profitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, - maxProfitReceivingPeriodCount, true, - input.Symbol); - - amount = amount.Add(profitsDictForEachProfitDetail.TryGetValue(input.Symbol, out var value) - ? value - : 0); - } + var output = GetAllProfitAmount(input.SchemeId, input.Symbol, input.Beneficiary); return new Int64Value { - Value = amount + Value = output.OneTimeClaimableProfitAmount }; } public override GetAllProfitAmountOutput GetAllProfitAmount(GetAllProfitAmountInput input) { - var profitItem = State.SchemeInfos[input.SchemeId]; + return GetAllProfitAmount(input.SchemeId, input.Symbol, input.Beneficiary); + } + + private GetAllProfitAmountOutput GetAllProfitAmount(Hash schemaId, string symbol, Address beneficiary) + { + var profitItem = State.SchemeInfos[schemaId]; Assert(profitItem != null, "Scheme not found."); - var beneficiary = input.Beneficiary ?? Context.Sender; - var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; + beneficiary = beneficiary ?? Context.Sender; + var profitDetails = State.ProfitDetailsMap[schemaId][beneficiary]; if (profitDetails == null) return new GetAllProfitAmountOutput { AllProfitAmount = 0, OneTimeClaimableProfitAmount = 0 }; - var profitVirtualAddress = Context.ConvertVirtualAddressToContractAddress(input.SchemeId); - // ReSharper disable once PossibleNullReferenceException var availableDetails = profitDetails.Details.Where(d => d.LastProfitPeriod < profitItem.CurrentPeriod && (d.LastProfitPeriod == 0 @@ -132,19 +103,19 @@ public override GetAllProfitAmountOutput GetAllProfitAmount(GetAllProfitAmountIn if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; var totalProfitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, - profitDetail.EndPeriod.Sub(profitDetail.LastProfitPeriod), true, input.Symbol); + profitDetail.EndPeriod.Sub(profitDetail.LastProfitPeriod), true, symbol); allProfitAmount = - allProfitAmount.Add(totalProfitsDictForEachProfitDetail.TryGetValue(input.Symbol, out var value) + allProfitAmount.Add(totalProfitsDictForEachProfitDetail.TryGetValue(symbol, out var value) ? value : 0); if(i >= profitableDetailCount) continue; var claimableProfitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, maxProfitReceivingPeriodCount, true, - input.Symbol); + symbol); claimableProfitAmount = claimableProfitAmount.Add( - claimableProfitsDictForEachProfitDetail.TryGetValue(input.Symbol, out var claimableValue) + claimableProfitsDictForEachProfitDetail.TryGetValue(symbol, out var claimableValue) ? claimableValue : 0); } @@ -158,46 +129,25 @@ public override GetAllProfitAmountOutput GetAllProfitAmount(GetAllProfitAmountIn public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) { - var scheme = State.SchemeInfos[input.SchemeId]; - Assert(scheme != null, "Scheme not found."); - var beneficiary = input.Beneficiary ?? Context.Sender; - var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; - - if (profitDetails == null) return new ReceivedProfitsMap(); - - // ReSharper disable once PossibleNullReferenceException - var availableDetails = profitDetails.Details.Where(d => - d.LastProfitPeriod < scheme.CurrentPeriod && (d.LastProfitPeriod == 0 - ? d.EndPeriod >= d.StartPeriod - : d.EndPeriod >= d.LastProfitPeriod) - ).ToList(); - - var profitableDetailCount = - Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); - var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); - - var profitsDict = new Dictionary(); - for (var i = 0; i < profitableDetailCount; i++) - { - var profitDetail = availableDetails[i]; - if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - - var profitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount,true); - AddProfitToDict(profitsDict, profitsDictForEachProfitDetail); - } + var output = GetAllProfitsMap(input.SchemeId, input.Beneficiary); return new ReceivedProfitsMap { - Value = { profitsDict } + Value = { output.OneTimeClaimableProfitsMap } }; } public override GetAllProfitsMapOutput GetAllProfitsMap(GetAllProfitsMapInput input) { - var scheme = State.SchemeInfos[input.SchemeId]; + return GetAllProfitsMap(input.SchemeId, input.Beneficiary); + } + + private GetAllProfitsMapOutput GetAllProfitsMap(Hash schemeId, Address beneficiary) + { + var scheme = State.SchemeInfos[schemeId]; Assert(scheme != null, "Scheme not found."); - var beneficiary = input.Beneficiary ?? Context.Sender; - var profitDetails = State.ProfitDetailsMap[input.SchemeId][beneficiary]; + beneficiary = beneficiary ?? Context.Sender; + var profitDetails = State.ProfitDetailsMap[schemeId][beneficiary]; if (profitDetails == null) return new GetAllProfitsMapOutput(); From 07adf842369c1f721757f1f9ce8b6e35fc11e13e Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 23 Jan 2024 15:38:40 +0800 Subject: [PATCH 025/128] Change to return all profit amount --- contract/AElf.Contracts.Profit/ViewMethods.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index 99a4d666d7..20fc498b11 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -65,7 +65,7 @@ public override Int64Value GetProfitAmount(GetProfitAmountInput input) return new Int64Value { - Value = output.OneTimeClaimableProfitAmount + Value = output.AllProfitAmount }; } @@ -133,7 +133,7 @@ public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) return new ReceivedProfitsMap { - Value = { output.OneTimeClaimableProfitsMap } + Value = { output.AllProfitsMap } }; } From d32538f82566d16768ecf5062f95f36836ac9207 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 23 Jan 2024 20:32:09 +0800 Subject: [PATCH 026/128] Fix unit test error --- .../Full/ReleaseProfitsFromTreasury.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs b/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs index cbe677415b..96657c3cd2 100644 --- a/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs +++ b/test/AElf.Contracts.Election.Tests/Full/ReleaseProfitsFromTreasury.cs @@ -383,11 +383,11 @@ public async Task CheckTreasuryProfitsDistribution_Test() })).Balance; var profitTester = GetProfitContractTester(VoterKeyPairs[0]); - var profitAmount = (await profitTester.GetProfitAmount.CallAsync(new GetProfitAmountInput + var profitAmount = (await profitTester.GetAllProfitAmount.CallAsync(new GetAllProfitAmountInput { SchemeId = ProfitItemsIds[ProfitType.CitizenWelfare], Symbol = EconomicContractsTestConstants.NativeTokenSymbol - })).Value; + })).OneTimeClaimableProfitAmount; profitAmount.ShouldBeGreaterThan(0); var profitResult = await profitTester.ClaimProfits.SendAsync(new ClaimProfitsInput @@ -636,11 +636,11 @@ private async Task GetProfitAmount(ProfitType type) break; } - return (await stub.GetProfitAmount.CallAsync(new GetProfitAmountInput + return (await stub.GetAllProfitAmount.CallAsync(new GetAllProfitAmountInput { SchemeId = ProfitItemsIds[type], Symbol = EconomicContractsTestConstants.NativeTokenSymbol - })).Value; + })).OneTimeClaimableProfitAmount; } private async Task GetReleasedAmount() From ef9ae577bd46828fb0d5ffa020cb02f5a84d8127 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 24 Jan 2024 18:52:18 +0800 Subject: [PATCH 027/128] Remove duplicate codes --- contract/AElf.Contracts.Profit/ViewMethods.cs | 74 ++++--------------- 1 file changed, 16 insertions(+), 58 deletions(-) diff --git a/contract/AElf.Contracts.Profit/ViewMethods.cs b/contract/AElf.Contracts.Profit/ViewMethods.cs index 20fc498b11..190204c3c5 100644 --- a/contract/AElf.Contracts.Profit/ViewMethods.cs +++ b/contract/AElf.Contracts.Profit/ViewMethods.cs @@ -61,79 +61,37 @@ private Hash GeneratePeriodVirtualAddressFromHash(Hash schemeId, long period) public override Int64Value GetProfitAmount(GetProfitAmountInput input) { - var output = GetAllProfitAmount(input.SchemeId, input.Symbol, input.Beneficiary); + var allProfitsMapResult = GetAllProfitsMap(input.SchemeId, input.Beneficiary, input.Symbol); return new Int64Value { - Value = output.AllProfitAmount + Value = allProfitsMapResult.AllProfitsMap.TryGetValue(input.Symbol, out var value) ? value : 0 }; } public override GetAllProfitAmountOutput GetAllProfitAmount(GetAllProfitAmountInput input) { - return GetAllProfitAmount(input.SchemeId, input.Symbol, input.Beneficiary); - } - - private GetAllProfitAmountOutput GetAllProfitAmount(Hash schemaId, string symbol, Address beneficiary) - { - var profitItem = State.SchemeInfos[schemaId]; - Assert(profitItem != null, "Scheme not found."); - beneficiary = beneficiary ?? Context.Sender; - var profitDetails = State.ProfitDetailsMap[schemaId][beneficiary]; - - if (profitDetails == null) return new GetAllProfitAmountOutput { AllProfitAmount = 0, OneTimeClaimableProfitAmount = 0 }; - - // ReSharper disable once PossibleNullReferenceException - var availableDetails = profitDetails.Details.Where(d => - d.LastProfitPeriod < profitItem.CurrentPeriod && (d.LastProfitPeriod == 0 - ? d.EndPeriod >= d.StartPeriod - : d.EndPeriod >= d.LastProfitPeriod) - ).ToList(); - - var allProfitAmount = 0L; - var claimableProfitAmount = 0L; - - var profitableDetailCount = - Math.Min(ProfitContractConstants.ProfitReceivingLimitForEachTime, availableDetails.Count); - var maxProfitReceivingPeriodCount = GetMaximumPeriodCountForProfitableDetail(profitableDetailCount); - - for (var i = 0;i < availableDetails.Count; i++) - { - var profitDetail = availableDetails[i]; - if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - - var totalProfitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, - profitDetail.EndPeriod.Sub(profitDetail.LastProfitPeriod), true, symbol); - allProfitAmount = - allProfitAmount.Add(totalProfitsDictForEachProfitDetail.TryGetValue(symbol, out var value) - ? value - : 0); - if(i >= profitableDetailCount) continue; - var claimableProfitsDictForEachProfitDetail = ProfitAllPeriods(profitItem, profitDetail, beneficiary, - maxProfitReceivingPeriodCount, true, - symbol); - - claimableProfitAmount = - claimableProfitAmount.Add( - claimableProfitsDictForEachProfitDetail.TryGetValue(symbol, out var claimableValue) - ? claimableValue - : 0); - } - + var allProfitsMapResult = GetAllProfitsMap(input.SchemeId, input.Beneficiary, input.Symbol); return new GetAllProfitAmountOutput { - AllProfitAmount = allProfitAmount, - OneTimeClaimableProfitAmount = claimableProfitAmount + AllProfitAmount = allProfitsMapResult.AllProfitsMap.TryGetValue(input.Symbol, out var allProfitAmount) + ? allProfitAmount + : 0, + OneTimeClaimableProfitAmount = + allProfitsMapResult.OneTimeClaimableProfitsMap.TryGetValue(input.Symbol, + out var oneTimeClaimableProfitAmount) + ? oneTimeClaimableProfitAmount + : 0 }; } public override ReceivedProfitsMap GetProfitsMap(ClaimProfitsInput input) { - var output = GetAllProfitsMap(input.SchemeId, input.Beneficiary); + var allProfitsMapResult = GetAllProfitsMap(input.SchemeId, input.Beneficiary); return new ReceivedProfitsMap { - Value = { output.AllProfitsMap } + Value = { allProfitsMapResult.AllProfitsMap } }; } @@ -142,7 +100,7 @@ public override GetAllProfitsMapOutput GetAllProfitsMap(GetAllProfitsMapInput in return GetAllProfitsMap(input.SchemeId, input.Beneficiary); } - private GetAllProfitsMapOutput GetAllProfitsMap(Hash schemeId, Address beneficiary) + private GetAllProfitsMapOutput GetAllProfitsMap(Hash schemeId, Address beneficiary, string symbol = null) { var scheme = State.SchemeInfos[schemeId]; Assert(scheme != null, "Scheme not found."); @@ -169,10 +127,10 @@ private GetAllProfitsMapOutput GetAllProfitsMap(Hash schemeId, Address beneficia var profitDetail = availableDetails[i]; if (profitDetail.LastProfitPeriod == 0) profitDetail.LastProfitPeriod = profitDetail.StartPeriod; - var totalProfitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, profitDetail.EndPeriod.Sub(profitDetail.LastProfitPeriod),true); + var totalProfitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, profitDetail.EndPeriod.Sub(profitDetail.LastProfitPeriod),true, symbol); AddProfitToDict(allProfitsDict, totalProfitsDictForEachProfitDetail); if(i >= profitableDetailCount) continue; - var claimableProfitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount,true); + var claimableProfitsDictForEachProfitDetail = ProfitAllPeriods(scheme, profitDetail, beneficiary, maxProfitReceivingPeriodCount,true, symbol); AddProfitToDict(claimableProfitsDict, claimableProfitsDictForEachProfitDetail); } From cc24fabea3e20078e9e80b46651b58c08ad52b7c Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Mon, 5 Feb 2024 18:33:35 +0800 Subject: [PATCH 028/128] feat: add RegularExpressions into whitelist --- .../Validators/Whitelist/IWhitelistProvider.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/AElf.CSharp.CodeOps/Validators/Whitelist/IWhitelistProvider.cs b/src/AElf.CSharp.CodeOps/Validators/Whitelist/IWhitelistProvider.cs index c04a54184c..e2b1a6b145 100644 --- a/src/AElf.CSharp.CodeOps/Validators/Whitelist/IWhitelistProvider.cs +++ b/src/AElf.CSharp.CodeOps/Validators/Whitelist/IWhitelistProvider.cs @@ -37,6 +37,7 @@ private void WhitelistAssemblies(Whitelist whitelist) .Assembly(System.Reflection.Assembly.Load("System.Runtime.Extensions"), Trust.Partial) .Assembly(System.Reflection.Assembly.Load("System.Private.CoreLib"), Trust.Partial) .Assembly(System.Reflection.Assembly.Load("System.ObjectModel"), Trust.Partial) + .Assembly(System.Reflection.Assembly.Load("System.Text.RegularExpressions"), Trust.Partial) .Assembly(System.Reflection.Assembly.Load("System.Linq"), Trust.Full) .Assembly(System.Reflection.Assembly.Load("System.Linq.Expressions"), Trust.Full) .Assembly(System.Reflection.Assembly.Load("System.Collections"), Trust.Full) @@ -142,6 +143,7 @@ private void WhitelistOthers(Whitelist whitelist) .Member(nameof(Encoding.UTF8), Permission.Allowed) .Member(nameof(Encoding.UTF8.GetByteCount), Permission.Allowed))) .Namespace("System.Numerics", Permission.Allowed) + .Namespace("System.Text.RegularExpressions", Permission.Allowed) ; } From d394d3811915d3f667c233f3e8fdcac624733460 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Mon, 5 Feb 2024 18:35:32 +0800 Subject: [PATCH 029/128] feat: symbol support letter and digit --- .../TokenContract_Helper.cs | 18 +++++++++--------- .../TokenContract_NFTHelper.cs | 4 ++-- .../TokenContract_NFT_Actions.cs | 2 +- .../TokenConverterContract.cs | 4 ++-- .../BVT/NftApplicationTests.cs | 19 +++++++++++++++++-- .../TokenConvertConnectorTest.cs | 2 +- .../TokenConverterContractTests.cs | 4 ++-- 7 files changed, 34 insertions(+), 19 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index ed210282d0..ea96874e42 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using AElf.Contracts.Parliament; using AElf.CSharp.Core; using AElf.Sdk.CSharp; @@ -14,20 +15,19 @@ namespace AElf.Contracts.MultiToken; public partial class TokenContract { - private static bool IsValidSymbolChar(char character) + private static bool IsValidSymbol(string symbol) { - return (character >= 'A' && character <= 'Z') || (character >= '0' && character <= '9') || - character == TokenContractConstants.NFTSymbolSeparator; + return Regex.IsMatch(symbol, "^[a-zA-Z0-9]+-?[0-9]*$"); } - private bool IsValidItemIdChar(char character) + private bool IsValidItemId(string symbolItemId) { - return character >= '0' && character <= '9'; + return Regex.IsMatch(symbolItemId, "^[0-9]+$"); } - private bool IsValidCreateSymbolChar(char character) + private bool IsValidCreateSymbol(string symbol) { - return character >= 'A' && character <= 'Z'; + return Regex.IsMatch(symbol, "^[a-zA-Z0-9]+$"); } private TokenInfo AssertValidToken(string symbol, long amount) @@ -40,7 +40,7 @@ private TokenInfo AssertValidToken(string symbol, long amount) private void AssertValidSymbolAndAmount(string symbol, long amount) { - Assert(!string.IsNullOrEmpty(symbol) && symbol.All(IsValidSymbolChar), + Assert(!string.IsNullOrEmpty(symbol) && IsValidSymbol(symbol), "Invalid symbol."); Assert(amount > 0, "Invalid amount."); } @@ -184,7 +184,7 @@ private void AssertCrossChainTransaction(Transaction originalTransaction, Addres private void RegisterTokenInfo(TokenInfo tokenInfo) { CheckTokenExists(tokenInfo.Symbol); - Assert(!string.IsNullOrEmpty(tokenInfo.Symbol) && tokenInfo.Symbol.All(IsValidSymbolChar), + Assert(!string.IsNullOrEmpty(tokenInfo.Symbol) && IsValidSymbol(tokenInfo.Symbol), "Invalid symbol."); Assert(!string.IsNullOrEmpty(tokenInfo.TokenName), "Token name can neither be null nor empty."); Assert(tokenInfo.TotalSupply > 0, "Invalid total supply."); diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs index bddfb444bf..8546bf11fe 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs @@ -7,9 +7,9 @@ public partial class TokenContract private SymbolType GetCreateInputSymbolType(string symbol) { var words = symbol.Split(TokenContractConstants.NFTSymbolSeparator); - Assert(words[0].Length > 0 && words[0].All(IsValidCreateSymbolChar), "Invalid Symbol input"); + Assert(words[0].Length > 0 && IsValidCreateSymbol(words[0]), "Invalid Symbol input"); if (words.Length == 1) return SymbolType.Token; - Assert(words.Length == 2 && words[1].Length > 0 && words[1].All(IsValidItemIdChar), "Invalid NFT Symbol input"); + Assert(words.Length == 2 && words[1].Length > 0 && IsValidItemId(words[1]), "Invalid NFT Symbol input"); return words[1] == TokenContractConstants.CollectionSymbolSuffix ? SymbolType.NftCollection : SymbolType.Nft; } diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index 1926727e68..adee8f0b85 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -91,7 +91,7 @@ private string GetNftCollectionSymbol(string inputSymbol) var words = symbol.Split(TokenContractConstants.NFTSymbolSeparator); const int tokenSymbolLength = 1; if (words.Length == tokenSymbolLength) return null; - Assert(words.Length == 2 && words[1].All(IsValidItemIdChar), "Invalid NFT Symbol Input"); + Assert(words.Length == 2 && IsValidItemId(words[1]), "Invalid NFT Symbol Input"); return symbol == $"{words[0]}-0" ? null : $"{words[0]}-0"; } diff --git a/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs b/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs index 1a7aaf80d2..c72b3eb903 100644 --- a/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs +++ b/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using System.Linq; +using System.Text.RegularExpressions; using AElf.Contracts.MultiToken; using AElf.CSharp.Core; using AElf.Sdk.CSharp; @@ -321,8 +322,7 @@ private static bool IsBetweenZeroAndOne(decimal number) private static bool IsValidSymbol(string symbol) { - return symbol.Length > 0 && - symbol.All(c => c >= 'A' && c <= 'Z'); + return Regex.IsMatch(symbol, "^[a-zA-Z0-9]+$"); } private static bool IsValidBaseSymbol(string symbol) diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs index 24055b432b..14dc5e2026 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs @@ -243,8 +243,23 @@ public async Task MultiTokenContract_Create_NFTCollection_Input_Check_Test() ExternalInfo = input.ExternalInfo, Owner = input.Owner }); - - var result = await TokenContractStub.Create.SendWithExceptionAsync(seedInput);; + + var result = await TokenContractStub.Create.SendAsync(seedInput); + result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + + seedInput = BuildSeedCreateInput(new CreateInput + { + Symbol = "ABC123()", + TokenName = input.TokenName, + TotalSupply = input.TotalSupply, + Decimals = input.Decimals, + Issuer = input.Issuer, + IssueChainId = input.IssueChainId, + ExternalInfo = input.ExternalInfo, + Owner = input.Owner + }); + + result = await TokenContractStub.Create.SendWithExceptionAsync(seedInput); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); result.TransactionResult.Error.ShouldContain("Invalid Symbol input"); } diff --git a/test/AElf.Contracts.TokenConverter.Tests/TokenConvertConnectorTest.cs b/test/AElf.Contracts.TokenConverter.Tests/TokenConvertConnectorTest.cs index 6c462e1d96..eadfb2f2f2 100644 --- a/test/AElf.Contracts.TokenConverter.Tests/TokenConvertConnectorTest.cs +++ b/test/AElf.Contracts.TokenConverter.Tests/TokenConvertConnectorTest.cs @@ -87,7 +87,7 @@ await ExecuteProposalForParliamentTransaction(TokenConverterContractAddress, [Theory] [InlineData("WRITE", "0.5", "0.5", "resource token symbol has existed")] [InlineData("", "0.5", "0.5", "resource token symbol should not be empty")] - [InlineData("N89", "0.2", "0.5", "Invalid symbol.")] + [InlineData("N89()", "0.2", "0.5", "Invalid symbol.")] [InlineData("MKA", "0", "0.5", "Connector Shares has to be a decimal between 0 and 1.")] [InlineData("JUN", "0.9", "1", "Connector Shares has to be a decimal between 0 and 1.")] public async Task AddPairConnector_With_Invalid_Input_Test(string tokenSymbol, string resourceWeight, diff --git a/test/AElf.Contracts.TokenConverter.Tests/TokenConverterContractTests.cs b/test/AElf.Contracts.TokenConverter.Tests/TokenConverterContractTests.cs index 2f598d96e1..c502b4e19d 100644 --- a/test/AElf.Contracts.TokenConverter.Tests/TokenConverterContractTests.cs +++ b/test/AElf.Contracts.TokenConverter.Tests/TokenConverterContractTests.cs @@ -84,7 +84,7 @@ public async Task Initialize_Failed_Test() //Base token symbol is invalid. { var input = GetLegalInitializeInput(); - input.BaseTokenSymbol = "elf1"; + input.BaseTokenSymbol = "elf1<>"; var result = (await DefaultStub.Initialize.SendWithExceptionAsync(input)).TransactionResult; result.Status.ShouldBe(TransactionResultStatus.Failed); result.Error.Contains("Base token symbol is invalid.").ShouldBeTrue(); @@ -111,7 +111,7 @@ public async Task Initialize_Failed_Test() //Invalid connector symbol { var input = GetLegalInitializeInput(); - input.Connectors[0].Symbol = "write"; + input.Connectors[0].Symbol = "write-0"; var result = (await DefaultStub.Initialize.SendWithExceptionAsync(input)).TransactionResult; result.Status.ShouldBe(TransactionResultStatus.Failed); result.Error.Contains("Invalid symbol.").ShouldBeTrue(); From 62b6f32d3d9874957d66120f580f96e6ae816ea6 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Tue, 6 Feb 2024 15:20:44 +0800 Subject: [PATCH 030/128] feat: update Regex in whitelist --- .../Validators/Whitelist/IWhitelistProvider.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/AElf.CSharp.CodeOps/Validators/Whitelist/IWhitelistProvider.cs b/src/AElf.CSharp.CodeOps/Validators/Whitelist/IWhitelistProvider.cs index e2b1a6b145..2d47d59a3e 100644 --- a/src/AElf.CSharp.CodeOps/Validators/Whitelist/IWhitelistProvider.cs +++ b/src/AElf.CSharp.CodeOps/Validators/Whitelist/IWhitelistProvider.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Text; +using System.Text.RegularExpressions; using AElf.Cryptography.SecretSharing; using AElf.CSharp.Core; using AElf.Kernel.SmartContract; @@ -143,7 +144,14 @@ private void WhitelistOthers(Whitelist whitelist) .Member(nameof(Encoding.UTF8), Permission.Allowed) .Member(nameof(Encoding.UTF8.GetByteCount), Permission.Allowed))) .Namespace("System.Numerics", Permission.Allowed) - .Namespace("System.Text.RegularExpressions", Permission.Allowed) + .Namespace("System.Text.RegularExpressions", Permission.Denied, type => type + .Type(nameof(Regex), Permission.Denied, member => member + .Member(nameof(Regex.IsMatch), Permission.Allowed) + .Member(nameof(Regex.Match), Permission.Allowed) + .Member(nameof(Regex.Matches), Permission.Allowed) + .Member(nameof(Regex.Replace), Permission.Allowed) + .Member(nameof(Regex.Split), Permission.Allowed) + )) ; } From 79dca5221e93c4641c6f55e890e8e45767d4ca2d Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Tue, 20 Feb 2024 13:56:20 +0800 Subject: [PATCH 031/128] feat:add token insensitive check --- contract/AElf.Contracts.MultiToken/TokenContractState.cs | 1 + contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/contract/AElf.Contracts.MultiToken/TokenContractState.cs b/contract/AElf.Contracts.MultiToken/TokenContractState.cs index c32b0ea62f..ac930d538d 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractState.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractState.cs @@ -9,6 +9,7 @@ public partial class TokenContractState : ContractState public StringState ChainPrimaryTokenSymbol { get; set; } public MappedState TokenInfos { get; set; } + public MappedState InsensitiveTokenExisting { get; set; } public MappedState SymbolSeedMap { get; set; } public MappedState Balances { get; set; } public MappedState Allowances { get; set; } diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index ea96874e42..05c0b0e984 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -191,6 +191,7 @@ private void RegisterTokenInfo(TokenInfo tokenInfo) Assert(tokenInfo.Issuer != null, "Invalid issuer address."); Assert(tokenInfo.Owner != null, "Invalid owner address."); State.TokenInfos[tokenInfo.Symbol] = tokenInfo; + State.InsensitiveTokenExisting[tokenInfo.Symbol.ToUpper()] = true; } private void CrossChainVerify(Hash transactionId, long parentChainHeight, int chainId, MerklePath merklePath) @@ -257,6 +258,7 @@ private void CheckTokenExists(string symbol) var empty = new TokenInfo(); var existing = State.TokenInfos[symbol]; Assert(existing == null || existing.Equals(empty), "Token already exists."); + Assert(!State.InsensitiveTokenExisting[symbol.ToUpper()], "Token already exists."); } private void CheckSymbolLength(string symbol, SymbolType symbolType) From 729019af754a0050bdbc81139c769d06487814de Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Thu, 22 Feb 2024 10:24:16 +0800 Subject: [PATCH 032/128] feat: check past token --- contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index 05c0b0e984..7008ebc802 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -258,6 +258,9 @@ private void CheckTokenExists(string symbol) var empty = new TokenInfo(); var existing = State.TokenInfos[symbol]; Assert(existing == null || existing.Equals(empty), "Token already exists."); + // check past token + existing = State.TokenInfos[symbol.ToUpper()]; + Assert(existing == null || existing.Equals(empty), "Token already exists."); Assert(!State.InsensitiveTokenExisting[symbol.ToUpper()], "Token already exists."); } From 7edbdc7e8c0a3e2dc92cd05da944a4e4e1a0a651 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Thu, 22 Feb 2024 15:39:13 +0800 Subject: [PATCH 033/128] feat:remove nft decimal restriction --- .../TokenContract_NFTHelper.cs | 4 --- .../TokenContract_NFT_Actions.cs | 2 -- .../BVT/NftApplicationTests.cs | 31 ------------------- 3 files changed, 37 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs index bddfb444bf..4c3efdf6a3 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs @@ -13,8 +13,4 @@ private SymbolType GetCreateInputSymbolType(string symbol) return words[1] == TokenContractConstants.CollectionSymbolSuffix ? SymbolType.NftCollection : SymbolType.Nft; } - private void AssertNFTCreateInput(CreateInput input) - { - Assert(input.Decimals == 0, "NFT's decimals must be 0"); - } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index 1926727e68..ac487122cb 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -9,13 +9,11 @@ public partial class TokenContract { private Empty CreateNFTCollection(CreateInput input) { - AssertNFTCreateInput(input); return CreateToken(input, SymbolType.NftCollection); } private Empty CreateNFTInfo(CreateInput input) { - AssertNFTCreateInput(input); var nftCollectionInfo = AssertNftCollectionExist(input.Symbol); input.IssueChainId = input.IssueChainId == 0 ? nftCollectionInfo.IssueChainId : input.IssueChainId; Assert(input.IssueChainId == nftCollectionInfo.IssueChainId, diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs index 24055b432b..8cec0aabbd 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs @@ -214,22 +214,6 @@ public async Task MultiTokenContract_Create_721Nft_Test() public async Task MultiTokenContract_Create_NFTCollection_Input_Check_Test() { var input = NftCollection721Info; - // Decimals check - { - var result = await CreateMutiTokenWithExceptionAsync(TokenContractStub, new CreateInput - { - Symbol = $"{input.Symbol}0", - TokenName = input.TokenName, - TotalSupply = input.TotalSupply, - Decimals = 8, - Issuer = input.Issuer, - IssueChainId = input.IssueChainId, - ExternalInfo = input.ExternalInfo, - Owner = input.Owner - }); - result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); - result.TransactionResult.Error.ShouldContain("NFT's decimals must be 0"); - } // Symbol check { var seedInput = BuildSeedCreateInput( new CreateInput @@ -289,21 +273,6 @@ public async Task MultiTokenContract_Create_NFT_Input_Check_Test() await CreateNftCollectionAsync(NftCollection721Info); var input = Nft721Info; - // Decimals check - { - var result = await CreateMutiTokenWithExceptionAsync(TokenContractStub, new CreateInput - { - Symbol = "GHJ-0", - TokenName = input.TokenName, - TotalSupply = input.TotalSupply, - Decimals = 8, - Issuer = input.Issuer, - IssueChainId = input.IssueChainId, - ExternalInfo = input.ExternalInfo - }); - result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); - result.TransactionResult.Error.ShouldContain("NFT's decimals must be 0"); - } // Symbol check { var result = await CreateSeedNftWithExceptionAsync(TokenContractStub, new CreateInput From 3a8eef1ea21daa95f989f6ac74ee4d3cc0891b0e Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Mon, 26 Feb 2024 14:00:54 +0800 Subject: [PATCH 034/128] feat: seed token insensitive check --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 2 +- contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs | 5 ++--- .../AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 249afc33ea..acb512864b 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -55,7 +55,7 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. if (!IsAddressInCreateWhiteList(Context.Sender) && input.Symbol != TokenContractConstants.SeedCollectionSymbol) { - var symbolSeed = State.SymbolSeedMap[input.Symbol]; + var symbolSeed = State.SymbolSeedMap[input.Symbol.ToUpper()]; CheckSeedNFT(symbolSeed, input.Symbol); // seed nft for one-time use only long balance = State.Balances[Context.Sender][symbolSeed]; diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index 7008ebc802..51061e5f53 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -256,11 +256,10 @@ private void CheckTokenAndCollectionExists(string symbol) private void CheckTokenExists(string symbol) { var empty = new TokenInfo(); - var existing = State.TokenInfos[symbol]; - Assert(existing == null || existing.Equals(empty), "Token already exists."); // check past token - existing = State.TokenInfos[symbol.ToUpper()]; + var existing = State.TokenInfos[symbol.ToUpper()]; Assert(existing == null || existing.Equals(empty), "Token already exists."); + // check new token Assert(!State.InsensitiveTokenExisting[symbol.ToUpper()], "Token already exists."); } diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index adee8f0b85..e118144df4 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -37,7 +37,7 @@ private Empty CreateNFTInfo(CreateInput input) CheckSymbolLength(ownedSymbol, ownedSymbolType); CheckTokenAndCollectionExists(ownedSymbol); CheckSymbolSeed(ownedSymbol); - State.SymbolSeedMap[ownedSymbol] = input.Symbol; + State.SymbolSeedMap[ownedSymbol.ToUpper()] = input.Symbol; } return CreateToken(input, SymbolType.Nft); @@ -45,7 +45,7 @@ private Empty CreateNFTInfo(CreateInput input) private void CheckSymbolSeed(string ownedSymbol) { - var oldSymbolSeed = State.SymbolSeedMap[ownedSymbol]; + var oldSymbolSeed = State.SymbolSeedMap[ownedSymbol.ToUpper()]; Assert(oldSymbolSeed == null || !State.TokenInfos[oldSymbolSeed].ExternalInfo.Value .TryGetValue(TokenContractConstants.SeedExpireTimeExternalInfoKey, From 6c4f6c808d59374b64af76c877b2a25945d498d5 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Mon, 26 Feb 2024 16:36:03 +0800 Subject: [PATCH 035/128] feat: create token on specified chain --- .../TokenContract_Actions.cs | 8 +++-- .../TokenContract_NFT_Actions.cs | 5 ++-- .../BVT/NftApplicationTests.cs | 30 +++++++++++++++++++ .../BVT/TokenApplicationTests.cs | 25 +++++++++------- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 249afc33ea..15c8553e3f 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -32,8 +32,6 @@ public override Empty InitializeFromParentChain(InitializeFromParentChainInput i /// public override Empty Create(CreateInput input) { - // can not call create on side chain - Assert(State.SideChainCreator.Value == null, "Failed to create token if side chain creator already set."); var inputSymbolType = GetCreateInputSymbolType(input.Symbol); if (input.Owner == null) { @@ -62,6 +60,12 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. DoTransferFrom(Context.Sender, Context.Self, Context.Self, symbolSeed, balance, ""); Burn(Context.Self, symbolSeed, balance); } + else + { + // can not call create on side chain + Assert(State.SideChainCreator.Value == null, + "Failed to create token if side chain creator already set."); + } } var tokenInfo = new TokenInfo diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index 1926727e68..61ae892907 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -18,14 +18,15 @@ private Empty CreateNFTInfo(CreateInput input) AssertNFTCreateInput(input); var nftCollectionInfo = AssertNftCollectionExist(input.Symbol); input.IssueChainId = input.IssueChainId == 0 ? nftCollectionInfo.IssueChainId : input.IssueChainId; - Assert(input.IssueChainId == nftCollectionInfo.IssueChainId, + Assert( + input.IssueChainId == nftCollectionInfo.IssueChainId && Context.ChainId == nftCollectionInfo.IssueChainId, "NFT create ChainId must be collection's issue chainId"); - var owner = nftCollectionInfo.Owner ?? nftCollectionInfo.Issuer; Assert(Context.Sender == owner && owner == input.Owner, "NFT owner must be collection's owner"); if (nftCollectionInfo.Symbol == TokenContractConstants.SeedCollectionSymbol) { + Assert(input.Decimals == 0 && input.TotalSupply == 1, "SEED must be unique."); Assert(input.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedOwnedSymbolExternalInfoKey, out var ownedSymbol), "OwnedSymbol does not exist."); Assert(input.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedExpireTimeExternalInfoKey, diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs index 24055b432b..dcf55368a8 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs @@ -184,6 +184,30 @@ private async Task> CreateNftCollectionAndNft(bool reuseItemId = tr return symbols; } + private async Task CreateNftFailed() + { + var collectionInfo = NftCollection1155Info; + collectionInfo.IssueChainId = 123; + var createCollectionRes = await CreateNftCollectionAsync(collectionInfo); + createCollectionRes.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + Nft1155Info.IssueChainId = 123; + var createNft2Res = await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput + { + Symbol = $"{collectionInfo.Symbol}{Nft1155Info.Symbol}", + TokenName = Nft1155Info.TokenName, + TotalSupply = Nft1155Info.TotalSupply, + Decimals = Nft1155Info.Decimals, + Issuer = Nft1155Info.Issuer, + IsBurnable = Nft1155Info.IsBurnable, + IssueChainId = Nft1155Info.IssueChainId, + ExternalInfo = Nft1155Info.ExternalInfo, + Owner = Nft1155Info.Issuer + }); + createNft2Res.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + createNft2Res.TransactionResult.Error.Contains("NFT create ChainId must be collection's issue chainId") + .ShouldBeTrue(); + } + private void AssertTokenEqual(TokenCreated log, TokenInfo input) { Assert.Equal(log.TokenName, input.TokenName); @@ -204,6 +228,12 @@ public async Task MultiTokenContract_Create_1155Nft_Test() await CreateNftCollectionAndNft(); } + [Fact(DisplayName = "[MultiToken_Nft] Create 1155 nfts failed.")] + public async Task MultiTokenContract_Create_1155Nft_failed_Test() + { + await CreateNftFailed(); + } + [Fact(DisplayName = "[MultiToken_Nft] Create 721 nfts.")] public async Task MultiTokenContract_Create_721Nft_Test() { diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index dba96e1023..0bb4ade958 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -999,16 +999,21 @@ public async Task Side_Chain_Creat_Token_Test() }); await ApproveWithMinersAsync(proposalId); await ParliamentContractStub.Release.SendAsync(proposalId); - var createTokenRet = await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput - { - Symbol = "ALI", - TokenName = "Ali", - Decimals = 4, - TotalSupply = 100_000, - Issuer = DefaultAddress, - Owner = DefaultAddress - }); - createTokenRet.TransactionResult.Error.ShouldContain( + + proposalId = await CreateProposalAsync(TokenContractAddress, + defaultParliament, nameof(TokenContractStub.Create), + new CreateInput + { + Symbol = "ALI", + TokenName = "Ali", + Decimals = 4, + TotalSupply = 100_000, + Issuer = DefaultAddress, + Owner = DefaultAddress + }); + await ApproveWithMinersAsync(proposalId); + var createTokenRe = await ParliamentContractStub.Release.SendWithExceptionAsync(proposalId); + createTokenRe.TransactionResult.Error.ShouldContain( "Failed to create token if side chain creator already set."); } From 5f364ff1e86fe7964b84f0d07870c98a0f6da5bb Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Thu, 29 Feb 2024 10:52:05 +0800 Subject: [PATCH 036/128] feat: add seed when cross chain create --- .../TokenContract_Actions.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 15c8553e3f..3d9331120d 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -127,7 +127,7 @@ private void CheckSeedNFT(string symbolSeed, String symbol) /// - /// Set primary token symbol. + /// SetSymbolSeed primary token symbol. /// /// /// @@ -472,7 +472,7 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) Owner = validateTokenInfoExistsInput.Owner ?? validateTokenInfoExistsInput.Issuer }; RegisterTokenInfo(tokenInfo); - + SetSymbolSeed(tokenInfo); Context.Fire(new TokenCreated { Symbol = validateTokenInfoExistsInput.Symbol, @@ -489,6 +489,17 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) return new Empty(); } + private void SetSymbolSeed(TokenInfo tokenInfo) + { + var collectionSymbol = GetNftCollectionSymbol(tokenInfo.Symbol); + if (collectionSymbol == TokenContractConstants.SeedCollectionSymbol && tokenInfo.ExternalInfo != null && + tokenInfo.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedOwnedSymbolExternalInfoKey, + out var ownedSymbol)) + { + State.SymbolSeedMap[ownedSymbol] = tokenInfo.Symbol; + } + } + public override Empty RegisterCrossChainTokenContractAddress(RegisterCrossChainTokenContractAddressInput input) { CheckCrossChainTokenContractRegistrationControllerAuthority(); From 0815a2b8b3f3e2ecd2bbc98c69d7531b1e84fbd6 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Thu, 29 Feb 2024 11:21:03 +0800 Subject: [PATCH 037/128] feat: SetSymbolSeed update --- .../TokenContract_Actions.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 3d9331120d..c099134932 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -491,13 +491,18 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) private void SetSymbolSeed(TokenInfo tokenInfo) { - var collectionSymbol = GetNftCollectionSymbol(tokenInfo.Symbol); - if (collectionSymbol == TokenContractConstants.SeedCollectionSymbol && tokenInfo.ExternalInfo != null && + if (GetNftCollectionSymbol(tokenInfo.Symbol) == TokenContractConstants.SeedCollectionSymbol && + tokenInfo.ExternalInfo != null && tokenInfo.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedOwnedSymbolExternalInfoKey, - out var ownedSymbol)) + out var ownedSymbol) && + tokenInfo.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedExpireTimeExternalInfoKey, + out var expirationTime) && + long.TryParse(expirationTime, out var expirationTimeLong) && + Context.CurrentBlockTime.Seconds <= expirationTimeLong) { State.SymbolSeedMap[ownedSymbol] = tokenInfo.Symbol; } + } public override Empty RegisterCrossChainTokenContractAddress(RegisterCrossChainTokenContractAddressInput input) From ab522bbf346745409ba7d6c78ece10d750e578ce Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Thu, 29 Feb 2024 15:43:03 +0800 Subject: [PATCH 038/128] feat: add batch approve --- .../TokenContractConstants.cs | 1 + .../TokenContractState.cs | 2 + .../TokenContract_Actions.cs | 48 +++++++-- protobuf/token_contract.proto | 8 +- protobuf/token_contract_impl.proto | 9 ++ .../BVT/TokenApplicationTests.cs | 97 +++++++++++++++++++ 6 files changed, 158 insertions(+), 7 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs index a14a4e9496..db4923fd4e 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs @@ -22,4 +22,5 @@ public static class TokenContractConstants public const string SeedCollectionSymbol = "SEED-0"; public const string SeedOwnedSymbolExternalInfoKey = "__seed_owned_symbol"; public const string SeedExpireTimeExternalInfoKey = "__seed_exp_time"; + public const int DefaultMaximumBatchApproveCount = 10; } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContractState.cs b/contract/AElf.Contracts.MultiToken/TokenContractState.cs index c32b0ea62f..dc386b311b 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractState.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractState.cs @@ -65,4 +65,6 @@ public partial class TokenContractState : ContractState public SingletonState
VoteContractAddress { get; set; } public SingletonState TokenIssuerAndOwnerModificationDisabled { get; set; } + + public SingletonState MaximumBatchApproveCount { get; set; } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 249afc33ea..ca328a6260 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -251,16 +251,28 @@ public override Empty TransferFrom(TransferFromInput input) public override Empty Approve(ApproveInput input) { - AssertValidInputAddress(input.Spender); - AssertValidToken(input.Symbol, input.Amount); - State.Allowances[Context.Sender][input.Spender][input.Symbol] = input.Amount; + Approve(input.Spender, input.Symbol, input.Amount); + return new Empty(); + } + + private void Approve(Address spender, string symbol, long amount) + { + AssertValidInputAddress(spender); + AssertValidToken(symbol, amount); + State.Allowances[Context.Sender][spender][symbol] = amount; Context.Fire(new Approved { Owner = Context.Sender, - Spender = input.Spender, - Symbol = input.Symbol, - Amount = input.Amount + Spender = spender, + Symbol = symbol, + Amount = amount }); + } + + public override Empty BatchApprove(BatchApproveInput input) + { + Assert(input.Value.Count <= GetMaximumBatchApproveCount(), "Exceeds the maximum batch approve count."); + foreach (var approve in input.Value) Approve(approve.Spender, approve.Symbol, approve.Amount); return new Empty(); } @@ -626,4 +638,28 @@ public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) Value = !State.TokenIssuerAndOwnerModificationDisabled.Value }; } + + public override Empty SetMaximumBatchApproveCount(Int32Value input) + { + AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); + Assert(input.Value != 0, "Invalid input."); + State.MaximumBatchApproveCount.Value = input.Value; + return new Empty(); + } + + public override Int32Value GetMaximumBatchApproveCount(Empty input) + { + return new Int32Value + { + Value = GetMaximumBatchApproveCount() + }; + } + + private int GetMaximumBatchApproveCount() + { + var maximumBatchApproveCount = State.MaximumBatchApproveCount.Value == 0 + ? TokenContractConstants.DefaultMaximumBatchApproveCount + : State.MaximumBatchApproveCount.Value; + return maximumBatchApproveCount; + } } \ No newline at end of file diff --git a/protobuf/token_contract.proto b/protobuf/token_contract.proto index d858bf5f1d..e1174b8b45 100644 --- a/protobuf/token_contract.proto +++ b/protobuf/token_contract.proto @@ -40,7 +40,10 @@ service TokenContract { // enabling the Spender to call TransferFrom. rpc Approve (ApproveInput) returns (google.protobuf.Empty) { } - + + rpc BatchApprove (BatchApproveInput) returns (google.protobuf.Empty) { + } + // This is the reverse operation for Approve, it will decrease the allowance. rpc UnApprove (UnApproveInput) returns (google.protobuf.Empty) { } @@ -354,6 +357,9 @@ message ApproveInput { // The amount of token to approve. int64 amount = 3; } +message BatchApproveInput { + repeated ApproveInput value = 1; +} message UnApproveInput { // The address that allowance will be decreased. diff --git a/protobuf/token_contract_impl.proto b/protobuf/token_contract_impl.proto index 9a7779ec45..ece30da14c 100644 --- a/protobuf/token_contract_impl.proto +++ b/protobuf/token_contract_impl.proto @@ -83,6 +83,11 @@ service TokenContractImpl { rpc RemoveTransactionFeeFreeAllowancesConfig (RemoveTransactionFeeFreeAllowancesConfigInput) returns (google.protobuf.Empty) { } + rpc SetMaximumBatchApproveCount (google.protobuf.Int32Value) returns (google.protobuf.Empty) { + + } + + // Delegatee sets the delegation and related information of the delegator based on a transaction. rpc SetTransactionFeeDelegateInfos (SetTransactionFeeDelegateInfosInput) returns (google.protobuf.Empty){ } @@ -173,6 +178,10 @@ service TokenContractImpl { rpc GetTokenIssuerAndOwnerModificationEnabled(google.protobuf.Empty) returns (google.protobuf.BoolValue) { option (aelf.is_view) = true; } + + rpc GetMaximumBatchApproveCount (google.protobuf.Empty) returns (google.protobuf.Int32Value) { + + } } message AdvanceResourceTokenInput { diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index dba96e1023..07b737dfd0 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -171,6 +171,103 @@ public async Task MultiTokenContract_Approve_ContractAddress_Test() basicAllowanceOutput.Allowance.ShouldBe(2000L); } + [Fact(DisplayName = "[MultiToken] BatchApprove token to Contract")] + public async Task MultiTokenContract_BatchApprove_ContractAddress_Test() + { + await CreateTokenAndIssue(); + var approveBasisResult = (await TokenContractStub.BatchApprove.SendAsync(new BatchApproveInput + { + Value = + { + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 2000L, + Spender = BasicFunctionContractAddress + }, + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 1000L, + Spender = OtherBasicFunctionContractAddress + }, + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 5000L, + Spender = TreasuryContractAddress + } + } + })).TransactionResult; + approveBasisResult.Status.ShouldBe(TransactionResultStatus.Mined); + + var basicAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = BasicFunctionContractAddress, + Symbol = SymbolForTest + }); + basicAllowanceOutput.Allowance.ShouldBe(2000L); + var otherBasicAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = OtherBasicFunctionContractAddress, + Symbol = SymbolForTest + }); + otherBasicAllowanceOutput.Allowance.ShouldBe(1000L); + var treasuryAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = TreasuryContractAddress, + Symbol = SymbolForTest + }); + treasuryAllowanceOutput.Allowance.ShouldBe(5000L); + } + + [Fact] + public async Task MultiTokenContract_SetMaximumBatchApproveCount_Test() + { + var result = await TokenContractStub.SetMaximumBatchApproveCount.SendWithExceptionAsync(new Int32Value + { + Value = 1 + }); + result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); + result.TransactionResult.Error.ShouldContain("Unauthorized behavior"); + var maximumBatchApproveCountOutput = await TokenContractStub.GetMaximumBatchApproveCount.CallAsync(new Empty()); + maximumBatchApproveCountOutput.Value.ShouldBe(10); + var defaultParliament = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); + var proposalId = await CreateProposalAsync(TokenContractAddress, + defaultParliament, nameof(TokenContractStub.SetMaximumBatchApproveCount), + new Int32Value + { + Value = 1 + }); + await ApproveWithMinersAsync(proposalId); + await ParliamentContractStub.Release.SendAsync(proposalId); + maximumBatchApproveCountOutput = await TokenContractStub.GetMaximumBatchApproveCount.CallAsync(new Empty()); + maximumBatchApproveCountOutput.Value.ShouldBe(1); + var approveBasisResult = (await TokenContractStub.BatchApprove.SendWithExceptionAsync(new BatchApproveInput + { + Value = + { + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 2000L, + Spender = BasicFunctionContractAddress + }, + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 1000L, + Spender = OtherBasicFunctionContractAddress + } + } + })).TransactionResult; + approveBasisResult.Status.ShouldBe(TransactionResultStatus.Failed); + approveBasisResult.Error.ShouldContain("Exceeds the maximum batch approve count"); + } + [Fact(DisplayName = "[MultiToken] Approve token out of owner's balance")] public async Task MultiTokenContract_Approve_OutOfAmount_Test() { From 7d84b6b68d907ede1c373439f576f19d72e8f8a0 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Fri, 1 Mar 2024 09:59:48 +0800 Subject: [PATCH 039/128] feat: update MaximumBatchApproveCount check --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index ca328a6260..ad7605faaa 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -642,7 +642,7 @@ public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) public override Empty SetMaximumBatchApproveCount(Int32Value input) { AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); - Assert(input.Value != 0, "Invalid input."); + Assert(input.Value > 0, "Invalid input."); State.MaximumBatchApproveCount.Value = input.Value; return new Empty(); } From 709555422e6062ca07431ff438c4271e6cceb448 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Fri, 1 Mar 2024 10:38:14 +0800 Subject: [PATCH 040/128] feat:update description --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index c099134932..574bed6287 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -127,7 +127,7 @@ private void CheckSeedNFT(string symbolSeed, String symbol) /// - /// SetSymbolSeed primary token symbol. + /// Set primary token symbol. /// /// /// From eb4ceaad5304e1fc9100e0faf805445a1d4e9c76 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Fri, 1 Mar 2024 11:53:47 +0800 Subject: [PATCH 041/128] feat: ApproveInputList distinct --- .../TokenContract_Actions.cs | 18 ++++++-- .../BVT/TokenApplicationTests.cs | 42 +++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index ad7605faaa..49f0556bb1 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -251,14 +251,14 @@ public override Empty TransferFrom(TransferFromInput input) public override Empty Approve(ApproveInput input) { + AssertValidInputAddress(input.Spender); + AssertValidToken(input.Symbol, input.Amount); Approve(input.Spender, input.Symbol, input.Amount); return new Empty(); } private void Approve(Address spender, string symbol, long amount) { - AssertValidInputAddress(spender); - AssertValidToken(symbol, amount); State.Allowances[Context.Sender][spender][symbol] = amount; Context.Fire(new Approved { @@ -271,8 +271,18 @@ private void Approve(Address spender, string symbol, long amount) public override Empty BatchApprove(BatchApproveInput input) { - Assert(input.Value.Count <= GetMaximumBatchApproveCount(), "Exceeds the maximum batch approve count."); - foreach (var approve in input.Value) Approve(approve.Spender, approve.Symbol, approve.Amount); + Assert(input != null && input.Value != null, "Invalid input ."); + foreach (var approve in input.Value) + { + AssertValidInputAddress(approve.Spender); + AssertValidToken(approve.Symbol, approve.Amount); + } + + var approveInputList = input.Value.GroupBy(approve => approve.Symbol + approve.Spender, approve => approve) + .Select(approve => approve.Last()).ToList(); + Assert(approveInputList.Count <= GetMaximumBatchApproveCount(), "Exceeds the maximum batch approve count."); + foreach (var approve in approveInputList) + Approve(approve.Spender, approve.Symbol, approve.Amount); return new Empty(); } diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index 07b737dfd0..fe80b7710f 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -222,6 +222,47 @@ public async Task MultiTokenContract_BatchApprove_ContractAddress_Test() Symbol = SymbolForTest }); treasuryAllowanceOutput.Allowance.ShouldBe(5000L); + + approveBasisResult = (await TokenContractStub.BatchApprove.SendAsync(new BatchApproveInput + { + Value = + { + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 1000L, + Spender = BasicFunctionContractAddress + }, + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 3000L, + Spender = BasicFunctionContractAddress + }, + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 3000L, + Spender = TreasuryContractAddress + } + } + })).TransactionResult; + approveBasisResult.Status.ShouldBe(TransactionResultStatus.Mined); + basicAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = BasicFunctionContractAddress, + Symbol = SymbolForTest + }); + basicAllowanceOutput.Allowance.ShouldBe(3000L); + + treasuryAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = TreasuryContractAddress, + Symbol = SymbolForTest + }); + treasuryAllowanceOutput.Allowance.ShouldBe(3000L); } [Fact] @@ -246,6 +287,7 @@ public async Task MultiTokenContract_SetMaximumBatchApproveCount_Test() await ParliamentContractStub.Release.SendAsync(proposalId); maximumBatchApproveCountOutput = await TokenContractStub.GetMaximumBatchApproveCount.CallAsync(new Empty()); maximumBatchApproveCountOutput.Value.ShouldBe(1); + await CreateTokenAndIssue(); var approveBasisResult = (await TokenContractStub.BatchApprove.SendWithExceptionAsync(new BatchApproveInput { Value = From cc028e2f551de6fc9ebbbfcae38a0b6edb5b722e Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Fri, 1 Mar 2024 14:00:41 +0800 Subject: [PATCH 042/128] feat: method rename --- .../TokenContractConstants.cs | 2 +- .../TokenContract_Actions.cs | 12 ++++++------ protobuf/token_contract_impl.proto | 4 ++-- .../BVT/TokenApplicationTests.cs | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs index db4923fd4e..59eae99a8c 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs @@ -22,5 +22,5 @@ public static class TokenContractConstants public const string SeedCollectionSymbol = "SEED-0"; public const string SeedOwnedSymbolExternalInfoKey = "__seed_owned_symbol"; public const string SeedExpireTimeExternalInfoKey = "__seed_exp_time"; - public const int DefaultMaximumBatchApproveCount = 10; + public const int DefaultMaxBatchApproveCount = 100; } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 49f0556bb1..8ccceb13d9 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -280,7 +280,7 @@ public override Empty BatchApprove(BatchApproveInput input) var approveInputList = input.Value.GroupBy(approve => approve.Symbol + approve.Spender, approve => approve) .Select(approve => approve.Last()).ToList(); - Assert(approveInputList.Count <= GetMaximumBatchApproveCount(), "Exceeds the maximum batch approve count."); + Assert(approveInputList.Count <= GetMaxBatchApproveCount(), "Exceeds the maximum batch approve count."); foreach (var approve in approveInputList) Approve(approve.Spender, approve.Symbol, approve.Amount); return new Empty(); @@ -649,7 +649,7 @@ public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) }; } - public override Empty SetMaximumBatchApproveCount(Int32Value input) + public override Empty SetMaxBatchApproveCount(Int32Value input) { AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); Assert(input.Value > 0, "Invalid input."); @@ -657,18 +657,18 @@ public override Empty SetMaximumBatchApproveCount(Int32Value input) return new Empty(); } - public override Int32Value GetMaximumBatchApproveCount(Empty input) + public override Int32Value GetMaxBatchApproveCount(Empty input) { return new Int32Value { - Value = GetMaximumBatchApproveCount() + Value = GetMaxBatchApproveCount() }; } - private int GetMaximumBatchApproveCount() + private int GetMaxBatchApproveCount() { var maximumBatchApproveCount = State.MaximumBatchApproveCount.Value == 0 - ? TokenContractConstants.DefaultMaximumBatchApproveCount + ? TokenContractConstants.DefaultMaxBatchApproveCount : State.MaximumBatchApproveCount.Value; return maximumBatchApproveCount; } diff --git a/protobuf/token_contract_impl.proto b/protobuf/token_contract_impl.proto index ece30da14c..5885914e80 100644 --- a/protobuf/token_contract_impl.proto +++ b/protobuf/token_contract_impl.proto @@ -83,7 +83,7 @@ service TokenContractImpl { rpc RemoveTransactionFeeFreeAllowancesConfig (RemoveTransactionFeeFreeAllowancesConfigInput) returns (google.protobuf.Empty) { } - rpc SetMaximumBatchApproveCount (google.protobuf.Int32Value) returns (google.protobuf.Empty) { + rpc SetMaxBatchApproveCount (google.protobuf.Int32Value) returns (google.protobuf.Empty) { } @@ -179,7 +179,7 @@ service TokenContractImpl { option (aelf.is_view) = true; } - rpc GetMaximumBatchApproveCount (google.protobuf.Empty) returns (google.protobuf.Int32Value) { + rpc GetMaxBatchApproveCount (google.protobuf.Empty) returns (google.protobuf.Int32Value) { } } diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index fe80b7710f..fec97b0026 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -268,24 +268,24 @@ public async Task MultiTokenContract_BatchApprove_ContractAddress_Test() [Fact] public async Task MultiTokenContract_SetMaximumBatchApproveCount_Test() { - var result = await TokenContractStub.SetMaximumBatchApproveCount.SendWithExceptionAsync(new Int32Value + var result = await TokenContractStub.SetMaxBatchApproveCount.SendWithExceptionAsync(new Int32Value { Value = 1 }); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); result.TransactionResult.Error.ShouldContain("Unauthorized behavior"); - var maximumBatchApproveCountOutput = await TokenContractStub.GetMaximumBatchApproveCount.CallAsync(new Empty()); - maximumBatchApproveCountOutput.Value.ShouldBe(10); + var maximumBatchApproveCountOutput = await TokenContractStub.GetMaxBatchApproveCount.CallAsync(new Empty()); + maximumBatchApproveCountOutput.Value.ShouldBe(100); var defaultParliament = await ParliamentContractStub.GetDefaultOrganizationAddress.CallAsync(new Empty()); var proposalId = await CreateProposalAsync(TokenContractAddress, - defaultParliament, nameof(TokenContractStub.SetMaximumBatchApproveCount), + defaultParliament, nameof(TokenContractStub.SetMaxBatchApproveCount), new Int32Value { Value = 1 }); await ApproveWithMinersAsync(proposalId); await ParliamentContractStub.Release.SendAsync(proposalId); - maximumBatchApproveCountOutput = await TokenContractStub.GetMaximumBatchApproveCount.CallAsync(new Empty()); + maximumBatchApproveCountOutput = await TokenContractStub.GetMaxBatchApproveCount.CallAsync(new Empty()); maximumBatchApproveCountOutput.Value.ShouldBe(1); await CreateTokenAndIssue(); var approveBasisResult = (await TokenContractStub.BatchApprove.SendWithExceptionAsync(new BatchApproveInput From b4263e3d2c5ce5930c08e842afb5b70e501794ec Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Fri, 1 Mar 2024 16:25:25 +0800 Subject: [PATCH 043/128] feat: update BatchApproveInputCount check --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 8ccceb13d9..5ba46b9623 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -272,15 +272,14 @@ private void Approve(Address spender, string symbol, long amount) public override Empty BatchApprove(BatchApproveInput input) { Assert(input != null && input.Value != null, "Invalid input ."); + Assert(input.Value.Count <= GetMaxBatchApproveCount(), "Exceeds the maximum batch approve count."); foreach (var approve in input.Value) { AssertValidInputAddress(approve.Spender); AssertValidToken(approve.Symbol, approve.Amount); } - var approveInputList = input.Value.GroupBy(approve => approve.Symbol + approve.Spender, approve => approve) .Select(approve => approve.Last()).ToList(); - Assert(approveInputList.Count <= GetMaxBatchApproveCount(), "Exceeds the maximum batch approve count."); foreach (var approve in approveInputList) Approve(approve.Spender, approve.Symbol, approve.Amount); return new Empty(); From db75acf5b4db8badddbf84c45d1522983f67cc80 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Sat, 2 Mar 2024 15:13:49 +0800 Subject: [PATCH 044/128] feat:seed cannot create token on side chain --- .../TokenContract_Actions.cs | 25 +++---------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 574bed6287..0159b7002c 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -50,6 +50,9 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. AssertValidCreateInput(input, symbolType); if (symbolType == SymbolType.Token || symbolType == SymbolType.NftCollection) { + // can not call create on side chain + Assert(State.SideChainCreator.Value == null, + "Failed to create token if side chain creator already set."); if (!IsAddressInCreateWhiteList(Context.Sender) && input.Symbol != TokenContractConstants.SeedCollectionSymbol) { @@ -60,12 +63,6 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. DoTransferFrom(Context.Sender, Context.Self, Context.Self, symbolSeed, balance, ""); Burn(Context.Self, symbolSeed, balance); } - else - { - // can not call create on side chain - Assert(State.SideChainCreator.Value == null, - "Failed to create token if side chain creator already set."); - } } var tokenInfo = new TokenInfo @@ -472,7 +469,6 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) Owner = validateTokenInfoExistsInput.Owner ?? validateTokenInfoExistsInput.Issuer }; RegisterTokenInfo(tokenInfo); - SetSymbolSeed(tokenInfo); Context.Fire(new TokenCreated { Symbol = validateTokenInfoExistsInput.Symbol, @@ -489,21 +485,6 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) return new Empty(); } - private void SetSymbolSeed(TokenInfo tokenInfo) - { - if (GetNftCollectionSymbol(tokenInfo.Symbol) == TokenContractConstants.SeedCollectionSymbol && - tokenInfo.ExternalInfo != null && - tokenInfo.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedOwnedSymbolExternalInfoKey, - out var ownedSymbol) && - tokenInfo.ExternalInfo.Value.TryGetValue(TokenContractConstants.SeedExpireTimeExternalInfoKey, - out var expirationTime) && - long.TryParse(expirationTime, out var expirationTimeLong) && - Context.CurrentBlockTime.Seconds <= expirationTimeLong) - { - State.SymbolSeedMap[ownedSymbol] = tokenInfo.Symbol; - } - - } public override Empty RegisterCrossChainTokenContractAddress(RegisterCrossChainTokenContractAddressInput input) { From aa1b5b30bf204472105826df62327a9c12bcd764 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Sun, 3 Mar 2024 14:35:05 +0800 Subject: [PATCH 045/128] feat:rename MaxBatchApproveCount --- .../AElf.Contracts.MultiToken/TokenContractState.cs | 2 +- .../AElf.Contracts.MultiToken/TokenContract_Actions.cs | 10 +++++----- .../BVT/TokenApplicationTests.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContractState.cs b/contract/AElf.Contracts.MultiToken/TokenContractState.cs index dc386b311b..b687cf1598 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractState.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractState.cs @@ -66,5 +66,5 @@ public partial class TokenContractState : ContractState public SingletonState TokenIssuerAndOwnerModificationDisabled { get; set; } - public SingletonState MaximumBatchApproveCount { get; set; } + public SingletonState MaxBatchApproveCount { get; set; } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 5ba46b9623..08ff42a689 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -272,7 +272,7 @@ private void Approve(Address spender, string symbol, long amount) public override Empty BatchApprove(BatchApproveInput input) { Assert(input != null && input.Value != null, "Invalid input ."); - Assert(input.Value.Count <= GetMaxBatchApproveCount(), "Exceeds the maximum batch approve count."); + Assert(input.Value.Count <= GetMaxBatchApproveCount(), "Exceeds the max batch approve count."); foreach (var approve in input.Value) { AssertValidInputAddress(approve.Spender); @@ -652,7 +652,7 @@ public override Empty SetMaxBatchApproveCount(Int32Value input) { AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); Assert(input.Value > 0, "Invalid input."); - State.MaximumBatchApproveCount.Value = input.Value; + State.MaxBatchApproveCount.Value = input.Value; return new Empty(); } @@ -666,9 +666,9 @@ public override Int32Value GetMaxBatchApproveCount(Empty input) private int GetMaxBatchApproveCount() { - var maximumBatchApproveCount = State.MaximumBatchApproveCount.Value == 0 + var maxBatchApproveCount = State.MaxBatchApproveCount.Value == 0 ? TokenContractConstants.DefaultMaxBatchApproveCount - : State.MaximumBatchApproveCount.Value; - return maximumBatchApproveCount; + : State.MaxBatchApproveCount.Value; + return maxBatchApproveCount; } } \ No newline at end of file diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index fec97b0026..a63eba113c 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -307,7 +307,7 @@ public async Task MultiTokenContract_SetMaximumBatchApproveCount_Test() } })).TransactionResult; approveBasisResult.Status.ShouldBe(TransactionResultStatus.Failed); - approveBasisResult.Error.ShouldContain("Exceeds the maximum batch approve count"); + approveBasisResult.Error.ShouldContain("Exceeds the max batch approve count"); } [Fact(DisplayName = "[MultiToken] Approve token out of owner's balance")] From 4f7f5639c31e6518620935f5a5af413555d67408 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Sun, 3 Mar 2024 17:11:41 +0800 Subject: [PATCH 046/128] feat:update BatchApprove check --- .../AElf.Contracts.MultiToken/TokenContract_Actions.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 162ba78c01..2a477cafe7 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -272,7 +272,7 @@ private void Approve(Address spender, string symbol, long amount) public override Empty BatchApprove(BatchApproveInput input) { - Assert(input != null && input.Value != null, "Invalid input ."); + Assert(input != null && input.Value != null && input.Value.Count > 0, "Invalid input ."); Assert(input.Value.Count <= GetMaxBatchApproveCount(), "Exceeds the max batch approve count."); foreach (var approve in input.Value) { @@ -651,8 +651,8 @@ public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) public override Empty SetMaxBatchApproveCount(Int32Value input) { - AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); Assert(input.Value > 0, "Invalid input."); + AssertSenderAddressWith(GetDefaultParliamentController().OwnerAddress); State.MaxBatchApproveCount.Value = input.Value; return new Empty(); } @@ -667,9 +667,8 @@ public override Int32Value GetMaxBatchApproveCount(Empty input) private int GetMaxBatchApproveCount() { - var maxBatchApproveCount = State.MaxBatchApproveCount.Value == 0 + return State.MaxBatchApproveCount.Value == 0 ? TokenContractConstants.DefaultMaxBatchApproveCount : State.MaxBatchApproveCount.Value; - return maxBatchApproveCount; } } \ No newline at end of file From fb34cee2f3359ddeef2725bcb05cff3fe54d1f3d Mon Sep 17 00:00:00 2001 From: eanzhao Date: Wed, 6 Mar 2024 17:12:34 +0800 Subject: [PATCH 047/128] Return true directly if code check is not enabled. --- .../Application/ICheckedCodeHashProvider.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/AElf.Kernel.CodeCheck/Application/ICheckedCodeHashProvider.cs b/src/AElf.Kernel.CodeCheck/Application/ICheckedCodeHashProvider.cs index ac8ccc666a..3c4daa24a5 100644 --- a/src/AElf.Kernel.CodeCheck/Application/ICheckedCodeHashProvider.cs +++ b/src/AElf.Kernel.CodeCheck/Application/ICheckedCodeHashProvider.cs @@ -13,10 +13,14 @@ public interface ICheckedCodeHashProvider internal class CheckedCodeHashProvider : BlockExecutedDataBaseProvider, ICheckedCodeHashProvider, ISingletonDependency { + private readonly CodeCheckOptions _codeCheckOptions; + public CheckedCodeHashProvider( - ICachedBlockchainExecutedDataService cachedBlockchainExecutedDataService) : + ICachedBlockchainExecutedDataService cachedBlockchainExecutedDataService, + IOptionsSnapshot codeCheckOptions) : base(cachedBlockchainExecutedDataService) { + _codeCheckOptions = codeCheckOptions.Value; Logger = NullLogger.Instance; } @@ -32,6 +36,11 @@ public async Task AddCodeHashAsync(BlockIndex blockIndex, Hash codeHash) public bool IsCodeHashExists(BlockIndex blockIndex, Hash codeHash) { + if (!_codeCheckOptions.CodeCheckEnabled) + { + return true; + } + var codeHashMap = GetBlockExecutedData(blockIndex); if (codeHashMap == null) return false; From 2753cb991d18085777539b27435f97804b38337b Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Wed, 6 Mar 2024 20:35:17 +0800 Subject: [PATCH 048/128] feat:Compatibility with historical NFT creations --- .../TokenContractConstants.cs | 1 + .../TokenContract_NFT_Actions.cs | 17 +++++++++-- .../BVT/NftApplicationTests.cs | 28 +++++++++++++++---- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs index a14a4e9496..95bb390726 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs @@ -22,4 +22,5 @@ public static class TokenContractConstants public const string SeedCollectionSymbol = "SEED-0"; public const string SeedOwnedSymbolExternalInfoKey = "__seed_owned_symbol"; public const string SeedExpireTimeExternalInfoKey = "__seed_exp_time"; + public const string NftCreateChainIdExternalInfoKey = "__nft_create_chain_id"; } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index 2e242cf589..2e8fa8bd29 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -17,10 +17,23 @@ private Empty CreateNFTInfo(CreateInput input) var nftCollectionInfo = AssertNftCollectionExist(input.Symbol); input.IssueChainId = input.IssueChainId == 0 ? nftCollectionInfo.IssueChainId : input.IssueChainId; Assert( - input.IssueChainId == nftCollectionInfo.IssueChainId && Context.ChainId == nftCollectionInfo.IssueChainId, - "NFT create ChainId must be collection's issue chainId"); + input.IssueChainId == nftCollectionInfo.IssueChainId, + "NFT issue ChainId must be collection's issue chainId"); + var owner = nftCollectionInfo.Owner ?? nftCollectionInfo.Issuer; Assert(Context.Sender == owner && owner == input.Owner, "NFT owner must be collection's owner"); + if (nftCollectionInfo.ExternalInfo != null && nftCollectionInfo.ExternalInfo.Value.TryGetValue( + TokenContractConstants.NftCreateChainIdExternalInfoKey, + out var nftCreateChainId) && long.TryParse(nftCreateChainId, out var nftCreateChainIdLong)) + { + Assert(nftCreateChainIdLong == Context.ChainId, + "NFT create ChainId must be collection's NFT create chainId"); + } + else + { + Assert(State.SideChainCreator.Value == null, + "Failed to create token if side chain creator already set."); + } if (nftCollectionInfo.Symbol == TokenContractConstants.SeedCollectionSymbol) { diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs index 6c92193b21..d6a6945ef2 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/NftApplicationTests.cs @@ -17,6 +17,7 @@ public static class NftCollectionMetaFields public static string BaseUriKey = "__nft_base_uri"; public static string NftType = "__nft_type"; public const string IsItemIdReuseKey = "__nft_is_item_id_reuse"; + public const string NftCreateChainIdExternalInfoKey = "__nft_create_chain_id"; } public static class NftInfoMetaFields @@ -186,11 +187,28 @@ private async Task> CreateNftCollectionAndNft(bool reuseItemId = tr private async Task CreateNftFailed() { - var collectionInfo = NftCollection1155Info; - collectionInfo.IssueChainId = 123; + var collectionInfo = new TokenInfo + { + Symbol = NftCollection1155Info.Symbol, + TokenName = NftCollection1155Info.TokenName, + TotalSupply = NftCollection1155Info.TotalSupply, + Decimals = NftCollection1155Info.Decimals, + Issuer = NftCollection1155Info.Issuer, + IssueChainId = NftCollection1155Info.IssueChainId, + ExternalInfo = new ExternalInfo() + { + Value = + { + { + NftCollectionMetaFields.NftCreateChainIdExternalInfoKey, + "1234" + } + } + }, + Owner = NftCollection1155Info.Issuer + }; var createCollectionRes = await CreateNftCollectionAsync(collectionInfo); createCollectionRes.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - Nft1155Info.IssueChainId = 123; var createNft2Res = await TokenContractStub.Create.SendWithExceptionAsync(new CreateInput { Symbol = $"{collectionInfo.Symbol}{Nft1155Info.Symbol}", @@ -204,7 +222,7 @@ private async Task CreateNftFailed() Owner = Nft1155Info.Issuer }); createNft2Res.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); - createNft2Res.TransactionResult.Error.Contains("NFT create ChainId must be collection's issue chainId") + createNft2Res.TransactionResult.Error.Contains("NFT create ChainId must be collection's NFT create chainId") .ShouldBeTrue(); } @@ -377,7 +395,7 @@ public async Task MultiTokenContract_Create_NFT_Input_Check_Test() Owner = input.Owner }); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); - result.TransactionResult.Error.ShouldContain("NFT create ChainId must be collection's issue chainId"); + result.TransactionResult.Error.ShouldContain("NFT issue ChainId must be collection's issue chainId"); } } From 777e040dc98c98b7b35b447cb5487c76021ec68c Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Thu, 7 Mar 2024 10:27:05 +0800 Subject: [PATCH 049/128] feat:Modify the verification order --- .../AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index 2e8fa8bd29..124d1dac73 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -19,9 +19,6 @@ private Empty CreateNFTInfo(CreateInput input) Assert( input.IssueChainId == nftCollectionInfo.IssueChainId, "NFT issue ChainId must be collection's issue chainId"); - - var owner = nftCollectionInfo.Owner ?? nftCollectionInfo.Issuer; - Assert(Context.Sender == owner && owner == input.Owner, "NFT owner must be collection's owner"); if (nftCollectionInfo.ExternalInfo != null && nftCollectionInfo.ExternalInfo.Value.TryGetValue( TokenContractConstants.NftCreateChainIdExternalInfoKey, out var nftCreateChainId) && long.TryParse(nftCreateChainId, out var nftCreateChainIdLong)) @@ -34,7 +31,9 @@ private Empty CreateNFTInfo(CreateInput input) Assert(State.SideChainCreator.Value == null, "Failed to create token if side chain creator already set."); } - + + var owner = nftCollectionInfo.Owner ?? nftCollectionInfo.Issuer; + Assert(Context.Sender == owner && owner == input.Owner, "NFT owner must be collection's owner"); if (nftCollectionInfo.Symbol == TokenContractConstants.SeedCollectionSymbol) { Assert(input.Decimals == 0 && input.TotalSupply == 1, "SEED must be unique."); From 7cf51056216a6f7963df685739ef60a2c49d9735 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 21 Mar 2024 13:08:39 +0800 Subject: [PATCH 050/128] Add logs for debug. --- .../Application/CodeCheckJobProcessor.cs | 54 +++++++++++-------- .../CodeCheckRequiredLogEventProcessor.cs | 46 +++++++++------- 2 files changed, 60 insertions(+), 40 deletions(-) diff --git a/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs b/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs index 99c53b9c35..9863b16db2 100644 --- a/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs +++ b/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs @@ -83,32 +83,44 @@ private TransformBlock CreateCodeCheckBufferBlock() private async Task ProcessCodeCheckJobAsync(CodeCheckJob job) { - var codeCheckResult = await _codeCheckService.PerformCodeCheckAsync(job.ContractCode, job.BlockHash, - job.BlockHeight, job.ContractCategory, job.IsSystemContract, job.IsUserContract); - - var codeHash = HashHelper.ComputeFrom(job.ContractCode); - Logger.LogInformation("Code check result: {codeCheckResult}, code hash: {codeHash}", codeCheckResult, - codeHash.ToHex()); - - if (!codeCheckResult) - return; - - if (job.IsUserContract) + Logger.LogInformation("Processing code check job started."); + try { - _codeCheckProposalService.AddReleasableProposal(job.CodeCheckProposalId, job.ProposedContractInputHash, - job.BlockHeight); - } + var codeCheckResult = await _codeCheckService.PerformCodeCheckAsync(job.ContractCode, job.BlockHash, + job.BlockHeight, job.ContractCategory, job.IsSystemContract, job.IsUserContract); - // Cache proposal id to generate system approval transaction later - _proposalService.AddNotApprovedProposal(job.CodeCheckProposalId, job.BlockHeight); + var codeHash = HashHelper.ComputeFrom(job.ContractCode); + Logger.LogInformation("Code check result: {codeCheckResult}, code hash: {codeHash}", codeCheckResult, + codeHash.ToHex()); + + if (!codeCheckResult) + { + Logger.LogError("Code check failed."); + return; + } + + if (job.IsUserContract) + { + _codeCheckProposalService.AddReleasableProposal(job.CodeCheckProposalId, job.ProposedContractInputHash, + job.BlockHeight); + } - await _checkedCodeHashProvider.AddCodeHashAsync(new BlockIndex + _proposalService.AddNotApprovedProposal(job.CodeCheckProposalId, job.BlockHeight); + + await _checkedCodeHashProvider.AddCodeHashAsync(new BlockIndex + { + BlockHash = job.BlockHash, + BlockHeight = job.BlockHeight + }, codeHash); + } + catch (Exception e) { - BlockHash = job.BlockHash, - BlockHeight = job.BlockHeight - }, codeHash); + Logger.LogError("Error while processing code check job: {e}", e); + } + + Logger.LogInformation("Processing code check job ended."); } - + private CodeCheckJob UpdateBucketIndex(CodeCheckJob job) { var assemblyLoadContext = new AssemblyLoadContext(null, true); diff --git a/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs b/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs index 7abeea313b..1ea0ed6730 100644 --- a/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs +++ b/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using AElf.CSharp.Core.Extension; @@ -48,28 +49,35 @@ public override async Task ProcessAsync(Block block, Dictionary l.Name == nameof(ProposalCreated)).NonIndexed) - .ProposalId; - - var code = eventData.Code.ToByteArray(); - var sendResult = await _codeCheckJobProcessor.SendAsync(new CodeCheckJob + try { - BlockHash = block.GetHash(), - BlockHeight = block.Height, - ContractCode = code, - ContractCategory = eventData.Category, - IsSystemContract = eventData.IsSystemContract, - IsUserContract = eventData.IsUserContract, - CodeCheckProposalId = proposalId, - ProposedContractInputHash = eventData.ProposedContractInputHash - }); + var proposalId = ProposalCreated.Parser + .ParseFrom(transactionResult.Logs.First(l => l.Name == nameof(ProposalCreated)).NonIndexed) + .ProposalId; + + var code = eventData.Code.ToByteArray(); + var sendResult = await _codeCheckJobProcessor.SendAsync(new CodeCheckJob + { + BlockHash = block.GetHash(), + BlockHeight = block.Height, + ContractCode = code, + ContractCategory = eventData.Category, + IsSystemContract = eventData.IsSystemContract, + IsUserContract = eventData.IsUserContract, + CodeCheckProposalId = proposalId, + ProposedContractInputHash = eventData.ProposedContractInputHash + }); - if (!sendResult) + if (!sendResult) + { + Logger.LogError( + "Unable to perform code check. BlockHash: {BlockHash}, BlockHeight: {BlockHeight}, CodeHash: {CodeHash}, ProposalId: {ProposalId}", + block.GetHash(), block.Height, HashHelper.ComputeFrom(code).ToHex(), proposalId.ToHex()); + } + } + catch (Exception e) { - Logger.LogError( - "Unable to perform code check. BlockHash: {BlockHash}, BlockHeight: {BlockHeight}, CodeHash: {CodeHash}, ProposalId: {ProposalId}", - block.GetHash(), block.Height, HashHelper.ComputeFrom(code).ToHex(), proposalId.ToHex()); + Logger.LogError("Error while processing CodeCheckRequired log event. {0}", e); } } } From 18e3742ae088722f934d2b073856ef2f598eba8e Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 21 Mar 2024 14:00:36 +0800 Subject: [PATCH 051/128] Log code check job --- src/AElf.Kernel.CodeCheck/Application/CodeCheckJob.cs | 8 ++++++++ .../CodeCheckRequiredLogEventProcessor.cs | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/AElf.Kernel.CodeCheck/Application/CodeCheckJob.cs b/src/AElf.Kernel.CodeCheck/Application/CodeCheckJob.cs index 7898215da7..d6e268718e 100644 --- a/src/AElf.Kernel.CodeCheck/Application/CodeCheckJob.cs +++ b/src/AElf.Kernel.CodeCheck/Application/CodeCheckJob.cs @@ -11,4 +11,12 @@ public class CodeCheckJob public Hash CodeCheckProposalId { get; set; } public Hash ProposedContractInputHash { get; set; } public long BucketIndex { get; set; } + + public override string ToString() + { + return $"BlockHash: {BlockHash}, BlockHeight: {BlockHeight}, ContractCategory: {ContractCategory}, " + + $"IsSystemContract: {IsSystemContract}, IsUserContract: {IsUserContract}, " + + $"CodeCheckProposalId: {CodeCheckProposalId}, ProposedContractInputHash: {ProposedContractInputHash}, " + + $"BucketIndex: {BucketIndex}"; + } } \ No newline at end of file diff --git a/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs b/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs index 1ea0ed6730..a05eddef1a 100644 --- a/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs +++ b/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs @@ -56,7 +56,7 @@ public override async Task ProcessAsync(Block block, Dictionary Date: Thu, 21 Mar 2024 19:57:38 +0800 Subject: [PATCH 052/128] Tune CreateCodeCheckBufferBlock method. --- .../Application/CodeCheckJobProcessor.cs | 24 ++++++------------- .../CodeCheckRequiredLogEventProcessor.cs | 2 +- .../Application/CodeCheckJobProcessorTests.cs | 2 +- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs b/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs index 9863b16db2..7a9705bffd 100644 --- a/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs +++ b/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs @@ -23,7 +23,7 @@ public class CodeCheckJobProcessor : ICodeCheckJobProcessor, ISingletonDependenc private readonly ICodeCheckService _codeCheckService; private readonly IProposalService _proposalService; private readonly ICodeCheckProposalService _codeCheckProposalService; - + public ILogger Logger { get; set; } public CodeCheckJobProcessor(IOptionsSnapshot codeCheckOptions, @@ -61,7 +61,7 @@ private TransformBlock CreateCodeCheckBufferBlock() BoundedCapacity = Math.Max(_codeCheckOptions.MaxBoundedCapacity, 1), MaxDegreeOfParallelism = _codeCheckOptions.MaxDegreeOfParallelism }); - + _codeCheckProcessesJobTransformBlock = new List>(); for (var i = 0; i < _codeCheckOptions.MaxDegreeOfParallelism; i++) { @@ -72,9 +72,7 @@ private TransformBlock CreateCodeCheckBufferBlock() BoundedCapacity = Math.Max(_codeCheckOptions.MaxBoundedCapacity, 1), EnsureOrdered = false }); - var index = i; - updateBucketIndexTransformBlock.LinkTo(processCodeCheckJobTransformBlock, linkOptions, - codeCheckJob => codeCheckJob.BucketIndex == index); + updateBucketIndexTransformBlock.LinkTo(processCodeCheckJobTransformBlock, linkOptions); _codeCheckProcessesJobTransformBlock.Add(processCodeCheckJobTransformBlock); } @@ -83,19 +81,16 @@ private TransformBlock CreateCodeCheckBufferBlock() private async Task ProcessCodeCheckJobAsync(CodeCheckJob job) { - Logger.LogInformation("Processing code check job started."); try { var codeCheckResult = await _codeCheckService.PerformCodeCheckAsync(job.ContractCode, job.BlockHash, job.BlockHeight, job.ContractCategory, job.IsSystemContract, job.IsUserContract); var codeHash = HashHelper.ComputeFrom(job.ContractCode); - Logger.LogInformation("Code check result: {codeCheckResult}, code hash: {codeHash}", codeCheckResult, - codeHash.ToHex()); if (!codeCheckResult) { - Logger.LogError("Code check failed."); + Logger.LogError("Code check failed for code hash: {codeHash}", codeHash.ToHex()); return; } @@ -116,20 +111,15 @@ await _checkedCodeHashProvider.AddCodeHashAsync(new BlockIndex catch (Exception e) { Logger.LogError("Error while processing code check job: {e}", e); + throw; } - - Logger.LogInformation("Processing code check job ended."); } private CodeCheckJob UpdateBucketIndex(CodeCheckJob job) { - var assemblyLoadContext = new AssemblyLoadContext(null, true); - var assembly = assemblyLoadContext.LoadFromStream(new MemoryStream(job.ContractCode)); - job.BucketIndex = - Math.Abs(HashHelper.ComputeFrom(assembly.GetName().Name).ToInt64() % _codeCheckOptions.MaxDegreeOfParallelism); - assemblyLoadContext.Unload(); - + Math.Abs(HashHelper.ComputeFrom(job.ContractCode).ToInt64() % _codeCheckOptions.MaxDegreeOfParallelism); + return job; } } \ No newline at end of file diff --git a/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs b/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs index a05eddef1a..b539979444 100644 --- a/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs +++ b/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs @@ -74,7 +74,7 @@ public override async Task ProcessAsync(Block block, Dictionary Date: Tue, 26 Mar 2024 10:48:55 +0800 Subject: [PATCH 053/128] Fix CreateCodeCheckBufferBlock --- .../Application/CodeCheckJobProcessor.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs b/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs index 7a9705bffd..90bfadca17 100644 --- a/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs +++ b/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs @@ -72,7 +72,9 @@ private TransformBlock CreateCodeCheckBufferBlock() BoundedCapacity = Math.Max(_codeCheckOptions.MaxBoundedCapacity, 1), EnsureOrdered = false }); - updateBucketIndexTransformBlock.LinkTo(processCodeCheckJobTransformBlock, linkOptions); + var index = i; + updateBucketIndexTransformBlock.LinkTo(processCodeCheckJobTransformBlock, linkOptions, + codeCheckJob => codeCheckJob.BucketIndex == index); _codeCheckProcessesJobTransformBlock.Add(processCodeCheckJobTransformBlock); } From 11e2002f9f67913bd9f898bc64f99d04e46db81d Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 28 Mar 2024 16:46:48 +0800 Subject: [PATCH 054/128] Use IOptionsMonitor instead of IOptionsSnapshot for CodeCheckOptions when using CodeCheckEnabled filed --- .../Application/CodeCheckValidationProvider.cs | 3 +-- .../Application/ICheckedCodeHashProvider.cs | 4 ++-- .../CodeCheckRequiredLogEventProcessor.cs | 5 +++-- .../Exception/CodeCheckJobException.cs | 14 ++++++++++++++ src/AElf.Launcher/appsettings.json | 6 +++--- .../Application/CodeCheckJobProcessorTests.cs | 2 +- 6 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 src/AElf.Kernel.CodeCheck/Exception/CodeCheckJobException.cs diff --git a/src/AElf.Kernel.CodeCheck/Application/CodeCheckValidationProvider.cs b/src/AElf.Kernel.CodeCheck/Application/CodeCheckValidationProvider.cs index 0d772880e4..357e127ac1 100644 --- a/src/AElf.Kernel.CodeCheck/Application/CodeCheckValidationProvider.cs +++ b/src/AElf.Kernel.CodeCheck/Application/CodeCheckValidationProvider.cs @@ -15,8 +15,7 @@ internal class CodeCheckValidationProvider : IBlockValidationProvider public CodeCheckValidationProvider(ISmartContractAddressService smartContractAddressService, IContractReaderFactory contractReaderFactory, - ICheckedCodeHashProvider checkedCodeHashProvider, - IOptionsSnapshot codeCheckOptions) + ICheckedCodeHashProvider checkedCodeHashProvider) { _smartContractAddressService = smartContractAddressService; _contractReaderFactory = contractReaderFactory; diff --git a/src/AElf.Kernel.CodeCheck/Application/ICheckedCodeHashProvider.cs b/src/AElf.Kernel.CodeCheck/Application/ICheckedCodeHashProvider.cs index 3c4daa24a5..3b33064455 100644 --- a/src/AElf.Kernel.CodeCheck/Application/ICheckedCodeHashProvider.cs +++ b/src/AElf.Kernel.CodeCheck/Application/ICheckedCodeHashProvider.cs @@ -17,10 +17,10 @@ internal class CheckedCodeHashProvider : BlockExecutedDataBaseProvider cachedBlockchainExecutedDataService, - IOptionsSnapshot codeCheckOptions) : + IOptionsMonitor codeCheckOptions) : base(cachedBlockchainExecutedDataService) { - _codeCheckOptions = codeCheckOptions.Value; + _codeCheckOptions = codeCheckOptions.CurrentValue; Logger = NullLogger.Instance; } diff --git a/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs b/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs index b539979444..4c84a307a8 100644 --- a/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs +++ b/src/AElf.Kernel.CodeCheck/CodeCheckRequiredLogEventProcessor.cs @@ -68,18 +68,19 @@ public override async Task ProcessAsync(Block block, Dictionary Date: Thu, 28 Mar 2024 16:59:11 +0800 Subject: [PATCH 055/128] Revert changes on appsettings.json --- src/AElf.Launcher/appsettings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AElf.Launcher/appsettings.json b/src/AElf.Launcher/appsettings.json index a7c64e2ee7..777432eb37 100644 --- a/src/AElf.Launcher/appsettings.json +++ b/src/AElf.Launcher/appsettings.json @@ -9,8 +9,8 @@ "StateDb": "redis://localhost:6379?db=1" }, "Account": { - "NodeAccount": "2r896yKhHsoNGhyJVe4ptA169P6LMvsC94BxA7xtrifSHuSdyd", - "NodeAccountPassword": "12345678" + "NodeAccount": "", + "NodeAccountPassword": "" }, "Network": { "BootNodes": [], From ac55d6cf1bc9a7e9ea5fbef5552fd9ff1dccb2f3 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 28 Mar 2024 17:00:45 +0800 Subject: [PATCH 056/128] Revert changes on appsettings.json --- src/AElf.Launcher/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AElf.Launcher/appsettings.json b/src/AElf.Launcher/appsettings.json index 777432eb37..80b0e93f8f 100644 --- a/src/AElf.Launcher/appsettings.json +++ b/src/AElf.Launcher/appsettings.json @@ -25,7 +25,7 @@ }, "Consensus": { "InitialMinerList": [], - "MiningInterval": 40000, + "MiningInterval": 4000, "StartTimestamp": 0, "PeriodSeconds": 604800, "MinerIncreaseInterval": 31536000 From e602e992bc48474d10b8c3ca06dc3d468aec8c07 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Fri, 29 Mar 2024 13:57:53 +0800 Subject: [PATCH 057/128] feat: update comment --- contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index 51061e5f53..b0a89a2717 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -256,7 +256,7 @@ private void CheckTokenAndCollectionExists(string symbol) private void CheckTokenExists(string symbol) { var empty = new TokenInfo(); - // check past token + // check old token var existing = State.TokenInfos[symbol.ToUpper()]; Assert(existing == null || existing.Equals(empty), "Token already exists."); // check new token From 7720b4f1c7e0ab8ec7bbe7508a68d5858b355c7d Mon Sep 17 00:00:00 2001 From: sherry Date: Fri, 29 Mar 2024 18:57:28 +0800 Subject: [PATCH 058/128] feat:support multi approve. --- .../TokenContractConstants.cs | 2 + .../TokenContract_ACS2_StatePathsProvider.cs | 15 +- .../TokenContract_Actions.cs | 8 +- .../TokenContract_Helper.cs | 64 +++ .../TokenContract_NFTHelper.cs | 2 +- .../TokenContract_NFT_Actions.cs | 53 ++- .../TokenContract_Views.cs | 15 +- .../BVT/ACS2_TokenResourceTests.cs | 20 +- .../BVT/TokenApplicationTests.cs | 374 ++++++++++++++++++ 9 files changed, 539 insertions(+), 14 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs index 8a41b78140..d73b5212e9 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs @@ -24,4 +24,6 @@ public static class TokenContractConstants public const string SeedExpireTimeExternalInfoKey = "__seed_exp_time"; public const string NftCreateChainIdExternalInfoKey = "__nft_create_chain_id"; public const int DefaultMaxBatchApproveCount = 100; + public const char GlobalAllowanceIdentifier = '*'; + } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs b/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs index 8d12f604e1..caa603f7b6 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs @@ -44,8 +44,6 @@ public override ResourceInfo GetResourceInfo(Transaction txn) { WritePaths = { - GetPath(nameof(TokenContractState.Allowances), args.From.ToString(), txn.From.ToString(), - args.Symbol), GetPath(nameof(TokenContractState.Balances), args.From.ToString(), args.Symbol), GetPath(nameof(TokenContractState.Balances), args.To.ToString(), args.Symbol), GetPath(nameof(TokenContractState.LockWhiteLists), args.Symbol, txn.From.ToString()) @@ -57,7 +55,7 @@ public override ResourceInfo GetResourceInfo(Transaction txn) GetPath(nameof(TokenContractState.TransactionFeeFreeAllowancesSymbolList)) } }; - + AddPathForAllowance(resourceInfo, txn.From.ToString(), args.From.ToString(), args.Symbol); AddPathForTransactionFee(resourceInfo, txn.From.ToString(), txn.MethodName); AddPathForDelegatees(resourceInfo, txn.From, txn.To, txn.MethodName); AddPathForTransactionFeeFreeAllowance(resourceInfo, txn.From); @@ -70,6 +68,17 @@ public override ResourceInfo GetResourceInfo(Transaction txn) } } + private void AddPathForAllowance(ResourceInfo resourceInfo, string txFrom, string spender, string symbol) + { + resourceInfo.WritePaths.Add(GetPath(nameof(TokenContractState.Allowances), txFrom, spender, symbol)); + resourceInfo.WritePaths.Add(GetPath(nameof(TokenContractState.Allowances), txFrom, spender, + GetGlobalAllowanceSymbol())); + if (GetSymbolType(symbol) == SymbolType.Nft || GetSymbolType(symbol) == SymbolType.NftCollection) + { + resourceInfo.WritePaths.Add(GetPath(nameof(TokenContractState.Allowances), txFrom, spender, + GetNftGlobalAllowanceSymbol(symbol))); + } + } private void AddPathForTransactionFee(ResourceInfo resourceInfo, string from, string methodName) { diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 2a477cafe7..60685415bd 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -32,7 +32,7 @@ public override Empty InitializeFromParentChain(InitializeFromParentChainInput i /// public override Empty Create(CreateInput input) { - var inputSymbolType = GetCreateInputSymbolType(input.Symbol); + var inputSymbolType = GetSymbolType(input.Symbol); if (input.Owner == null) { input.Owner = input.Issuer; @@ -253,7 +253,7 @@ public override Empty TransferFrom(TransferFromInput input) public override Empty Approve(ApproveInput input) { AssertValidInputAddress(input.Spender); - AssertValidToken(input.Symbol, input.Amount); + AssertValidApproveTokenAndAmount(input.Symbol, input.Amount); Approve(input.Spender, input.Symbol, input.Amount); return new Empty(); } @@ -277,7 +277,7 @@ public override Empty BatchApprove(BatchApproveInput input) foreach (var approve in input.Value) { AssertValidInputAddress(approve.Spender); - AssertValidToken(approve.Symbol, approve.Amount); + AssertValidApproveTokenAndAmount(approve.Symbol, approve.Amount); } var approveInputList = input.Value.GroupBy(approve => approve.Symbol + approve.Spender, approve => approve) .Select(approve => approve.Last()).ToList(); @@ -289,7 +289,7 @@ public override Empty BatchApprove(BatchApproveInput input) public override Empty UnApprove(UnApproveInput input) { AssertValidInputAddress(input.Spender); - AssertValidToken(input.Symbol, input.Amount); + AssertValidApproveTokenAndAmount(input.Symbol, input.Amount); var oldAllowance = State.Allowances[Context.Sender][input.Spender][input.Symbol]; var amountOrAll = Math.Min(input.Amount, oldAllowance); State.Allowances[Context.Sender][input.Spender][input.Symbol] = oldAllowance.Sub(amountOrAll); diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index ed210282d0..25e20bbc88 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -38,6 +38,70 @@ private TokenInfo AssertValidToken(string symbol, long amount) return tokenInfo; } + private void AssertValidApproveTokenAndAmount(string symbol, long amount) + { + Assert(!string.IsNullOrEmpty(symbol) && symbol.All(IsValidApproveSymbolChar), + "Invalid symbol."); + Assert(amount > 0, "Invalid amount."); + var approveSymbol = GetApproveSymbol(symbol); + if (approveSymbol.All(IsValidGlobalAllowanceIdentifier)) return; + var tokenInfo = State.TokenInfos[approveSymbol]; + Assert(tokenInfo != null && !string.IsNullOrEmpty(tokenInfo.Symbol), + $"Token is not found. {symbol},{approveSymbol}"); + } + + private string GetApproveSymbol(string symbol) + { + var words = symbol.Split(TokenContractConstants.NFTSymbolSeparator); + Assert(words[0].Length > 0, "Invalid symbol length."); + if (words.Length == 1) + { + Assert( + words[0].All(IsValidApproveTokenSymbolChar) && CheckGlobalAllowanceIdentifierCount(words[0]), + "Invalid token symbol."); + return symbol; + } + + Assert(words.Length == 2, "Invalid symbol length."); + Assert( + words[0].All(IsValidCreateSymbolChar) && + words[1].Length > 0 && words[1].All(IsValidApproveItemIdChar) && + CheckGlobalAllowanceIdentifierCount(words[1]), "Invalid NFT Symbol."); + return words[1].All(IsValidGlobalAllowanceIdentifier) ? GetCollectionSymbol(words[0]) : symbol; + } + + private bool CheckGlobalAllowanceIdentifierCount(string word) + { + return word.Count(c => c.Equals(TokenContractConstants.GlobalAllowanceIdentifier)) <= 1; + } + + private string GetCollectionSymbol(string symbolPrefix) + { + return $"{symbolPrefix}-{TokenContractConstants.CollectionSymbolSuffix}"; + } + + private static bool IsValidApproveSymbolChar(char character) + { + return (character >= 'A' && character <= 'Z') || (character >= '0' && character <= '9') || + character == TokenContractConstants.NFTSymbolSeparator || + character == TokenContractConstants.GlobalAllowanceIdentifier; + } + + private bool IsValidApproveTokenSymbolChar(char character) + { + return IsValidCreateSymbolChar(character) || character == TokenContractConstants.GlobalAllowanceIdentifier; + } + + private bool IsValidApproveItemIdChar(char character) + { + return IsValidItemIdChar(character) || character == TokenContractConstants.GlobalAllowanceIdentifier; + } + + private bool IsValidGlobalAllowanceIdentifier(char character) + { + return character == TokenContractConstants.GlobalAllowanceIdentifier; + } + private void AssertValidSymbolAndAmount(string symbol, long amount) { Assert(!string.IsNullOrEmpty(symbol) && symbol.All(IsValidSymbolChar), diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs index 4c3efdf6a3..23f66d6081 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFTHelper.cs @@ -4,7 +4,7 @@ namespace AElf.Contracts.MultiToken; public partial class TokenContract { - private SymbolType GetCreateInputSymbolType(string symbol) + private SymbolType GetSymbolType(string symbol) { var words = symbol.Split(TokenContractConstants.NFTSymbolSeparator); Assert(words[0].Length > 0 && words[0].All(IsValidCreateSymbolChar), "Invalid Symbol input"); diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index 124d1dac73..d060b7cf2a 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -43,7 +43,7 @@ private Empty CreateNFTInfo(CreateInput input) out var expirationTime) && long.TryParse(expirationTime, out var expirationTimeLong) && Context.CurrentBlockTime.Seconds <= expirationTimeLong, "Invalid ownedSymbol."); - var ownedSymbolType = GetCreateInputSymbolType(ownedSymbol); + var ownedSymbolType = GetSymbolType(ownedSymbol); Assert(ownedSymbolType != SymbolType.Nft, "Invalid OwnedSymbol."); CheckSymbolLength(ownedSymbol, ownedSymbolType); CheckTokenAndCollectionExists(ownedSymbol); @@ -73,7 +73,7 @@ private void DoTransferFrom(Address from, Address to, Address spender, string sy AssertValidInputAddress(to); // First check allowance. - var allowance = State.Allowances[from][spender][symbol]; + var allowance = GetAllowance(from, spender, symbol, amount, out var allowanceSymbol); if (allowance < amount) { if (IsInWhiteList(new IsInWhiteListInput { Symbol = symbol, Address = spender }).Value) @@ -92,7 +92,54 @@ private void DoTransferFrom(Address from, Address to, Address spender, string sy DoTransfer(from, to, symbol, amount, memo); DealWithExternalInfoDuringTransfer(new TransferFromInput() { From = from, To = to, Symbol = symbol, Amount = amount, Memo = memo }); - State.Allowances[from][spender][symbol] = allowance.Sub(amount); + State.Allowances[from][spender][allowanceSymbol] = allowance.Sub(amount); + } + + private long GetAllowance(Address from, Address spender, string sourceSymbol, long amount, + out string allowanceSymbol) + { + allowanceSymbol = sourceSymbol; + var allowance = State.Allowances[from][spender][sourceSymbol]; + if (allowance >= amount) return allowance; + var tokenType = GetSymbolType(sourceSymbol); + if (tokenType == SymbolType.Token) + { + allowance = GetGlobalAllowance(from, spender, out allowanceSymbol); + } + else + { + allowance = GetNftGlobalAllowance(from, spender, sourceSymbol, out allowanceSymbol); + if (allowance >= amount) return allowance; + allowance = GetGlobalAllowance(from, spender, out allowanceSymbol); + } + + return allowance; + } + + + private long GetGlobalAllowance(Address from, Address spender, out string allowanceSymbol) + { + allowanceSymbol = GetGlobalAllowanceSymbol(); + return State.Allowances[from][spender][allowanceSymbol]; + } + + private long GetNftGlobalAllowance(Address from, Address spender, string sourceSymbol, + out string allowanceSymbol) + { + allowanceSymbol = GetNftGlobalAllowanceSymbol(sourceSymbol); + return State.Allowances[from][spender][allowanceSymbol]; + } + + private string GetNftGlobalAllowanceSymbol(string sourceSymbol) + { + // "AAA-*" + return $"{sourceSymbol.Split(TokenContractConstants.NFTSymbolSeparator)[0]}-{TokenContractConstants.GlobalAllowanceIdentifier}"; + } + + private string GetGlobalAllowanceSymbol() + { + // "*" + return TokenContractConstants.GlobalAllowanceIdentifier.ToString(); } diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs index 8366261ebf..c42289d509 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using AElf.Sdk.CSharp; using AElf.Types; using Google.Protobuf.WellKnownTypes; @@ -48,12 +49,22 @@ public override GetBalanceOutput GetBalance(GetBalanceInput input) [View] public override GetAllowanceOutput GetAllowance(GetAllowanceInput input) { + var allowance = State.Allowances[input.Owner][input.Spender][input.Symbol]; + if (!input.Symbol.Contains(TokenContractConstants.GlobalAllowanceIdentifier)) + { + var symbolType = GetSymbolType(input.Symbol); + allowance = Math.Max(allowance, GetGlobalAllowance(input.Owner,input.Spender,out _)); + if (symbolType == SymbolType.Nft || symbolType == SymbolType.NftCollection) + { + allowance = Math.Max(allowance, GetNftGlobalAllowance(input.Owner, input.Spender, input.Symbol, out _)); + } + } return new GetAllowanceOutput { Symbol = input.Symbol, Owner = input.Owner, Spender = input.Spender, - Allowance = State.Allowances[input.Owner][input.Spender][input.Symbol] + Allowance = allowance }; } diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/ACS2_TokenResourceTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/ACS2_TokenResourceTests.cs index 35f84f7154..8729acf7c1 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/ACS2_TokenResourceTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/ACS2_TokenResourceTests.cs @@ -48,6 +48,24 @@ public async Task ACS2_GetResourceInfo_TransferFrom_Test() result.NonParallelizable.ShouldBeFalse(); result.WritePaths.Count.ShouldBeGreaterThan(0); } + + [Fact] + public async Task ACS2_GetResourceInfo_TransferFrom_NFT_Test() + { + var transaction = GenerateTokenTransaction(Accounts[0].Address, nameof(TokenContractStub.TransferFrom), + new TransferFromInput + { + Amount = 100, + Symbol = "ABC-1", + From = Accounts[1].Address, + To = Accounts[2].Address, + Memo = "Test get resource" + }); + + var result = await Acs2BaseStub.GetResourceInfo.CallAsync(transaction); + result.NonParallelizable.ShouldBeFalse(); + result.WritePaths.Count.ShouldBeGreaterThan(0); + } private async Task
GetDefaultParliamentAddressAsync() { @@ -179,7 +197,7 @@ await TokenContractStubDelegate3.SetTransactionFeeDelegateInfos.SendAsync(new Se var result = await Acs2BaseStub.GetResourceInfo.CallAsync(transaction); result.NonParallelizable.ShouldBeFalse(); - result.WritePaths.Count.ShouldBe(9); + result.WritePaths.Count.ShouldBe(10); } [Fact] diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index 49642bae3a..41b0d81028 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -504,6 +504,380 @@ await TreasuryContractStub.Donate.SendAsync(new DonateInput afterTransferFromBalance.Balance.ShouldBe(beforeTransferFromBalance.Balance.Sub(transferAmount)); } + private async Task CreateNft() + { + await CreateMutiTokenAsync(TokenContractStub, new CreateInput + { + TokenName = "Test", + TotalSupply = TotalSupply, + Decimals = 0, + Issuer = DefaultAddress, + Owner = DefaultAddress, + IssueChainId = _chainId, + Symbol = "ABC-0" + }); + await TokenContractStub.Create.SendAsync(new CreateInput + { + TokenName = "Test", + TotalSupply = TotalSupply, + Decimals = 0, + Issuer = DefaultAddress, + Owner = DefaultAddress, + IssueChainId = _chainId, + Symbol = "ABC-1" + }); + } + [Fact] + public async Task MultiTokenContract_TransferFrom_Nft_Global_Test() + { + await CreateNft(); + await TokenContractStub.Issue.SendAsync(new IssueInput + { + Symbol = "ABC-1", + Amount = 100, + To = DefaultAddress, + Memo = "test" + }); + await TokenContractStub.Issue.SendAsync(new IssueInput + { + Symbol = "ABC-1", + Amount = 200, + To = User1Address, + Memo = "test" + }); + var balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = DefaultAddress, + Symbol = "ABC-1" + }); + balance.Balance.ShouldBe(100); + balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = User1Address, + Symbol = "ABC-1" + }); + balance.Balance.ShouldBe(200); + await TokenContractStub.Approve.SendAsync(new ApproveInput + { + Amount = 1000, + Symbol = "*", + Spender = User1Address + }); + + await TokenContractStub.Approve.SendAsync(new ApproveInput + { + Amount = 1, + Symbol = "ABC-*", + Spender = User1Address + }); + var allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "ABC-1" + }); + allowance.Allowance.ShouldBe(1000); + allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "ELF" + }); + allowance.Allowance.ShouldBe(1000); + var user1Stub = + GetTester(TokenContractAddress, User1KeyPair); + var result2 = await user1Stub.TransferFrom.SendAsync(new TransferFromInput + { + Amount = 50, + From = DefaultAddress, + Memo = "test", + Symbol = "ABC-1", + To = User1Address + }); + result2.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "ABC-1" + }); + allowance.Allowance.ShouldBe(1000-50); + balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = DefaultAddress, + Symbol = "ABC-1" + }); + balance.Balance.ShouldBe(50); + balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = User1Address, + Symbol = "ABC-1" + }); + balance.Balance.ShouldBe(250); + } + + [Fact] + public async Task MultiTokenContract_TransferFrom_Nft_Collection_Test() + { + await CreateNft(); + await TokenContractStub.Issue.SendAsync(new IssueInput + { + Symbol = "ABC-1", + Amount = 100, + To = DefaultAddress, + Memo = "test" + }); + await TokenContractStub.Issue.SendAsync(new IssueInput + { + Symbol = "ABC-1", + Amount = 200, + To = User1Address, + Memo = "test" + }); + await TokenContractStub.Approve.SendAsync(new ApproveInput + { + Amount = 20, + Symbol = "*", + Spender = User1Address + }); + + await TokenContractStub.Approve.SendAsync(new ApproveInput + { + Amount = 1000, + Symbol = "ABC-*", + Spender = User1Address + }); + var allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "ABC-1" + }); + allowance.Allowance.ShouldBe(1000); + allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "ELF" + }); + allowance.Allowance.ShouldBe(20); + var user1Stub = + GetTester(TokenContractAddress, User1KeyPair); + var result2 = await user1Stub.TransferFrom.SendAsync(new TransferFromInput + { + Amount = 50, + From = DefaultAddress, + Memo = "test", + Symbol = "ABC-1", + To = User1Address + }); + result2.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "ABC-1" + }); + allowance.Allowance.ShouldBe(1000-50); + allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "*" + }); + allowance.Allowance.ShouldBe(20); + + } + + [Fact] + public async Task MultiTokenContract_TransferFrom_Token_Test() + { + await CreateAndIssueToken(); + await TokenContractStub.Approve.SendAsync(new ApproveInput + { + Amount = 100_00000000, + Symbol = "*", + Spender = User1Address + }); + var allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "SSS" + }); + allowance.Allowance.ShouldBe(100_00000000); + allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "ELF" + }); + allowance.Allowance.ShouldBe(100_00000000); + var user1Stub = + GetTester(TokenContractAddress, User1KeyPair); + var result2 = await user1Stub.TransferFrom.SendAsync(new TransferFromInput + { + Amount = 50_00000000, + From = DefaultAddress, + Memo = "test", + Symbol = "SSS", + To = User1Address + }); + result2.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "SSS" + }); + allowance.Allowance.ShouldBe(100_00000000-50_00000000); + var balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = DefaultAddress, + Symbol = "SSS" + }); + balance.Balance.ShouldBe(TotalSupply - 50_00000000); + balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = User1Address, + Symbol = "SSS" + }); + balance.Balance.ShouldBe(50_00000000); + } + + private async Task CreateAndIssueToken() + { + await CreateMutiTokenAsync(TokenContractStub, new CreateInput + { + TokenName = "Test", + TotalSupply = TotalSupply, + Decimals = 8, + Issuer = DefaultAddress, + Owner = DefaultAddress, + IssueChainId = _chainId, + Symbol = "SSS" + }); + await TokenContractStub.Issue.SendAsync(new IssueInput + { + Symbol = "SSS", + Amount = TotalSupply, + To = DefaultAddress, + Memo = "Issue" + }); + var balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = DefaultAddress, + Symbol = "SSS" + }); + balance.Balance.ShouldBe(TotalSupply); + } + [Fact] + public async Task MultiTokenContract_Approve_Test_New() + { + await CreateAndIssueToken(); + await TokenContractStub.Approve.SendAsync(new ApproveInput + { + Spender = User1Address, + Symbol = "SSS", + Amount = 100_000000000 + }); + var allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "SSS" + }); + allowance.Allowance.ShouldBe(100_000000000); + await TokenContractStub.Approve.SendAsync(new ApproveInput + { + Spender = User1Address, + Symbol = "*", + Amount = 200_000000000 + }); + allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "SSS" + }); + allowance.Allowance.ShouldBe(200_000000000); + allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "*" + }); + allowance.Allowance.ShouldBe(200_000000000); + await TokenContractStub.UnApprove.SendAsync(new UnApproveInput + { + Spender = User1Address, + Symbol = "*", + Amount = 20_000000000 + }); + allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "*" + }); + allowance.Allowance.ShouldBe(200_000000000-20_000000000); + } + + [Fact] + public async Task MultiTokenContract_Approve_Test_New_Fail() + { + await CreateAndIssueToken(); + { + var executionResult = await TokenContractStub.Approve.SendWithExceptionAsync(new ApproveInput + { + Spender = User1Address, + Symbol = "SSS*", + Amount = 100_000000000 + }); + executionResult.TransactionResult.Error.ShouldContain("Token is not found"); + } + { + var executionResult = await TokenContractStub.Approve.SendWithExceptionAsync(new ApproveInput + { + Spender = User1Address, + Symbol = "SSS**", + Amount = 100_000000000 + }); + executionResult.TransactionResult.Error.ShouldContain("Invalid token symbol."); + } + } + + [Fact] + public async Task MultiTokenContract_Approve_Test_New_Nft_Fail() + { + await CreateNft(); + await TokenContractStub.Issue.SendAsync(new IssueInput + { + Symbol = "ABC-1", + Amount = 100, + To = DefaultAddress, + Memo = "test" + }); + { + var executionResult = await TokenContractStub.Approve.SendWithExceptionAsync(new ApproveInput + { + Spender = User1Address, + Symbol = "AB*-*", + Amount = 100_000000000 + }); + executionResult.TransactionResult.Error.ShouldContain("Invalid NFT Symbol"); + } + { + var executionResult = await TokenContractStub.Approve.SendWithExceptionAsync(new ApproveInput + { + Spender = User1Address, + Symbol = "ABC-*9", + Amount = 100_000000000 + }); + executionResult.TransactionResult.Error.ShouldContain("Token is not found"); + } + } + private async Task CreateTokenAndIssue(List
whitelist = null, Address issueTo = null) { if (whitelist == null) From e40c07204215b6b3f3542b022e6f3864b1373410 Mon Sep 17 00:00:00 2001 From: sherry Date: Fri, 29 Mar 2024 19:00:53 +0800 Subject: [PATCH 059/128] feat:format. --- contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index 25e20bbc88..cc6a9c36b7 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -82,8 +82,7 @@ private string GetCollectionSymbol(string symbolPrefix) private static bool IsValidApproveSymbolChar(char character) { - return (character >= 'A' && character <= 'Z') || (character >= '0' && character <= '9') || - character == TokenContractConstants.NFTSymbolSeparator || + return IsValidSymbolChar(character) || character == TokenContractConstants.GlobalAllowanceIdentifier; } From 8ae27bd7bdd894833ac09e8fc9f97d3f052a2ffa Mon Sep 17 00:00:00 2001 From: sherry Date: Fri, 29 Mar 2024 19:03:00 +0800 Subject: [PATCH 060/128] feat:format. --- contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index cc6a9c36b7..e7e77b1433 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -80,7 +80,7 @@ private string GetCollectionSymbol(string symbolPrefix) return $"{symbolPrefix}-{TokenContractConstants.CollectionSymbolSuffix}"; } - private static bool IsValidApproveSymbolChar(char character) + private bool IsValidApproveSymbolChar(char character) { return IsValidSymbolChar(character) || character == TokenContractConstants.GlobalAllowanceIdentifier; From f45eab90733e52ebd2c9c216907acb430055e197 Mon Sep 17 00:00:00 2001 From: sherry Date: Mon, 1 Apr 2024 14:11:56 +0800 Subject: [PATCH 061/128] feat:rename and cr. --- .../TokenContractConstants.cs | 2 +- .../TokenContract_ACS2_StatePathsProvider.cs | 15 +++--- .../TokenContract_Helper.cs | 49 +++++++++---------- .../TokenContract_NFT_Actions.cs | 14 +++--- .../TokenContract_Views.cs | 4 +- 5 files changed, 40 insertions(+), 44 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs index d73b5212e9..74d419c518 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs @@ -24,6 +24,6 @@ public static class TokenContractConstants public const string SeedExpireTimeExternalInfoKey = "__seed_exp_time"; public const string NftCreateChainIdExternalInfoKey = "__nft_create_chain_id"; public const int DefaultMaxBatchApproveCount = 100; - public const char GlobalAllowanceIdentifier = '*'; + public const char AllSymbolIdentifier = '*'; } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs b/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs index caa603f7b6..3454ead9a8 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs @@ -55,7 +55,7 @@ public override ResourceInfo GetResourceInfo(Transaction txn) GetPath(nameof(TokenContractState.TransactionFeeFreeAllowancesSymbolList)) } }; - AddPathForAllowance(resourceInfo, txn.From.ToString(), args.From.ToString(), args.Symbol); + AddPathForAllowance(resourceInfo, args.From.ToString(), txn.From.ToString(), args.Symbol); AddPathForTransactionFee(resourceInfo, txn.From.ToString(), txn.MethodName); AddPathForDelegatees(resourceInfo, txn.From, txn.To, txn.MethodName); AddPathForTransactionFeeFreeAllowance(resourceInfo, txn.From); @@ -68,14 +68,15 @@ public override ResourceInfo GetResourceInfo(Transaction txn) } } - private void AddPathForAllowance(ResourceInfo resourceInfo, string txFrom, string spender, string symbol) + private void AddPathForAllowance(ResourceInfo resourceInfo, string from, string spender, string symbol) { - resourceInfo.WritePaths.Add(GetPath(nameof(TokenContractState.Allowances), txFrom, spender, symbol)); - resourceInfo.WritePaths.Add(GetPath(nameof(TokenContractState.Allowances), txFrom, spender, - GetGlobalAllowanceSymbol())); - if (GetSymbolType(symbol) == SymbolType.Nft || GetSymbolType(symbol) == SymbolType.NftCollection) + resourceInfo.WritePaths.Add(GetPath(nameof(TokenContractState.Allowances), from, spender, symbol)); + resourceInfo.WritePaths.Add(GetPath(nameof(TokenContractState.Allowances), from, spender, + GetAllSymbolIdentifier())); + var symbolType = GetSymbolType(symbol); + if (symbolType == SymbolType.Nft || symbolType == SymbolType.NftCollection) { - resourceInfo.WritePaths.Add(GetPath(nameof(TokenContractState.Allowances), txFrom, spender, + resourceInfo.WritePaths.Add(GetPath(nameof(TokenContractState.Allowances), from, spender, GetNftGlobalAllowanceSymbol(symbol))); } } diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index e7e77b1433..5495c0d445 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -40,39 +40,39 @@ private TokenInfo AssertValidToken(string symbol, long amount) private void AssertValidApproveTokenAndAmount(string symbol, long amount) { - Assert(!string.IsNullOrEmpty(symbol) && symbol.All(IsValidApproveSymbolChar), - "Invalid symbol."); Assert(amount > 0, "Invalid amount."); - var approveSymbol = GetApproveSymbol(symbol); - if (approveSymbol.All(IsValidGlobalAllowanceIdentifier)) return; - var tokenInfo = State.TokenInfos[approveSymbol]; - Assert(tokenInfo != null && !string.IsNullOrEmpty(tokenInfo.Symbol), - $"Token is not found. {symbol},{approveSymbol}"); + AssertApproveToken(symbol); } - private string GetApproveSymbol(string symbol) + private void ValidTokenExists(string symbol) + { + var tokenInfo = State.TokenInfos[symbol]; + Assert(tokenInfo != null && !string.IsNullOrEmpty(tokenInfo.Symbol), + $"Token is not found. {symbol}"); + } + + private void AssertApproveToken(string symbol) { + Assert(!string.IsNullOrEmpty(symbol) && symbol.All(IsValidApproveSymbolChar) && CheckAllSymbolIdentifierCount(symbol), + "Invalid symbol."); var words = symbol.Split(TokenContractConstants.NFTSymbolSeparator); Assert(words[0].Length > 0, "Invalid symbol length."); if (words.Length == 1) { - Assert( - words[0].All(IsValidApproveTokenSymbolChar) && CheckGlobalAllowanceIdentifierCount(words[0]), - "Invalid token symbol."); - return symbol; + if (words[0].All(IsValidAllSymbolIdentifier)) return; + ValidTokenExists(words[0]); } - Assert(words.Length == 2, "Invalid symbol length."); Assert( words[0].All(IsValidCreateSymbolChar) && - words[1].Length > 0 && words[1].All(IsValidApproveItemIdChar) && - CheckGlobalAllowanceIdentifierCount(words[1]), "Invalid NFT Symbol."); - return words[1].All(IsValidGlobalAllowanceIdentifier) ? GetCollectionSymbol(words[0]) : symbol; + words[1].Length > 0 && words[1].All(IsValidApproveItemIdChar), "Invalid NFT Symbol."); + var nftSymbol = words[1].All(IsValidAllSymbolIdentifier) ? GetCollectionSymbol(words[0]) : symbol; + ValidTokenExists(nftSymbol); } - private bool CheckGlobalAllowanceIdentifierCount(string word) + private bool CheckAllSymbolIdentifierCount(string word) { - return word.Count(c => c.Equals(TokenContractConstants.GlobalAllowanceIdentifier)) <= 1; + return word.Count(c => c.Equals(TokenContractConstants.AllSymbolIdentifier)) <= 1; } private string GetCollectionSymbol(string symbolPrefix) @@ -83,22 +83,17 @@ private string GetCollectionSymbol(string symbolPrefix) private bool IsValidApproveSymbolChar(char character) { return IsValidSymbolChar(character) || - character == TokenContractConstants.GlobalAllowanceIdentifier; - } - - private bool IsValidApproveTokenSymbolChar(char character) - { - return IsValidCreateSymbolChar(character) || character == TokenContractConstants.GlobalAllowanceIdentifier; + character == TokenContractConstants.AllSymbolIdentifier; } private bool IsValidApproveItemIdChar(char character) { - return IsValidItemIdChar(character) || character == TokenContractConstants.GlobalAllowanceIdentifier; + return IsValidItemIdChar(character) || character == TokenContractConstants.AllSymbolIdentifier; } - private bool IsValidGlobalAllowanceIdentifier(char character) + private bool IsValidAllSymbolIdentifier(char character) { - return character == TokenContractConstants.GlobalAllowanceIdentifier; + return character == TokenContractConstants.AllSymbolIdentifier; } private void AssertValidSymbolAndAmount(string symbol, long amount) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index d060b7cf2a..d86f419bc0 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -104,22 +104,22 @@ private long GetAllowance(Address from, Address spender, string sourceSymbol, lo var tokenType = GetSymbolType(sourceSymbol); if (tokenType == SymbolType.Token) { - allowance = GetGlobalAllowance(from, spender, out allowanceSymbol); + allowance = GetAllSymbolAllowance(from, spender, out allowanceSymbol); } else { allowance = GetNftGlobalAllowance(from, spender, sourceSymbol, out allowanceSymbol); if (allowance >= amount) return allowance; - allowance = GetGlobalAllowance(from, spender, out allowanceSymbol); + allowance = GetAllSymbolAllowance(from, spender, out allowanceSymbol); } return allowance; } - private long GetGlobalAllowance(Address from, Address spender, out string allowanceSymbol) + private long GetAllSymbolAllowance(Address from, Address spender, out string allowanceSymbol) { - allowanceSymbol = GetGlobalAllowanceSymbol(); + allowanceSymbol = GetAllSymbolIdentifier(); return State.Allowances[from][spender][allowanceSymbol]; } @@ -133,13 +133,13 @@ private long GetNftGlobalAllowance(Address from, Address spender, string sourceS private string GetNftGlobalAllowanceSymbol(string sourceSymbol) { // "AAA-*" - return $"{sourceSymbol.Split(TokenContractConstants.NFTSymbolSeparator)[0]}-{TokenContractConstants.GlobalAllowanceIdentifier}"; + return $"{sourceSymbol.Split(TokenContractConstants.NFTSymbolSeparator)[0]}-{TokenContractConstants.AllSymbolIdentifier}"; } - private string GetGlobalAllowanceSymbol() + private string GetAllSymbolIdentifier() { // "*" - return TokenContractConstants.GlobalAllowanceIdentifier.ToString(); + return TokenContractConstants.AllSymbolIdentifier.ToString(); } diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs index c42289d509..ed96c5ccd8 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs @@ -50,10 +50,10 @@ public override GetBalanceOutput GetBalance(GetBalanceInput input) public override GetAllowanceOutput GetAllowance(GetAllowanceInput input) { var allowance = State.Allowances[input.Owner][input.Spender][input.Symbol]; - if (!input.Symbol.Contains(TokenContractConstants.GlobalAllowanceIdentifier)) + if (!input.Symbol.Contains(TokenContractConstants.AllSymbolIdentifier)) { var symbolType = GetSymbolType(input.Symbol); - allowance = Math.Max(allowance, GetGlobalAllowance(input.Owner,input.Spender,out _)); + allowance = Math.Max(allowance, GetAllSymbolAllowance(input.Owner,input.Spender,out _)); if (symbolType == SymbolType.Nft || symbolType == SymbolType.NftCollection) { allowance = Math.Max(allowance, GetNftGlobalAllowance(input.Owner, input.Spender, input.Symbol, out _)); From 78ef1648f04348a5c88ae9cba03c45849f76d3b0 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Mon, 1 Apr 2024 19:31:24 +0800 Subject: [PATCH 062/128] feat: change CodeCheckProposalExpirationTime --- .../BasicContractZero.cs | 15 +++- .../BasicContractZeroState.cs | 3 + .../BasicContractZero_Constants.cs | 2 +- .../BasicContractZero_Helper.cs | 9 ++- protobuf/basic_contract_zero.proto | 7 ++ .../GenesisContractAuthTest.cs | 72 +++++++++++++++++-- 6 files changed, 100 insertions(+), 8 deletions(-) diff --git a/contract/AElf.Contracts.Genesis/BasicContractZero.cs b/contract/AElf.Contracts.Genesis/BasicContractZero.cs index d673fbf306..dad07e8672 100644 --- a/contract/AElf.Contracts.Genesis/BasicContractZero.cs +++ b/contract/AElf.Contracts.Genesis/BasicContractZero.cs @@ -84,6 +84,12 @@ public override Int32Value GetContractProposalExpirationTimePeriod(Empty input) return new Int32Value { Value = expirationTimePeriod }; } + public override Int32Value GetCodeCheckProposalExpirationTimePeriod(Empty input) + { + var expirationTimePeriod = GetCodeCheckProposalExpirationTimePeriod(); + return new Int32Value { Value = expirationTimePeriod }; + } + public override Address GetSigner(Address input) { return State.SignerMap[input]; @@ -245,7 +251,7 @@ public override Hash ProposeContractCodeCheck(ContractCodeCheckInput input) ContractMethodName = input.CodeCheckReleaseMethod, Params = input.ContractInput, OrganizationAddress = codeCheckController.OwnerAddress, - ExpiredTime = Context.CurrentBlockTime.AddSeconds(CodeCheckProposalExpirationTimePeriod) + ExpiredTime = Context.CurrentBlockTime.AddSeconds(GetCodeCheckProposalExpirationTimePeriod()) }, OriginProposer = proposedInfo.Proposer }; @@ -392,6 +398,13 @@ public override Empty SetContractProposalExpirationTimePeriod(SetContractProposa return new Empty(); } + public override Empty SetCodeCheckProposalExpirationTimePeriod(Int32Value input) + { + AssertSenderAddressWith(State.ContractDeploymentController.Value.OwnerAddress); + State.CodeCheckProposalExpirationTimePeriod.Value = input.Value; + return new Empty(); + } + public override DeployUserSmartContractOutput DeployUserSmartContract(UserContractDeploymentInput input) { AssertInlineDeployOrUpdateUserContract(); diff --git a/contract/AElf.Contracts.Genesis/BasicContractZeroState.cs b/contract/AElf.Contracts.Genesis/BasicContractZeroState.cs index 4e6a5dc8a6..8324b35afe 100644 --- a/contract/AElf.Contracts.Genesis/BasicContractZeroState.cs +++ b/contract/AElf.Contracts.Genesis/BasicContractZeroState.cs @@ -36,4 +36,7 @@ public partial class BasicContractZeroState : ContractState public SingletonState ContractProposalExpirationTimePeriod { get; set; } public MappedState SignerMap { get; set; } + + public SingletonState CodeCheckProposalExpirationTimePeriod { get; set; } + } \ No newline at end of file diff --git a/contract/AElf.Contracts.Genesis/BasicContractZero_Constants.cs b/contract/AElf.Contracts.Genesis/BasicContractZero_Constants.cs index 6ed4e650d4..3aa989214a 100644 --- a/contract/AElf.Contracts.Genesis/BasicContractZero_Constants.cs +++ b/contract/AElf.Contracts.Genesis/BasicContractZero_Constants.cs @@ -3,7 +3,7 @@ namespace AElf.Contracts.Genesis; public partial class BasicContractZero { public const int ContractProposalExpirationTimePeriod = 259200; // 60 * 60 * 72 - public const int CodeCheckProposalExpirationTimePeriod = 600; // 60 * 10 + public const int DefaultCodeCheckProposalExpirationTimePeriod = 600; // 60 * 10 private const int MinimalApprovalThreshold = 6667; private const int MaximalAbstentionThreshold = 1000; private const int MaximalRejectionThreshold = 1000; diff --git a/contract/AElf.Contracts.Genesis/BasicContractZero_Helper.cs b/contract/AElf.Contracts.Genesis/BasicContractZero_Helper.cs index b82fd27996..edbd094793 100644 --- a/contract/AElf.Contracts.Genesis/BasicContractZero_Helper.cs +++ b/contract/AElf.Contracts.Genesis/BasicContractZero_Helper.cs @@ -286,6 +286,13 @@ private int GetCurrentContractProposalExpirationTimePeriod() : State.ContractProposalExpirationTimePeriod.Value; } + private int GetCodeCheckProposalExpirationTimePeriod() + { + return State.CodeCheckProposalExpirationTimePeriod.Value == 0 + ? DefaultCodeCheckProposalExpirationTimePeriod + : State.CodeCheckProposalExpirationTimePeriod.Value; + } + private void AssertCurrentMiner() { RequireConsensusContractStateSet(); @@ -310,7 +317,7 @@ private void SendUserContractProposal(Hash proposingInputHash, string releaseMet { Proposer = Context.Self, Status = ContractProposingInputStatus.CodeCheckProposed, - ExpiredTime = Context.CurrentBlockTime.AddSeconds(CodeCheckProposalExpirationTimePeriod), + ExpiredTime = Context.CurrentBlockTime.AddSeconds(GetCodeCheckProposalExpirationTimePeriod()), Author = Context.Sender }; State.ContractProposingInputMap[proposingInputHash] = proposedInfo; diff --git a/protobuf/basic_contract_zero.proto b/protobuf/basic_contract_zero.proto index 30960f8433..0967cf866d 100644 --- a/protobuf/basic_contract_zero.proto +++ b/protobuf/basic_contract_zero.proto @@ -37,6 +37,9 @@ service BasicContractZero { rpc SetContractProposalExpirationTimePeriod(SetContractProposalExpirationTimePeriodInput) returns(google.protobuf.Empty){ } + + rpc SetCodeCheckProposalExpirationTimePeriod(google.protobuf.Int32Value) returns(google.protobuf.Empty){ + } // Query the ContractDeploymentController authority info. rpc GetContractDeploymentController (google.protobuf.Empty) returns (AuthorityInfo) { @@ -51,6 +54,10 @@ service BasicContractZero { rpc GetContractProposalExpirationTimePeriod(google.protobuf.Empty) returns (google.protobuf.Int32Value){ option (aelf.is_view) = true; } + + rpc GetCodeCheckProposalExpirationTimePeriod(google.protobuf.Empty) returns (google.protobuf.Int32Value){ + option (aelf.is_view) = true; + } } message InitializeInput{ diff --git a/test/AElf.Contracts.Genesis.Tests/GenesisContractAuthTest.cs b/test/AElf.Contracts.Genesis.Tests/GenesisContractAuthTest.cs index 3962b989b8..82ab726523 100644 --- a/test/AElf.Contracts.Genesis.Tests/GenesisContractAuthTest.cs +++ b/test/AElf.Contracts.Genesis.Tests/GenesisContractAuthTest.cs @@ -1372,6 +1372,67 @@ public async Task ChangeCodeCheckController_Test() } } + [Fact] + public async Task SetCodeCheckProposalExpirationTime_Test() + { + var createOrganizationResult = await Tester.ExecuteContractWithMiningAsync(ParliamentAddress, + nameof(ParliamentContractImplContainer.ParliamentContractImplStub.CreateOrganization), + new CreateOrganizationInput + { + ProposalReleaseThreshold = new ProposalReleaseThreshold + { + MinimalApprovalThreshold = 1000, + MinimalVoteThreshold = 1000 + } + }); + var organizationAddress = Address.Parser.ParseFrom(createOrganizationResult.ReturnValue); + + var defaultTime = await Tester.CallContractMethodAsync(BasicContractZeroAddress, + nameof(BasicContractZeroImplContainer.BasicContractZeroImplStub.GetCodeCheckProposalExpirationTimePeriod), + new Empty()); + var proposalExpirationTime = Int32Value.Parser.ParseFrom(defaultTime); + Assert.True(proposalExpirationTime.Value == 600); + + var byteResult = await Tester.CallContractMethodAsync(BasicContractZeroAddress, + nameof(BasicContractZeroImplContainer.BasicContractZeroImplStub.GetContractDeploymentController), + new Empty()); + var contractDeploymentController = AuthorityInfo.Parser.ParseFrom(byteResult); + + const string methodName = + nameof(BasicContractZeroImplContainer.BasicContractZeroImplStub.SetCodeCheckProposalExpirationTimePeriod); + { + var proposalId = await CreateProposalAsync(Tester, ParliamentAddress, + organizationAddress, methodName, + new Int32Value + { + Value = 86400 + } + ); + await ApproveWithMinersAsync(Tester, ParliamentAddress, proposalId); + var txResult = await ReleaseProposalAsync(Tester, ParliamentAddress, proposalId); + txResult.Status.ShouldBe(TransactionResultStatus.Failed); + txResult.Error.ShouldContain("Unauthorized behavior."); + } + { + var proposalId = await CreateProposalAsync(Tester, ParliamentAddress, + contractDeploymentController.OwnerAddress, methodName, + new Int32Value + { + Value = 86400 + }); + await ApproveWithMinersAsync(Tester, ParliamentAddress, proposalId); + var txResult2 = await ReleaseProposalAsync(Tester, ParliamentAddress, proposalId); + txResult2.Status.ShouldBe(TransactionResultStatus.Mined); + + byteResult = await Tester.CallContractMethodAsync(BasicContractZeroAddress, + nameof(BasicContractZeroImplContainer.BasicContractZeroImplStub + .GetCodeCheckProposalExpirationTimePeriod), + new Empty()); + var newProposalExpirationTime = Int32Value.Parser.ParseFrom(byteResult); + Assert.True(newProposalExpirationTime.Value == 86400); + } + } + [Fact] public async Task SetContractProposalExpirationTime_Test() { @@ -1386,18 +1447,18 @@ public async Task SetContractProposalExpirationTime_Test() } }); var organizationAddress = Address.Parser.ParseFrom(createOrganizationResult.ReturnValue); - + var defaultTime = await Tester.CallContractMethodAsync(BasicContractZeroAddress, nameof(BasicContractZeroImplContainer.BasicContractZeroImplStub.GetContractProposalExpirationTimePeriod), new Empty()); var contractProposalExpirationTime = Int32Value.Parser.ParseFrom(defaultTime); Assert.True(contractProposalExpirationTime.Value == 259200); - + var byteResult = await Tester.CallContractMethodAsync(BasicContractZeroAddress, nameof(BasicContractZeroImplContainer.BasicContractZeroImplStub.GetContractDeploymentController), new Empty()); var contractDeploymentController = AuthorityInfo.Parser.ParseFrom(byteResult); - + const string methodName = nameof(BasicContractZeroImplContainer.BasicContractZeroImplStub.SetContractProposalExpirationTimePeriod); { @@ -1422,9 +1483,10 @@ public async Task SetContractProposalExpirationTime_Test() await ApproveWithMinersAsync(Tester, ParliamentAddress, proposalId); var txResult2 = await ReleaseProposalAsync(Tester, ParliamentAddress, proposalId); txResult2.Status.ShouldBe(TransactionResultStatus.Mined); - + byteResult = await Tester.CallContractMethodAsync(BasicContractZeroAddress, - nameof(BasicContractZeroImplContainer.BasicContractZeroImplStub.GetContractProposalExpirationTimePeriod), + nameof(BasicContractZeroImplContainer.BasicContractZeroImplStub + .GetContractProposalExpirationTimePeriod), new Empty()); var newContractProposalExpirationTime = Int32Value.Parser.ParseFrom(byteResult); Assert.True(newContractProposalExpirationTime.Value == 86400); From f359480b4cf6bd06dd75d331d3171b83f3d3e11d Mon Sep 17 00:00:00 2001 From: sherry Date: Mon, 1 Apr 2024 19:50:28 +0800 Subject: [PATCH 063/128] feat:cr and add view method. --- .../TokenContract_ACS2_StatePathsProvider.cs | 2 +- .../TokenContract_Helper.cs | 44 +++----- .../TokenContract_NFT_Actions.cs | 8 +- .../TokenContract_Views.cs | 47 ++++++-- protobuf/token_contract.proto | 5 + .../BVT/TokenApplicationTests.cs | 104 +++++++++++++++--- 6 files changed, 146 insertions(+), 64 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs b/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs index 3454ead9a8..77a1113381 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs @@ -77,7 +77,7 @@ private void AddPathForAllowance(ResourceInfo resourceInfo, string from, string if (symbolType == SymbolType.Nft || symbolType == SymbolType.NftCollection) { resourceInfo.WritePaths.Add(GetPath(nameof(TokenContractState.Allowances), from, spender, - GetNftGlobalAllowanceSymbol(symbol))); + GetNftAllowanceSymbolIdentifier(symbol))); } } diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index 5495c0d445..e4428d8172 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -53,49 +53,31 @@ private void ValidTokenExists(string symbol) private void AssertApproveToken(string symbol) { - Assert(!string.IsNullOrEmpty(symbol) && symbol.All(IsValidApproveSymbolChar) && CheckAllSymbolIdentifierCount(symbol), - "Invalid symbol."); + Assert(!string.IsNullOrEmpty(symbol), "Symbol can not be null."); var words = symbol.Split(TokenContractConstants.NFTSymbolSeparator); - Assert(words[0].Length > 0, "Invalid symbol length."); + var symbolPrefix = words[0]; + var allSymbolIdentifier = GetAllSymbolIdentifier(); + Assert(symbolPrefix.Length > 0 && (symbolPrefix.All(IsValidCreateSymbolChar) || symbolPrefix.Equals(allSymbolIdentifier)), "Invalid symbol."); if (words.Length == 1) { - if (words[0].All(IsValidAllSymbolIdentifier)) return; - ValidTokenExists(words[0]); + if (!symbolPrefix.Equals(allSymbolIdentifier)) + { + ValidTokenExists(symbolPrefix); + } + return; } Assert(words.Length == 2, "Invalid symbol length."); - Assert( - words[0].All(IsValidCreateSymbolChar) && - words[1].Length > 0 && words[1].All(IsValidApproveItemIdChar), "Invalid NFT Symbol."); - var nftSymbol = words[1].All(IsValidAllSymbolIdentifier) ? GetCollectionSymbol(words[0]) : symbol; + var itemId = words[1]; + Assert(itemId.Length > 0 && (itemId.All(IsValidItemIdChar) || itemId.Equals(allSymbolIdentifier)), "Invalid NFT Symbol."); + var nftSymbol = itemId.Equals(allSymbolIdentifier) ? GetCollectionSymbol(symbolPrefix) : symbol; ValidTokenExists(nftSymbol); } - - private bool CheckAllSymbolIdentifierCount(string word) - { - return word.Count(c => c.Equals(TokenContractConstants.AllSymbolIdentifier)) <= 1; - } - + private string GetCollectionSymbol(string symbolPrefix) { return $"{symbolPrefix}-{TokenContractConstants.CollectionSymbolSuffix}"; } - private bool IsValidApproveSymbolChar(char character) - { - return IsValidSymbolChar(character) || - character == TokenContractConstants.AllSymbolIdentifier; - } - - private bool IsValidApproveItemIdChar(char character) - { - return IsValidItemIdChar(character) || character == TokenContractConstants.AllSymbolIdentifier; - } - - private bool IsValidAllSymbolIdentifier(char character) - { - return character == TokenContractConstants.AllSymbolIdentifier; - } - private void AssertValidSymbolAndAmount(string symbol, long amount) { Assert(!string.IsNullOrEmpty(symbol) && symbol.All(IsValidSymbolChar), diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index d86f419bc0..b8055cd2f1 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -108,7 +108,7 @@ private long GetAllowance(Address from, Address spender, string sourceSymbol, lo } else { - allowance = GetNftGlobalAllowance(from, spender, sourceSymbol, out allowanceSymbol); + allowance = GetNftSymbolAllowance(from, spender, sourceSymbol, out allowanceSymbol); if (allowance >= amount) return allowance; allowance = GetAllSymbolAllowance(from, spender, out allowanceSymbol); } @@ -123,14 +123,14 @@ private long GetAllSymbolAllowance(Address from, Address spender, out string all return State.Allowances[from][spender][allowanceSymbol]; } - private long GetNftGlobalAllowance(Address from, Address spender, string sourceSymbol, + private long GetNftSymbolAllowance(Address from, Address spender, string sourceSymbol, out string allowanceSymbol) { - allowanceSymbol = GetNftGlobalAllowanceSymbol(sourceSymbol); + allowanceSymbol = GetNftAllowanceSymbolIdentifier(sourceSymbol); return State.Allowances[from][spender][allowanceSymbol]; } - private string GetNftGlobalAllowanceSymbol(string sourceSymbol) + private string GetNftAllowanceSymbolIdentifier(string sourceSymbol) { // "AAA-*" return $"{sourceSymbol.Split(TokenContractConstants.NFTSymbolSeparator)[0]}-{TokenContractConstants.AllSymbolIdentifier}"; diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs index ed96c5ccd8..2610ed7083 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs @@ -45,29 +45,52 @@ public override GetBalanceOutput GetBalance(GetBalanceInput input) Balance = GetBalance(input.Owner, input.Symbol) }; } - + [View] public override GetAllowanceOutput GetAllowance(GetAllowanceInput input) { - var allowance = State.Allowances[input.Owner][input.Spender][input.Symbol]; - if (!input.Symbol.Contains(TokenContractConstants.AllSymbolIdentifier)) - { - var symbolType = GetSymbolType(input.Symbol); - allowance = Math.Max(allowance, GetAllSymbolAllowance(input.Owner,input.Spender,out _)); - if (symbolType == SymbolType.Nft || symbolType == SymbolType.NftCollection) - { - allowance = Math.Max(allowance, GetNftGlobalAllowance(input.Owner, input.Spender, input.Symbol, out _)); - } - } return new GetAllowanceOutput { Symbol = input.Symbol, Owner = input.Owner, Spender = input.Spender, - Allowance = allowance + Allowance = State.Allowances[input.Owner][input.Spender][input.Symbol] + }; + } + + [View] + public override GetAllowanceOutput GetAvailableAllowance(GetAllowanceInput input) + { + var result = new GetAllowanceOutput + { + Symbol = input.Symbol, + Owner = input.Owner, + Spender = input.Spender, }; + var symbol = input.Symbol; + var allowance = State.Allowances[input.Owner][input.Spender][symbol]; + if (CheckSymbolIdentifier(symbol)) + { + result.Allowance = allowance; + return result; + } + var symbolType = GetSymbolType(symbol); + allowance = Math.Max(allowance, GetAllSymbolAllowance(input.Owner,input.Spender,out _)); + if (symbolType == SymbolType.Nft || symbolType == SymbolType.NftCollection) + { + allowance = Math.Max(allowance, GetNftSymbolAllowance(input.Owner, input.Spender, symbol, out _)); + } + result.Allowance = allowance; + return result; } + private bool CheckSymbolIdentifier(string symbol) + { + var words = symbol.Split(TokenContractConstants.NFTSymbolSeparator); + var allSymbolIdentifier = GetAllSymbolIdentifier(); + return words[0].Equals(allSymbolIdentifier) || (words.Length > 1 && words[1].Equals(allSymbolIdentifier)); + } + public override BoolValue IsInWhiteList(IsInWhiteListInput input) { return new BoolValue { Value = State.LockWhiteLists[input.Symbol][input.Address] }; diff --git a/protobuf/token_contract.proto b/protobuf/token_contract.proto index e1174b8b45..038b0ca5b8 100644 --- a/protobuf/token_contract.proto +++ b/protobuf/token_contract.proto @@ -174,6 +174,11 @@ service TokenContract { option (aelf.is_view) = true; } + // Query the account's available allowance for other addresses + rpc GetAvailableAllowance (GetAllowanceInput) returns (GetAllowanceOutput) { + option (aelf.is_view) = true; + } + // Check whether the token is in the whitelist of an address, // which can be called TransferFrom to transfer the token under the condition of not being credited. rpc IsInWhiteList (IsInWhiteListInput) returns (google.protobuf.BoolValue) { diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs index 41b0d81028..201e56d6e6 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenApplicationTests.cs @@ -576,14 +576,32 @@ await TokenContractStub.Approve.SendAsync(new ApproveInput Spender = User1Address, Symbol = "ABC-1" }); - allowance.Allowance.ShouldBe(1000); + allowance.Allowance.ShouldBe(0); allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput { Owner = DefaultAddress, Spender = User1Address, Symbol = "ELF" }); - allowance.Allowance.ShouldBe(1000); + allowance.Allowance.ShouldBe(0); + { + var realAllowance = await TokenContractStub.GetAvailableAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "ABC-1" + }); + realAllowance.Allowance.ShouldBe(1000); + } + { + var realAllowance = await TokenContractStub.GetAvailableAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "ELF" + }); + realAllowance.Allowance.ShouldBe(1000); + } var user1Stub = GetTester(TokenContractAddress, User1KeyPair); var result2 = await user1Stub.TransferFrom.SendAsync(new TransferFromInput @@ -595,7 +613,16 @@ await TokenContractStub.Approve.SendAsync(new ApproveInput To = User1Address }); result2.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + var realAllowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "ABC-1" + }); + realAllowance.Allowance.ShouldBe(0); + } + allowance = await TokenContractStub.GetAvailableAllowance.CallAsync(new GetAllowanceInput { Owner = DefaultAddress, Spender = User1Address, @@ -647,14 +674,23 @@ await TokenContractStub.Approve.SendAsync(new ApproveInput Symbol = "ABC-*", Spender = User1Address }); - var allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + var realAllowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "ABC-1" + }); + realAllowance.Allowance.ShouldBe(0); + } + var allowance = await TokenContractStub.GetAvailableAllowance.CallAsync(new GetAllowanceInput { Owner = DefaultAddress, Spender = User1Address, Symbol = "ABC-1" }); allowance.Allowance.ShouldBe(1000); - allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + allowance = await TokenContractStub.GetAvailableAllowance.CallAsync(new GetAllowanceInput { Owner = DefaultAddress, Spender = User1Address, @@ -672,7 +708,7 @@ await TokenContractStub.Approve.SendAsync(new ApproveInput To = User1Address }); result2.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + allowance = await TokenContractStub.GetAvailableAllowance.CallAsync(new GetAllowanceInput { Owner = DefaultAddress, Spender = User1Address, @@ -705,14 +741,32 @@ await TokenContractStub.Approve.SendAsync(new ApproveInput Spender = User1Address, Symbol = "SSS" }); - allowance.Allowance.ShouldBe(100_00000000); + allowance.Allowance.ShouldBe(0); + { + var realAllowance = await TokenContractStub.GetAvailableAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "SSS" + }); + realAllowance.Allowance.ShouldBe(100_00000000); + } allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput { Owner = DefaultAddress, Spender = User1Address, Symbol = "ELF" }); - allowance.Allowance.ShouldBe(100_00000000); + allowance.Allowance.ShouldBe(0); + { + var realAllowance = await TokenContractStub.GetAvailableAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "SSS" + }); + realAllowance.Allowance.ShouldBe(100_00000000); + } var user1Stub = GetTester(TokenContractAddress, User1KeyPair); var result2 = await user1Stub.TransferFrom.SendAsync(new TransferFromInput @@ -724,7 +778,7 @@ await TokenContractStub.Approve.SendAsync(new ApproveInput To = User1Address }); result2.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); - allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + allowance = await TokenContractStub.GetAvailableAllowance.CallAsync(new GetAllowanceInput { Owner = DefaultAddress, Spender = User1Address, @@ -794,14 +848,23 @@ await TokenContractStub.Approve.SendAsync(new ApproveInput Symbol = "*", Amount = 200_000000000 }); - allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + var realAllowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = User1Address, + Symbol = "SSS" + }); + realAllowance.Allowance.ShouldBe(100_000000000); + } + allowance = await TokenContractStub.GetAvailableAllowance.CallAsync(new GetAllowanceInput { Owner = DefaultAddress, Spender = User1Address, Symbol = "SSS" }); allowance.Allowance.ShouldBe(200_000000000); - allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + allowance = await TokenContractStub.GetAvailableAllowance.CallAsync(new GetAllowanceInput { Owner = DefaultAddress, Spender = User1Address, @@ -814,7 +877,7 @@ await TokenContractStub.UnApprove.SendAsync(new UnApproveInput Symbol = "*", Amount = 20_000000000 }); - allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + allowance = await TokenContractStub.GetAvailableAllowance.CallAsync(new GetAllowanceInput { Owner = DefaultAddress, Spender = User1Address, @@ -834,7 +897,7 @@ public async Task MultiTokenContract_Approve_Test_New_Fail() Symbol = "SSS*", Amount = 100_000000000 }); - executionResult.TransactionResult.Error.ShouldContain("Token is not found"); + executionResult.TransactionResult.Error.ShouldContain("Invalid symbol."); } { var executionResult = await TokenContractStub.Approve.SendWithExceptionAsync(new ApproveInput @@ -843,7 +906,16 @@ public async Task MultiTokenContract_Approve_Test_New_Fail() Symbol = "SSS**", Amount = 100_000000000 }); - executionResult.TransactionResult.Error.ShouldContain("Invalid token symbol."); + executionResult.TransactionResult.Error.ShouldContain("Invalid symbol."); + } + { + var executionResult = await TokenContractStub.Approve.SendWithExceptionAsync(new ApproveInput + { + Spender = User1Address, + Symbol = "*-*", + Amount = 100_000000000 + }); + executionResult.TransactionResult.Error.ShouldContain("Token is not found"); } } @@ -865,7 +937,7 @@ await TokenContractStub.Issue.SendAsync(new IssueInput Symbol = "AB*-*", Amount = 100_000000000 }); - executionResult.TransactionResult.Error.ShouldContain("Invalid NFT Symbol"); + executionResult.TransactionResult.Error.ShouldContain("Invalid Symbol"); } { var executionResult = await TokenContractStub.Approve.SendWithExceptionAsync(new ApproveInput @@ -874,7 +946,7 @@ await TokenContractStub.Issue.SendAsync(new IssueInput Symbol = "ABC-*9", Amount = 100_000000000 }); - executionResult.TransactionResult.Error.ShouldContain("Token is not found"); + executionResult.TransactionResult.Error.ShouldContain("Invalid NFT Symbol."); } } From a74872bd24e12b6258638393f9fd2a143a033699 Mon Sep 17 00:00:00 2001 From: sherry Date: Tue, 2 Apr 2024 10:52:09 +0800 Subject: [PATCH 064/128] feat:rename. --- .../TokenContract_ACS2_StatePathsProvider.cs | 2 +- .../TokenContract_NFT_Actions.cs | 8 ++++---- contract/AElf.Contracts.MultiToken/TokenContract_Views.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs b/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs index 77a1113381..8d013d3180 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_ACS2_StatePathsProvider.cs @@ -77,7 +77,7 @@ private void AddPathForAllowance(ResourceInfo resourceInfo, string from, string if (symbolType == SymbolType.Nft || symbolType == SymbolType.NftCollection) { resourceInfo.WritePaths.Add(GetPath(nameof(TokenContractState.Allowances), from, spender, - GetNftAllowanceSymbolIdentifier(symbol))); + GetNftCollectionAllSymbolIdentifier(symbol))); } } diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index b8055cd2f1..db20e29b21 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -108,7 +108,7 @@ private long GetAllowance(Address from, Address spender, string sourceSymbol, lo } else { - allowance = GetNftSymbolAllowance(from, spender, sourceSymbol, out allowanceSymbol); + allowance = GetNftCollectionAllSymbolAllowance(from, spender, sourceSymbol, out allowanceSymbol); if (allowance >= amount) return allowance; allowance = GetAllSymbolAllowance(from, spender, out allowanceSymbol); } @@ -123,14 +123,14 @@ private long GetAllSymbolAllowance(Address from, Address spender, out string all return State.Allowances[from][spender][allowanceSymbol]; } - private long GetNftSymbolAllowance(Address from, Address spender, string sourceSymbol, + private long GetNftCollectionAllSymbolAllowance(Address from, Address spender, string sourceSymbol, out string allowanceSymbol) { - allowanceSymbol = GetNftAllowanceSymbolIdentifier(sourceSymbol); + allowanceSymbol = GetNftCollectionAllSymbolIdentifier(sourceSymbol); return State.Allowances[from][spender][allowanceSymbol]; } - private string GetNftAllowanceSymbolIdentifier(string sourceSymbol) + private string GetNftCollectionAllSymbolIdentifier(string sourceSymbol) { // "AAA-*" return $"{sourceSymbol.Split(TokenContractConstants.NFTSymbolSeparator)[0]}-{TokenContractConstants.AllSymbolIdentifier}"; diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs index 2610ed7083..7c825d6725 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs @@ -78,7 +78,7 @@ public override GetAllowanceOutput GetAvailableAllowance(GetAllowanceInput input allowance = Math.Max(allowance, GetAllSymbolAllowance(input.Owner,input.Spender,out _)); if (symbolType == SymbolType.Nft || symbolType == SymbolType.NftCollection) { - allowance = Math.Max(allowance, GetNftSymbolAllowance(input.Owner, input.Spender, symbol, out _)); + allowance = Math.Max(allowance, GetNftCollectionAllSymbolAllowance(input.Owner, input.Spender, symbol, out _)); } result.Allowance = allowance; return result; From 160bed1a75652148747b5454f0724f0426168d78 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Wed, 3 Apr 2024 11:47:43 +0800 Subject: [PATCH 065/128] feat: get fees while filtering Empty objects --- contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs index 25f853ec11..412ed35d96 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs @@ -320,14 +320,14 @@ private void SetOrRefreshTransactionFeeFreeAllowances(Address address) private Dictionary GetBaseFeeDictionary(MethodFees methodFees) { - return methodFees.Fees + return methodFees.Fees.Where(f => !string.IsNullOrEmpty(f.Symbol)) .GroupBy(f => f.Symbol, f => f.BasicFee) .ToDictionary(g => g.Key, g => g.Sum()); } private Dictionary GetUserContractFeeDictionary(UserContractMethodFees fees) { - return fees.Fees + return fees.Fees.Where(f => !string.IsNullOrEmpty(f.Symbol)) .GroupBy(f => f.Symbol, f => f.BasicFee) .ToDictionary(g => g.Key, g => g.Sum()); } From 0ad656bdc94feeaf3a56a63b7c38f1864942e502 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Mon, 8 Apr 2024 14:31:03 +0800 Subject: [PATCH 066/128] feat: update DefaultCodeCheckProposalExpirationTimePeriod --- contract/AElf.Contracts.Genesis/BasicContractZero_Constants.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.Genesis/BasicContractZero_Constants.cs b/contract/AElf.Contracts.Genesis/BasicContractZero_Constants.cs index 3aa989214a..03911596ef 100644 --- a/contract/AElf.Contracts.Genesis/BasicContractZero_Constants.cs +++ b/contract/AElf.Contracts.Genesis/BasicContractZero_Constants.cs @@ -3,7 +3,7 @@ namespace AElf.Contracts.Genesis; public partial class BasicContractZero { public const int ContractProposalExpirationTimePeriod = 259200; // 60 * 60 * 72 - public const int DefaultCodeCheckProposalExpirationTimePeriod = 600; // 60 * 10 + public const int DefaultCodeCheckProposalExpirationTimePeriod = 900; // 60 * 15 private const int MinimalApprovalThreshold = 6667; private const int MaximalAbstentionThreshold = 1000; private const int MaximalRejectionThreshold = 1000; From 517c88ea9e3dc85729ea94538eb8e03253a86e50 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Mon, 8 Apr 2024 14:36:15 +0800 Subject: [PATCH 067/128] feat: update ut --- test/AElf.Contracts.Genesis.Tests/GenesisContractAuthTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/AElf.Contracts.Genesis.Tests/GenesisContractAuthTest.cs b/test/AElf.Contracts.Genesis.Tests/GenesisContractAuthTest.cs index 82ab726523..a5d5b5bde9 100644 --- a/test/AElf.Contracts.Genesis.Tests/GenesisContractAuthTest.cs +++ b/test/AElf.Contracts.Genesis.Tests/GenesisContractAuthTest.cs @@ -1391,7 +1391,7 @@ public async Task SetCodeCheckProposalExpirationTime_Test() nameof(BasicContractZeroImplContainer.BasicContractZeroImplStub.GetCodeCheckProposalExpirationTimePeriod), new Empty()); var proposalExpirationTime = Int32Value.Parser.ParseFrom(defaultTime); - Assert.True(proposalExpirationTime.Value == 600); + Assert.True(proposalExpirationTime.Value == 900); var byteResult = await Tester.CallContractMethodAsync(BasicContractZeroAddress, nameof(BasicContractZeroImplContainer.BasicContractZeroImplStub.GetContractDeploymentController), From 91cd0952482e450a1ecbedf6870cbece0383db33 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Tue, 9 Apr 2024 13:59:21 +0800 Subject: [PATCH 068/128] feat:update UserContractMethodFeeKey --- contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs | 2 +- .../ExecutionPluginForUserContractMethodFeeTest.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs index 412ed35d96..554ca1c480 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs @@ -140,7 +140,7 @@ private UserContractMethodFees GetActualFee(Address contractAddress, string meth //configuration_key:UserContractMethod_contractAddress_methodName var spec = State.ConfigurationContract.GetConfiguration.Call(new StringValue { - Value = $"{TokenContractConstants.UserContractMethodFeeKey}_{contractAddress}_{methodName}" + Value = $"{TokenContractConstants.UserContractMethodFeeKey}_{contractAddress.ToBase58()}_{methodName}" }); var fee = new UserContractMethodFees(); if (!spec.Value.IsNullOrEmpty()) diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs index 25e8daa5f4..575fc30f5f 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs @@ -282,7 +282,8 @@ public async Task ChargeFee_SpecConfigurationFee() }; var createProposalInput = new SetConfigurationInput { - Key = $"{ConfigurationKey}_{_testContractAddress}_{nameof(TestContractContainer.TestContractStub.TestMethod)}", + Key = + $"{ConfigurationKey}_{_testContractAddress.ToBase58()}_{nameof(TestContractContainer.TestContractStub.TestMethod)}", Value = transactionFee.ToByteString() }; await ConfigurationStub.SetConfiguration.SendAsync(createProposalInput); From 53778ba22e6da3ab888a04ad1a842fe44d9ba0fa Mon Sep 17 00:00:00 2001 From: eanzhao Date: Wed, 10 Apr 2024 17:15:10 +0800 Subject: [PATCH 069/128] Improve CodeCheckJobProcessor.SendAsync --- .../Application/CodeCheckJobProcessor.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs b/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs index 90bfadca17..1b94688096 100644 --- a/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs +++ b/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs @@ -16,7 +16,7 @@ public interface ICodeCheckJobProcessor public class CodeCheckJobProcessor : ICodeCheckJobProcessor, ISingletonDependency { - private readonly TransformBlock _codeCheckTransformBlock; + private TransformBlock _codeCheckTransformBlock; private List> _codeCheckProcessesJobTransformBlock; private readonly CodeCheckOptions _codeCheckOptions; private readonly ICheckedCodeHashProvider _checkedCodeHashProvider; @@ -42,7 +42,18 @@ public CodeCheckJobProcessor(IOptionsSnapshot codeCheckOptions public async Task SendAsync(CodeCheckJob job) { - return await _codeCheckTransformBlock.SendAsync(job); + var codeCheckJobSendResult = await _codeCheckTransformBlock.SendAsync(job); + if (!codeCheckJobSendResult) + { + Logger.LogError( + $"Failed to send code check job. " + + $"Input count: {_codeCheckTransformBlock.InputCount}, " + + $"output count: {_codeCheckTransformBlock.OutputCount}"); + Logger.LogError("Trying to recovery."); + _codeCheckTransformBlock = CreateCodeCheckBufferBlock(); + } + + return codeCheckJobSendResult; } public async Task CompleteAsync() From c79229ab5236c8c65af09d754d8dfbcfd12fe3ca Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 11 Apr 2024 12:06:22 +0800 Subject: [PATCH 070/128] Do not create new buffer block after sending failure. --- .../Application/CodeCheckJobProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs b/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs index 1b94688096..bcb74dfd14 100644 --- a/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs +++ b/src/AElf.Kernel.CodeCheck/Application/CodeCheckJobProcessor.cs @@ -49,8 +49,8 @@ public async Task SendAsync(CodeCheckJob job) $"Failed to send code check job. " + $"Input count: {_codeCheckTransformBlock.InputCount}, " + $"output count: {_codeCheckTransformBlock.OutputCount}"); - Logger.LogError("Trying to recovery."); - _codeCheckTransformBlock = CreateCodeCheckBufferBlock(); + //Logger.LogError("Trying to recovery."); + //_codeCheckTransformBlock = CreateCodeCheckBufferBlock(); } return codeCheckJobSendResult; From ec5d62b9f54c27affe1d633f0f5c830421c2d9c1 Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Mon, 15 Apr 2024 14:52:40 +0800 Subject: [PATCH 071/128] feat: add input check --- contract/AElf.Contracts.Genesis/BasicContractZero.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/contract/AElf.Contracts.Genesis/BasicContractZero.cs b/contract/AElf.Contracts.Genesis/BasicContractZero.cs index dad07e8672..536bc87bb6 100644 --- a/contract/AElf.Contracts.Genesis/BasicContractZero.cs +++ b/contract/AElf.Contracts.Genesis/BasicContractZero.cs @@ -401,6 +401,7 @@ public override Empty SetContractProposalExpirationTimePeriod(SetContractProposa public override Empty SetCodeCheckProposalExpirationTimePeriod(Int32Value input) { AssertSenderAddressWith(State.ContractDeploymentController.Value.OwnerAddress); + Assert(input.Value > 0, "Invalid expiration time period."); State.CodeCheckProposalExpirationTimePeriod.Value = input.Value; return new Empty(); } From b95310add947a6d484c3ec4c26216cae5816a3f1 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Sun, 28 Apr 2024 12:18:54 +0800 Subject: [PATCH 072/128] Add MatchCollection & Match to code check white list. --- .../Validators/Whitelist/IWhitelistProvider.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/AElf.CSharp.CodeOps/Validators/Whitelist/IWhitelistProvider.cs b/src/AElf.CSharp.CodeOps/Validators/Whitelist/IWhitelistProvider.cs index 2d47d59a3e..7c9f30c65d 100644 --- a/src/AElf.CSharp.CodeOps/Validators/Whitelist/IWhitelistProvider.cs +++ b/src/AElf.CSharp.CodeOps/Validators/Whitelist/IWhitelistProvider.cs @@ -151,7 +151,10 @@ private void WhitelistOthers(Whitelist whitelist) .Member(nameof(Regex.Matches), Permission.Allowed) .Member(nameof(Regex.Replace), Permission.Allowed) .Member(nameof(Regex.Split), Permission.Allowed) - )) + ) + .Type(nameof(MatchCollection), Permission.Allowed) + .Type(nameof(Match), Permission.Allowed) + ) ; } From e8b6957d1efdc25dcf541448a8d4024e9c10065f Mon Sep 17 00:00:00 2001 From: eanzhao Date: Sun, 28 Apr 2024 14:09:21 +0800 Subject: [PATCH 073/128] Add MigrateConnectorTokens method to Token Converter contract. --- .../TokenConverterContract.cs | 20 ++++++++++++++++++- protobuf/token_converter_contract.proto | 5 ++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs b/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs index 1a7aaf80d2..21eac65289 100644 --- a/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs +++ b/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs @@ -12,6 +12,9 @@ namespace AElf.Contracts.TokenConverter; public partial class TokenConverterContract : TokenConverterContractImplContainer.TokenConverterContractImplBase { private const string NtTokenPrefix = "nt"; + private const string NewNtTokenPrefix = "(nt)"; + public const string PayTxFeeSymbolListName = "SymbolListToPayTxFee"; + public const string PayRentalSymbolListName = "SymbolListToPayRental"; #region Actions @@ -77,7 +80,7 @@ public override Empty AddPairConnector(PairConnectorParam input) AssertPerformedByConnectorController(); Assert(!string.IsNullOrEmpty(input.ResourceConnectorSymbol), "resource token symbol should not be empty"); - var nativeConnectorSymbol = NtTokenPrefix.Append(input.ResourceConnectorSymbol); + var nativeConnectorSymbol = NewNtTokenPrefix.Append(input.ResourceConnectorSymbol); Assert(State.Connectors[input.ResourceConnectorSymbol] == null, "resource token symbol has existed"); var resourceConnector = new Connector @@ -304,6 +307,21 @@ public override Empty ChangeConnectorController(AuthorityInfo input) return new Empty(); } + public override Empty MigrateConnectorTokens(Empty input) + { + foreach (var resourceTokenSymbol in Context.Variables.GetStringArray(PayTxFeeSymbolListName) + .Union(Context.Variables.GetStringArray(PayRentalSymbolListName))) + { + var newConnectorTokenSymbol = NewNtTokenPrefix.Append(resourceTokenSymbol); + var oldConnectorTokenSymbol = NtTokenPrefix.Append(resourceTokenSymbol); + + // Migrate + State.Connectors[newConnectorTokenSymbol] = State.Connectors[oldConnectorTokenSymbol]; + } + + return new Empty(); + } + #endregion Actions #region Helpers diff --git a/protobuf/token_converter_contract.proto b/protobuf/token_converter_contract.proto index af6e99c10c..5f92194185 100644 --- a/protobuf/token_converter_contract.proto +++ b/protobuf/token_converter_contract.proto @@ -51,7 +51,10 @@ service TokenConverterContract { // Set the governance authority information for TokenConvert contract. rpc ChangeConnectorController (AuthorityInfo) returns (google.protobuf.Empty) { } - + + rpc MigrateConnectorTokens (google.protobuf.Empty) returns (google.protobuf.Empty) { + } + // Query the pair connector according to token symbol. rpc GetPairConnector (TokenSymbol) returns (PairConnector) { option (aelf.is_view) = true; From 72d9b2a178e691238c20f1136d29f11461e48730 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Sun, 28 Apr 2024 15:26:13 +0800 Subject: [PATCH 074/128] Add test cases for MigrateConnectorTokens and tune method impl. --- .../TokenConverterContract.cs | 22 ++- .../ConnectorTokenMigrateTest.cs | 141 ++++++++++++++++++ 2 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 test/AElf.Contracts.TokenConverter.Tests/ConnectorTokenMigrateTest.cs diff --git a/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs b/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs index 21eac65289..f9ad710946 100644 --- a/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs +++ b/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs @@ -12,7 +12,7 @@ namespace AElf.Contracts.TokenConverter; public partial class TokenConverterContract : TokenConverterContractImplContainer.TokenConverterContractImplBase { private const string NtTokenPrefix = "nt"; - private const string NewNtTokenPrefix = "(nt)"; + private const string NewNtTokenPrefix = "(NT)"; public const string PayTxFeeSymbolListName = "SymbolListToPayTxFee"; public const string PayRentalSymbolListName = "SymbolListToPayRental"; @@ -313,10 +313,26 @@ public override Empty MigrateConnectorTokens(Empty input) .Union(Context.Variables.GetStringArray(PayRentalSymbolListName))) { var newConnectorTokenSymbol = NewNtTokenPrefix.Append(resourceTokenSymbol); - var oldConnectorTokenSymbol = NtTokenPrefix.Append(resourceTokenSymbol); + + if (State.Connectors[resourceTokenSymbol] == null) + { + continue; + } + + var oldConnectorTokenSymbol = State.Connectors[resourceTokenSymbol].RelatedSymbol; // Migrate - State.Connectors[newConnectorTokenSymbol] = State.Connectors[oldConnectorTokenSymbol]; + + State.Connectors[resourceTokenSymbol].RelatedSymbol = newConnectorTokenSymbol; + + if (State.Connectors[oldConnectorTokenSymbol] != null) + { + var connector = State.Connectors[oldConnectorTokenSymbol]; + connector.Symbol = newConnectorTokenSymbol; + State.Connectors[newConnectorTokenSymbol] = connector; + } + + State.DepositBalance[newConnectorTokenSymbol] = State.DepositBalance[oldConnectorTokenSymbol]; } return new Empty(); diff --git a/test/AElf.Contracts.TokenConverter.Tests/ConnectorTokenMigrateTest.cs b/test/AElf.Contracts.TokenConverter.Tests/ConnectorTokenMigrateTest.cs new file mode 100644 index 0000000000..54e152d442 --- /dev/null +++ b/test/AElf.Contracts.TokenConverter.Tests/ConnectorTokenMigrateTest.cs @@ -0,0 +1,141 @@ +using System; +using System.Threading.Tasks; +using AElf.CSharp.Core; +using AElf.Types; +using Google.Protobuf.WellKnownTypes; +using Shouldly; +using Xunit; + +namespace AElf.Contracts.TokenConverter; + +public partial class TokenConverterContractTests +{ + [Fact] + public async Task CanBuyResourceTokenAfterMigration() + { + await CreateRamToken(); + await InitializeTreasuryContractAsync(); + await InitializeTokenConverterContract(); + await PrepareToBuyAndSell(); + + await DefaultStub.MigrateConnectorTokens.SendAsync(new Empty()); + + //check the price and fee + var fromConnectorBalance = ELFConnector.VirtualBalance; + var fromConnectorWeight = decimal.Parse(ELFConnector.Weight); + var toConnectorBalance = await GetBalanceAsync(WriteSymbol, TokenConverterContractAddress); + var toConnectorWeight = decimal.Parse(WriteConnector.Weight); + + var amountToPay = BancorHelper.GetAmountToPayFromReturn(fromConnectorBalance, fromConnectorWeight, + toConnectorBalance, toConnectorWeight, 1000L); + var depositAmountBeforeBuy = await DefaultStub.GetDepositConnectorBalance.CallAsync(new StringValue + { + Value = WriteConnector.Symbol + }); + var fee = Convert.ToInt64(amountToPay * 5 / 1000); + + var buyResult = (await DefaultStub.Buy.SendAsync( + new BuyInput + { + Symbol = WriteConnector.Symbol, + Amount = 1000L, + PayLimit = amountToPay + fee + 10L + })).TransactionResult; + buyResult.Status.ShouldBe(TransactionResultStatus.Mined); + + //Verify the outcome of the transaction + var depositAmountAfterBuy = await DefaultStub.GetDepositConnectorBalance.CallAsync(new StringValue + { + Value = WriteConnector.Symbol + }); + depositAmountAfterBuy.Value.Sub(depositAmountBeforeBuy.Value).ShouldBe(amountToPay); + var balanceOfTesterWrite = await GetBalanceAsync(WriteSymbol, DefaultSender); + balanceOfTesterWrite.ShouldBe(1000L); + + var elfBalanceLoggedInTokenConvert = await DefaultStub.GetDepositConnectorBalance.CallAsync(new StringValue + { + Value = WriteConnector.Symbol + }); + elfBalanceLoggedInTokenConvert.Value.ShouldBe(ELFConnector.VirtualBalance + amountToPay); + var balanceOfElfToken = await GetBalanceAsync(NativeSymbol, TokenConverterContractAddress); + balanceOfElfToken.ShouldBe(amountToPay); + + var donatedFee = await TreasuryContractStub.GetUndistributedDividends.CallAsync(new Empty()); + donatedFee.Value[NativeSymbol].ShouldBe(fee.Div(2)); + + var balanceOfRamToken = await GetBalanceAsync(WriteSymbol, TokenConverterContractAddress); + balanceOfRamToken.ShouldBe(100_0000L - 1000L); + + var balanceOfTesterToken = await GetBalanceAsync(NativeSymbol, DefaultSender); + balanceOfTesterToken.ShouldBe(100_0000L - amountToPay - fee); + } + + [Fact] + public async Task CanSellResourceTokenAfterMigration() + { + await CreateRamToken(); + await InitializeTreasuryContractAsync(); + await InitializeTokenConverterContract(); + await PrepareToBuyAndSell(); + + await DefaultStub.MigrateConnectorTokens.SendAsync(new Empty()); + + var buyResult = (await DefaultStub.Buy.SendAsync( + new BuyInput + { + Symbol = WriteConnector.Symbol, + Amount = 1000L, + PayLimit = 1010L + })).TransactionResult; + buyResult.Status.ShouldBe(TransactionResultStatus.Mined); + + //Balance before Sell + var treasuryBeforeSell = + (await TreasuryContractStub.GetUndistributedDividends.CallAsync(new Empty())).Value[NativeSymbol]; + var balanceOfElfToken = await GetBalanceAsync(NativeSymbol, TokenConverterContractAddress); + var balanceOfTesterToken = await GetBalanceAsync(NativeSymbol, DefaultSender); + + //check the price and fee + var toConnectorBalance = ELFConnector.VirtualBalance + balanceOfElfToken; + var toConnectorWeight = decimal.Parse(ELFConnector.Weight); + var fromConnectorBalance = await GetBalanceAsync(WriteSymbol, TokenConverterContractAddress); + var fromConnectorWeight = decimal.Parse(WriteConnector.Weight); + + var amountToReceive = BancorHelper.GetReturnFromPaid(fromConnectorBalance, fromConnectorWeight, + toConnectorBalance, toConnectorWeight, 1000L); + var depositAmountBeforeSell = await DefaultStub.GetDepositConnectorBalance.CallAsync(new StringValue + { + Value = WriteConnector.Symbol + }); + var fee = Convert.ToInt64(amountToReceive * 5 / 1000); + + var sellResult = (await DefaultStub.Sell.SendAsync(new SellInput + { + Symbol = WriteConnector.Symbol, + Amount = 1000L, + ReceiveLimit = amountToReceive - fee - 10L + })).TransactionResult; + sellResult.Status.ShouldBe(TransactionResultStatus.Mined); + + //Verify the outcome of the transaction + var depositAmountAfterSell = await DefaultStub.GetDepositConnectorBalance.CallAsync(new StringValue + { + Value = WriteConnector.Symbol + }); + depositAmountBeforeSell.Value.Sub(depositAmountAfterSell.Value).ShouldBe(amountToReceive); + var balanceOfTesterRam = await GetBalanceAsync(WriteSymbol, DefaultSender); + balanceOfTesterRam.ShouldBe(0L); + + var treasuryAfterSell = await TreasuryContractStub.GetUndistributedDividends.CallAsync(new Empty()); + treasuryAfterSell.Value[NativeSymbol].ShouldBe(fee.Div(2) + treasuryBeforeSell); + + var balanceOfElfTokenAfterSell = await GetBalanceAsync(NativeSymbol, TokenConverterContractAddress); + balanceOfElfTokenAfterSell.ShouldBe(balanceOfElfToken - amountToReceive); + + var balanceOfRamToken = await GetBalanceAsync(WriteSymbol, TokenConverterContractAddress); + balanceOfRamToken.ShouldBe(100_0000L); + + var balanceOfTesterTokenAfterSell = await GetBalanceAsync(NativeSymbol, DefaultSender); + balanceOfTesterTokenAfterSell.ShouldBe(balanceOfTesterToken + amountToReceive - fee); + } +} \ No newline at end of file From a791b2f0cb9a3136d95f3a4688ec0fd2d1e9791a Mon Sep 17 00:00:00 2001 From: eanzhao Date: Sun, 28 Apr 2024 16:17:55 +0800 Subject: [PATCH 075/128] Now MigrateConnectorTokens method cannot execute more than once. --- .../TokenConverterContract.cs | 2 ++ .../ConnectorTokenMigrateTest.cs | 17 +++++++++++++++-- .../TokenConverterContractTests.cs | 10 +++++----- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs b/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs index f9ad710946..1957516b09 100644 --- a/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs +++ b/contract/AElf.Contracts.TokenConverter/TokenConverterContract.cs @@ -321,6 +321,8 @@ public override Empty MigrateConnectorTokens(Empty input) var oldConnectorTokenSymbol = State.Connectors[resourceTokenSymbol].RelatedSymbol; + Assert(!oldConnectorTokenSymbol.StartsWith(NewNtTokenPrefix), "Already migrated."); + // Migrate State.Connectors[resourceTokenSymbol].RelatedSymbol = newConnectorTokenSymbol; diff --git a/test/AElf.Contracts.TokenConverter.Tests/ConnectorTokenMigrateTest.cs b/test/AElf.Contracts.TokenConverter.Tests/ConnectorTokenMigrateTest.cs index 54e152d442..e6ec3b2886 100644 --- a/test/AElf.Contracts.TokenConverter.Tests/ConnectorTokenMigrateTest.cs +++ b/test/AElf.Contracts.TokenConverter.Tests/ConnectorTokenMigrateTest.cs @@ -13,7 +13,7 @@ public partial class TokenConverterContractTests [Fact] public async Task CanBuyResourceTokenAfterMigration() { - await CreateRamToken(); + await CreateWriteToken(); await InitializeTreasuryContractAsync(); await InitializeTokenConverterContract(); await PrepareToBuyAndSell(); @@ -73,7 +73,7 @@ public async Task CanBuyResourceTokenAfterMigration() [Fact] public async Task CanSellResourceTokenAfterMigration() { - await CreateRamToken(); + await CreateWriteToken(); await InitializeTreasuryContractAsync(); await InitializeTokenConverterContract(); await PrepareToBuyAndSell(); @@ -138,4 +138,17 @@ public async Task CanSellResourceTokenAfterMigration() var balanceOfTesterTokenAfterSell = await GetBalanceAsync(NativeSymbol, DefaultSender); balanceOfTesterTokenAfterSell.ShouldBe(balanceOfTesterToken + amountToReceive - fee); } + + [Fact] + public async Task MigrateTwiceTest() + { + await CreateWriteToken(); + await InitializeTreasuryContractAsync(); + await InitializeTokenConverterContract(); + await PrepareToBuyAndSell(); + + await DefaultStub.MigrateConnectorTokens.SendAsync(new Empty()); + var result = await DefaultStub.MigrateConnectorTokens.SendWithExceptionAsync(new Empty()); + result.TransactionResult.Error.ShouldContain("Already migrated."); + } } \ No newline at end of file diff --git a/test/AElf.Contracts.TokenConverter.Tests/TokenConverterContractTests.cs b/test/AElf.Contracts.TokenConverter.Tests/TokenConverterContractTests.cs index 2f598d96e1..438ce585ac 100644 --- a/test/AElf.Contracts.TokenConverter.Tests/TokenConverterContractTests.cs +++ b/test/AElf.Contracts.TokenConverter.Tests/TokenConverterContractTests.cs @@ -158,7 +158,7 @@ public async Task Initialize_With_Default_Base_Token_Test() [Fact] public async Task Buy_Success_Test() { - await CreateRamToken(); + await CreateWriteToken(); await InitializeTreasuryContractAsync(); await InitializeTokenConverterContract(); await PrepareToBuyAndSell(); @@ -216,7 +216,7 @@ public async Task Buy_Success_Test() [Fact] public async Task Buy_With_Invalid_Input_Test() { - await CreateRamToken(); + await CreateWriteToken(); await InitializeTokenConverterContract(); await PrepareToBuyAndSell(); @@ -244,7 +244,7 @@ public async Task Buy_With_Invalid_Input_Test() [Fact] public async Task Sell_Success_Test() { - await CreateRamToken(); + await CreateWriteToken(); await InitializeTreasuryContractAsync(); await InitializeTokenConverterContract(); await PrepareToBuyAndSell(); @@ -311,7 +311,7 @@ public async Task Sell_Success_Test() [Fact] public async Task Sell_With_Invalid_Input_Test() { - await CreateRamToken(); + await CreateWriteToken(); await InitializeTreasuryContractAsync(); await InitializeTokenConverterContract(); await PrepareToBuyAndSell(); @@ -361,7 +361,7 @@ private InitializeInput GetLegalInitializeInput() }; } - private async Task CreateRamToken() + private async Task CreateWriteToken() { await ExecuteProposalForParliamentTransaction(TokenContractAddress, nameof(TokenContractStub.Create), new CreateInput From 9c8178cbc3e3c67ed7b916f577336e51d6f6b6d6 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 9 May 2024 11:50:17 +0800 Subject: [PATCH 076/128] Resolve conflicts. --- .../TokenContractConstants.cs | 1 + .../TokenContractState.cs | 8 + .../TokenContract_ACS1_MethodFeeProvider.cs | 7 +- .../TokenContract_Actions.cs | 103 ++++++- .../TokenContract_Fees.cs | 7 +- .../TokenContract_Helper.cs | 32 +- .../TokenContract_NFT_Actions.cs | 4 +- .../TokenContract_Views.cs | 14 +- protobuf/token_contract.proto | 14 + .../BVT/TokenAliasTests.cs | 284 ++++++++++++++++++ .../MultiTokenContractCrossChainTest.cs | 186 +++++++++++- 11 files changed, 630 insertions(+), 30 deletions(-) create mode 100644 test/AElf.Contracts.MultiToken.Tests/BVT/TokenAliasTests.cs diff --git a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs index a14a4e9496..9179e6fc11 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractConstants.cs @@ -14,6 +14,7 @@ public static class TokenContractConstants public const string LockCallbackExternalInfoKey = "aelf_lock_callback"; public const string UnlockCallbackExternalInfoKey = "aelf_unlock_callback"; public const string LogEventExternalInfoKey = "aelf_log_event"; + public const string TokenAliasExternalInfoKey = "aelf_token_alias"; public const int DELEGATEE_MAX_COUNT = 24; public const char NFTSymbolSeparator = '-'; public const int NFTSymbolMaxLength = 30; diff --git a/contract/AElf.Contracts.MultiToken/TokenContractState.cs b/contract/AElf.Contracts.MultiToken/TokenContractState.cs index c32b0ea62f..e29d18d95c 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContractState.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContractState.cs @@ -8,6 +8,11 @@ public partial class TokenContractState : ContractState public StringState NativeTokenSymbol { get; set; } public StringState ChainPrimaryTokenSymbol { get; set; } + + /// + /// WARNING: Use GetTokenInfo & SetTokenInfo to operate TokenInfos + /// due to token symbol alias feature. + /// public MappedState TokenInfos { get; set; } public MappedState SymbolSeedMap { get; set; } public MappedState Balances { get; set; } @@ -65,4 +70,7 @@ public partial class TokenContractState : ContractState public SingletonState
VoteContractAddress { get; set; } public SingletonState TokenIssuerAndOwnerModificationDisabled { get; set; } + + // Alias -> Actual Symbol + public MappedState SymbolAliasMap { get; set; } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_ACS1_MethodFeeProvider.cs b/contract/AElf.Contracts.MultiToken/TokenContract_ACS1_MethodFeeProvider.cs index 9ef198b39c..e24c2d8acc 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_ACS1_MethodFeeProvider.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_ACS1_MethodFeeProvider.cs @@ -123,9 +123,12 @@ private bool CheckOrganizationExist(AuthorityInfo authorityInfo) private void AssertValidFeeToken(string symbol, long amount) { AssertValidSymbolAndAmount(symbol, amount); - if (State.TokenInfos[symbol] == null) + var tokenInfo = GetTokenInfo(symbol); + if (tokenInfo == null) + { throw new AssertionException("Token is not found"); - Assert(State.TokenInfos[symbol].IsBurnable, $"Token {symbol} cannot set as method fee."); + } + Assert(tokenInfo.IsBurnable, $"Token {symbol} cannot set as method fee."); } #endregion diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 249afc33ea..da59304f94 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; using AElf.CSharp.Core; using AElf.Sdk.CSharp; using AElf.Standards.ACS0; @@ -76,6 +77,7 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. ExternalInfo = input.ExternalInfo ?? new ExternalInfo(), Owner = input.Owner }; + CheckTokenExists(tokenInfo.Symbol); RegisterTokenInfo(tokenInfo); if (string.IsNullOrEmpty(State.NativeTokenSymbol.Value)) { @@ -109,7 +111,7 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. private void CheckSeedNFT(string symbolSeed, String symbol) { Assert(!string.IsNullOrEmpty(symbolSeed), "Seed NFT does not exist."); - var tokenInfo = State.TokenInfos[symbolSeed]; + var tokenInfo = GetTokenInfo(symbolSeed); Assert(tokenInfo != null, "Seed NFT does not exist."); Assert(State.Balances[Context.Sender][symbolSeed] > 0, "Seed NFT balance is not enough."); Assert(tokenInfo.ExternalInfo != null && tokenInfo.ExternalInfo.Value.TryGetValue( @@ -130,7 +132,7 @@ private void CheckSeedNFT(string symbolSeed, String symbol) public override Empty SetPrimaryTokenSymbol(SetPrimaryTokenSymbolInput input) { Assert(State.ChainPrimaryTokenSymbol.Value == null, "Failed to set primary token symbol."); - Assert(!string.IsNullOrWhiteSpace(input.Symbol) && State.TokenInfos[input.Symbol] != null, "Invalid input symbol."); + Assert(!string.IsNullOrWhiteSpace(input.Symbol) && GetTokenInfo(input.Symbol) != null, "Invalid input symbol."); State.ChainPrimaryTokenSymbol.Value = input.Symbol; Context.Fire(new ChainPrimaryTokenSymbolSet { TokenSymbol = input.Symbol }); @@ -155,7 +157,7 @@ public override Empty Issue(IssueInput input) tokenInfo.Supply = tokenInfo.Supply.Add(input.Amount); Assert(tokenInfo.Issued <= tokenInfo.TotalSupply, "Total supply exceeded"); - State.TokenInfos[input.Symbol] = tokenInfo; + SetTokenInfo(tokenInfo); ModifyBalance(input.To, input.Symbol, input.Amount); Context.Fire(new Issued @@ -404,7 +406,7 @@ public override Empty TakeResourceTokenBack(TakeResourceTokenBackInput input) public override Empty ValidateTokenInfoExists(ValidateTokenInfoExistsInput input) { Assert(!string.IsNullOrWhiteSpace(input.Symbol), "Invalid input symbol."); - var tokenInfo = State.TokenInfos[input.Symbol]; + var tokenInfo = GetTokenInfo(input.Symbol); if (tokenInfo == null) throw new AssertionException("Token validation failed."); var validationResult = tokenInfo.TokenName == input.TokenName && @@ -467,6 +469,9 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) ExternalInfo = new ExternalInfo { Value = { validateTokenInfoExistsInput.ExternalInfo } }, Owner = validateTokenInfoExistsInput.Owner ?? validateTokenInfoExistsInput.Issuer }; + + SyncSymbolAliasFromTokenInfo(tokenInfo); + RegisterTokenInfo(tokenInfo); Context.Fire(new TokenCreated @@ -570,7 +575,7 @@ public override Empty CrossChainReceiveToken(CrossChainReceiveTokenInput input) State.VerifiedCrossChainTransferTransaction[transferTransactionId] = true; tokenInfo.Supply = tokenInfo.Supply.Add(amount); Assert(tokenInfo.Supply <= tokenInfo.TotalSupply, "Total supply exceeded"); - State.TokenInfos[symbol] = tokenInfo; + SetTokenInfo(tokenInfo); ModifyBalance(receivingAddress, symbol, amount); Context.Fire(new CrossChainReceived @@ -597,7 +602,7 @@ public override Empty ModifyTokenIssuerAndOwner(ModifyTokenIssuerAndOwnerInput i Assert(input.Issuer != null && !input.Issuer.Value.IsNullOrEmpty(), "Invalid input issuer."); Assert(input.Owner != null && !input.Owner.Value.IsNullOrEmpty(), "Invalid input owner."); - var tokenInfo = State.TokenInfos[input.Symbol]; + var tokenInfo = GetTokenInfo(input.Symbol); Assert(tokenInfo != null, "Token is not found."); Assert(tokenInfo.Issuer == Context.Sender, "Only token issuer can set token issuer and owner."); @@ -626,4 +631,90 @@ public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) Value = !State.TokenIssuerAndOwnerModificationDisabled.Value }; } + + /// + /// For example: + /// Symbol: SGR-1, Alias: SGR + /// Symbol: ABC-233, Alias: ABC + /// + /// + /// + public override Empty SetSymbolAlias(SetSymbolAliasInput input) + { + // Alias setting can only work for NFT Item for now. + + // Can only happen on Main Chain. + Assert(Context.ChainId == ChainHelper.ConvertBase58ToChainId("AELF"), + "Symbol alias setting only works on MainChain."); + + var collectionSymbol = GetNftCollectionSymbol(input.Symbol); + + // Current Rule: Alias must be the collection symbol. + Assert(input.Alias == collectionSymbol); + + var collectionTokenInfo = GetTokenInfo(collectionSymbol); + if (collectionTokenInfo == null) + { + throw new AssertionException($"NFT Collection {collectionSymbol} not found."); + } + + Assert(collectionTokenInfo.Owner == Context.Sender, "No permission."); + + collectionTokenInfo.ExternalInfo.Value[TokenContractConstants.TokenAliasExternalInfoKey] + = JsonSerializer.Serialize(new Dictionary + { + { input.Symbol, input.Alias } + }); + + SetTokenInfo(collectionTokenInfo); + + State.SymbolAliasMap[input.Alias] = input.Symbol; + + Context.LogDebug(() => $"Token alias added: {input.Symbol} -> {input.Alias}"); + + Context.Fire(new SymbolAliasAdded + { + Symbol = input.Symbol, + Alias = input.Alias + }); + + return new Empty(); + } + + private void SyncSymbolAliasFromTokenInfo(TokenInfo newTokenInfo) + { + var maybePreviousTokenInfo = State.TokenInfos[newTokenInfo.Symbol].Clone(); + + if (maybePreviousTokenInfo != null && IsAliasSettingExists(maybePreviousTokenInfo)) + { + var (previousSymbol, _) = ExtractAliasSetting(maybePreviousTokenInfo); + State.SymbolAliasMap.Remove(previousSymbol); + } + + if (IsAliasSettingExists(newTokenInfo)) + { + var (newSymbol, newAlias) = ExtractAliasSetting(newTokenInfo); + State.SymbolAliasMap[newSymbol] = newAlias; + + Context.Fire(new SymbolAliasAdded + { + Symbol = newSymbol, + Alias = newAlias + }); + } + } + + private bool IsAliasSettingExists(TokenInfo tokenInfo) + { + return tokenInfo.ExternalInfo != null && + tokenInfo.ExternalInfo.Value.Count > 0 && + tokenInfo.ExternalInfo.Value.ContainsKey(TokenContractConstants.TokenAliasExternalInfoKey); + } + + private KeyValuePair ExtractAliasSetting(TokenInfo tokenInfo) + { + var tokenAliasSetting = tokenInfo.ExternalInfo.Value[TokenContractConstants.TokenAliasExternalInfoKey]; + var aliasSetting = JsonSerializer.Deserialize>(tokenAliasSetting); + return aliasSetting.First(); + } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs index 25f853ec11..eb1f0bdb6a 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Fees.cs @@ -620,7 +620,6 @@ public override Empty SetSymbolsToPayTxSizeFee(SymbolListToPayTxSizeFee input) var isPrimaryTokenExist = false; var symbolList = new List(); var primaryTokenSymbol = GetPrimaryTokenSymbol(new Empty()); - var primaryTokenInfo = State.TokenInfos[primaryTokenSymbol.Value]; Assert(!string.IsNullOrEmpty(primaryTokenSymbol.Value), "primary token does not exist"); foreach (var tokenWeightInfo in input.SymbolsToPayTxSizeFee) { @@ -1153,7 +1152,7 @@ private void TransferTransactionFeesToFeeReceiver(string symbol, long totalAmoun if (totalAmount <= 0) return; - var tokenInfo = State.TokenInfos[symbol]; + var tokenInfo = GetTokenInfo(symbol); if (!tokenInfo.IsBurnable) { return; @@ -1268,7 +1267,7 @@ public override Empty ConfigTransactionFeeFreeAllowances(ConfigTransactionFeeFre private void ValidateToken(string symbol) { Assert(!string.IsNullOrWhiteSpace(symbol), "Invalid input symbol"); - Assert(State.TokenInfos[symbol] != null, $"Symbol {symbol} not exist"); + Assert(GetTokenInfo(symbol) != null, $"Symbol {symbol} not exist"); } public override Empty RemoveTransactionFeeFreeAllowancesConfig(RemoveTransactionFeeFreeAllowancesConfigInput input) @@ -1411,7 +1410,7 @@ private bool IsDelegationEnough(string txSymbol, string baseSymbol, long cost, private void AssertSymbolToPayTxFeeIsValid(string tokenSymbol, out long totalSupply) { - var tokenInfo = State.TokenInfos[tokenSymbol]; + var tokenInfo = GetTokenInfo(tokenSymbol); if (tokenInfo == null) { throw new AssertionException($"Token is not found. {tokenSymbol}"); diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index ed210282d0..e54d899bb6 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -33,7 +33,7 @@ private bool IsValidCreateSymbolChar(char character) private TokenInfo AssertValidToken(string symbol, long amount) { AssertValidSymbolAndAmount(symbol, amount); - var tokenInfo = State.TokenInfos[symbol]; + var tokenInfo = GetTokenInfo(symbol); Assert(tokenInfo != null && !string.IsNullOrEmpty(tokenInfo.Symbol), $"Token is not found. {symbol}"); return tokenInfo; } @@ -183,7 +183,6 @@ private void AssertCrossChainTransaction(Transaction originalTransaction, Addres private void RegisterTokenInfo(TokenInfo tokenInfo) { - CheckTokenExists(tokenInfo.Symbol); Assert(!string.IsNullOrEmpty(tokenInfo.Symbol) && tokenInfo.Symbol.All(IsValidSymbolChar), "Invalid symbol."); Assert(!string.IsNullOrEmpty(tokenInfo.TokenName), "Token name can neither be null nor empty."); @@ -225,7 +224,7 @@ private AuthorityInfo GetCrossChainTokenContractRegistrationController() private int GetIssueChainId(string symbol) { - var tokenInfo = State.TokenInfos[symbol]; + var tokenInfo = GetTokenInfo(symbol); return tokenInfo.IssueChainId; } @@ -255,7 +254,7 @@ private void CheckTokenAndCollectionExists(string symbol) private void CheckTokenExists(string symbol) { var empty = new TokenInfo(); - var existing = State.TokenInfos[symbol]; + var existing = GetTokenInfo(symbol); Assert(existing == null || existing.Equals(empty), "Token already exists."); } @@ -278,7 +277,7 @@ private void CheckCrossChainTokenContractRegistrationControllerAuthority() private void DealWithExternalInfoDuringLocking(TransferFromInput input) { - var tokenInfo = State.TokenInfos[input.Symbol]; + var tokenInfo = GetTokenInfo(input.Symbol); if (tokenInfo.ExternalInfo == null) return; if (tokenInfo.ExternalInfo.Value.ContainsKey(TokenContractConstants.LockCallbackExternalInfoKey)) { @@ -293,7 +292,7 @@ private void DealWithExternalInfoDuringLocking(TransferFromInput input) private void DealWithExternalInfoDuringTransfer(TransferFromInput input) { - var tokenInfo = State.TokenInfos[input.Symbol]; + var tokenInfo = GetTokenInfo(input.Symbol); if (tokenInfo.ExternalInfo == null) return; if (tokenInfo.ExternalInfo.Value.ContainsKey(TokenContractConstants.TransferCallbackExternalInfoKey)) { @@ -308,7 +307,7 @@ private void DealWithExternalInfoDuringTransfer(TransferFromInput input) private void DealWithExternalInfoDuringUnlock(TransferFromInput input) { - var tokenInfo = State.TokenInfos[input.Symbol]; + var tokenInfo = GetTokenInfo(input.Symbol); if (tokenInfo.ExternalInfo == null) return; if (tokenInfo.ExternalInfo.Value.ContainsKey(TokenContractConstants.UnlockCallbackExternalInfoKey)) { @@ -358,4 +357,23 @@ private Address GetVoteContractAddress() return State.VoteContractAddress.Value; } + + private TokenInfo GetTokenInfo(string symbolOrAlias) + { + var tokenInfo = State.TokenInfos[symbolOrAlias]; + if (tokenInfo != null) return tokenInfo; + var actualTokenSymbol = State.SymbolAliasMap[symbolOrAlias]; + if (!string.IsNullOrEmpty(actualTokenSymbol)) + { + tokenInfo = State.TokenInfos[actualTokenSymbol]; + } + + return tokenInfo; + } + + private void SetTokenInfo(TokenInfo tokenInfo) + { + var symbol = tokenInfo.Symbol; + State.TokenInfos[symbol] = tokenInfo; + } } \ No newline at end of file diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index 1926727e68..86ed8d3a1c 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -47,7 +47,7 @@ private void CheckSymbolSeed(string ownedSymbol) { var oldSymbolSeed = State.SymbolSeedMap[ownedSymbol]; - Assert(oldSymbolSeed == null || !State.TokenInfos[oldSymbolSeed].ExternalInfo.Value + Assert(oldSymbolSeed == null || !GetTokenInfo(oldSymbolSeed).ExternalInfo.Value .TryGetValue(TokenContractConstants.SeedExpireTimeExternalInfoKey, out var oldSymbolSeedExpireTime) || !long.TryParse(oldSymbolSeedExpireTime, out var symbolSeedExpireTime) @@ -99,7 +99,7 @@ private TokenInfo AssertNftCollectionExist(string symbol) { var collectionSymbol = GetNftCollectionSymbol(symbol); if (collectionSymbol == null) return null; - var collectionInfo = State.TokenInfos[collectionSymbol]; + var collectionInfo = GetTokenInfo(collectionSymbol); Assert(collectionInfo != null, "NFT collection not exist"); return collectionInfo; } diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs index 8366261ebf..5f42c7b4c6 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs @@ -10,12 +10,12 @@ public partial class TokenContract [View] public override TokenInfo GetTokenInfo(GetTokenInfoInput input) { - return State.TokenInfos[input.Symbol]; + return GetTokenInfo(input.Symbol); } public override TokenInfo GetNativeTokenInfo(Empty input) { - return State.TokenInfos[State.NativeTokenSymbol.Value]; + return GetTokenInfo(State.NativeTokenSymbol.Value); } public override TokenInfoList GetResourceTokenInfo(Empty input) @@ -23,13 +23,13 @@ public override TokenInfoList GetResourceTokenInfo(Empty input) var tokenInfoList = new TokenInfoList(); foreach (var symbol in Context.Variables.GetStringArray(TokenContractConstants.PayTxFeeSymbolListName) .Where(symbol => - State.TokenInfos[symbol] != null)) - tokenInfoList.Value.Add(State.TokenInfos[symbol]); + GetTokenInfo(symbol) != null)) + tokenInfoList.Value.Add(GetTokenInfo(symbol)); foreach (var symbol in Context.Variables.GetStringArray(TokenContractConstants.PayRentalSymbolListName) .Where(symbol => - State.TokenInfos[symbol] != null)) - tokenInfoList.Value.Add(State.TokenInfos[symbol]); + GetTokenInfo(symbol) != null)) + tokenInfoList.Value.Add(GetTokenInfo(symbol)); return tokenInfoList; } @@ -216,7 +216,7 @@ public override StringList GetReservedExternalInfoKeyList(Empty input) private bool IsTokenAvailableForMethodFee(string symbol) { - var tokenInfo = State.TokenInfos[symbol]; + var tokenInfo = GetTokenInfo(symbol); if (tokenInfo == null) throw new AssertionException("Token is not found."); return tokenInfo.IsBurnable; } diff --git a/protobuf/token_contract.proto b/protobuf/token_contract.proto index d858bf5f1d..1b84b6b5f3 100644 --- a/protobuf/token_contract.proto +++ b/protobuf/token_contract.proto @@ -141,6 +141,9 @@ service TokenContract { rpc RemoveTransactionFeeDelegatee (RemoveTransactionFeeDelegateeInput) returns (google.protobuf.Empty){ } + rpc SetSymbolAlias (SetSymbolAliasInput) returns (google.protobuf.Empty){ + } + // Get all delegatees' address of delegator from input rpc GetTransactionFeeDelegatees (GetTransactionFeeDelegateesInput) returns (GetTransactionFeeDelegateesOutput) { option (aelf.is_view) = true; @@ -683,6 +686,11 @@ message GetTransactionFeeDelegateesOutput { repeated aelf.Address delegatee_addresses = 1; } +message SetSymbolAliasInput { + string symbol = 1; + string alias = 2; +} + // Events message Transferred { @@ -852,4 +860,10 @@ message TransactionFeeDelegationCancelled { aelf.Address delegator = 1 [(aelf.is_indexed) = true]; aelf.Address delegatee = 2 [(aelf.is_indexed) = true]; aelf.Address caller = 3 [(aelf.is_indexed) = true]; +} + +message SymbolAliasAdded { + option (aelf.is_event) = true; + string symbol = 1 [(aelf.is_indexed) = true]; + string alias = 2 [(aelf.is_indexed) = true]; } \ No newline at end of file diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenAliasTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenAliasTests.cs new file mode 100644 index 0000000000..f758efec70 --- /dev/null +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenAliasTests.cs @@ -0,0 +1,284 @@ +using System.Threading.Tasks; +using AElf.Types; +using Google.Protobuf.WellKnownTypes; +using Shouldly; +using Xunit; + +namespace AElf.Contracts.MultiToken; + +public partial class MultiTokenContractTests +{ + public const string TokenAliasExternalInfoKey = "aelf_token_alias"; + + [Fact] + public async Task SetTokenAlias_NFTCollection_Test() + { + var symbols = await CreateNftCollectionAndNft(); + await TokenContractStub.SetSymbolAlias.SendAsync(new SetSymbolAliasInput + { + Symbol = symbols[1], + Alias = "TP" + }); + + { + // Check TokenInfo of NFT Collection. + var tokenInfo = await TokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput + { + Symbol = symbols[0] + }); + tokenInfo.ExternalInfo.Value.ContainsKey(TokenAliasExternalInfoKey); + tokenInfo.ExternalInfo.Value[TokenAliasExternalInfoKey].ShouldBe("{\"TP-31175\":\"TP\"}"); + } + + { + // Check TokenInfo of NFT Item. + var tokenInfo = await TokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput + { + Symbol = "TP" + }); + tokenInfo.Symbol.ShouldBe(symbols[1]); + } + + { + // Check alias. + var alias = await TokenContractStub.GetTokenAlias.CallAsync(new StringValue { Value = "TP-31175" }); + alias.Value.ShouldBe("TP"); + } + + { + var alias = await TokenContractStub.GetSymbolByAlias.CallAsync(new StringValue { Value = "TP" }); + alias.Value.ShouldBe("TP-31175"); + } + } + + [Fact] + public async Task SetTokenAlias_NFTCollection_CollectionSymbol_Test() + { + await CreateNftCollectionAndNft(); + await TokenContractStub.SetSymbolAlias.SendAsync(new SetSymbolAliasInput + { + Symbol = "TP-0", + Alias = "TP" + }); + + { + // Check TokenInfo of NFT Collection. + var tokenInfo = await TokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput + { + Symbol = "TP-0" + }); + tokenInfo.ExternalInfo.Value.ContainsKey(TokenAliasExternalInfoKey); + tokenInfo.ExternalInfo.Value[TokenAliasExternalInfoKey].ShouldBe("{\"TP-0\":\"TP\"}"); + } + + { + // Check TokenInfo of NFT Item. + var tokenInfo = await TokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput + { + Symbol = "TP" + }); + tokenInfo.Symbol.ShouldBe("TP-0"); + } + + { + // Check alias. + var alias = await TokenContractStub.GetTokenAlias.CallAsync(new StringValue { Value = "TP-0" }); + alias.Value.ShouldBe("TP"); + } + + { + var alias = await TokenContractStub.GetSymbolByAlias.CallAsync(new StringValue { Value = "TP" }); + alias.Value.ShouldBe("TP-0"); + } + } + + [Fact] + public async Task SetTokenAlias_FT_Test() + { + await CreateNormalTokenAsync(); + + // Set token alias for FT. + var result = await TokenContractStub.SetSymbolAlias.SendWithExceptionAsync(new SetSymbolAliasInput + { + Symbol = AliceCoinTokenInfo.Symbol, + }); + result.TransactionResult.Error.ShouldContain("Token alias can only be set for NFT Item."); + } + + [Fact] + public async Task CreateTokenWithAlias_Test() + { + var createCollectionResult = await CreateNftCollectionAsync(NftCollection1155WithAliasInfo); + createCollectionResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); + + await CreateNftAsync(NftCollection1155WithAliasInfo.Symbol, Nft721Info); + + { + // Check alias. + var alias = await TokenContractStub.GetTokenAlias.CallAsync(new StringValue { Value = "TP-31175" }); + alias.Value.ShouldBe("TP"); + } + + { + // Check TokenInfo of NFT Item. + var tokenInfo = await TokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput + { + Symbol = "TP" + }); + tokenInfo.Symbol.ShouldBe("TP-31175"); + } + } + + [Fact] + public async Task CreateTokenWithAlias_FT_Test() + { + var createInput = new CreateInput + { + Symbol = AliceCoinTokenInfo.Symbol, + TokenName = AliceCoinTokenInfo.TokenName, + TotalSupply = AliceCoinTokenInfo.TotalSupply, + Decimals = AliceCoinTokenInfo.Decimals, + Issuer = AliceCoinTokenInfo.Issuer, + Owner = AliceCoinTokenInfo.Issuer, + IsBurnable = AliceCoinTokenInfo.IsBurnable, + LockWhiteList = + { + BasicFunctionContractAddress, + OtherBasicFunctionContractAddress, + TokenConverterContractAddress, + TreasuryContractAddress + }, + ExternalInfo = new ExternalInfo + { + Value = + { + { TokenAliasExternalInfoKey, "{\"ALICE-111\":\"ALICE\"}" } + } + } + }; + await CreateSeedNftAsync(TokenContractStub, createInput); + var result = await TokenContractStub.Create.SendWithExceptionAsync(createInput); + result.TransactionResult.Error.ShouldContain("Token alias can only be set for NFT Item."); + } + + [Fact] + public async Task TransferViaAlias_Test() + { + await CreateTokenWithAlias_Test(); + + await TokenContractStub.Issue.SendAsync(new IssueInput + { + Symbol = "TP-31175", + Amount = 1, + To = DefaultAddress + }); + + { + var balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = DefaultAddress, + Symbol = "TP" + }); + balance.Balance.ShouldBe(1); + } + + await TokenContractStub.Transfer.SendAsync(new TransferInput + { + // Transfer via alias. + Symbol = "TP", + Amount = 1, + To = User1Address + }); + + { + var balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = User1Address, + Symbol = "TP" + }); + balance.Balance.ShouldBe(1); + } + } + + [Fact] + public async Task ApproveAndTransferFromViaAlias_Test() + { + await CreateTokenWithAlias_Test(); + + await TokenContractStub.Issue.SendAsync(new IssueInput + { + Symbol = "TP-31175", + Amount = 1, + To = DefaultAddress + }); + + await TokenContractStub.Approve.SendAsync(new ApproveInput + { + Symbol = "TP", + Amount = 1, + Spender = User1Address + }); + + await TokenContractStubUser.TransferFrom.SendAsync(new TransferFromInput + { + Symbol = "TP", + Amount = 1, + From = DefaultAddress, + To = User2Address, + }); + + { + var balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = User2Address, + Symbol = "TP" + }); + balance.Balance.ShouldBe(1); + } + } + + [Fact] + public async Task GetBalanceOfNotExistToken_Test() + { + var balance = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput + { + Owner = User2Address, + Symbol = "TP" + }); + balance.Balance.ShouldBe(0); + } + + [Fact] + public async Task GetAllowanceOfNotExistToken_Test() + { + var allowance = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = User2Address, + Symbol = "TP", + Spender = DefaultAddress + }); + allowance.Allowance.ShouldBe(0); + } + + private TokenInfo NftCollection1155WithAliasInfo => new() + { + Symbol = "TP-", + TokenName = "Trump Digital Trading Cards #1155", + TotalSupply = TotalSupply, + Decimals = 0, + Issuer = DefaultAddress, + IssueChainId = _chainId, + ExternalInfo = new ExternalInfo + { + Value = + { + { + NftCollectionMetaFields.ImageUrlKey, + "https://i.seadn.io/gcs/files/0f5cdfaaf687de2ebb5834b129a5bef3.png?auto=format&w=3840" + }, + { NftCollectionMetaFields.NftType, NftType }, + { TokenAliasExternalInfoKey, "{\"TP-31175\":\"TP\"}" } + } + } + }; +} \ No newline at end of file diff --git a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTest.cs b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTest.cs index 4c76a04b09..03ed5f1335 100644 --- a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTest.cs +++ b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Threading.Tasks; using AElf.Contracts.Parliament; using AElf.ContractTestBase.ContractTestKit; @@ -11,6 +12,7 @@ using AElf.Standards.ACS7; using AElf.Types; using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; using Shouldly; using Xunit; @@ -19,11 +21,13 @@ namespace AElf.Contracts.MultiToken; public class MultiTokenContractCrossChainTest : MultiTokenContractCrossChainTestBase { private const string SymbolForTesting = "ELFTEST"; + private const string NFTSymbolForTesting = "ELFNFT"; private const string NativeToken = "ELF"; private static readonly long _totalSupply = 1000L; private readonly Hash _fakeBlockHeader = HashHelper.ComputeFrom("fakeBlockHeader"); private readonly int _parentChainHeightOfCreation = 5; private readonly string sideChainSymbol = "STA"; + public const string TokenAliasExternalInfoKey = "aelf_token_alias"; #region register test @@ -387,6 +391,146 @@ public async Task CrossChainCreateToken_With_Invalid_Verification_Test() Assert.True(result.Status == TransactionResultStatus.Failed); Assert.Contains("Invalid transaction", result.Error); } + + [Fact] + public async Task SideChain_CrossChainSideChainCreateToken_WithAlias_Test() + { + await GenerateSideChainAsync(); + await RegisterSideChainContractAddressOnMainChainAsync(); + + // Main chain create token + await BootMinerChangeRoundAsync(AEDPoSContractStub, true); + var createTransaction = await CreateTransactionForNFTCreation(TokenContractStub, + DefaultAccount.Address, $"{NFTSymbolForTesting}-0", TokenContractAddress); + var blockExecutedSet = await MineAsync(new List { createTransaction }); + var createResult = blockExecutedSet.TransactionResultMap[createTransaction.GetHash()]; + Assert.True(createResult.Status == TransactionResultStatus.Mined, createResult.Error); + + var createdTokenInfo = await TokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput + { + Symbol = $"{NFTSymbolForTesting}-0" + }); + var tokenValidationTransaction = CreateTokenInfoValidationTransaction(createdTokenInfo, + TokenContractStub); + + blockExecutedSet = await MineAsync(new List { tokenValidationTransaction }); + var merklePath = GetTransactionMerklePathAndRoot(tokenValidationTransaction, out var blockRoot); + await IndexMainChainTransactionAsync(blockExecutedSet.Height, blockRoot, blockRoot); + var crossChainCreateTokenInput = new CrossChainCreateTokenInput + { + FromChainId = MainChainId, + ParentChainHeight = blockExecutedSet.Height, + TransactionBytes = tokenValidationTransaction.ToByteString(), + MerklePath = merklePath + }; + // Side chain cross chain create + var executionResult = + await SideChainTokenContractStub.CrossChainCreateToken.SendAsync(crossChainCreateTokenInput); + executionResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined, + executionResult.TransactionResult.Error); + + var newTokenInfo = await SideChainTokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput + { + Symbol = $"{NFTSymbolForTesting}-0" + }); + newTokenInfo.TotalSupply.ShouldBe(_totalSupply); + + var alias = await SideChainTokenContractStub.GetTokenAlias.CallAsync(new StringValue + { + Value = $"{NFTSymbolForTesting}-{1}" + }); + alias.Value.ShouldBe(NFTSymbolForTesting); + } + + [Fact] + public async Task SideChain_CrossChainSideChainCreateToken_SetAliasAndSyncAgain_Test() + { + await GenerateSideChainAsync(); + await RegisterSideChainContractAddressOnMainChainAsync(); + + // Main chain create token + await BootMinerChangeRoundAsync(AEDPoSContractStub, true); + var createTransaction = await CreateTransactionForNFTCreation(TokenContractStub, + DefaultAccount.Address, $"{NFTSymbolForTesting}-0", TokenContractAddress, false); + var blockExecutedSet = await MineAsync(new List { createTransaction }); + var createResult = blockExecutedSet.TransactionResultMap[createTransaction.GetHash()]; + Assert.True(createResult.Status == TransactionResultStatus.Mined, createResult.Error); + + // Sync for the first time + { + var createdTokenInfo = await TokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput + { + Symbol = $"{NFTSymbolForTesting}-0" + }); + var tokenValidationTransaction = CreateTokenInfoValidationTransaction(createdTokenInfo, + TokenContractStub); + + blockExecutedSet = await MineAsync(new List { tokenValidationTransaction }); + var merklePath = GetTransactionMerklePathAndRoot(tokenValidationTransaction, out var blockRoot); + await IndexMainChainTransactionAsync(blockExecutedSet.Height, blockRoot, blockRoot); + var crossChainCreateTokenInput = new CrossChainCreateTokenInput + { + FromChainId = MainChainId, + ParentChainHeight = blockExecutedSet.Height, + TransactionBytes = tokenValidationTransaction.ToByteString(), + MerklePath = merklePath + }; + // Side chain cross chain create + var executionResult = + await SideChainTokenContractStub.CrossChainCreateToken.SendAsync(crossChainCreateTokenInput); + executionResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined, + executionResult.TransactionResult.Error); + } + + // Set Alias + { + var setAliasTransaction = await TokenContractStub.SetSymbolAlias.SendAsync(new SetSymbolAliasInput + { + Symbol = $"{NFTSymbolForTesting}-1", + Alias = NFTSymbolForTesting + }); + var setAliasResult = setAliasTransaction.TransactionResult; + setAliasResult.Status.ShouldBe(TransactionResultStatus.Mined); + } + + // Sync for the second time + { + var createdTokenInfo = await TokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput + { + Symbol = $"{NFTSymbolForTesting}-0" + }); + var tokenValidationTransaction = CreateTokenInfoValidationTransaction(createdTokenInfo, + TokenContractStub); + + blockExecutedSet = await MineAsync(new List { tokenValidationTransaction }); + var merklePath = GetTransactionMerklePathAndRoot(tokenValidationTransaction, out var blockRoot); + await IndexMainChainTransactionAsync(blockExecutedSet.Height, blockRoot, blockRoot); + var crossChainCreateTokenInput = new CrossChainCreateTokenInput + { + FromChainId = MainChainId, + ParentChainHeight = blockExecutedSet.Height, + TransactionBytes = tokenValidationTransaction.ToByteString(), + MerklePath = merklePath + }; + // Side chain cross chain create + var executionResult = + await SideChainTokenContractStub.CrossChainCreateToken.SendAsync(crossChainCreateTokenInput); + executionResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined, + executionResult.TransactionResult.Error); + } + + var newTokenInfo = await SideChainTokenContractStub.GetTokenInfo.CallAsync(new GetTokenInfoInput + { + Symbol = $"{NFTSymbolForTesting}-0" + }); + newTokenInfo.TotalSupply.ShouldBe(_totalSupply); + + var alias = await SideChainTokenContractStub.GetTokenAlias.CallAsync(new StringValue + { + Value = $"{NFTSymbolForTesting}-1" + }); + alias.Value.ShouldBe(NFTSymbolForTesting); + } #endregion @@ -891,12 +1035,48 @@ private async Task CreateTransactionForTokenCreation( await CreateSeedNftAsync(tokenContractImplStub, input, lockWhiteAddress); return tokenContractImplStub.Create.GetTransaction(input); } + + private async Task CreateTransactionForNFTCreation( + TokenContractImplContainer.TokenContractImplStub tokenContractImplStub, + Address issuer, string symbol, Address lockWhiteAddress, bool withAlias = true) + { + await CreateSeedNftCollection(tokenContractImplStub, issuer); + var tokenInfo = GetTokenInfo(symbol, issuer); + var input = new CreateInput + { + Symbol = tokenInfo.Symbol, + Decimals = 0, + Issuer = tokenInfo.Issuer, + Owner = tokenInfo.Issuer, + IsBurnable = tokenInfo.IsBurnable, + TokenName = tokenInfo.TokenName, + TotalSupply = tokenInfo.TotalSupply, + }; + if (withAlias) + { + input.ExternalInfo = new ExternalInfo + { + Value = + { + { + TokenAliasExternalInfoKey, JsonSerializer.Serialize(new Dictionary + { + { $"{NFTSymbolForTesting}-{1}", NFTSymbolForTesting } + }) + } + } + }; + } + + await CreateSeedNftAsync(tokenContractImplStub, input, lockWhiteAddress); + return tokenContractImplStub.Create.GetTransaction(input); + } private Transaction CreateTokenInfoValidationTransaction(TokenInfo createdTokenInfo, TokenContractImplContainer.TokenContractImplStub tokenContractImplStub) { - return tokenContractImplStub.ValidateTokenInfoExists.GetTransaction(new ValidateTokenInfoExistsInput + var input = new ValidateTokenInfoExistsInput { TokenName = createdTokenInfo.TokenName, Symbol = createdTokenInfo.Symbol, @@ -906,7 +1086,9 @@ private Transaction CreateTokenInfoValidationTransaction(TokenInfo createdTokenI IsBurnable = createdTokenInfo.IsBurnable, TotalSupply = createdTokenInfo.TotalSupply, IssueChainId = createdTokenInfo.IssueChainId - }); + }; + input.ExternalInfo.Add(createdTokenInfo.ExternalInfo.Value); + return tokenContractImplStub.ValidateTokenInfoExists.GetTransaction(input); } private TokenInfo GetTokenInfo(string symbol, Address issuer, bool isBurnable = true) From d45856b8b9e2ee9339d945baf2e6dfbe97b22f1e Mon Sep 17 00:00:00 2001 From: eanzhao Date: Tue, 30 Apr 2024 18:50:43 +0800 Subject: [PATCH 077/128] Add SymbolAliasDeleted event and try not use System.Text.Json and Regex --- .../TokenContract_Actions.cs | 21 ++++++++++++------- protobuf/token_contract.proto | 6 ++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index da59304f94..5d2f7268f6 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text.Json; using AElf.CSharp.Core; using AElf.Sdk.CSharp; using AElf.Standards.ACS0; @@ -661,10 +660,7 @@ public override Empty SetSymbolAlias(SetSymbolAliasInput input) Assert(collectionTokenInfo.Owner == Context.Sender, "No permission."); collectionTokenInfo.ExternalInfo.Value[TokenContractConstants.TokenAliasExternalInfoKey] - = JsonSerializer.Serialize(new Dictionary - { - { input.Symbol, input.Alias } - }); + = $"{{\"{input.Symbol}\":\"{input.Alias}\"}}"; SetTokenInfo(collectionTokenInfo); @@ -687,8 +683,14 @@ private void SyncSymbolAliasFromTokenInfo(TokenInfo newTokenInfo) if (maybePreviousTokenInfo != null && IsAliasSettingExists(maybePreviousTokenInfo)) { - var (previousSymbol, _) = ExtractAliasSetting(maybePreviousTokenInfo); + var (previousSymbol, previousAlias) = ExtractAliasSetting(maybePreviousTokenInfo); State.SymbolAliasMap.Remove(previousSymbol); + + Context.Fire(new SymbolAliasDeleted + { + Symbol = previousSymbol, + Alias = previousAlias + }); } if (IsAliasSettingExists(newTokenInfo)) @@ -714,7 +716,10 @@ private bool IsAliasSettingExists(TokenInfo tokenInfo) private KeyValuePair ExtractAliasSetting(TokenInfo tokenInfo) { var tokenAliasSetting = tokenInfo.ExternalInfo.Value[TokenContractConstants.TokenAliasExternalInfoKey]; - var aliasSetting = JsonSerializer.Deserialize>(tokenAliasSetting); - return aliasSetting.First(); + tokenAliasSetting = tokenAliasSetting.Trim('{', '}'); + var parts = tokenAliasSetting.Split(':'); + var key = parts[0].Trim().Trim('\"'); + var value = parts[1].Trim().Trim('\"'); + return new KeyValuePair(key, value); } } \ No newline at end of file diff --git a/protobuf/token_contract.proto b/protobuf/token_contract.proto index 1b84b6b5f3..9adc6ed3fd 100644 --- a/protobuf/token_contract.proto +++ b/protobuf/token_contract.proto @@ -866,4 +866,10 @@ message SymbolAliasAdded { option (aelf.is_event) = true; string symbol = 1 [(aelf.is_indexed) = true]; string alias = 2 [(aelf.is_indexed) = true]; +} + +message SymbolAliasDeleted { + option (aelf.is_event) = true; + string symbol = 1 [(aelf.is_indexed) = true]; + string alias = 2 [(aelf.is_indexed) = true]; } \ No newline at end of file From d06e2294c9aa3ef5570b84656ecf5caff35d2b50 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Tue, 7 May 2024 10:41:17 +0800 Subject: [PATCH 078/128] Detect token alias setting during token creation. --- .../TokenContract_Actions.cs | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 5d2f7268f6..ac4a476448 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -76,6 +76,12 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. ExternalInfo = input.ExternalInfo ?? new ExternalInfo(), Owner = input.Owner }; + + if (symbolType is SymbolType.Token or SymbolType.NftCollection) + { + MaybeSetTokenAlias(tokenInfo); + } + CheckTokenExists(tokenInfo.Symbol); RegisterTokenInfo(tokenInfo); if (string.IsNullOrEmpty(State.NativeTokenSymbol.Value)) @@ -693,17 +699,7 @@ private void SyncSymbolAliasFromTokenInfo(TokenInfo newTokenInfo) }); } - if (IsAliasSettingExists(newTokenInfo)) - { - var (newSymbol, newAlias) = ExtractAliasSetting(newTokenInfo); - State.SymbolAliasMap[newSymbol] = newAlias; - - Context.Fire(new SymbolAliasAdded - { - Symbol = newSymbol, - Alias = newAlias - }); - } + MaybeSetTokenAlias(newTokenInfo); } private bool IsAliasSettingExists(TokenInfo tokenInfo) @@ -722,4 +718,19 @@ private KeyValuePair ExtractAliasSetting(TokenInfo tokenInfo) var value = parts[1].Trim().Trim('\"'); return new KeyValuePair(key, value); } + + private void MaybeSetTokenAlias(TokenInfo tokenInfo) + { + if (IsAliasSettingExists(tokenInfo)) + { + var (symbol, alias) = ExtractAliasSetting(tokenInfo); + State.SymbolAliasMap[symbol] = alias; + + Context.Fire(new SymbolAliasAdded + { + Symbol = symbol, + Alias = alias + }); + } + } } \ No newline at end of file From 851b650b3cfe902cdd1bdc1af5fad17c919ad786 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Tue, 7 May 2024 10:43:45 +0800 Subject: [PATCH 079/128] Now the token alias can only be set once. --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index ac4a476448..a456bfbe48 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -653,6 +653,9 @@ public override Empty SetSymbolAlias(SetSymbolAliasInput input) "Symbol alias setting only works on MainChain."); var collectionSymbol = GetNftCollectionSymbol(input.Symbol); + + // For now, token alias can only be set once. + Assert(State.SymbolAliasMap[input.Alias] == null, $"Token alias {input.Alias} already exists."); // Current Rule: Alias must be the collection symbol. Assert(input.Alias == collectionSymbol); From 2594eb27ed8d0a2f847f5628762a759774a94009 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Tue, 7 May 2024 16:22:41 +0800 Subject: [PATCH 080/128] Bug fix: validations on token creation --- .../TokenContract_Actions.cs | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index a456bfbe48..e2003eee67 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -77,9 +77,10 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. Owner = input.Owner }; - if (symbolType is SymbolType.Token or SymbolType.NftCollection) + if (IsAliasSettingExists(tokenInfo)) { - MaybeSetTokenAlias(tokenInfo); + Assert(symbolType == SymbolType.NftCollection, "Only NFT Collection can set alias."); + SetTokenAlias(tokenInfo); } CheckTokenExists(tokenInfo.Symbol); @@ -647,18 +648,18 @@ public override BoolValue GetTokenIssuerAndOwnerModificationEnabled(Empty input) public override Empty SetSymbolAlias(SetSymbolAliasInput input) { // Alias setting can only work for NFT Item for now. + // And the setting exists on the TokenInfo of the NFT Collection. // Can only happen on Main Chain. Assert(Context.ChainId == ChainHelper.ConvertBase58ToChainId("AELF"), "Symbol alias setting only works on MainChain."); var collectionSymbol = GetNftCollectionSymbol(input.Symbol); - + // For now, token alias can only be set once. Assert(State.SymbolAliasMap[input.Alias] == null, $"Token alias {input.Alias} already exists."); - // Current Rule: Alias must be the collection symbol. - Assert(input.Alias == collectionSymbol); + CheckTokenAlias(input.Alias, collectionSymbol); var collectionTokenInfo = GetTokenInfo(collectionSymbol); if (collectionTokenInfo == null) @@ -702,7 +703,10 @@ private void SyncSymbolAliasFromTokenInfo(TokenInfo newTokenInfo) }); } - MaybeSetTokenAlias(newTokenInfo); + if (IsAliasSettingExists(newTokenInfo)) + { + SetTokenAlias(newTokenInfo); + } } private bool IsAliasSettingExists(TokenInfo tokenInfo) @@ -722,18 +726,26 @@ private KeyValuePair ExtractAliasSetting(TokenInfo tokenInfo) return new KeyValuePair(key, value); } - private void MaybeSetTokenAlias(TokenInfo tokenInfo) + private void SetTokenAlias(TokenInfo tokenInfo) { - if (IsAliasSettingExists(tokenInfo)) + var (symbol, alias) = ExtractAliasSetting(tokenInfo); + State.SymbolAliasMap[symbol] = alias; + + CheckTokenAlias(alias, GetNftCollectionSymbol(tokenInfo.Symbol)); + + Context.Fire(new SymbolAliasAdded { - var (symbol, alias) = ExtractAliasSetting(tokenInfo); - State.SymbolAliasMap[symbol] = alias; + Symbol = symbol, + Alias = alias + }); + } - Context.Fire(new SymbolAliasAdded - { - Symbol = symbol, - Alias = alias - }); - } + private void CheckTokenAlias(string alias, string collectionSymbol) + { + // Current Rule: Alias must be the seed name. + var parts = collectionSymbol.Split(TokenContractConstants.NFTSymbolSeparator); + Assert(parts.Length == 2, $"Incorrect collection symbol: {collectionSymbol}."); + Assert(parts.Last() == TokenContractConstants.CollectionSymbolSuffix, "Incorrect collection symbol suffix."); + Assert(alias == parts.First(), $"Alias for an item of {collectionSymbol} cannot be {alias}."); } } \ No newline at end of file From 40cd8b9248e5882c2ba7070a02a2696425669307 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Tue, 7 May 2024 17:24:12 +0800 Subject: [PATCH 081/128] Fix CheckTokenAlias in case of collectionSymbol is null. --- .../AElf.Contracts.MultiToken/TokenContract_Actions.cs | 5 +++++ .../AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index e2003eee67..688934270f 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -742,6 +742,11 @@ private void SetTokenAlias(TokenInfo tokenInfo) private void CheckTokenAlias(string alias, string collectionSymbol) { + if (collectionSymbol == null) + { + throw new AssertionException("Token alias can only be set for NFT Item."); + } + // Current Rule: Alias must be the seed name. var parts = collectionSymbol.Split(TokenContractConstants.NFTSymbolSeparator); Assert(parts.Length == 2, $"Incorrect collection symbol: {collectionSymbol}."); diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index 86ed8d3a1c..d6854f7490 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -84,7 +84,12 @@ private void DoTransferFrom(Address from, Address to, Address spender, string sy State.Allowances[from][spender][symbol] = allowance.Sub(amount); } - + /// + /// ELF -> null + /// NFT-1 -> NFT-0 + /// + /// + /// Return null if inputSymbol is not NFT. private string GetNftCollectionSymbol(string inputSymbol) { var symbol = inputSymbol; From f98978a4697b576575614f8ebcffce2862fe1978 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Wed, 8 May 2024 10:01:36 +0800 Subject: [PATCH 082/128] Tune Transfer, TransferFrom, Approve, UnApprove methods to adopt token alias. --- .../TokenContract_Actions.cs | 40 +++++++++---------- .../TokenContract_Helper.cs | 5 ++- .../TokenContract_NFT_Actions.cs | 1 - .../TokenContract_Views.cs | 29 +++++++++++--- protobuf/token_contract.proto | 4 ++ 5 files changed, 51 insertions(+), 28 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 688934270f..bab35a1864 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -178,14 +178,14 @@ public override Empty Issue(IssueInput input) public override Empty Transfer(TransferInput input) { - AssertValidToken(input.Symbol, input.Amount); - DoTransfer(Context.Sender, input.To, input.Symbol, input.Amount, input.Memo); + var tokenInfo = AssertValidToken(input.Symbol, input.Amount); + DoTransfer(Context.Sender, input.To, tokenInfo.Symbol, input.Amount, input.Memo); DealWithExternalInfoDuringTransfer(new TransferFromInput { From = Context.Sender, To = input.To, Amount = input.Amount, - Symbol = input.Symbol, + Symbol = tokenInfo.Symbol, Memo = input.Memo }); return new Empty(); @@ -252,21 +252,21 @@ public override Empty Unlock(UnlockInput input) public override Empty TransferFrom(TransferFromInput input) { - AssertValidToken(input.Symbol, input.Amount); - DoTransferFrom(input.From, input.To, Context.Sender, input.Symbol, input.Amount, input.Memo); + var tokenInfo = AssertValidToken(input.Symbol, input.Amount); + DoTransferFrom(input.From, input.To, Context.Sender, tokenInfo.Symbol, input.Amount, input.Memo); return new Empty(); } public override Empty Approve(ApproveInput input) { AssertValidInputAddress(input.Spender); - AssertValidToken(input.Symbol, input.Amount); - State.Allowances[Context.Sender][input.Spender][input.Symbol] = input.Amount; + var tokenInfo = AssertValidToken(input.Symbol, input.Amount); + State.Allowances[Context.Sender][input.Spender][tokenInfo.Symbol] = input.Amount; Context.Fire(new Approved { Owner = Context.Sender, Spender = input.Spender, - Symbol = input.Symbol, + Symbol = tokenInfo.Symbol, Amount = input.Amount }); return new Empty(); @@ -275,15 +275,15 @@ public override Empty Approve(ApproveInput input) public override Empty UnApprove(UnApproveInput input) { AssertValidInputAddress(input.Spender); - AssertValidToken(input.Symbol, input.Amount); - var oldAllowance = State.Allowances[Context.Sender][input.Spender][input.Symbol]; + var tokenInfo = AssertValidToken(input.Symbol, input.Amount); + var oldAllowance = State.Allowances[Context.Sender][input.Spender][tokenInfo.Symbol]; var amountOrAll = Math.Min(input.Amount, oldAllowance); - State.Allowances[Context.Sender][input.Spender][input.Symbol] = oldAllowance.Sub(amountOrAll); + State.Allowances[Context.Sender][input.Spender][tokenInfo.Symbol] = oldAllowance.Sub(amountOrAll); Context.Fire(new UnApproved { Owner = Context.Sender, Spender = input.Spender, - Symbol = input.Symbol, + Symbol = tokenInfo.Symbol, Amount = amountOrAll }); return new Empty(); @@ -522,21 +522,21 @@ public override Empty RegisterCrossChainTokenContractAddress(RegisterCrossChainT /// public override Empty CrossChainTransfer(CrossChainTransferInput input) { - AssertValidToken(input.Symbol, input.Amount); + var tokenInfo = AssertValidToken(input.Symbol, input.Amount); AssertValidMemo(input.Memo); - var issueChainId = GetIssueChainId(input.Symbol); + var issueChainId = GetIssueChainId(tokenInfo.Symbol); Assert(issueChainId == input.IssueChainId, "Incorrect issue chain id."); var burnInput = new BurnInput { Amount = input.Amount, - Symbol = input.Symbol + Symbol = tokenInfo.Symbol }; Burn(burnInput); Context.Fire(new CrossChainTransferred { From = Context.Sender, To = input.To, - Symbol = input.Symbol, + Symbol = tokenInfo.Symbol, Amount = input.Amount, IssueChainId = input.IssueChainId, Memo = input.Memo, @@ -567,14 +567,14 @@ public override Empty CrossChainReceiveToken(CrossChainReceiveTokenInput input) var transferSender = transferTransaction.From; var tokenInfo = AssertValidToken(symbol, amount); - var issueChainId = GetIssueChainId(symbol); + var issueChainId = GetIssueChainId(tokenInfo.Symbol); Assert(issueChainId == crossChainTransferInput.IssueChainId, "Incorrect issue chain id."); Assert(targetChainId == Context.ChainId, "Unable to claim cross chain token."); var registeredTokenContractAddress = State.CrossChainTransferWhiteList[input.FromChainId]; AssertCrossChainTransaction(transferTransaction, registeredTokenContractAddress, nameof(CrossChainTransfer)); Context.LogDebug(() => - $"symbol == {symbol}, amount == {amount}, receivingAddress == {receivingAddress}, targetChainId == {targetChainId}"); + $"symbol == {tokenInfo.Symbol}, amount == {amount}, receivingAddress == {receivingAddress}, targetChainId == {targetChainId}"); CrossChainVerify(transferTransactionId, input.ParentChainHeight, input.FromChainId, input.MerklePath); @@ -582,13 +582,13 @@ public override Empty CrossChainReceiveToken(CrossChainReceiveTokenInput input) tokenInfo.Supply = tokenInfo.Supply.Add(amount); Assert(tokenInfo.Supply <= tokenInfo.TotalSupply, "Total supply exceeded"); SetTokenInfo(tokenInfo); - ModifyBalance(receivingAddress, symbol, amount); + ModifyBalance(receivingAddress, tokenInfo.Symbol, amount); Context.Fire(new CrossChainReceived { From = transferSender, To = receivingAddress, - Symbol = symbol, + Symbol = tokenInfo.Symbol, Amount = amount, Memo = crossChainTransferInput.Memo, FromChainId = input.FromChainId, diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index e54d899bb6..35e31eec47 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -126,9 +126,10 @@ private List GetSymbolListSortedByExpirationTime(TransactionFeeFreeAllow private long GetBalance(Address address, string symbol) { AssertValidInputAddress(address); - Assert(!string.IsNullOrWhiteSpace(symbol), "Invalid symbol."); + var tokenInfo = GetTokenInfo(symbol); + Assert(!string.IsNullOrWhiteSpace(tokenInfo.Symbol), "Invalid symbol."); - return State.Balances[address][symbol]; + return State.Balances[address][tokenInfo.Symbol]; } // private MethodFeeFreeAllowance GetFreeFeeAllowance(MethodFeeFreeAllowances freeAllowances, string symbol) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index d6854f7490..7c2349c4a3 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -55,7 +55,6 @@ private void CheckSymbolSeed(string ownedSymbol) "OwnedSymbol has been created"); } - private void DoTransferFrom(Address from, Address to, Address spender, string symbol, long amount, string memo) { AssertValidInputAddress(from); diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs index 5f42c7b4c6..9195d35b4c 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs @@ -37,23 +37,25 @@ public override TokenInfoList GetResourceTokenInfo(Empty input) [View] public override GetBalanceOutput GetBalance(GetBalanceInput input) { + var symbol = GetActualTokenSymbol(input.Symbol); return new GetBalanceOutput { - Symbol = input.Symbol, + Symbol = symbol, Owner = input.Owner, - Balance = GetBalance(input.Owner, input.Symbol) + Balance = GetBalance(input.Owner, symbol) }; } [View] public override GetAllowanceOutput GetAllowance(GetAllowanceInput input) { + var symbol = GetActualTokenSymbol(input.Symbol); return new GetAllowanceOutput { - Symbol = input.Symbol, + Symbol = symbol, Owner = input.Owner, Spender = input.Spender, - Allowance = State.Allowances[input.Owner][input.Spender][input.Symbol] + Allowance = State.Allowances[input.Owner][input.Spender][symbol] }; } @@ -199,7 +201,6 @@ public override BoolValue IsTokenAvailableForMethodFee(StringValue input) }; } - public override StringList GetReservedExternalInfoKeyList(Empty input) { return new StringList @@ -228,4 +229,22 @@ private bool IsAddressInCreateWhiteList(Address address) address == Context.GetContractAddressByName(SmartContractConstants.EconomicContractSystemName) || address == Context.GetContractAddressByName(SmartContractConstants.CrossChainContractSystemName); } + + public override StringValue GetTokenAlias(StringValue input) + { + return new StringValue + { + Value = GetActualTokenSymbol(input.Value) + }; + } + + private string GetActualTokenSymbol(string aliasOrSymbol) + { + if (State.TokenInfos[aliasOrSymbol] == null) + { + return State.SymbolAliasMap[aliasOrSymbol]; + } + + return aliasOrSymbol; + } } \ No newline at end of file diff --git a/protobuf/token_contract.proto b/protobuf/token_contract.proto index 9adc6ed3fd..c2453ff3c5 100644 --- a/protobuf/token_contract.proto +++ b/protobuf/token_contract.proto @@ -229,6 +229,10 @@ service TokenContract { rpc GetTransactionFeeDelegationsOfADelegatee(GetTransactionFeeDelegationsOfADelegateeInput) returns(TransactionFeeDelegations){ option (aelf.is_view) = true; } + + rpc GetTokenAlias (google.protobuf.StringValue) returns (google.protobuf.StringValue) { + option (aelf.is_view) = true; + } } message TokenInfo { From b0d521c71b2cae2a979bf671c3683123cd0b046a Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 9 May 2024 11:54:08 +0800 Subject: [PATCH 083/128] Fix SetTokenAlias --- .../AElf.Contracts.MultiToken/TokenContract_Actions.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index bab35a1864..a55edbf0ce 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -716,6 +716,11 @@ private bool IsAliasSettingExists(TokenInfo tokenInfo) tokenInfo.ExternalInfo.Value.ContainsKey(TokenContractConstants.TokenAliasExternalInfoKey); } + /// + /// Extract alias setting from ExternalInfo. + /// + /// + /// (Symbol, Alias) private KeyValuePair ExtractAliasSetting(TokenInfo tokenInfo) { var tokenAliasSetting = tokenInfo.ExternalInfo.Value[TokenContractConstants.TokenAliasExternalInfoKey]; @@ -729,9 +734,9 @@ private KeyValuePair ExtractAliasSetting(TokenInfo tokenInfo) private void SetTokenAlias(TokenInfo tokenInfo) { var (symbol, alias) = ExtractAliasSetting(tokenInfo); - State.SymbolAliasMap[symbol] = alias; + State.SymbolAliasMap[alias] = symbol; - CheckTokenAlias(alias, GetNftCollectionSymbol(tokenInfo.Symbol)); + CheckTokenAlias(alias, tokenInfo.Symbol); Context.Fire(new SymbolAliasAdded { From ac07be04ca939aa8cce7626126f3097b6e769e50 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 9 May 2024 11:54:50 +0800 Subject: [PATCH 084/128] Fix an error message. --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index a55edbf0ce..f7f695805c 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -79,7 +79,7 @@ private Empty CreateToken(CreateInput input, SymbolType symbolType = SymbolType. if (IsAliasSettingExists(tokenInfo)) { - Assert(symbolType == SymbolType.NftCollection, "Only NFT Collection can set alias."); + Assert(symbolType == SymbolType.NftCollection, "Token alias can only be set for NFT Item."); SetTokenAlias(tokenInfo); } From 9f963e7559cb1c3f3873e12aa09a9f86c1897c80 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Wed, 8 May 2024 16:30:24 +0800 Subject: [PATCH 085/128] Add method: GetSymbolByAlias --- .../AElf.Contracts.MultiToken/TokenContract_Views.cs | 11 +++++++++++ protobuf/token_contract.proto | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs index 9195d35b4c..8a960a8ce1 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs @@ -231,6 +231,17 @@ private bool IsAddressInCreateWhiteList(Address address) } public override StringValue GetTokenAlias(StringValue input) + { + var collectionSymbol = GetNftCollectionSymbol(input.Value); + var tokenInfo = GetTokenInfo(collectionSymbol); + var (_, alias) = ExtractAliasSetting(tokenInfo); + return new StringValue + { + Value = alias + }; + } + + public override StringValue GetSymbolByAlias(StringValue input) { return new StringValue { diff --git a/protobuf/token_contract.proto b/protobuf/token_contract.proto index c2453ff3c5..63573f3d19 100644 --- a/protobuf/token_contract.proto +++ b/protobuf/token_contract.proto @@ -233,6 +233,10 @@ service TokenContract { rpc GetTokenAlias (google.protobuf.StringValue) returns (google.protobuf.StringValue) { option (aelf.is_view) = true; } + + rpc GetSymbolByAlias (google.protobuf.StringValue) returns (google.protobuf.StringValue) { + option (aelf.is_view) = true; + } } message TokenInfo { From 34b8a068e3b7123d257600724c3b35724cc572e9 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 9 May 2024 11:55:19 +0800 Subject: [PATCH 086/128] Fix ExtractAliasSetting --- .../TokenContract_Actions.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index f7f695805c..6437ca3ec4 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -490,7 +490,7 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) IsBurnable = validateTokenInfoExistsInput.IsBurnable, IssueChainId = validateTokenInfoExistsInput.IssueChainId, ExternalInfo = new ExternalInfo { Value = { validateTokenInfoExistsInput.ExternalInfo } }, - Owner = tokenInfo.Owner + Owner = tokenInfo.Owner, }); return new Empty(); @@ -689,18 +689,16 @@ public override Empty SetSymbolAlias(SetSymbolAliasInput input) private void SyncSymbolAliasFromTokenInfo(TokenInfo newTokenInfo) { - var maybePreviousTokenInfo = State.TokenInfos[newTokenInfo.Symbol].Clone(); + var maybePreviousTokenInfo = State.TokenInfos[newTokenInfo.Symbol]?.Clone(); if (maybePreviousTokenInfo != null && IsAliasSettingExists(maybePreviousTokenInfo)) { var (previousSymbol, previousAlias) = ExtractAliasSetting(maybePreviousTokenInfo); - State.SymbolAliasMap.Remove(previousSymbol); - Context.Fire(new SymbolAliasDeleted + if (!string.IsNullOrEmpty(previousSymbol)) { - Symbol = previousSymbol, - Alias = previousAlias - }); + return; + } } if (IsAliasSettingExists(newTokenInfo)) @@ -723,6 +721,11 @@ private bool IsAliasSettingExists(TokenInfo tokenInfo) /// (Symbol, Alias) private KeyValuePair ExtractAliasSetting(TokenInfo tokenInfo) { + if (!tokenInfo.ExternalInfo.Value.ContainsKey(TokenContractConstants.TokenAliasExternalInfoKey)) + { + return new KeyValuePair(string.Empty, string.Empty); + } + var tokenAliasSetting = tokenInfo.ExternalInfo.Value[TokenContractConstants.TokenAliasExternalInfoKey]; tokenAliasSetting = tokenAliasSetting.Trim('{', '}'); var parts = tokenAliasSetting.Split(':'); From cc5f3e32ef16bb3fc5d34d932901b023f123cb99 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 9 May 2024 11:57:15 +0800 Subject: [PATCH 087/128] Now collection symbol can also set alias. --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 2 +- contract/AElf.Contracts.MultiToken/TokenContract_Views.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 6437ca3ec4..5f0d85eefa 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -654,7 +654,7 @@ public override Empty SetSymbolAlias(SetSymbolAliasInput input) Assert(Context.ChainId == ChainHelper.ConvertBase58ToChainId("AELF"), "Symbol alias setting only works on MainChain."); - var collectionSymbol = GetNftCollectionSymbol(input.Symbol); + var collectionSymbol = GetNftCollectionSymbol(input.Symbol, true); // For now, token alias can only be set once. Assert(State.SymbolAliasMap[input.Alias] == null, $"Token alias {input.Alias} already exists."); diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs index 8a960a8ce1..5b970c1d55 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs @@ -232,7 +232,7 @@ private bool IsAddressInCreateWhiteList(Address address) public override StringValue GetTokenAlias(StringValue input) { - var collectionSymbol = GetNftCollectionSymbol(input.Value); + var collectionSymbol = GetNftCollectionSymbol(input.Value, true); var tokenInfo = GetTokenInfo(collectionSymbol); var (_, alias) = ExtractAliasSetting(tokenInfo); return new StringValue From 4583f322dd5450be8008fcd414c05ba75150606d Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 9 May 2024 01:04:38 +0800 Subject: [PATCH 088/128] Bug fix: GetBalance & GetAllowance for not existing token failed to return value. --- .../AElf.Contracts.MultiToken/TokenContract_Helper.cs | 8 +++----- contract/AElf.Contracts.MultiToken/TokenContract_Views.cs | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index 35e31eec47..d67c4d9374 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -122,14 +122,12 @@ private List GetSymbolListSortedByExpirationTime(TransactionFeeFreeAllow fromAddress][t]).Seconds).ToList(); } - private long GetBalance(Address address, string symbol) { AssertValidInputAddress(address); - var tokenInfo = GetTokenInfo(symbol); - Assert(!string.IsNullOrWhiteSpace(tokenInfo.Symbol), "Invalid symbol."); - - return State.Balances[address][tokenInfo.Symbol]; + var actualSymbol = GetActualTokenSymbol(symbol); + Assert(!string.IsNullOrWhiteSpace(actualSymbol), "Invalid symbol."); + return State.Balances[address][actualSymbol]; } // private MethodFeeFreeAllowance GetFreeFeeAllowance(MethodFeeFreeAllowances freeAllowances, string symbol) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs index 5b970c1d55..af8334a0ce 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Views.cs @@ -40,7 +40,7 @@ public override GetBalanceOutput GetBalance(GetBalanceInput input) var symbol = GetActualTokenSymbol(input.Symbol); return new GetBalanceOutput { - Symbol = symbol, + Symbol = input.Symbol, Owner = input.Owner, Balance = GetBalance(input.Owner, symbol) }; @@ -253,7 +253,7 @@ private string GetActualTokenSymbol(string aliasOrSymbol) { if (State.TokenInfos[aliasOrSymbol] == null) { - return State.SymbolAliasMap[aliasOrSymbol]; + return State.SymbolAliasMap[aliasOrSymbol] ?? aliasOrSymbol; } return aliasOrSymbol; From b7d66f20bf23dc4089fdbaf6976969f7c116613e Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 9 May 2024 12:07:38 +0800 Subject: [PATCH 089/128] Improve GetNftCollectionSymbol --- .../AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs index 7c2349c4a3..bd9d9b54b6 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_NFT_Actions.cs @@ -86,17 +86,20 @@ private void DoTransferFrom(Address from, Address to, Address spender, string sy /// /// ELF -> null /// NFT-1 -> NFT-0 + /// If isAllowCollection == true: NFT-0 -> NFT-0 + /// If isAllowCollection == false: NFT-0 -> null /// /// + /// /// Return null if inputSymbol is not NFT. - private string GetNftCollectionSymbol(string inputSymbol) + private string GetNftCollectionSymbol(string inputSymbol, bool isAllowCollection = false) { var symbol = inputSymbol; var words = symbol.Split(TokenContractConstants.NFTSymbolSeparator); const int tokenSymbolLength = 1; if (words.Length == tokenSymbolLength) return null; Assert(words.Length == 2 && words[1].All(IsValidItemIdChar), "Invalid NFT Symbol Input"); - return symbol == $"{words[0]}-0" ? null : $"{words[0]}-0"; + return symbol == $"{words[0]}-0" ? (isAllowCollection ? $"{words[0]}-0" : null) : $"{words[0]}-0"; } private TokenInfo AssertNftCollectionExist(string symbol) From eaff5fbedbfcf097256c640aac0bf730c1b509d5 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 9 May 2024 13:58:23 +0800 Subject: [PATCH 090/128] GetCreateInputSymbolType -> GetSymbolType --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 102b596bd5..3e27641eb2 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -32,7 +32,7 @@ public override Empty InitializeFromParentChain(InitializeFromParentChainInput i /// public override Empty Create(CreateInput input) { - var inputSymbolType = GetCreateInputSymbolType(input.Symbol); + var inputSymbolType = GetSymbolType(input.Symbol); if (input.Owner == null) { input.Owner = input.Issuer; From 1edf0676201acab88ed9d2c056753e35e83fb796 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 9 May 2024 14:30:43 +0800 Subject: [PATCH 091/128] Fix Approve and one test case. --- .../AElf.Contracts.MultiToken/TokenContract_Actions.cs | 5 +++-- .../MultiTokenContractCrossChainTest.cs | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 3e27641eb2..dd684c9138 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -261,8 +261,9 @@ public override Empty TransferFrom(TransferFromInput input) public override Empty Approve(ApproveInput input) { AssertValidInputAddress(input.Spender); - AssertValidApproveTokenAndAmount(input.Symbol, input.Amount); - Approve(input.Spender, input.Symbol, input.Amount); + var actualSymbol = GetActualTokenSymbol(input.Symbol); + AssertValidApproveTokenAndAmount(actualSymbol, input.Amount); + Approve(input.Spender, actualSymbol, input.Amount); return new Empty(); } diff --git a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTest.cs b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTest.cs index 03ed5f1335..30a6d59c74 100644 --- a/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTest.cs +++ b/test/AElf.Contracts.MultiTokenCrossChainTransfer.Tests/MultiTokenContractCrossChainTest.cs @@ -325,7 +325,7 @@ public async Task SideChain_CrossChainSideChainCreateToken_Test() } - [Fact] + [Fact(Skip = "Now we allow this.")] public async Task SideChain_CrossChainCreateToken_WithAlreadyCreated_Test() { await GenerateSideChainAsync(); @@ -1087,7 +1087,10 @@ private Transaction CreateTokenInfoValidationTransaction(TokenInfo createdTokenI TotalSupply = createdTokenInfo.TotalSupply, IssueChainId = createdTokenInfo.IssueChainId }; - input.ExternalInfo.Add(createdTokenInfo.ExternalInfo.Value); + if (createdTokenInfo.ExternalInfo != null) + { + input.ExternalInfo.Add(createdTokenInfo.ExternalInfo.Value); + } return tokenContractImplStub.ValidateTokenInfoExists.GetTransaction(input); } From b082847b4a76594a0ea9aaff62b20f82dd03ca86 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 9 May 2024 16:21:15 +0800 Subject: [PATCH 092/128] Fix BatchApprove and add a test case: BatchApproveWithAlias_Test --- .../TokenContract_Actions.cs | 3 +- .../BVT/TokenAliasTests.cs | 95 +++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index dd684c9138..8dccb4305a 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -287,7 +287,8 @@ public override Empty BatchApprove(BatchApproveInput input) foreach (var approve in input.Value) { AssertValidInputAddress(approve.Spender); - AssertValidApproveTokenAndAmount(approve.Symbol, approve.Amount); + var actualSymbol = GetActualTokenSymbol(approve.Symbol); + AssertValidApproveTokenAndAmount(actualSymbol, approve.Amount); } var approveInputList = input.Value.GroupBy(approve => approve.Symbol + approve.Spender, approve => approve) .Select(approve => approve.Last()).ToList(); diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenAliasTests.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenAliasTests.cs index f758efec70..d95e92816a 100644 --- a/test/AElf.Contracts.MultiToken.Tests/BVT/TokenAliasTests.cs +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/TokenAliasTests.cs @@ -260,6 +260,101 @@ public async Task GetAllowanceOfNotExistToken_Test() allowance.Allowance.ShouldBe(0); } + [Fact] + public async Task BatchApproveWithAlias_Test() + { + await SetTokenAlias_NFTCollection_Test(); + await CreateTokenAndIssue(); + var approveBasisResult = (await TokenContractStub.BatchApprove.SendAsync(new BatchApproveInput + { + Value = + { + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 2000L, + Spender = BasicFunctionContractAddress + }, + new ApproveInput + { + Symbol = "TP", + Amount = 1000L, + Spender = OtherBasicFunctionContractAddress + }, + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 5000L, + Spender = TreasuryContractAddress + } + } + })).TransactionResult; + approveBasisResult.Status.ShouldBe(TransactionResultStatus.Mined); + + var basicAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = BasicFunctionContractAddress, + Symbol = SymbolForTest + }); + basicAllowanceOutput.Allowance.ShouldBe(2000L); + var otherBasicAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = OtherBasicFunctionContractAddress, + Symbol = "TP" + }); + otherBasicAllowanceOutput.Allowance.ShouldBe(1000L); + var treasuryAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = TreasuryContractAddress, + Symbol = SymbolForTest + }); + treasuryAllowanceOutput.Allowance.ShouldBe(5000L); + + approveBasisResult = (await TokenContractStub.BatchApprove.SendAsync(new BatchApproveInput + { + Value = + { + new ApproveInput + { + Symbol = "TP", + Amount = 1000L, + Spender = BasicFunctionContractAddress + }, + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 3000L, + Spender = BasicFunctionContractAddress + }, + new ApproveInput + { + Symbol = SymbolForTest, + Amount = 3000L, + Spender = TreasuryContractAddress + } + } + })).TransactionResult; + approveBasisResult.Status.ShouldBe(TransactionResultStatus.Mined); + basicAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = BasicFunctionContractAddress, + Symbol = SymbolForTest + }); + basicAllowanceOutput.Allowance.ShouldBe(3000L); + + treasuryAllowanceOutput = await TokenContractStub.GetAllowance.CallAsync(new GetAllowanceInput + { + Owner = DefaultAddress, + Spender = TreasuryContractAddress, + Symbol = SymbolForTest + }); + treasuryAllowanceOutput.Allowance.ShouldBe(3000L); + } + private TokenInfo NftCollection1155WithAliasInfo => new() { Symbol = "TP-", From e44844329d4574a5c3bc73d51e8b53cf5ba3efda Mon Sep 17 00:00:00 2001 From: eanzhao Date: Fri, 10 May 2024 09:23:28 +0800 Subject: [PATCH 093/128] Change permission setting for SetSymbolAlias. --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 8dccb4305a..e97c76f96b 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -716,7 +716,8 @@ public override Empty SetSymbolAlias(SetSymbolAliasInput input) throw new AssertionException($"NFT Collection {collectionSymbol} not found."); } - Assert(collectionTokenInfo.Owner == Context.Sender, "No permission."); + Assert(collectionTokenInfo.Owner == Context.Sender || collectionTokenInfo.Issuer == Context.Sender, + "No permission."); collectionTokenInfo.ExternalInfo.Value[TokenContractConstants.TokenAliasExternalInfoKey] = $"{{\"{input.Symbol}\":\"{input.Alias}\"}}"; From 04324e13fade643bd5c2fd7856dde26483a135af Mon Sep 17 00:00:00 2001 From: eanzhao Date: Fri, 10 May 2024 15:02:21 +0800 Subject: [PATCH 094/128] Fix CrossChainCreateToken --- .../TokenContract_Actions.cs | 24 +++++++++++++++---- .../TokenContract_Helper.cs | 11 +++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index e97c76f96b..b6513ccb2a 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -502,9 +502,20 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) Owner = validateTokenInfoExistsInput.Owner ?? validateTokenInfoExistsInput.Issuer }; - SyncSymbolAliasFromTokenInfo(tokenInfo); - - RegisterTokenInfo(tokenInfo); + if (State.TokenInfos[tokenInfo.Symbol] == null) + { + RegisterTokenInfo(tokenInfo); + } + else + { + if (SyncSymbolAliasFromTokenInfo(tokenInfo) && + validateTokenInfoExistsInput.ExternalInfo.TryGetValue(TokenContractConstants.TokenAliasExternalInfoKey, + out var tokenAliasSetting)) + { + State.TokenInfos[tokenInfo.Symbol].ExternalInfo.Value + .Add(TokenContractConstants.TokenAliasExternalInfoKey, tokenAliasSetting); + } + } Context.Fire(new TokenCreated { @@ -737,7 +748,7 @@ public override Empty SetSymbolAlias(SetSymbolAliasInput input) return new Empty(); } - private void SyncSymbolAliasFromTokenInfo(TokenInfo newTokenInfo) + private bool SyncSymbolAliasFromTokenInfo(TokenInfo newTokenInfo) { var maybePreviousTokenInfo = State.TokenInfos[newTokenInfo.Symbol]?.Clone(); @@ -747,14 +758,17 @@ private void SyncSymbolAliasFromTokenInfo(TokenInfo newTokenInfo) if (!string.IsNullOrEmpty(previousSymbol)) { - return; + return false; } } if (IsAliasSettingExists(newTokenInfo)) { SetTokenAlias(newTokenInfo); + return true; } + + return false; } private bool IsAliasSettingExists(TokenInfo tokenInfo) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index 4fb811cde7..58df19726f 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -230,6 +230,17 @@ private void RegisterTokenInfo(TokenInfo tokenInfo) Assert(tokenInfo.Owner != null, "Invalid owner address."); State.TokenInfos[tokenInfo.Symbol] = tokenInfo; } + + private void RegisterOrUpdateTokenInfo(TokenInfo tokenInfo) + { + Assert(!string.IsNullOrEmpty(tokenInfo.Symbol) && tokenInfo.Symbol.All(IsValidSymbolChar), + "Invalid symbol."); + Assert(!string.IsNullOrEmpty(tokenInfo.TokenName), "Token name can neither be null nor empty."); + Assert(tokenInfo.TotalSupply > 0, "Invalid total supply."); + Assert(tokenInfo.Issuer != null, "Invalid issuer address."); + Assert(tokenInfo.Owner != null, "Invalid owner address."); + State.TokenInfos[tokenInfo.Symbol] = tokenInfo; + } private void CrossChainVerify(Hash transactionId, long parentChainHeight, int chainId, MerklePath merklePath) { From 469f163ac2d0dd597a5ef55b26b2b81e715b04a7 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Fri, 10 May 2024 15:13:37 +0800 Subject: [PATCH 095/128] Won't fire TokenCreated event on side chain if only changed external info. --- .../TokenContract_Actions.cs | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index b6513ccb2a..80c4bb3d1f 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -505,6 +505,18 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) if (State.TokenInfos[tokenInfo.Symbol] == null) { RegisterTokenInfo(tokenInfo); + Context.Fire(new TokenCreated + { + Symbol = validateTokenInfoExistsInput.Symbol, + TokenName = validateTokenInfoExistsInput.TokenName, + TotalSupply = validateTokenInfoExistsInput.TotalSupply, + Decimals = validateTokenInfoExistsInput.Decimals, + Issuer = validateTokenInfoExistsInput.Issuer, + IsBurnable = validateTokenInfoExistsInput.IsBurnable, + IssueChainId = validateTokenInfoExistsInput.IssueChainId, + ExternalInfo = new ExternalInfo { Value = { validateTokenInfoExistsInput.ExternalInfo } }, + Owner = tokenInfo.Owner, + }); } else { @@ -517,19 +529,6 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) } } - Context.Fire(new TokenCreated - { - Symbol = validateTokenInfoExistsInput.Symbol, - TokenName = validateTokenInfoExistsInput.TokenName, - TotalSupply = validateTokenInfoExistsInput.TotalSupply, - Decimals = validateTokenInfoExistsInput.Decimals, - Issuer = validateTokenInfoExistsInput.Issuer, - IsBurnable = validateTokenInfoExistsInput.IsBurnable, - IssueChainId = validateTokenInfoExistsInput.IssueChainId, - ExternalInfo = new ExternalInfo { Value = { validateTokenInfoExistsInput.ExternalInfo } }, - Owner = tokenInfo.Owner, - }); - return new Empty(); } From f2152fa389beef0867041fd3de48e2e151d2901e Mon Sep 17 00:00:00 2001 From: eanzhao Date: Fri, 10 May 2024 15:20:29 +0800 Subject: [PATCH 096/128] Fix CrossChainCreateToken --- contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 80c4bb3d1f..a4433b82b3 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -502,6 +502,7 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) Owner = validateTokenInfoExistsInput.Owner ?? validateTokenInfoExistsInput.Issuer }; + var isSymbolAliasSet = SyncSymbolAliasFromTokenInfo(tokenInfo); if (State.TokenInfos[tokenInfo.Symbol] == null) { RegisterTokenInfo(tokenInfo); @@ -520,7 +521,7 @@ public override Empty CrossChainCreateToken(CrossChainCreateTokenInput input) } else { - if (SyncSymbolAliasFromTokenInfo(tokenInfo) && + if (isSymbolAliasSet && validateTokenInfoExistsInput.ExternalInfo.TryGetValue(TokenContractConstants.TokenAliasExternalInfoKey, out var tokenAliasSetting)) { From 1e542c7e378a6c456d14ac8c9122f324c03e0976 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Fri, 10 May 2024 15:52:49 +0800 Subject: [PATCH 097/128] Simplify SyncSymbolAliasFromTokenInfo --- .../TokenContract_Actions.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index a4433b82b3..154e18846b 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -750,18 +750,6 @@ public override Empty SetSymbolAlias(SetSymbolAliasInput input) private bool SyncSymbolAliasFromTokenInfo(TokenInfo newTokenInfo) { - var maybePreviousTokenInfo = State.TokenInfos[newTokenInfo.Symbol]?.Clone(); - - if (maybePreviousTokenInfo != null && IsAliasSettingExists(maybePreviousTokenInfo)) - { - var (previousSymbol, previousAlias) = ExtractAliasSetting(maybePreviousTokenInfo); - - if (!string.IsNullOrEmpty(previousSymbol)) - { - return false; - } - } - if (IsAliasSettingExists(newTokenInfo)) { SetTokenAlias(newTokenInfo); From 6035cbb9b98e8f45a300117a5ca18a07206cef9d Mon Sep 17 00:00:00 2001 From: eanzhao Date: Fri, 10 May 2024 15:54:34 +0800 Subject: [PATCH 098/128] Fix SyncSymbolAliasFromTokenInfo --- .../AElf.Contracts.MultiToken/TokenContract_Actions.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs index 154e18846b..a79beae8f7 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Actions.cs @@ -750,6 +750,13 @@ public override Empty SetSymbolAlias(SetSymbolAliasInput input) private bool SyncSymbolAliasFromTokenInfo(TokenInfo newTokenInfo) { + var maybePreviousTokenInfo = State.TokenInfos[newTokenInfo.Symbol]?.Clone(); + + if (maybePreviousTokenInfo != null && IsAliasSettingExists(maybePreviousTokenInfo)) + { + return false; + } + if (IsAliasSettingExists(newTokenInfo)) { SetTokenAlias(newTokenInfo); From e3fcda3f0aad82d627994177ffe636d07330f892 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Mon, 20 May 2024 11:04:28 +0800 Subject: [PATCH 099/128] Fix the regex pattern of IsValidSymbol --- .../TokenContract_Helper.cs | 2 +- .../BVT/SymbolValidationTest.cs | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 test/AElf.Contracts.MultiToken.Tests/BVT/SymbolValidationTest.cs diff --git a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs index b0a89a2717..40616c8973 100644 --- a/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs +++ b/contract/AElf.Contracts.MultiToken/TokenContract_Helper.cs @@ -17,7 +17,7 @@ public partial class TokenContract { private static bool IsValidSymbol(string symbol) { - return Regex.IsMatch(symbol, "^[a-zA-Z0-9]+-?[0-9]*$"); + return Regex.IsMatch(symbol, "^[a-zA-Z0-9]+(-[0-9]+)?$"); } private bool IsValidItemId(string symbolItemId) diff --git a/test/AElf.Contracts.MultiToken.Tests/BVT/SymbolValidationTest.cs b/test/AElf.Contracts.MultiToken.Tests/BVT/SymbolValidationTest.cs new file mode 100644 index 0000000000..3d3f94dd9a --- /dev/null +++ b/test/AElf.Contracts.MultiToken.Tests/BVT/SymbolValidationTest.cs @@ -0,0 +1,23 @@ +using System.Text.RegularExpressions; +using Shouldly; +using Xunit; + +namespace AElf.Contracts.MultiToken; + +public class SymbolValidationTest +{ + private const string RegexPattern = "^[a-zA-Z0-9]+(-[0-9]+)?$"; + + [Theory] + [InlineData("ELF", true)] + [InlineData("ELF-", false)] + [InlineData("ABC-123", true)] + [InlineData("abc-1", true)] + [InlineData("ABC-ABC", false)] + [InlineData("ABC--", false)] + [InlineData("121-1", true)] + public void SymbolValidation(string symbol, bool isValid) + { + Regex.IsMatch(symbol, RegexPattern).ShouldBe(isValid); + } +} \ No newline at end of file From 5ba1de8433d5e3462273703da67b875005d05376 Mon Sep 17 00:00:00 2001 From: ssun0121 Date: Thu, 23 Nov 2023 20:32:56 +0800 Subject: [PATCH 100/128] feat:add charging address. --- .../Extensions/TransactionResultExtensions.cs | 13 ++++-- .../Dto/CalculateTransactionFeeOutput.cs | 5 ++ .../Services/TransactionAppService.cs | 13 +++++- .../Infrastructure/CalculateFunctionTest.cs | 13 ++++-- .../ExecutionPluginForMethodFeeTest.cs | 31 ++++++++----- ...ExecutionPluginForMethodFeeWithForkTest.cs | 5 +- ...utionPluginForUserContractMethodFeeTest.cs | 46 +++++++++++++------ 7 files changed, 89 insertions(+), 37 deletions(-) diff --git a/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs b/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs index ff1233b211..cd35c882f8 100644 --- a/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs +++ b/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs @@ -8,13 +8,18 @@ namespace AElf.Kernel.FeeCalculation.Extensions; public static class TransactionResultExtensions { - public static Dictionary GetChargedTransactionFees(this TransactionResult transactionResult) + public static Dictionary> GetChargedTransactionFees( + this TransactionResult transactionResult) { return transactionResult.Logs .Where(l => l.Name == nameof(TransactionFeeCharged)) - .Select(l => TransactionFeeCharged.Parser.ParseFrom(l.NonIndexed)) - .GroupBy(fee => fee.Symbol, fee => fee.Amount) - .ToDictionary(g => g.Key, g => g.Sum()); + .GroupBy( + log => TransactionFeeCharged.Parser.ParseFrom(log.Indexed[0]).ChargingAddress, + log => TransactionFeeCharged.Parser.ParseFrom(log.NonIndexed)) + .ToDictionary(e => e.Key, + e => e + .GroupBy(fee => fee.Symbol, fee => fee.Amount) + .ToDictionary(g => g.Key, g => g.Sum())); } public static Dictionary GetConsumedResourceTokens(this TransactionResult transactionResult) diff --git a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs index bbba632175..1c01c7e60c 100644 --- a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs +++ b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using AElf.Types; namespace AElf.WebApp.Application.Chain.Dto; @@ -9,4 +10,8 @@ public class CalculateTransactionFeeOutput public Dictionary TransactionFee { get; set; } public Dictionary ResourceFee { get; set; } + + public string Error { get; set; } + + public Address TransactionFeeChargingAddress { get; set; } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs index 01b8459811..db03099725 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs @@ -17,6 +17,7 @@ using Google.Protobuf.WellKnownTypes; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using Volo.Abp; using Volo.Abp.EventBus.Local; using Volo.Abp.ObjectMapping; @@ -47,17 +48,21 @@ public class TransactionAppService : AElfAppService, ITransactionAppService private readonly ITransactionReadOnlyExecutionService _transactionReadOnlyExecutionService; private readonly ITransactionResultStatusCacheProvider _transactionResultStatusCacheProvider; private readonly IPlainTransactionExecutingService _plainTransactionExecutingService; + private readonly WebAppOptions _webAppOptions; + public TransactionAppService(ITransactionReadOnlyExecutionService transactionReadOnlyExecutionService, IBlockchainService blockchainService, IObjectMapper objectMapper, ITransactionResultStatusCacheProvider transactionResultStatusCacheProvider, - IPlainTransactionExecutingService plainTransactionExecutingService) + IPlainTransactionExecutingService plainTransactionExecutingService, + IOptionsMonitor webAppOptions) { _transactionReadOnlyExecutionService = transactionReadOnlyExecutionService; _blockchainService = blockchainService; _objectMapper = objectMapper; _transactionResultStatusCacheProvider = transactionResultStatusCacheProvider; _plainTransactionExecutingService = plainTransactionExecutingService; + _webAppOptions = webAppOptions.CurrentValue; LocalEventBus = NullLocalEventBus.Instance; Logger = NullLogger.Instance; @@ -292,13 +297,17 @@ private async Task EstimateTransactionFee(Transac var transactionFees = executionReturnSets.FirstOrDefault()?.TransactionResult.GetChargedTransactionFees(); var resourceFees = executionReturnSets.FirstOrDefault()?.TransactionResult.GetConsumedResourceTokens(); + var chargingAddress = transactionFees?.Keys.First(); result.Success = true; - result.TransactionFee = transactionFees; + result.TransactionFeeChargingAddress = chargingAddress; + result.TransactionFee = transactionFees?[chargingAddress]; result.ResourceFee = resourceFees; } else { result.Success = false; + result.Error = TransactionErrorResolver.TakeErrorMessage( + executionReturnSets.FirstOrDefault()?.TransactionResult.Error, _webAppOptions.IsDebugMode); } return result; diff --git a/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs b/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs index 2e8484cd82..78c3d0419a 100644 --- a/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs +++ b/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using AElf.Contracts.MultiToken; using AElf.CSharp.Core.Extension; @@ -65,30 +66,34 @@ public void GetChargedTransactionFees_Test() var transactionResult = new TransactionResult(); transactionResult.Logs.Add(new TransactionFeeCharged { + ChargingAddress = SampleAddress.AddressList[0], Amount = 1, Symbol = "ELF" }.ToLogEvent()); transactionResult.Logs.Add(new TransactionFeeCharged { + ChargingAddress = SampleAddress.AddressList[0], Amount = 2, Symbol = "ELF" }.ToLogEvent()); transactionResult.Logs.Add(new TransactionFeeCharged { + ChargingAddress = SampleAddress.AddressList[1], Amount = 3, Symbol = "TEST" }.ToLogEvent()); transactionResult.Logs.Add(new TransactionFeeCharged { + ChargingAddress = SampleAddress.AddressList[1], Amount = 4, Symbol = "TEST" }.ToLogEvent()); var feeDic = transactionResult.GetChargedTransactionFees(); feeDic.Count.ShouldBe(2); - feeDic.Keys.First().ShouldBe("ELF"); - feeDic.Values.First().ShouldBe(3); - feeDic.Keys.Last().ShouldBe("TEST"); - feeDic.Values.Last().ShouldBe(7); + feeDic.Keys.First().ShouldBe(SampleAddress.AddressList[0]); + feeDic.Values.First()["ELF"].ShouldBe(3); + feeDic.Keys.Last().ShouldBe(SampleAddress.AddressList[1]); + feeDic.Values.Last()["TEST"].ShouldBe(7); } [Fact] diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTest.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTest.cs index b265f1cca9..1264e6fb14 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTest.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeTest.cs @@ -268,14 +268,14 @@ public async Task ChargeFee_SuccessfulTest() chargingAddress.ShouldContain(dummy.Transaction.From); var transactionFeeDic = dummy.TransactionResult.GetChargedTransactionFees(); - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(DefaultSender,transactionFeeDic); var after = await tokenContractStub.GetBalance.CallAsync(new GetBalanceInput { Owner = DefaultSender, Symbol = "ELF" }); - after.Balance.ShouldBe(before.Balance - transactionFeeDic[before.Symbol]); + after.Balance.ShouldBe(before.Balance - transactionFeeDic[DefaultAddress][before.Symbol]); } private static List
GetChargingAddress(TransactionResult transactionResult) @@ -284,7 +284,7 @@ private static List
GetChargingAddress(TransactionResult transactionRes return relatedLogs.Select(l => TransactionFeeCharged.Parser.ParseFrom(l.Indexed[0]).ChargingAddress).ToList(); } - private async Task CheckTransactionFeesMapAsync(Dictionary transactionFeeDic) + private async Task CheckTransactionFeesMapAsync(Address chargingAddress, Dictionary> transactionFeeDic) { var chain = await _blockchainService.GetChainAsync(); var transactionFeesMap = await _totalTransactionFeesMapProvider.GetTotalTransactionFeesMapAsync(new ChainContext @@ -293,7 +293,13 @@ private async Task CheckTransactionFeesMapAsync(Dictionary transac BlockHeight = chain.BestChainHeight }); foreach (var transactionFee in transactionFeeDic) - transactionFeesMap.Value[transactionFee.Key].ShouldBe(transactionFee.Value); + { + transactionFee.Key.ShouldBe(chargingAddress); + foreach (var value in transactionFee.Value) + { + transactionFeesMap.Value[value.Key].ShouldBe(value.Value); + } + } } [Fact] @@ -324,7 +330,7 @@ await tokenContractStub.Transfer.SendAsync(new TransferInput dummy.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); dummy.TransactionResult.Error.ShouldBe("Pre-Error: Transaction fee not enough."); var transactionFeeDic = dummy.TransactionResult.GetChargedTransactionFees(); - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(Accounts[1].Address,transactionFeeDic); var afterFee = (await tokenContractStub.GetBalance.CallAsync(new GetBalanceInput { @@ -332,7 +338,7 @@ await tokenContractStub.Transfer.SendAsync(new TransferInput Symbol = "ELF" })).Balance; afterFee.ShouldBe(0); - transactionFeeDic["ELF"].ShouldBe(issueAmount); + transactionFeeDic[Accounts[1].Address]["ELF"].ShouldBe(issueAmount); } [Theory] @@ -378,7 +384,7 @@ await tokenContractStub.Transfer.SendAsync(new TransferInput Symbol = chargedSymbol ?? "ELF" })).Balance; - Dictionary transactionFeeDic; + Dictionary> transactionFeeDic; var userTestContractStub = GetTester(_testContractAddress, Accounts[1].KeyPair); if (isChargingSuccessful) @@ -387,8 +393,9 @@ await tokenContractStub.Transfer.SendAsync(new TransferInput dummyResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); if (chargedSymbol != null) { - dummyResult.TransactionResult.GetChargedTransactionFees().Keys.ShouldContain(chargedSymbol); - dummyResult.TransactionResult.GetChargedTransactionFees().Values.ShouldContain(chargedAmount); + dummyResult.TransactionResult.GetChargedTransactionFees().Keys.ShouldContain(Accounts[1].Address); + dummyResult.TransactionResult.GetChargedTransactionFees()[Accounts[1].Address].Keys.ShouldContain(chargedSymbol); + dummyResult.TransactionResult.GetChargedTransactionFees()[Accounts[1].Address].Values.ShouldContain(chargedAmount); } transactionFeeDic = dummyResult.TransactionResult.GetChargedTransactionFees(); @@ -399,13 +406,13 @@ await tokenContractStub.Transfer.SendAsync(new TransferInput dummyResult.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); dummyResult.TransactionResult.Error.ShouldBe("Pre-Error: Transaction fee not enough."); if (chargedSymbol != null) - dummyResult.TransactionResult.GetChargedTransactionFees().Keys.ShouldContain(chargedSymbol); + dummyResult.TransactionResult.GetChargedTransactionFees()[Accounts[1].Address].Keys.ShouldContain(chargedSymbol); transactionFeeDic = dummyResult.TransactionResult.GetChargedTransactionFees(); } - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(Accounts[1].Address,transactionFeeDic); if (chargedSymbol != null) - transactionFeeDic[chargedSymbol].ShouldBe(chargedAmount); + transactionFeeDic[Accounts[1].Address][chargedSymbol].ShouldBe(chargedAmount); var finalBalance = (await tokenContractStub.GetBalance.CallAsync(new GetBalanceInput { diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeWithForkTest.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeWithForkTest.cs index 6a6ffe5a73..02aebc36aa 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeWithForkTest.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForMethodFeeWithForkTest.cs @@ -55,8 +55,9 @@ await SetMethodFeeWithProposalAsync(new MethodFees Memo = Guid.NewGuid().ToString(), To = SampleAddress.AddressList[0] }); + var fromAddress = Address.FromPublicKey(Tester.KeyPair.PublicKey); var transactionResult = await Tester.GetTransactionResultAsync(result.Item2.GetHash()); - var targetFee = transactionResult.GetChargedTransactionFees().First().Value; + var targetFee = transactionResult.GetChargedTransactionFees()[fromAddress].First().Value; var transactionFeesMap = await GetTransactionFeesMapAsync(new ChainContext { @@ -97,7 +98,7 @@ await SetMethodFeeWithProposalAsync(new MethodFees To = SampleAddress.AddressList[0] }); transactionResult = await Tester.GetTransactionResultAsync(result.Item2.GetHash()); - var fee = transactionResult.GetChargedTransactionFees().First().Value; + var fee = transactionResult.GetChargedTransactionFees()[fromAddress].First().Value; transactionFeesMap = await GetTransactionFeesMapAsync(new ChainContext { BlockHash = result.Item1.GetHash(), BlockHeight = result.Item1.Height diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs index 25e8daa5f4..688abbd017 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForMethodFee.Tests/ExecutionPluginForUserContractMethodFeeTest.cs @@ -75,14 +75,14 @@ public async Task ChargeUserContractFeeTest_Success() var result = await _testContractStub.TestMethod.SendAsync(new Empty()); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var transactionFeeDic = result.TransactionResult.GetChargedTransactionFees(); - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(DefaultAddress,transactionFeeDic); var after = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { Owner = DefaultAddress, Symbol = "ELF" }); - after.Balance.ShouldBe(beforeBalance.Balance - transactionFeeDic[beforeBalance.Symbol]); + after.Balance.ShouldBe(beforeBalance.Balance - transactionFeeDic[DefaultAddress][beforeBalance.Symbol]); } [Fact] @@ -97,7 +97,7 @@ public async Task ChargeUserContractFeeTest_Success_BaseFeeIsFree() var result = await _testContractStub.TestMethod.SendAsync(new Empty()); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var transactionFeeDic = result.TransactionResult.GetChargedTransactionFees(); - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(DefaultAddress,transactionFeeDic); var after = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { @@ -193,7 +193,7 @@ await TokenContractStub.Transfer.SendAsync(new TransferInput Symbol = chargedSymbol ?? "ELF" })).Balance; - Dictionary transactionFeeDic; + Dictionary> transactionFeeDic; var userTestContractStub = GetTester(_testContractAddress, Accounts[1].KeyPair); if (isChargingSuccessful) @@ -202,8 +202,13 @@ await TokenContractStub.Transfer.SendAsync(new TransferInput result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); if (chargedSymbol != null) { - result.TransactionResult.GetChargedTransactionFees().Keys.ShouldContain(chargedSymbol); - result.TransactionResult.GetChargedTransactionFees().Values.ShouldContain(chargedAmount); + var token = new Dictionary + { + [chargedSymbol] = chargedAmount + }; + result.TransactionResult.GetChargedTransactionFees().Keys.ShouldContain(Accounts[1].Address); + var fee = result.TransactionResult.GetChargedTransactionFees()[Accounts[1].Address]; + fee.ShouldContainKeyAndValue(chargedSymbol,chargedAmount); } transactionFeeDic = result.TransactionResult.GetChargedTransactionFees(); @@ -214,13 +219,21 @@ await TokenContractStub.Transfer.SendAsync(new TransferInput result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Failed); result.TransactionResult.Error.ShouldBe("Pre-Error: Transaction fee not enough."); if (chargedSymbol != null) - result.TransactionResult.GetChargedTransactionFees().Keys.ShouldContain(chargedSymbol); + { + var token = new Dictionary + { + [chargedSymbol] = chargedAmount + }; + result.TransactionResult.GetChargedTransactionFees().Keys.ShouldContain(Accounts[1].Address); + var fee = result.TransactionResult.GetChargedTransactionFees()[Accounts[1].Address]; + fee.ShouldContainKeyAndValue(chargedSymbol,chargedAmount); + } transactionFeeDic = result.TransactionResult.GetChargedTransactionFees(); } - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(Accounts[1].Address,transactionFeeDic); if (chargedSymbol != null) - transactionFeeDic[chargedSymbol].ShouldBe(chargedAmount); + transactionFeeDic[Accounts[1].Address][chargedSymbol].ShouldBe(chargedAmount); var finalBalance = (await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { @@ -253,7 +266,7 @@ public async Task ChargeFee_SizeFeeIsFree() var result = await _testContractStub.TestMethod.SendAsync(new Empty()); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var transactionFeeDic = result.TransactionResult.GetChargedTransactionFees(); - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(DefaultAddress, transactionFeeDic); var after = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { @@ -294,7 +307,7 @@ public async Task ChargeFee_SpecConfigurationFee() var result = await _testContractStub.TestMethod.SendAsync(new Empty()); result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined); var transactionFeeDic = result.TransactionResult.GetChargedTransactionFees(); - await CheckTransactionFeesMapAsync(transactionFeeDic); + await CheckTransactionFeesMapAsync(DefaultAddress, transactionFeeDic); var after = await TokenContractStub.GetBalance.CallAsync(new GetBalanceInput { @@ -356,7 +369,7 @@ private async Task Initialize() } } - private async Task CheckTransactionFeesMapAsync(Dictionary transactionFeeDic) + private async Task CheckTransactionFeesMapAsync(Address chargingAddress, Dictionary> transactionFeeDic) { var chain = await _blockchainService.GetChainAsync(); var transactionFeesMap = await _totalTransactionFeesMapProvider.GetTotalTransactionFeesMapAsync(new ChainContext @@ -365,7 +378,14 @@ private async Task CheckTransactionFeesMapAsync(Dictionary transac BlockHeight = chain.BestChainHeight }); foreach (var transactionFee in transactionFeeDic) - transactionFeesMap.Value[transactionFee.Key].ShouldBe(transactionFee.Value); + { + transactionFee.Key.ShouldBe(chargingAddress); + foreach (var value in transactionFee.Value) + { + transactionFeesMap.Value[value.Key].ShouldBe(value.Value); + } + } + } private async Task SetUserContractFeeAsync(int amount) From a236cb9467e625b89576257633e32a09ca1716b3 Mon Sep 17 00:00:00 2001 From: ssun0121 Date: Fri, 24 Nov 2023 13:48:45 +0800 Subject: [PATCH 101/128] feat:change type to string. --- .../Dto/CalculateTransactionFeeOutput.cs | 2 +- .../Services/TransactionAppService.cs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs index 1c01c7e60c..c73eee55d6 100644 --- a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs +++ b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs @@ -13,5 +13,5 @@ public class CalculateTransactionFeeOutput public string Error { get; set; } - public Address TransactionFeeChargingAddress { get; set; } + public string TransactionFeeChargingAddress { get; set; } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs index db03099725..fac39bbcd2 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs @@ -299,8 +299,11 @@ private async Task EstimateTransactionFee(Transac var resourceFees = executionReturnSets.FirstOrDefault()?.TransactionResult.GetConsumedResourceTokens(); var chargingAddress = transactionFees?.Keys.First(); result.Success = true; - result.TransactionFeeChargingAddress = chargingAddress; - result.TransactionFee = transactionFees?[chargingAddress]; + result.TransactionFeeChargingAddress = chargingAddress?.ToBase58(); + if (chargingAddress != null) + { + result.TransactionFee = transactionFees[chargingAddress]; + } result.ResourceFee = resourceFees; } else From a42af1c2021ccad35948bf6d0e1d6d2577228bb8 Mon Sep 17 00:00:00 2001 From: ssun0121 Date: Tue, 28 Nov 2023 01:29:56 +0800 Subject: [PATCH 102/128] feat:change output structure. --- .../Extensions/TransactionResultExtensions.cs | 10 ++++-- .../Dto/CalculateTransactionFeeOutput.cs | 17 ++++++++-- .../Services/TransactionAppService.cs | 33 +++++++++++++++---- .../ExecutionPluginForResourceFeeTest.cs | 4 +-- .../BlockChainAppServiceTest.cs | 6 ++++ 5 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs b/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs index cd35c882f8..138d7853f3 100644 --- a/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs +++ b/src/AElf.Kernel.FeeCalculation/Extensions/TransactionResultExtensions.cs @@ -22,12 +22,16 @@ public static Dictionary> GetChargedTransactio .ToDictionary(g => g.Key, g => g.Sum())); } - public static Dictionary GetConsumedResourceTokens(this TransactionResult transactionResult) + public static Dictionary> GetConsumedResourceTokens(this TransactionResult transactionResult) { var relatedLogs = transactionResult.Logs.Where(l => l.Name == nameof(ResourceTokenCharged)).ToList(); - if (!relatedLogs.Any()) return new Dictionary(); + if (!relatedLogs.Any()) return new Dictionary>(); return relatedLogs.Select(l => ResourceTokenCharged.Parser.ParseFrom(l.NonIndexed)) - .ToDictionary(e => e.Symbol, e => e.Amount); + .GroupBy(g => g.ContractAddress) + .ToDictionary(e => e.Key, + e => e + .GroupBy(fee => fee.Symbol, fee => fee.Amount) + .ToDictionary(g => g.Key, g => g.Sum())); } public static Dictionary GetOwningResourceTokens(this TransactionResult transactionResult) diff --git a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs index c73eee55d6..97dda4aa1c 100644 --- a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs +++ b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using AElf.Types; @@ -7,11 +8,21 @@ public class CalculateTransactionFeeOutput { public bool Success { get; set; } + [Obsolete("ThisField is deprecated, please use NewField instead.")] public Dictionary TransactionFee { get; set; } - + + [Obsolete("ThisField is deprecated, please use NewField instead.")] public Dictionary ResourceFee { get; set; } + + public List TransactionFeeList { get; set; } + + public List ResourceFeeList { get; set; } public string Error { get; set; } - - public string TransactionFeeChargingAddress { get; set; } +} + +public class FeeDto +{ + public string ChargingAddress { get; set; } + public Dictionary Fee { get; set; } } \ No newline at end of file diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs index fac39bbcd2..529ab1448c 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs @@ -297,14 +297,11 @@ private async Task EstimateTransactionFee(Transac var transactionFees = executionReturnSets.FirstOrDefault()?.TransactionResult.GetChargedTransactionFees(); var resourceFees = executionReturnSets.FirstOrDefault()?.TransactionResult.GetConsumedResourceTokens(); - var chargingAddress = transactionFees?.Keys.First(); result.Success = true; - result.TransactionFeeChargingAddress = chargingAddress?.ToBase58(); - if (chargingAddress != null) - { - result.TransactionFee = transactionFees[chargingAddress]; - } - result.ResourceFee = resourceFees; + result.TransactionFee = GetFeeValue(transactionFees); + result.ResourceFee = GetFeeValue(resourceFees); + result.TransactionFeeList = GetFeeList(transactionFees); + result.ResourceFeeList = GetFeeList(resourceFees); } else { @@ -316,6 +313,28 @@ private async Task EstimateTransactionFee(Transac return result; } + private Dictionary GetFeeValue(Dictionary> feeMap) + { + return feeMap?.SelectMany(pair => pair.Value) + .GroupBy(p => p.Key) + .ToDictionary(g => g.Key, g => g.Sum(pair => pair.Value)); + } + + private List GetFeeList(Dictionary> feeMap) + { + var feeList = new List(); + if (feeMap != null) + { + feeList.AddRange(feeMap.Select(fee => new FeeDto + { + ChargingAddress = fee.Key.ToBase58(), + Fee = fee.Value + })); + } + + return feeList; + } + private async Task PublishTransactionsAsync(string[] rawTransactions) { var txIds = new string[rawTransactions.Length]; diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTest.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTest.cs index 6a47875b59..08d30b7593 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTest.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTest.cs @@ -258,8 +258,8 @@ public async Task CompareConsumptions() write.ShouldBeGreaterThan(write1); traffic.ShouldBe(traffic1); - var consumedTokens1 = txResult1.GetConsumedResourceTokens(); - var consumedTokens2 = txResult2.GetConsumedResourceTokens(); + var consumedTokens1 = txResult1.GetConsumedResourceTokens()[DefaultSender]; + var consumedTokens2 = txResult2.GetConsumedResourceTokens()[DefaultSender]; consumedTokens1["READ"].ShouldBeGreaterThan(consumedTokens2["READ"]); consumedTokens1["WRITE"].ShouldBeGreaterThan(consumedTokens2["WRITE"]); consumedTokens1["TRAFFIC"].ShouldBe(consumedTokens2["TRAFFIC"]); diff --git a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs index b898977dc3..33a1e3681e 100644 --- a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs +++ b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs @@ -1682,6 +1682,12 @@ public async Task CalculateTransactionFee_Success_Test() }; var response = await PostResponseAsObjectAsync("/api/blockChain/CalculateTransactionFee", parameters); response.Success.ShouldBe(true); + response.TransactionFeeList[0].ChargingAddress.ShouldBe(transaction.From.ToBase58()); + response.TransactionFeeList[0].Fee.First().Key.ShouldBe("ELF"); + response.TransactionFeeList[0].Fee.First().Value.ShouldBeGreaterThan(10000000L); + response.TransactionFee.First().Key.ShouldBe("ELF"); + response.TransactionFee.First().Value.ShouldBeGreaterThan(10000000L); + } [Fact] From ac5937a3b82447e62a98a3edaf40bd230b3387e5 Mon Sep 17 00:00:00 2001 From: ssun0121 Date: Tue, 28 Nov 2023 11:02:50 +0800 Subject: [PATCH 103/128] feat:Modifying the description. --- .../Dto/CalculateTransactionFeeOutput.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs index 97dda4aa1c..ffbbfc1274 100644 --- a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs +++ b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs @@ -8,10 +8,10 @@ public class CalculateTransactionFeeOutput { public bool Success { get; set; } - [Obsolete("ThisField is deprecated, please use NewField instead.")] + [Obsolete("This property is deprecated and will be removed in the next version. Use the TransactionFeeList instead.")] public Dictionary TransactionFee { get; set; } - [Obsolete("ThisField is deprecated, please use NewField instead.")] + [Obsolete("This property is deprecated and will be removed in the next version. Use the TransactionFeeList instead.")] public Dictionary ResourceFee { get; set; } public List TransactionFeeList { get; set; } From 2c5e90e1231250d0e607a41d0814a5fadf374811 Mon Sep 17 00:00:00 2001 From: ssun0121 Date: Tue, 28 Nov 2023 14:13:27 +0800 Subject: [PATCH 104/128] feat:change output property. --- .../Dto/CalculateTransactionFeeOutput.cs | 8 +++---- .../Services/TransactionAppService.cs | 22 ++++++++----------- .../BlockChainAppServiceTest.cs | 6 ++--- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs index ffbbfc1274..1446177873 100644 --- a/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs +++ b/src/AElf.WebApp.Application.Chain/Dto/CalculateTransactionFeeOutput.cs @@ -8,15 +8,15 @@ public class CalculateTransactionFeeOutput { public bool Success { get; set; } - [Obsolete("This property is deprecated and will be removed in the next version. Use the TransactionFeeList instead.")] + [Obsolete("This property is deprecated and will be removed in the next version. Use the TransactionFees instead.")] public Dictionary TransactionFee { get; set; } - [Obsolete("This property is deprecated and will be removed in the next version. Use the TransactionFeeList instead.")] + [Obsolete("This property is deprecated and will be removed in the next version. Use the ResourceFees instead.")] public Dictionary ResourceFee { get; set; } - public List TransactionFeeList { get; set; } + public FeeDto TransactionFees { get; set; } - public List ResourceFeeList { get; set; } + public FeeDto ResourceFees { get; set; } public string Error { get; set; } } diff --git a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs index 529ab1448c..f4836f8128 100644 --- a/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs +++ b/src/AElf.WebApp.Application.Chain/Services/TransactionAppService.cs @@ -300,8 +300,8 @@ private async Task EstimateTransactionFee(Transac result.Success = true; result.TransactionFee = GetFeeValue(transactionFees); result.ResourceFee = GetFeeValue(resourceFees); - result.TransactionFeeList = GetFeeList(transactionFees); - result.ResourceFeeList = GetFeeList(resourceFees); + result.TransactionFees = GetFee(transactionFees); + result.ResourceFees = GetFee(resourceFees); } else { @@ -320,19 +320,15 @@ private Dictionary GetFeeValue(Dictionary g.Key, g => g.Sum(pair => pair.Value)); } - private List GetFeeList(Dictionary> feeMap) + private FeeDto GetFee(Dictionary> feeMap) { - var feeList = new List(); - if (feeMap != null) + var fee = feeMap?.Select(f => new FeeDto { - feeList.AddRange(feeMap.Select(fee => new FeeDto - { - ChargingAddress = fee.Key.ToBase58(), - Fee = fee.Value - })); - } - - return feeList; + ChargingAddress = f.Key.ToBase58(), + Fee = f.Value + }).FirstOrDefault(); + + return fee; } private async Task PublishTransactionsAsync(string[] rawTransactions) diff --git a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs index 33a1e3681e..f2b2cf456c 100644 --- a/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs +++ b/test/AElf.WebApp.Application.Chain.Tests/BlockChainAppServiceTest.cs @@ -1682,9 +1682,9 @@ public async Task CalculateTransactionFee_Success_Test() }; var response = await PostResponseAsObjectAsync("/api/blockChain/CalculateTransactionFee", parameters); response.Success.ShouldBe(true); - response.TransactionFeeList[0].ChargingAddress.ShouldBe(transaction.From.ToBase58()); - response.TransactionFeeList[0].Fee.First().Key.ShouldBe("ELF"); - response.TransactionFeeList[0].Fee.First().Value.ShouldBeGreaterThan(10000000L); + response.TransactionFees.ChargingAddress.ShouldBe(transaction.From.ToBase58()); + response.TransactionFees.Fee.First().Key.ShouldBe("ELF"); + response.TransactionFees.Fee.First().Value.ShouldBeGreaterThan(10000000L); response.TransactionFee.First().Key.ShouldBe("ELF"); response.TransactionFee.First().Value.ShouldBeGreaterThan(10000000L); From 76ebad20ccc8d00c62b958a946d018c6217cb842 Mon Sep 17 00:00:00 2001 From: ssun0121 Date: Tue, 28 Nov 2023 16:14:11 +0800 Subject: [PATCH 105/128] test:add test. --- .../Infrastructure/CalculateFunctionTest.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs b/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs index 78c3d0419a..211d309fb0 100644 --- a/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs +++ b/test/AElf.Kernel.FeeCalculation.Tests/Infrastructure/CalculateFunctionTest.cs @@ -77,6 +77,18 @@ public void GetChargedTransactionFees_Test() Symbol = "ELF" }.ToLogEvent()); transactionResult.Logs.Add(new TransactionFeeCharged + { + ChargingAddress = SampleAddress.AddressList[0], + Amount = 3, + Symbol = "USDT" + }.ToLogEvent()); + transactionResult.Logs.Add(new TransactionFeeCharged + { + ChargingAddress = SampleAddress.AddressList[0], + Amount = 4, + Symbol = "USDT" + }.ToLogEvent()); + transactionResult.Logs.Add(new TransactionFeeCharged { ChargingAddress = SampleAddress.AddressList[1], Amount = 3, @@ -91,7 +103,8 @@ public void GetChargedTransactionFees_Test() var feeDic = transactionResult.GetChargedTransactionFees(); feeDic.Count.ShouldBe(2); feeDic.Keys.First().ShouldBe(SampleAddress.AddressList[0]); - feeDic.Values.First()["ELF"].ShouldBe(3); + feeDic.Values.First()["ELF"].ShouldBe(3); + feeDic.Values.First()["USDT"].ShouldBe(7); feeDic.Keys.Last().ShouldBe(SampleAddress.AddressList[1]); feeDic.Values.Last()["TEST"].ShouldBe(7); } From d00e5f39c7423e16e3267f1645e5cf594bbb2b12 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Mon, 20 May 2024 11:54:16 +0800 Subject: [PATCH 106/128] Throw exception StateKeyOverSizeException if the length of state key is greater than 255. --- .../ISmartContractBridgeContext.cs | 20 +++++++++++++++++++ .../HostSmartContractBridgeContext.cs | 5 +++++ .../SmartContractConstants.cs | 2 ++ 3 files changed, 27 insertions(+) diff --git a/src/AElf.Kernel.SmartContract.Shared/ISmartContractBridgeContext.cs b/src/AElf.Kernel.SmartContract.Shared/ISmartContractBridgeContext.cs index 5f119f84b5..8840c15b01 100644 --- a/src/AElf.Kernel.SmartContract.Shared/ISmartContractBridgeContext.cs +++ b/src/AElf.Kernel.SmartContract.Shared/ISmartContractBridgeContext.cs @@ -202,4 +202,24 @@ public StateOverSizeException(string message, Exception inner) : base(message, i protected StateOverSizeException(SerializationInfo info, StreamingContext context) : base(info, context) { } +} + +[Serializable] +public class StateKeyOverSizeException : SmartContractBridgeException +{ + public StateKeyOverSizeException() + { + } + + public StateKeyOverSizeException(string message) : base(message) + { + } + + public StateKeyOverSizeException(string message, Exception inner) : base(message, inner) + { + } + + protected StateKeyOverSizeException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } } \ No newline at end of file diff --git a/src/AElf.Kernel.SmartContract/HostSmartContractBridgeContext.cs b/src/AElf.Kernel.SmartContract/HostSmartContractBridgeContext.cs index 5f867b012d..3b723c3227 100644 --- a/src/AElf.Kernel.SmartContract/HostSmartContractBridgeContext.cs +++ b/src/AElf.Kernel.SmartContract/HostSmartContractBridgeContext.cs @@ -111,6 +111,11 @@ public void Initialize(ITransactionContext transactionContext) public async Task GetStateAsync(string key) { + if (key.Length > SmartContractConstants.StateKeyMaximumLength) + { + throw new StateKeyOverSizeException( + $"Length of state key {key} exceeds limit of {SmartContractConstants.StateKeyMaximumLength}."); + } return await _smartContractBridgeService.GetStateAsync( Self, key, CurrentHeight - 1, PreviousBlockHash); } diff --git a/src/AElf.Kernel.SmartContract/SmartContractConstants.cs b/src/AElf.Kernel.SmartContract/SmartContractConstants.cs index 0a6cb05208..abb71f9d20 100644 --- a/src/AElf.Kernel.SmartContract/SmartContractConstants.cs +++ b/src/AElf.Kernel.SmartContract/SmartContractConstants.cs @@ -7,4 +7,6 @@ public class SmartContractConstants public const int ExecutionBranchThreshold = 15000; public const int StateSizeLimit = 128 * 1024; + + public const int StateKeyMaximumLength = 255; } \ No newline at end of file From 67a6d025cb35b04cd38f0c61d4c8c489e3200bd8 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Mon, 20 May 2024 14:44:37 +0800 Subject: [PATCH 107/128] Tune StateKeyMaximumLength --- src/AElf.Kernel.SmartContract/SmartContractConstants.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AElf.Kernel.SmartContract/SmartContractConstants.cs b/src/AElf.Kernel.SmartContract/SmartContractConstants.cs index abb71f9d20..9a210289ce 100644 --- a/src/AElf.Kernel.SmartContract/SmartContractConstants.cs +++ b/src/AElf.Kernel.SmartContract/SmartContractConstants.cs @@ -8,5 +8,6 @@ public class SmartContractConstants public const int StateSizeLimit = 128 * 1024; - public const int StateKeyMaximumLength = 255; + // The prefix `vs` occupies 2 lengths. + public const int StateKeyMaximumLength = 255 - 2; } \ No newline at end of file From c619c66b5488540ad176a0fa8d8d4d8a2812fea3 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Tue, 21 May 2024 09:55:54 +0800 Subject: [PATCH 108/128] Fix TestMappedStateSizeLimit --- .../PatchedContractSecurityTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/AElf.Contracts.TestContract.Tests/PatchedContractSecurityTests.cs b/test/AElf.Contracts.TestContract.Tests/PatchedContractSecurityTests.cs index 587a9fff7a..b2c8df1424 100644 --- a/test/AElf.Contracts.TestContract.Tests/PatchedContractSecurityTests.cs +++ b/test/AElf.Contracts.TestContract.Tests/PatchedContractSecurityTests.cs @@ -262,7 +262,7 @@ await TestBasicSecurityContractStub.TestMappedState.SendAsync(new ProtobufInput StringValue = str } }); - txResult.TransactionResult.Error.ShouldContain($"exceeds limit of {stateSizeLimit}"); + txResult.TransactionResult.Error.ShouldContain($"exceeds limit"); var str1 = Encoding.UTF8.GetString(new byte[10]); var message = new ProtobufMessage @@ -309,7 +309,7 @@ await TestBasicSecurityContractStub.TestMapped2State.SendAsync(new ProtobufInput { ProtobufValue = new ProtobufMessage() }); - txResult.TransactionResult.Error.ShouldContain($"exceeds limit of {stateSizeLimit}"); + txResult.TransactionResult.Error.ShouldContain($"exceeds limit"); var str1 = Encoding.UTF8.GetString(new byte[10]); var message = new ProtobufMessage From 8f91a6ac2eb7002a0a9edbbbd39bef0e6aa21938 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Tue, 21 May 2024 13:01:47 +0800 Subject: [PATCH 109/128] Fix CompareConsumptions test --- .../ExecutionPluginForResourceFeeTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTest.cs b/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTest.cs index 08d30b7593..af015ab1bb 100644 --- a/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTest.cs +++ b/test/AElf.Kernel.SmartContract.ExecutionPluginForResourceFee.Tests/ExecutionPluginForResourceFeeTest.cs @@ -258,8 +258,8 @@ public async Task CompareConsumptions() write.ShouldBeGreaterThan(write1); traffic.ShouldBe(traffic1); - var consumedTokens1 = txResult1.GetConsumedResourceTokens()[DefaultSender]; - var consumedTokens2 = txResult2.GetConsumedResourceTokens()[DefaultSender]; + var consumedTokens1 = txResult1.GetConsumedResourceTokens()[TestContractAddress]; + var consumedTokens2 = txResult2.GetConsumedResourceTokens()[TestContractAddress]; consumedTokens1["READ"].ShouldBeGreaterThan(consumedTokens2["READ"]); consumedTokens1["WRITE"].ShouldBeGreaterThan(consumedTokens2["WRITE"]); consumedTokens1["TRAFFIC"].ShouldBe(consumedTokens2["TRAFFIC"]); From e337b4e041ce1d05db93d5b13ba6af828f620f5d Mon Sep 17 00:00:00 2001 From: eanzhao Date: Mon, 24 Jun 2024 18:36:49 +0800 Subject: [PATCH 110/128] Add title and description filed to proposals. --- .../AElf.Contracts.Association/Association.cs | 4 +++- .../Association_Helper.cs | 8 ++++++-- .../AElf.Contracts.Parliament/Parliament.cs | 4 +++- .../Parliament_Helper.cs | 11 +++++++++-- .../AElf.Contracts.Referendum/Referendum.cs | 4 +++- .../Referendum_Helper.cs | 12 ++++++++++-- protobuf/acs3.proto | 18 +++++++++++++++++- protobuf/association_contract.proto | 4 ++++ protobuf/parliament_contract.proto | 4 ++++ protobuf/referendum_contract.proto | 4 ++++ .../AssociationContractTests.cs | 6 +++++- 11 files changed, 68 insertions(+), 11 deletions(-) diff --git a/contract/AElf.Contracts.Association/Association.cs b/contract/AElf.Contracts.Association/Association.cs index db9a2d81dd..f00b978f27 100644 --- a/contract/AElf.Contracts.Association/Association.cs +++ b/contract/AElf.Contracts.Association/Association.cs @@ -35,7 +35,9 @@ public override ProposalOutput GetProposal(Hash proposalId) ToBeReleased = readyToRelease, ApprovalCount = proposal.Approvals.Count, RejectionCount = proposal.Rejections.Count, - AbstentionCount = proposal.Abstentions.Count + AbstentionCount = proposal.Abstentions.Count, + Title = proposal.Title, + Description = proposal.Description }; } diff --git a/contract/AElf.Contracts.Association/Association_Helper.cs b/contract/AElf.Contracts.Association/Association_Helper.cs index c246df26ee..4cde797b85 100644 --- a/contract/AElf.Contracts.Association/Association_Helper.cs +++ b/contract/AElf.Contracts.Association/Association_Helper.cs @@ -154,7 +154,9 @@ private Hash CreateNewProposal(CreateProposalInput input) OrganizationAddress = input.OrganizationAddress, ProposalId = proposalId, Proposer = Context.Sender, - ProposalDescriptionUrl = input.ProposalDescriptionUrl + ProposalDescriptionUrl = input.ProposalDescriptionUrl, + Title = input.Title, + Description = input.Description }; Assert(Validate(proposal), "Invalid proposal."); Assert(State.Proposals[proposalId] == null, "Proposal already exists."); @@ -162,7 +164,9 @@ private Hash CreateNewProposal(CreateProposalInput input) Context.Fire(new ProposalCreated { ProposalId = proposalId, - OrganizationAddress = input.OrganizationAddress + OrganizationAddress = input.OrganizationAddress, + Title = input.Title, + Description = input.Description }); return proposalId; } diff --git a/contract/AElf.Contracts.Parliament/Parliament.cs b/contract/AElf.Contracts.Parliament/Parliament.cs index 98ae606896..7e52c07d2d 100644 --- a/contract/AElf.Contracts.Parliament/Parliament.cs +++ b/contract/AElf.Contracts.Parliament/Parliament.cs @@ -241,7 +241,9 @@ public override ProposalOutput GetProposal(Hash proposalId) ToBeReleased = Validate(proposal) && IsReleaseThresholdReached(proposal, organization), ApprovalCount = proposal.Approvals.Count, RejectionCount = proposal.Rejections.Count, - AbstentionCount = proposal.Abstentions.Count + AbstentionCount = proposal.Abstentions.Count, + Title = proposal.Title, + Description = proposal.Description }; } diff --git a/contract/AElf.Contracts.Parliament/Parliament_Helper.cs b/contract/AElf.Contracts.Parliament/Parliament_Helper.cs index 7e1a763d2b..3a2d664260 100644 --- a/contract/AElf.Contracts.Parliament/Parliament_Helper.cs +++ b/contract/AElf.Contracts.Parliament/Parliament_Helper.cs @@ -234,13 +234,20 @@ private Hash CreateNewProposal(CreateProposalInput input) OrganizationAddress = input.OrganizationAddress, ProposalId = proposalId, Proposer = Context.Sender, - ProposalDescriptionUrl = input.ProposalDescriptionUrl + ProposalDescriptionUrl = input.ProposalDescriptionUrl, + Title = input.Title, + Description = input.Description }; Assert(Validate(proposal), "Invalid proposal."); Assert(State.Proposals[proposalId] == null, "Proposal already exists."); State.Proposals[proposalId] = proposal; Context.Fire(new ProposalCreated - { ProposalId = proposalId, OrganizationAddress = input.OrganizationAddress }); + { + ProposalId = proposalId, + OrganizationAddress = input.OrganizationAddress, + Title = input.Title, + Description = input.Description + }); return proposalId; } diff --git a/contract/AElf.Contracts.Referendum/Referendum.cs b/contract/AElf.Contracts.Referendum/Referendum.cs index f6112e2827..b2d63bf78f 100644 --- a/contract/AElf.Contracts.Referendum/Referendum.cs +++ b/contract/AElf.Contracts.Referendum/Referendum.cs @@ -202,7 +202,9 @@ public override ProposalOutput GetProposal(Hash proposalId) ToBeReleased = readyToRelease, ApprovalCount = proposal.ApprovalCount, RejectionCount = proposal.RejectionCount, - AbstentionCount = proposal.AbstentionCount + AbstentionCount = proposal.AbstentionCount, + Title = proposal.Title, + Description = proposal.Description }; } diff --git a/contract/AElf.Contracts.Referendum/Referendum_Helper.cs b/contract/AElf.Contracts.Referendum/Referendum_Helper.cs index 02b00d07c1..ef83167322 100644 --- a/contract/AElf.Contracts.Referendum/Referendum_Helper.cs +++ b/contract/AElf.Contracts.Referendum/Referendum_Helper.cs @@ -168,11 +168,19 @@ private Hash CreateNewProposal(CreateProposalInput input) Params = input.Params, OrganizationAddress = input.OrganizationAddress, Proposer = Context.Sender, - ProposalDescriptionUrl = input.ProposalDescriptionUrl + ProposalDescriptionUrl = input.ProposalDescriptionUrl, + Title = input.Title, + Description = input.Description }; Assert(Validate(proposal), "Invalid proposal."); State.Proposals[proposalId] = proposal; - Context.Fire(new ProposalCreated { ProposalId = proposalId, OrganizationAddress = input.OrganizationAddress }); + Context.Fire(new ProposalCreated + { + ProposalId = proposalId, + OrganizationAddress = input.OrganizationAddress, + Title = input.Title, + Description = input.Description + }); return proposalId; } diff --git a/protobuf/acs3.proto b/protobuf/acs3.proto index 65ee66e8ac..79b3391202 100644 --- a/protobuf/acs3.proto +++ b/protobuf/acs3.proto @@ -90,6 +90,10 @@ message CreateProposalInput { string proposal_description_url = 6; // The token is for proposal id generation and with this token, proposal id can be calculated before proposing. aelf.Hash token = 7; + // Title of this proposal. + string title = 8; + // Description of this proposal. + string description = 9; } message ProposalOutput { @@ -115,6 +119,10 @@ message ProposalOutput { int64 rejection_count = 10; // Abstention count for this proposal. int64 abstention_count = 11; + // Title of this proposal. + string title = 12; + // Description of this proposal. + string description = 13; } message ProposalReleaseThreshold { @@ -159,7 +167,11 @@ message ProposalCreated{ // The id of the created proposal. aelf.Hash proposal_id = 1; // The organization address of the created proposal. - aelf.Address organization_address=2 [(aelf.is_indexed) = true]; + aelf.Address organization_address = 2 [(aelf.is_indexed) = true]; + // Title of this proposal. + string title = 3; + // Description of this proposal. + string description = 4; } message ProposalReleased{ @@ -168,6 +180,10 @@ message ProposalReleased{ aelf.Hash proposal_id = 1; // The organization address of the released proposal. aelf.Address organization_address=2 [(aelf.is_indexed) = true]; + // Title of this proposal. + string title = 3; + // Description of this proposal. + string description = 4; } message OrganizationCreated{ diff --git a/protobuf/association_contract.proto b/protobuf/association_contract.proto index 2efb8210c6..5b2a78e2b7 100644 --- a/protobuf/association_contract.proto +++ b/protobuf/association_contract.proto @@ -96,6 +96,10 @@ message ProposalInfo { repeated aelf.Address abstentions = 10; // Url is used for proposal describing. string proposal_description_url = 11; + // Title of this proposal. + string title = 12; + // Description of this proposal. + string description = 13; } message OrganizationMemberList { diff --git a/protobuf/parliament_contract.proto b/protobuf/parliament_contract.proto index e5d2ef4422..8d0fca5bef 100644 --- a/protobuf/parliament_contract.proto +++ b/protobuf/parliament_contract.proto @@ -136,6 +136,10 @@ message ProposalInfo { repeated aelf.Address abstentions = 10; // Url is used for proposal describing. string proposal_description_url = 11; + // Title of this proposal. + string title = 12; + // Description of this proposal. + string description = 13; } message InitializeInput{ diff --git a/protobuf/referendum_contract.proto b/protobuf/referendum_contract.proto index 82af016f13..53bb243b6e 100644 --- a/protobuf/referendum_contract.proto +++ b/protobuf/referendum_contract.proto @@ -101,6 +101,10 @@ message ProposalInfo { int64 abstention_count = 10; // Url is used for proposal describing. string proposal_description_url = 11; + // Title of this proposal. + string title = 12; + // Description of this proposal. + string description = 13; } message CreateOrganizationBySystemContractInput { diff --git a/test/AElf.Contracts.Association.Tests/AssociationContractTests.cs b/test/AElf.Contracts.Association.Tests/AssociationContractTests.cs index 0da11d0fa3..89ec1025ed 100644 --- a/test/AElf.Contracts.Association.Tests/AssociationContractTests.cs +++ b/test/AElf.Contracts.Association.Tests/AssociationContractTests.cs @@ -169,6 +169,8 @@ public async Task Get_Proposal_Test() getProposal.Output.OrganizationAddress.ShouldBe(organizationAddress); getProposal.Output.ToAddress.ShouldBe(TokenContractAddress); getProposal.Output.Params.ShouldBe(transferInput.ToByteString()); + getProposal.Output.Title.ShouldNotBeNullOrEmpty(); + getProposal.Output.Description.ShouldNotBeNullOrEmpty(); } } @@ -1231,7 +1233,9 @@ private async Task CreateProposalAsync(ECKeyPair proposalKeyPair, Address ToAddress = TokenContractAddress, Params = transferInput.ToByteString(), ExpiredTime = BlockTimeProvider.GetBlockTime().AddDays(2), - OrganizationAddress = organizationAddress + OrganizationAddress = organizationAddress, + Title = "Token Transfer", + Description = "Transfer 100 ELF to Reviewer1's address", }; var proposal = await associationContractStub.CreateProposal.SendAsync(createProposalInput); var proposalCreated = ProposalCreated.Parser.ParseFrom(proposal.TransactionResult.Logs From b37568d8899c2a2689ead7a1a786d51e6fe9f786 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Tue, 25 Jun 2024 10:46:14 +0800 Subject: [PATCH 111/128] Add limit to the length of title and description of proposals. --- .../AElf.Contracts.Association/AssociationConstants.cs | 7 +++++++ .../AElf.Contracts.Association/Association_Helper.cs | 9 +++++++++ .../AElf.Contracts.Parliament/ParliamentConstants.cs | 7 +++++++ contract/AElf.Contracts.Parliament/Parliament_Helper.cs | 9 +++++++++ .../AElf.Contracts.Referendum/ReferendumConstants.cs | 7 +++++++ contract/AElf.Contracts.Referendum/Referendum_Helper.cs | 9 +++++++++ 6 files changed, 48 insertions(+) create mode 100644 contract/AElf.Contracts.Association/AssociationConstants.cs create mode 100644 contract/AElf.Contracts.Parliament/ParliamentConstants.cs create mode 100644 contract/AElf.Contracts.Referendum/ReferendumConstants.cs diff --git a/contract/AElf.Contracts.Association/AssociationConstants.cs b/contract/AElf.Contracts.Association/AssociationConstants.cs new file mode 100644 index 0000000000..46cbe81529 --- /dev/null +++ b/contract/AElf.Contracts.Association/AssociationConstants.cs @@ -0,0 +1,7 @@ +namespace AElf.Contracts.Association; + +public static class AssociationConstants +{ + public const int MaxLengthForTitle = 255; + public const int MaxLengthForDescription = 10200; +} \ No newline at end of file diff --git a/contract/AElf.Contracts.Association/Association_Helper.cs b/contract/AElf.Contracts.Association/Association_Helper.cs index 4cde797b85..277071e28f 100644 --- a/contract/AElf.Contracts.Association/Association_Helper.cs +++ b/contract/AElf.Contracts.Association/Association_Helper.cs @@ -144,6 +144,7 @@ private Hash GenerateProposalId(CreateProposalInput input) private Hash CreateNewProposal(CreateProposalInput input) { + CheckCreateProposalInput(input); var proposalId = GenerateProposalId(input); var proposal = new ProposalInfo { @@ -170,4 +171,12 @@ private Hash CreateNewProposal(CreateProposalInput input) }); return proposalId; } + + private void CheckCreateProposalInput(CreateProposalInput input) + { + // Check the length of title + Assert(input.Title.Length <= AssociationConstants.MaxLengthForTitle, "Title is too long."); + // Check the length of description + Assert(input.Title.Length <= AssociationConstants.MaxLengthForDescription, "Description is too long."); + } } \ No newline at end of file diff --git a/contract/AElf.Contracts.Parliament/ParliamentConstants.cs b/contract/AElf.Contracts.Parliament/ParliamentConstants.cs new file mode 100644 index 0000000000..a5026afd21 --- /dev/null +++ b/contract/AElf.Contracts.Parliament/ParliamentConstants.cs @@ -0,0 +1,7 @@ +namespace AElf.Contracts.Parliament; + +public static class ParliamentConstants +{ + public const int MaxLengthForTitle = 255; + public const int MaxLengthForDescription = 10200; +} \ No newline at end of file diff --git a/contract/AElf.Contracts.Parliament/Parliament_Helper.cs b/contract/AElf.Contracts.Parliament/Parliament_Helper.cs index 3a2d664260..56342ef679 100644 --- a/contract/AElf.Contracts.Parliament/Parliament_Helper.cs +++ b/contract/AElf.Contracts.Parliament/Parliament_Helper.cs @@ -224,6 +224,7 @@ private Hash GenerateProposalId(CreateProposalInput input) private Hash CreateNewProposal(CreateProposalInput input) { + CheckCreateProposalInput(input); var proposalId = GenerateProposalId(input); var proposal = new ProposalInfo { @@ -250,6 +251,14 @@ private Hash CreateNewProposal(CreateProposalInput input) }); return proposalId; } + + private void CheckCreateProposalInput(CreateProposalInput input) + { + // Check the length of title + Assert(input.Title.Length <= ParliamentConstants.MaxLengthForTitle, "Title is too long."); + // Check the length of description + Assert(input.Title.Length <= ParliamentConstants.MaxLengthForDescription, "Description is too long."); + } private Address CreateNewOrganization(CreateOrganizationInput input) { diff --git a/contract/AElf.Contracts.Referendum/ReferendumConstants.cs b/contract/AElf.Contracts.Referendum/ReferendumConstants.cs new file mode 100644 index 0000000000..fd409fccc4 --- /dev/null +++ b/contract/AElf.Contracts.Referendum/ReferendumConstants.cs @@ -0,0 +1,7 @@ +namespace AElf.Contracts.Referendum; + +public static class ReferendumConstants +{ + public const int MaxLengthForTitle = 255; + public const int MaxLengthForDescription = 10200; +} \ No newline at end of file diff --git a/contract/AElf.Contracts.Referendum/Referendum_Helper.cs b/contract/AElf.Contracts.Referendum/Referendum_Helper.cs index ef83167322..31959fc451 100644 --- a/contract/AElf.Contracts.Referendum/Referendum_Helper.cs +++ b/contract/AElf.Contracts.Referendum/Referendum_Helper.cs @@ -158,6 +158,7 @@ private Hash GenerateProposalId(CreateProposalInput input) private Hash CreateNewProposal(CreateProposalInput input) { + CheckCreateProposalInput(input); var proposalId = GenerateProposalId(input); Assert(State.Proposals[proposalId] == null, "Proposal already exists."); var proposal = new ProposalInfo @@ -184,6 +185,14 @@ private Hash CreateNewProposal(CreateProposalInput input) return proposalId; } + + private void CheckCreateProposalInput(CreateProposalInput input) + { + // Check the length of title + Assert(input.Title.Length <= ReferendumConstants.MaxLengthForTitle, "Title is too long."); + // Check the length of description + Assert(input.Title.Length <= ReferendumConstants.MaxLengthForDescription, "Description is too long."); + } private void AssertIsAuthorizedProposer(Address organizationAddress, Address proposer) { From baa517845141077fa5944f469ebac842a6cf7558 Mon Sep 17 00:00:00 2001 From: JimAelf <165892081+JimAelf@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:41:15 +0800 Subject: [PATCH 112/128] update issue template update issue template --- .github/ISSUE_TEMPLATE/bug_report.md | 42 ++++++++++++----------- .github/ISSUE_TEMPLATE/config.yml | 10 ++++++ .github/ISSUE_TEMPLATE/feature_request.md | 29 ++++++++++------ 3 files changed, 50 insertions(+), 31 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 6e4b2c3526..ea59817cf7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,31 +1,33 @@ --- -name: Bug report -about: Create a report to help us improve +name: 👾 Bug Report +about: Report a bug or issue with the project. title: '' -labels: '' +labels: 'bug' assignees: '' --- -**Describe the bug** +### Description A clear and concise description of what the bug is. -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error +### Steps To Reproduce +1. Log in... +2. Ensure that... +3. Allow a long period of inactivity to pass... +4. Observe that... +5. Attempt to log in... -**Expected behavior** -A clear and concise description of what you expected to happen. +### Current Behavior +- After the period of inactivity... +- When the user tries to log in using another method... +- This causes a bug due to... -**Screenshots** -If applicable, add screenshots to help explain your problem. +### Expected Behavior +- After a long period of inactivity... +- When a user logs in successfully... +- This ensures that only... -**Environment (please complete the following information):** - - OS and version: [e.g. Ubuntu 18.04] - - Dotnet core version [e.g. 2.2.106] - -**Additional context** -Add any other context about the problem here. +### Environment +- Platform: PC +- Node: v18.18.0 +- Browser: Chrome 126.0.6478.56 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..163c1c437c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,10 @@ +blank_issues_enabled: false +issue_template: + - name: 👾 Bug Report + description: Report a bug or issue with the project. + labels: ["bug"] + template: bug_report.md + - name: 💡 Feature Request + description: Create a new ticket for a new feature request. + labels: ["enhancement"] + template: feature_request.md \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7d61..a4b817533a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,20 +1,27 @@ --- -name: Feature request -about: Suggest an idea for this project +name: 💡 Feature Request +about: Create a new ticket for a new feature request title: '' -labels: '' +labels: 'enhancement' assignees: '' --- -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] +### Expected Behavior +Describe the expected behavior here. -**Describe the solution you'd like** -A clear and concise description of what you want to happen. +### Specifications +As a `user`, I would like to `action` so that `reason`. -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. +**Features:** +- describe feature details here. -**Additional context** -Add any other context or screenshots about the feature request here. +**Development Tasks:** +- [ ] Task 1 +- [ ] Task 2 + +### Dependencies +List any dependencies that are required for this feature by providing links to the issues or repositories. + +### References +List any references that are related to this feature request. \ No newline at end of file From 612468d739cd80eac0250b0a7d451d684809ba55 Mon Sep 17 00:00:00 2001 From: "chopin.fan" Date: Thu, 27 Jun 2024 09:42:59 +0800 Subject: [PATCH 113/128] Add a SonarQube configuration file to implement static code analysis. --- .github/workflows/sonarqube.yaml | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/sonarqube.yaml diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml new file mode 100644 index 0000000000..0209b437e1 --- /dev/null +++ b/.github/workflows/sonarqube.yaml @@ -0,0 +1,42 @@ +on: + pull_request: + types: [opened, synchronize, reopened] + +name: PR Static Code Analysis +jobs: + static-code-analysis: + runs-on: ubuntu-latest + steps: + - name: Code Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: '7.0' + - name: Set up JDK 17 + uses: actions/setup-java@v1 + with: + java-version: 17 + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache SonarQube scanner + id: cache-sonar-scanner + uses: actions/cache@v1 + with: + path: ./.sonar/scanner + key: ${{ runner.os }}-sonar-scanner + restore-keys: ${{ runner.os }}-sonar-scanner + - name: Install SonarScanner for .NET + run: dotnet tool update dotnet-sonarscanner --tool-path ./.sonar/scanner + - name: Add .NET global tools to PATH + run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH + - name: Begin SonarQube analysis + run: | + ./.sonar/scanner/dotnet-sonarscanner begin /k:"AElf" /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" + dotnet build + ./.sonar/scanner/dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" \ No newline at end of file From 8010f68613d900029d12286927de3ccf010cef9b Mon Sep 17 00:00:00 2001 From: "chopin.fan" Date: Thu, 27 Jun 2024 14:35:00 +0800 Subject: [PATCH 114/128] To clarify the specific project to be built. --- .github/workflows/sonarqube.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml index 0209b437e1..4f021c18b0 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarqube.yaml @@ -38,5 +38,5 @@ jobs: - name: Begin SonarQube analysis run: | ./.sonar/scanner/dotnet-sonarscanner begin /k:"AElf" /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" - dotnet build - ./.sonar/scanner/dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" \ No newline at end of file + dotnet build AElf.All.sln + ./.sonar/scanner/dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" From 98846f27a59c747a1a24ade0be29a6938405c369 Mon Sep 17 00:00:00 2001 From: "chopin.fan" Date: Thu, 27 Jun 2024 14:50:24 +0800 Subject: [PATCH 115/128] Add protobuf in sonarqube.yaml --- .github/workflows/sonarqube.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml index 4f021c18b0..19c1f8357b 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarqube.yaml @@ -35,8 +35,10 @@ jobs: run: dotnet tool update dotnet-sonarscanner --tool-path ./.sonar/scanner - name: Add .NET global tools to PATH run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH + - name: Install protobuf + run: sudo apt-get update && sudo apt-get install -y protobuf-compiler - name: Begin SonarQube analysis run: | ./.sonar/scanner/dotnet-sonarscanner begin /k:"AElf" /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" dotnet build AElf.All.sln - ./.sonar/scanner/dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" + ./.sonar/scanner/dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" \ No newline at end of file From 784bcb5a0d02ff65eebf80edf4525662aa8c4669 Mon Sep 17 00:00:00 2001 From: "chopin.fan" Date: Thu, 27 Jun 2024 15:01:24 +0800 Subject: [PATCH 116/128] Change the version of Protobuf to 3.19.4 --- .github/workflows/sonarqube.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml index 19c1f8357b..d4066c57ad 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarqube.yaml @@ -35,8 +35,10 @@ jobs: run: dotnet tool update dotnet-sonarscanner --tool-path ./.sonar/scanner - name: Add .NET global tools to PATH run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH - - name: Install protobuf - run: sudo apt-get update && sudo apt-get install -y protobuf-compiler + - name: Install Protoc + uses: arduino/setup-protoc@v3 + with: + version: "19.4" - name: Begin SonarQube analysis run: | ./.sonar/scanner/dotnet-sonarscanner begin /k:"AElf" /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" From 66de35c0b59b8a017198007eb101f7521d364769 Mon Sep 17 00:00:00 2001 From: "chopin.fan" Date: Thu, 27 Jun 2024 15:03:40 +0800 Subject: [PATCH 117/128] Change the version of Protobuf to 3.19.4 --- .github/workflows/sonarqube.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml index d4066c57ad..abc7545e76 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarqube.yaml @@ -38,7 +38,7 @@ jobs: - name: Install Protoc uses: arduino/setup-protoc@v3 with: - version: "19.4" + version: "3.19.4" - name: Begin SonarQube analysis run: | ./.sonar/scanner/dotnet-sonarscanner begin /k:"AElf" /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" From 6b701bff8a99afbd7703fda35d8d03a535288653 Mon Sep 17 00:00:00 2001 From: "chopin.fan" Date: Thu, 27 Jun 2024 15:11:33 +0800 Subject: [PATCH 118/128] Change the version of Protobuf to 3.19.4 --- .github/workflows/sonarqube.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml index abc7545e76..29ec611228 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarqube.yaml @@ -35,10 +35,10 @@ jobs: run: dotnet tool update dotnet-sonarscanner --tool-path ./.sonar/scanner - name: Add .NET global tools to PATH run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH - - name: Install Protoc - uses: arduino/setup-protoc@v3 - with: - version: "3.19.4" + - name: Install Protobuf 3.19.4 + run: | + sudo apt-get update + sudo apt-get install -y protobuf-compiler=3.19.4 - name: Begin SonarQube analysis run: | ./.sonar/scanner/dotnet-sonarscanner begin /k:"AElf" /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" From 54d980ee2c0cab70df6ee42d0442c8a689baaefe Mon Sep 17 00:00:00 2001 From: "chopin.fan" Date: Thu, 27 Jun 2024 15:13:26 +0800 Subject: [PATCH 119/128] Change the version of Protobuf to 3.19.4 --- .github/workflows/sonarqube.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml index 29ec611228..1f70cc8d32 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarqube.yaml @@ -38,7 +38,7 @@ jobs: - name: Install Protobuf 3.19.4 run: | sudo apt-get update - sudo apt-get install -y protobuf-compiler=3.19.4 + sudo apt-get install -y protobuf-compiler=3.19.4-1 - name: Begin SonarQube analysis run: | ./.sonar/scanner/dotnet-sonarscanner begin /k:"AElf" /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" From 9f3b2766b6d9825dc545b27985af4f4431fc997a Mon Sep 17 00:00:00 2001 From: "chopin.fan" Date: Thu, 27 Jun 2024 15:35:22 +0800 Subject: [PATCH 120/128] Change the version of Protobuf to 3.19.4 --- .github/workflows/sonarqube.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml index 1f70cc8d32..32bcb0fbd2 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarqube.yaml @@ -35,10 +35,11 @@ jobs: run: dotnet tool update dotnet-sonarscanner --tool-path ./.sonar/scanner - name: Add .NET global tools to PATH run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH - - name: Install Protobuf 3.19.4 + - name: Install Protobuf for C# run: | - sudo apt-get update - sudo apt-get install -y protobuf-compiler=3.19.4-1 + dotnet tool install --global Grpc.Tools --version 2.51.0 + dotnet tool install --global Google.Protobuf.Tools --version 3.19.4 + echo "$HOME/.dotnet/tools" >> $GITHUB_PATH - name: Begin SonarQube analysis run: | ./.sonar/scanner/dotnet-sonarscanner begin /k:"AElf" /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" From 4006f5b2239442372183175aa918e90cdbe24c5c Mon Sep 17 00:00:00 2001 From: "chopin.fan" Date: Thu, 27 Jun 2024 15:38:45 +0800 Subject: [PATCH 121/128] Change the version of Protobuf to 3.19.4 --- .github/workflows/sonarqube.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml index 32bcb0fbd2..19c1f8357b 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarqube.yaml @@ -35,11 +35,8 @@ jobs: run: dotnet tool update dotnet-sonarscanner --tool-path ./.sonar/scanner - name: Add .NET global tools to PATH run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH - - name: Install Protobuf for C# - run: | - dotnet tool install --global Grpc.Tools --version 2.51.0 - dotnet tool install --global Google.Protobuf.Tools --version 3.19.4 - echo "$HOME/.dotnet/tools" >> $GITHUB_PATH + - name: Install protobuf + run: sudo apt-get update && sudo apt-get install -y protobuf-compiler - name: Begin SonarQube analysis run: | ./.sonar/scanner/dotnet-sonarscanner begin /k:"AElf" /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" From 6540d8fba5d1be51d345d638e00b4fef6247018d Mon Sep 17 00:00:00 2001 From: "chopin.fan" Date: Thu, 27 Jun 2024 15:44:16 +0800 Subject: [PATCH 122/128] Change dotnet-version to 6. --- .github/workflows/sonarqube.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml index 19c1f8357b..cf33b0dd86 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarqube.yaml @@ -13,7 +13,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-dotnet@v4 with: - dotnet-version: '7.0' + dotnet-version: '6.0' - name: Set up JDK 17 uses: actions/setup-java@v1 with: From b41e056de354f631fc0deec35d3df639f29e50bd Mon Sep 17 00:00:00 2001 From: "chopin.fan" Date: Thu, 27 Jun 2024 16:04:54 +0800 Subject: [PATCH 123/128] dotnet-version: '6.0' -> dotnet-version: '7.0' --- .github/workflows/sonarqube.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml index cf33b0dd86..19c1f8357b 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarqube.yaml @@ -13,7 +13,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-dotnet@v4 with: - dotnet-version: '6.0' + dotnet-version: '7.0' - name: Set up JDK 17 uses: actions/setup-java@v1 with: From 39d6aa122c268803ba7b8a348270bda1e374f444 Mon Sep 17 00:00:00 2001 From: "chopin.fan" Date: Thu, 27 Jun 2024 16:16:40 +0800 Subject: [PATCH 124/128] Change the version of Protobuf to 3.19.4 --- .github/workflows/sonarqube.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml index 19c1f8357b..01e315a740 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarqube.yaml @@ -14,6 +14,8 @@ jobs: - uses: actions/setup-dotnet@v4 with: dotnet-version: '7.0' + - name: Create temporary global.json + run: echo '{"sdk":{"version":"7.0.410"}}' > ./global.json - name: Set up JDK 17 uses: actions/setup-java@v1 with: @@ -41,4 +43,7 @@ jobs: run: | ./.sonar/scanner/dotnet-sonarscanner begin /k:"AElf" /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" dotnet build AElf.All.sln - ./.sonar/scanner/dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" \ No newline at end of file + ./.sonar/scanner/dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" + + + \ No newline at end of file From 1b650b1d1d0728014972b1a39cc821a996ffc3bc Mon Sep 17 00:00:00 2001 From: jason20240511 Date: Thu, 27 Jun 2024 16:49:38 +0800 Subject: [PATCH 125/128] feat:add a benchmark github action. --- .github/workflows/benchmark_action.yml | 48 ++++++++++++ bench/AElf.Benchmark/HtmlSummaryExporter.cs | 82 +++++++++++++++++++++ bench/AElf.Benchmark/Program.cs | 13 +++- 3 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/benchmark_action.yml create mode 100644 bench/AElf.Benchmark/HtmlSummaryExporter.cs diff --git a/.github/workflows/benchmark_action.yml b/.github/workflows/benchmark_action.yml new file mode 100644 index 0000000000..1717d86906 --- /dev/null +++ b/.github/workflows/benchmark_action.yml @@ -0,0 +1,48 @@ +name: benchmark_action +on: + push: + tags: + - '**' + branches: + - '**' + + + +env: + DOTNET_INSTALL_DIR: "./.dotnet" + Solution_Name: AElf.All.sln + Service_Name: AELF + +jobs: + test: + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup dotnet + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '6.0' + + - name: 'Download AElf build tools' + run: bash scripts/download_binary.sh + + - name: 'Install protobuf' + run: bash scripts/install_protobuf.sh + + - name: Install dependencies + run: dotnet restore bench/AElf.Benchmark/AElf.Benchmark.csproj --verbosity quiet + + - name: BenchMark + run: | + cd bench/AElf.Benchmark + dotnet run --filter '*MinerTests*' + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ env.Service_Name }} + path: bench/AElf.Benchmark/BenchmarkDotNet.Artifacts/results + retention-days: 30 \ No newline at end of file diff --git a/bench/AElf.Benchmark/HtmlSummaryExporter.cs b/bench/AElf.Benchmark/HtmlSummaryExporter.cs new file mode 100644 index 0000000000..1b9f7756c1 --- /dev/null +++ b/bench/AElf.Benchmark/HtmlSummaryExporter.cs @@ -0,0 +1,82 @@ +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Reports; + +using System.Collections.Generic; +using System.IO; + + +public class HtmlSummaryExporter : IExporter +{ + public string Name => nameof(HtmlSummaryExporter); + + public void ExportToLog(Summary summary, ILogger logger) + { + + } + + public IEnumerable ExportToFiles(Summary summary, ILogger consoleLogger) + { + string directoryPath = summary.ResultsDirectoryPath; + string outputPath = Path.Combine(directoryPath, "Summary.html"); + + var htmlFiles = Directory.GetFiles(directoryPath, "*.html"); + + using (StreamWriter writer = new StreamWriter(outputPath)) + { + writer.WriteLine(""); + writer.WriteLine(""); + writer.WriteLine("Benchmark Summary"); + + writer.WriteLine(""); + + writer.WriteLine(""); + writer.WriteLine(""); + + foreach (var file in htmlFiles) + { + string fileName = Path.GetFileName(file); + writer.WriteLine($"

{fileName}

"); + string content = File.ReadAllText(file); + string bodyContent = GetBodyContent(content); + writer.WriteLine(bodyContent); + } + + writer.WriteLine(""); + writer.WriteLine(""); + } + + consoleLogger.WriteLine($"Summary HTML file created successfully at {outputPath}."); + + return new[] { outputPath }; + } + + private string GetBodyContent(string html) + { + int bodyStartIndex = html.IndexOf("") + "".Length; + int bodyEndIndex = html.IndexOf(""); + if (bodyStartIndex >= 0 && bodyEndIndex >= 0) + { + return html.Substring(bodyStartIndex, bodyEndIndex - bodyStartIndex); + } + return string.Empty; + } + + private string GetStyleContent(string html) + { + int styleStartIndex = html.IndexOf(""); + if (styleStartIndex >= 0 && styleEndIndex >= 0) + { + return html.Substring(styleStartIndex, styleEndIndex - styleStartIndex); + } + return string.Empty; + } +} \ No newline at end of file diff --git a/bench/AElf.Benchmark/Program.cs b/bench/AElf.Benchmark/Program.cs index bc0d10d859..9d71a23611 100644 --- a/bench/AElf.Benchmark/Program.cs +++ b/bench/AElf.Benchmark/Program.cs @@ -2,6 +2,10 @@ using System.IO; using System.Reflection; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Exporters.Csv; +using BenchmarkDotNet.Exporters.Xml; +using BenchmarkDotNet.Reports; using BenchmarkDotNet.Running; using Volo.Abp; @@ -18,8 +22,13 @@ private static void Main(string[] args) })) { application.Initialize(); - BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig()); - } + var config = new DebugInProcessConfig() + .WithSummaryStyle(SummaryStyle.Default.WithMaxParameterColumnWidth(50)) + .AddExporter(XmlExporter.Default) + .AddExporter(HtmlExporter.Default) + .AddExporter(new HtmlSummaryExporter()) + .AddExporter(CsvExporter.Default); + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config); } } private static void RegisterAssemblyResolveEvent() From 9ab3a99564c67c9c625657970c58df7f19230ce5 Mon Sep 17 00:00:00 2001 From: jason20240511 Date: Thu, 27 Jun 2024 16:58:40 +0800 Subject: [PATCH 126/128] feat:add comments to enhance code readability --- .bettercodehub.yml | 2 ++ .gitignore | 2 +- .readthedocs.yml | 2 +- AElf.ContractTools.targets | 3 ++- AElf.Contracts.sln | 3 +-- AElf.sln.DotSettings | 29 ++++++++++++++----------- CodeCoverage.runsettings | 1 + appveyor.yml | 1 + azure-myget-publish.yml | 1 + azure-pipelines.yml | 2 +- build.cake | 1 - build.config | 2 +- build.ps1 | 1 + build.sh | 2 +- common.props | 2 +- docker/docker-compose.yml | 1 - docker/start-cli.sh | 1 - docker/start-node.sh | 2 +- docs-sphinx/index.rst | 2 +- docs/main-structure.md | 2 +- scripts/build.sh | 2 +- scripts/deploy_docker.sh | 2 +- scripts/deploy_myget.sh | 2 +- scripts/deploy_myget_daily.sh | 2 +- scripts/deploy_nuget.sh | 2 +- scripts/download_binary.bat | 2 +- scripts/download_binary.sh | 2 +- scripts/generate_contract_base.bat | 2 +- scripts/generate_contract_base.sh | 2 +- scripts/generate_contract_code.bat | 2 +- scripts/generate_contract_code.sh | 2 +- scripts/generate_contract_reference.bat | 2 +- scripts/generate_contract_reference.sh | 2 +- scripts/generate_contract_stub.bat | 2 +- scripts/generate_contract_stub.sh | 2 +- scripts/generate_event_only.bat | 2 +- scripts/generate_event_only.sh | 2 +- scripts/install_protobuf.sh | 2 +- scripts/test.sh | 2 +- scripts/upload_coverage.sh | 2 +- 40 files changed, 54 insertions(+), 48 deletions(-) diff --git a/.bettercodehub.yml b/.bettercodehub.yml index 4ace3ad3ba..d1a64f481a 100644 --- a/.bettercodehub.yml +++ b/.bettercodehub.yml @@ -1,4 +1,6 @@ +# Depth of components to analyze (2 levels deep) component_depth: 2 +# Programming languages to analyze languages: - csharp diff --git a/.gitignore b/.gitignore index 04d0c6ea86..49a6e74115 100644 --- a/.gitignore +++ b/.gitignore @@ -312,4 +312,4 @@ tools .idea/.idea.AElf/.idea/projectSettingsUpdater.xml .idea/.idea.AElf/.idea/vcs.xml .idea/.idea.AElf/.idea/workspace.xml -.idea/.idea.AElf/riderModule.iml +.idea/.idea.AElf/riderModule.iml \ No newline at end of file diff --git a/.readthedocs.yml b/.readthedocs.yml index 56b2002e45..c717656544 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -14,7 +14,7 @@ sphinx: # Optionally build your docs in additional formats such as PDF and ePub formats: all -# Optionally set the version of Python and requirements required to build your docs +# Optionally set the version of python and requirements required to build your docs python: version: 3.7 install: diff --git a/AElf.ContractTools.targets b/AElf.ContractTools.targets index 3c3608d258..86de821de0 100644 --- a/AElf.ContractTools.targets +++ b/AElf.ContractTools.targets @@ -32,7 +32,8 @@ - + + $(ProjectDir)/Protobuf diff --git a/AElf.Contracts.sln b/AElf.Contracts.sln index ac50a9f51e..486a48541b 100644 --- a/AElf.Contracts.sln +++ b/AElf.Contracts.sln @@ -1,5 +1,4 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "contract", "contract", "{651F0F6E-86CF-42D2-9110-5F3EAE5704F0}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{26990847-E0A2-4FCC-8C71-267CA77557CD}" diff --git a/AElf.sln.DotSettings b/AElf.sln.DotSettings index 04a90e14d0..f728bd7795 100644 --- a/AElf.sln.DotSettings +++ b/AElf.sln.DotSettings @@ -1,14 +1,17 @@ - + False - JS - True - True - True - True - True - True - True - True - True - True - True + JS + True + True + True + True + True + True + True + True + True + True + True diff --git a/CodeCoverage.runsettings b/CodeCoverage.runsettings index 609fa6a0a2..bf1e25abec 100644 --- a/CodeCoverage.runsettings +++ b/CodeCoverage.runsettings @@ -1,4 +1,5 @@ + diff --git a/appveyor.yml b/appveyor.yml index 7be64af079..8239ba530d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,3 +1,4 @@ +# appveyor configuration file version: 1.0.0.{build} skip_tags: true os: Windows Server 2012 R2 diff --git a/azure-myget-publish.yml b/azure-myget-publish.yml index d271eb5595..9a404026cb 100644 --- a/azure-myget-publish.yml +++ b/azure-myget-publish.yml @@ -1,3 +1,4 @@ + # Azure DevOps pipeline configuration for publishing NuGet packages to MyGet pool: vmImage: ubuntu-latest pr: none diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c7761f5875..ab968a5312 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,4 +1,4 @@ -# File: azure-pipelines.yml +# Azure Pipelines configuration file jobs: - template: templates/build-template-window.yml parameters: diff --git a/build.cake b/build.cake index 11219ab033..e32a698f15 100755 --- a/build.cake +++ b/build.cake @@ -112,7 +112,6 @@ Task("Test-with-Codecov") actions.Add(action); } - var options = new ParallelOptions { MaxDegreeOfParallelism = 1, //CancellationToken = cancellationToken diff --git a/build.config b/build.config index 015ef8eca4..d956e69e20 100644 --- a/build.config +++ b/build.config @@ -1,3 +1,3 @@ #!/usr/bin/env bash CAKE_VERSION=0.37.0 -DOTNET_VERSION=6.0.300 +DOTNET_VERSION=6.0.300 \ No newline at end of file diff --git a/build.ps1 b/build.ps1 index e48d09a09c..cb56d5562f 100644 --- a/build.ps1 +++ b/build.ps1 @@ -6,6 +6,7 @@ $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent [string] $CakeVersion = '' [string] $DotNetVersion= '' + foreach($line in Get-Content (Join-Path $PSScriptRoot 'build.config')) { if ($line -like 'CAKE_VERSION=*') { diff --git a/build.sh b/build.sh index 06bcee8070..4f00fd2e4e 100755 --- a/build.sh +++ b/build.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Define varibles +# Define variables SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) source $SCRIPT_DIR/build.config TOOLS_DIR=$SCRIPT_DIR/tools diff --git a/common.props b/common.props index bae225b0b3..95bad4d697 100644 --- a/common.props +++ b/common.props @@ -11,4 +11,4 @@ - + \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 14baea36ad..1d1b2aca06 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -9,4 +9,3 @@ services: - 8001:8000 volumes: - /opt:/opt - diff --git a/docker/start-cli.sh b/docker/start-cli.sh index e5bcf63182..daaf665766 100755 --- a/docker/start-cli.sh +++ b/docker/start-cli.sh @@ -2,4 +2,3 @@ BIND_VOLUME='-v /opt:/opt' CONFIGURE_PATH='-w /opt/aelf-node' docker run -it --rm $BIND_VOLUME $CONFIGURE_PATH aelf/node - diff --git a/docker/start-node.sh b/docker/start-node.sh index a63b5c9798..b258ddaee4 100755 --- a/docker/start-node.sh +++ b/docker/start-node.sh @@ -2,4 +2,4 @@ PUBLISH_PORT='-p 6800:6800 -p 8000:8000' BIND_VOLUME='-v /opt:/opt' CONfIGURE_PATH='/opt/aelf-node' -docker run -itd $PUBLISH_PORT $BIND_VOLUME -w $CONfIGURE_PATH aelf/node dotnet /app/AElf.Launcher.dll --config.path $CONfIGURE_PATH +docker run -itd $PUBLISH_PORT $BIND_VOLUME -w $CONfIGURE_PATH aelf/node dotnet /app/AElf.Launcher.dll --config.path $CONfIGURE_PATH \ No newline at end of file diff --git a/docs-sphinx/index.rst b/docs-sphinx/index.rst index 83e9ac6929..3cff784021 100644 --- a/docs-sphinx/index.rst +++ b/docs-sphinx/index.rst @@ -78,4 +78,4 @@ Welcome to AElf's official documentation! tutorials/cross-chain/running-side-chain tutorials/__run-node - getting-started/smart-contract-development/developing-smart-contracts/index + getting-started/smart-contract-development/developing-smart-contracts/index \ No newline at end of file diff --git a/docs/main-structure.md b/docs/main-structure.md index ba12f8e728..a079e012fe 100644 --- a/docs/main-structure.md +++ b/docs/main-structure.md @@ -88,4 +88,4 @@ * [Google cloud](resources/cloud/gcp/GCP.md) * [Browser Extension](resources/browser-extension.md) * [Joining AElf's testnet](resources/testnet.md) - * [Running a side chain](tutorials/cross-chain/running-side-chain.md) + * [Running a side chain](tutorials/cross-chain/running-side-chain.md) \ No newline at end of file diff --git a/scripts/build.sh b/scripts/build.sh index 83356576d6..f614ac0aaa 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -6,4 +6,4 @@ dotnet build /clp:ErrorsOnly /p:GeneratePackageOnBuild=false -v quiet "AElf.All. if [[ $? -ne 0 ]] ; then echo "Build failed." exit 1 -fi +fi \ No newline at end of file diff --git a/scripts/deploy_docker.sh b/scripts/deploy_docker.sh index 3224bfb7ad..e7a768efdb 100644 --- a/scripts/deploy_docker.sh +++ b/scripts/deploy_docker.sh @@ -13,4 +13,4 @@ docker build -t aelf/node:${TAG} ~/aelf/. docker tag aelf/node:${TAG} aelf/node:latest docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" docker push aelf/node:${TAG} -docker push aelf/node:latest +docker push aelf/node:latest \ No newline at end of file diff --git a/scripts/deploy_myget.sh b/scripts/deploy_myget.sh index 0931a825cf..09b26015ca 100644 --- a/scripts/deploy_myget.sh +++ b/scripts/deploy_myget.sh @@ -31,4 +31,4 @@ do fi done cd ../ -done +done \ No newline at end of file diff --git a/scripts/deploy_myget_daily.sh b/scripts/deploy_myget_daily.sh index 4843b3f7ab..b6916f79b2 100644 --- a/scripts/deploy_myget_daily.sh +++ b/scripts/deploy_myget_daily.sh @@ -34,4 +34,4 @@ do fi done cd ../ -done +done \ No newline at end of file diff --git a/scripts/deploy_nuget.sh b/scripts/deploy_nuget.sh index 0fdc981c3e..ef9f49c37d 100644 --- a/scripts/deploy_nuget.sh +++ b/scripts/deploy_nuget.sh @@ -31,4 +31,4 @@ do fi done cd ../ -done +done \ No newline at end of file diff --git a/scripts/download_binary.bat b/scripts/download_binary.bat index 02382a3bb9..7ec3dc18ca 100644 --- a/scripts/download_binary.bat +++ b/scripts/download_binary.bat @@ -13,4 +13,4 @@ if not exist "%scriptdir%contract_csharp_plugin.exe" ( ) echo "unzip file: %file%" unzip %scriptdir%%filename% -d %scriptdir% -) +) \ No newline at end of file diff --git a/scripts/download_binary.sh b/scripts/download_binary.sh index 49b8fdbe77..0234693718 100644 --- a/scripts/download_binary.sh +++ b/scripts/download_binary.sh @@ -30,4 +30,4 @@ if [[ ! -f ${plugin} ]]; then # Unzip unzip -o ${filename} -d "${scriptdir}" -fi +fi \ No newline at end of file diff --git a/scripts/generate_contract_base.bat b/scripts/generate_contract_base.bat index 57ca166469..ed022fa2c9 100644 --- a/scripts/generate_contract_base.bat +++ b/scripts/generate_contract_base.bat @@ -8,4 +8,4 @@ protoc --proto_path=../../protobuf ^ --contract_opt=%2,nocontract ^ --contract_out=./Protobuf/Generated ^ --plugin=protoc-gen-contract="%scriptdir%contract_csharp_plugin.exe" ^ -%1 +%1 \ No newline at end of file diff --git a/scripts/generate_contract_base.sh b/scripts/generate_contract_base.sh index 28a1550d3e..6d35a165aa 100755 --- a/scripts/generate_contract_base.sh +++ b/scripts/generate_contract_base.sh @@ -13,4 +13,4 @@ protoc --proto_path=${solutiondir}/protobuf \ --contract_opt="$2",nocontract \ --contract_out=./Protobuf/Generated \ --plugin=protoc-gen-contract=${plugin} \ -$1 +$1 \ No newline at end of file diff --git a/scripts/generate_contract_code.bat b/scripts/generate_contract_code.bat index 3b95aad75b..ccd91e4ede 100644 --- a/scripts/generate_contract_code.bat +++ b/scripts/generate_contract_code.bat @@ -7,4 +7,4 @@ protoc --proto_path=../../protobuf ^ --csharp_opt=file_extension=.g.cs ^ --contract_out=./Protobuf/Generated ^ --plugin=protoc-gen-contract="%scriptdir%contract_csharp_plugin.exe" ^ -%* +%* \ No newline at end of file diff --git a/scripts/generate_contract_code.sh b/scripts/generate_contract_code.sh index 60093c9a13..7dec613fc8 100755 --- a/scripts/generate_contract_code.sh +++ b/scripts/generate_contract_code.sh @@ -12,4 +12,4 @@ protoc --proto_path=${solutiondir}/protobuf \ --csharp_opt=file_extension=.g.cs \ --contract_out=./Protobuf/Generated \ --plugin=protoc-gen-contract=${plugin} \ -$@ +$@ \ No newline at end of file diff --git a/scripts/generate_contract_reference.bat b/scripts/generate_contract_reference.bat index 97fee83d02..6c69a31a54 100644 --- a/scripts/generate_contract_reference.bat +++ b/scripts/generate_contract_reference.bat @@ -8,4 +8,4 @@ protoc --proto_path=../../protobuf ^ --contract_opt=reference ^ --contract_out=internal_access:./Protobuf/Generated ^ --plugin=protoc-gen-contract="%scriptdir%contract_csharp_plugin.exe" ^ -%* +%* \ No newline at end of file diff --git a/scripts/generate_contract_reference.sh b/scripts/generate_contract_reference.sh index 47f8027ed1..4fe24547ed 100755 --- a/scripts/generate_contract_reference.sh +++ b/scripts/generate_contract_reference.sh @@ -13,4 +13,4 @@ protoc --proto_path=${solutiondir}/protobuf \ --contract_opt=reference \ --contract_out=internal_access:./Protobuf/Generated \ --plugin=protoc-gen-contract="${plugin}" \ -$@ +$@ \ No newline at end of file diff --git a/scripts/generate_contract_stub.bat b/scripts/generate_contract_stub.bat index 9e1120e8cd..67f8d11149 100644 --- a/scripts/generate_contract_stub.bat +++ b/scripts/generate_contract_stub.bat @@ -9,4 +9,4 @@ protoc --proto_path=../../protobuf ^ --contract_opt=internal_access ^ --contract_out=./Protobuf/Generated ^ --plugin=protoc-gen-contract="%scriptdir%contract_csharp_plugin.exe" ^ -%* +%* \ No newline at end of file diff --git a/scripts/generate_contract_stub.sh b/scripts/generate_contract_stub.sh index ac303fabb7..1e8faa7db1 100755 --- a/scripts/generate_contract_stub.sh +++ b/scripts/generate_contract_stub.sh @@ -18,4 +18,4 @@ protoc --proto_path=${solutiondir}/protobuf \ --contract_opt=internal_access \ --contract_out=${destdir} \ --plugin=protoc-gen-contract="${plugin}" \ -$@ +$@ \ No newline at end of file diff --git a/scripts/generate_event_only.bat b/scripts/generate_event_only.bat index 31dd3e1d16..c4e8fb587d 100644 --- a/scripts/generate_event_only.bat +++ b/scripts/generate_event_only.bat @@ -8,4 +8,4 @@ protoc --proto_path=../../protobuf ^ --contract_opt=nocontract ^ --contract_out=internal_access:./Protobuf/Generated ^ --plugin=protoc-gen-contract="%scriptdir%contract_csharp_plugin.exe" ^ -%* +%* \ No newline at end of file diff --git a/scripts/generate_event_only.sh b/scripts/generate_event_only.sh index db7d22d03f..8bc466768a 100755 --- a/scripts/generate_event_only.sh +++ b/scripts/generate_event_only.sh @@ -13,4 +13,4 @@ protoc --proto_path=${solutiondir}/protobuf \ --contract_opt=nocontract \ --contract_out=internal_access:./Protobuf/Generated \ --plugin=protoc-gen-contract=${plugin} \ -$@ +$@ \ No newline at end of file diff --git a/scripts/install_protobuf.sh b/scripts/install_protobuf.sh index 0d6400db02..941fd8e0dd 100644 --- a/scripts/install_protobuf.sh +++ b/scripts/install_protobuf.sh @@ -35,4 +35,4 @@ elif [[ ${osn} == "linux" ]]; then # Optional: change owner sudo chown ${USER} /usr/local/bin/protoc sudo chown -R ${USER} /usr/local/include/google -fi +fi \ No newline at end of file diff --git a/scripts/test.sh b/scripts/test.sh index 7922415954..47b7b1de60 100644 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -20,4 +20,4 @@ for i in *Tests ; do echo "Test Run Successful." -done +done \ No newline at end of file diff --git a/scripts/upload_coverage.sh b/scripts/upload_coverage.sh index 082982d9a3..70326d9daf 100644 --- a/scripts/upload_coverage.sh +++ b/scripts/upload_coverage.sh @@ -2,4 +2,4 @@ curl -s https://codecov.io/bash > codecov chmod +x codecov -./codecov -f "../test/results/coverage.opencover.xml" -t $1 +./codecov -f "../test/results/coverage.opencover.xml" -t $1 \ No newline at end of file From 1cee8b9bef0d7b8fd4603d56f06af8b307f43d13 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Fri, 28 Jun 2024 17:13:10 +0800 Subject: [PATCH 127/128] Validate the length of ProposalDescriptionUrl field of proposals. --- contract/AElf.Contracts.Association/AssociationConstants.cs | 1 + contract/AElf.Contracts.Association/Association_Helper.cs | 3 +++ contract/AElf.Contracts.Parliament/ParliamentConstants.cs | 1 + contract/AElf.Contracts.Parliament/Parliament_Helper.cs | 3 +++ contract/AElf.Contracts.Referendum/ReferendumConstants.cs | 1 + contract/AElf.Contracts.Referendum/Referendum_Helper.cs | 3 +++ 6 files changed, 12 insertions(+) diff --git a/contract/AElf.Contracts.Association/AssociationConstants.cs b/contract/AElf.Contracts.Association/AssociationConstants.cs index 46cbe81529..d2afee8e13 100644 --- a/contract/AElf.Contracts.Association/AssociationConstants.cs +++ b/contract/AElf.Contracts.Association/AssociationConstants.cs @@ -4,4 +4,5 @@ public static class AssociationConstants { public const int MaxLengthForTitle = 255; public const int MaxLengthForDescription = 10200; + public const int MaxLengthForProposalDescriptionUrl = 255; } \ No newline at end of file diff --git a/contract/AElf.Contracts.Association/Association_Helper.cs b/contract/AElf.Contracts.Association/Association_Helper.cs index 277071e28f..5436ccb48c 100644 --- a/contract/AElf.Contracts.Association/Association_Helper.cs +++ b/contract/AElf.Contracts.Association/Association_Helper.cs @@ -178,5 +178,8 @@ private void CheckCreateProposalInput(CreateProposalInput input) Assert(input.Title.Length <= AssociationConstants.MaxLengthForTitle, "Title is too long."); // Check the length of description Assert(input.Title.Length <= AssociationConstants.MaxLengthForDescription, "Description is too long."); + // Check the length of description url + Assert(input.ProposalDescriptionUrl.Length <= AssociationConstants.MaxLengthForProposalDescriptionUrl, + "Description url is too long."); } } \ No newline at end of file diff --git a/contract/AElf.Contracts.Parliament/ParliamentConstants.cs b/contract/AElf.Contracts.Parliament/ParliamentConstants.cs index a5026afd21..a1646221e8 100644 --- a/contract/AElf.Contracts.Parliament/ParliamentConstants.cs +++ b/contract/AElf.Contracts.Parliament/ParliamentConstants.cs @@ -4,4 +4,5 @@ public static class ParliamentConstants { public const int MaxLengthForTitle = 255; public const int MaxLengthForDescription = 10200; + public const int MaxLengthForProposalDescriptionUrl = 255; } \ No newline at end of file diff --git a/contract/AElf.Contracts.Parliament/Parliament_Helper.cs b/contract/AElf.Contracts.Parliament/Parliament_Helper.cs index 56342ef679..18122b53ce 100644 --- a/contract/AElf.Contracts.Parliament/Parliament_Helper.cs +++ b/contract/AElf.Contracts.Parliament/Parliament_Helper.cs @@ -258,6 +258,9 @@ private void CheckCreateProposalInput(CreateProposalInput input) Assert(input.Title.Length <= ParliamentConstants.MaxLengthForTitle, "Title is too long."); // Check the length of description Assert(input.Title.Length <= ParliamentConstants.MaxLengthForDescription, "Description is too long."); + // Check the length of description url + Assert(input.ProposalDescriptionUrl.Length <= ParliamentConstants.MaxLengthForProposalDescriptionUrl, + "Description url is too long."); } private Address CreateNewOrganization(CreateOrganizationInput input) diff --git a/contract/AElf.Contracts.Referendum/ReferendumConstants.cs b/contract/AElf.Contracts.Referendum/ReferendumConstants.cs index fd409fccc4..89eded59d7 100644 --- a/contract/AElf.Contracts.Referendum/ReferendumConstants.cs +++ b/contract/AElf.Contracts.Referendum/ReferendumConstants.cs @@ -4,4 +4,5 @@ public static class ReferendumConstants { public const int MaxLengthForTitle = 255; public const int MaxLengthForDescription = 10200; + public const int MaxLengthForProposalDescriptionUrl = 255; } \ No newline at end of file diff --git a/contract/AElf.Contracts.Referendum/Referendum_Helper.cs b/contract/AElf.Contracts.Referendum/Referendum_Helper.cs index 31959fc451..e764654621 100644 --- a/contract/AElf.Contracts.Referendum/Referendum_Helper.cs +++ b/contract/AElf.Contracts.Referendum/Referendum_Helper.cs @@ -192,6 +192,9 @@ private void CheckCreateProposalInput(CreateProposalInput input) Assert(input.Title.Length <= ReferendumConstants.MaxLengthForTitle, "Title is too long."); // Check the length of description Assert(input.Title.Length <= ReferendumConstants.MaxLengthForDescription, "Description is too long."); + // Check the length of description url + Assert(input.ProposalDescriptionUrl.Length <= ReferendumConstants.MaxLengthForProposalDescriptionUrl, + "Description url is too long."); } private void AssertIsAuthorizedProposer(Address organizationAddress, Address proposer) From 3dbd5dfe2a123e7b4a6c94a075a2d83b7a9620a8 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Mon, 1 Jul 2024 11:05:39 +0800 Subject: [PATCH 128/128] Fix judgements and add tests --- contract/AElf.Contracts.Association/Association_Helper.cs | 2 +- contract/AElf.Contracts.Parliament/Parliament_Helper.cs | 2 +- contract/AElf.Contracts.Referendum/Referendum_Helper.cs | 2 +- .../ParliamentContractTest.cs | 6 +++++- .../ReferendumContractTest.cs | 6 +++++- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/contract/AElf.Contracts.Association/Association_Helper.cs b/contract/AElf.Contracts.Association/Association_Helper.cs index 5436ccb48c..cc1a7d0643 100644 --- a/contract/AElf.Contracts.Association/Association_Helper.cs +++ b/contract/AElf.Contracts.Association/Association_Helper.cs @@ -177,7 +177,7 @@ private void CheckCreateProposalInput(CreateProposalInput input) // Check the length of title Assert(input.Title.Length <= AssociationConstants.MaxLengthForTitle, "Title is too long."); // Check the length of description - Assert(input.Title.Length <= AssociationConstants.MaxLengthForDescription, "Description is too long."); + Assert(input.Description.Length <= AssociationConstants.MaxLengthForDescription, "Description is too long."); // Check the length of description url Assert(input.ProposalDescriptionUrl.Length <= AssociationConstants.MaxLengthForProposalDescriptionUrl, "Description url is too long."); diff --git a/contract/AElf.Contracts.Parliament/Parliament_Helper.cs b/contract/AElf.Contracts.Parliament/Parliament_Helper.cs index 18122b53ce..b1317eceb2 100644 --- a/contract/AElf.Contracts.Parliament/Parliament_Helper.cs +++ b/contract/AElf.Contracts.Parliament/Parliament_Helper.cs @@ -257,7 +257,7 @@ private void CheckCreateProposalInput(CreateProposalInput input) // Check the length of title Assert(input.Title.Length <= ParliamentConstants.MaxLengthForTitle, "Title is too long."); // Check the length of description - Assert(input.Title.Length <= ParliamentConstants.MaxLengthForDescription, "Description is too long."); + Assert(input.Description.Length <= ParliamentConstants.MaxLengthForDescription, "Description is too long."); // Check the length of description url Assert(input.ProposalDescriptionUrl.Length <= ParliamentConstants.MaxLengthForProposalDescriptionUrl, "Description url is too long."); diff --git a/contract/AElf.Contracts.Referendum/Referendum_Helper.cs b/contract/AElf.Contracts.Referendum/Referendum_Helper.cs index e764654621..9b6e4343d9 100644 --- a/contract/AElf.Contracts.Referendum/Referendum_Helper.cs +++ b/contract/AElf.Contracts.Referendum/Referendum_Helper.cs @@ -191,7 +191,7 @@ private void CheckCreateProposalInput(CreateProposalInput input) // Check the length of title Assert(input.Title.Length <= ReferendumConstants.MaxLengthForTitle, "Title is too long."); // Check the length of description - Assert(input.Title.Length <= ReferendumConstants.MaxLengthForDescription, "Description is too long."); + Assert(input.Description.Length <= ReferendumConstants.MaxLengthForDescription, "Description is too long."); // Check the length of description url Assert(input.ProposalDescriptionUrl.Length <= ReferendumConstants.MaxLengthForProposalDescriptionUrl, "Description url is too long."); diff --git a/test/AElf.Contracts.Parliament.Tests/ParliamentContractTest.cs b/test/AElf.Contracts.Parliament.Tests/ParliamentContractTest.cs index 5a82089232..caeb34f557 100644 --- a/test/AElf.Contracts.Parliament.Tests/ParliamentContractTest.cs +++ b/test/AElf.Contracts.Parliament.Tests/ParliamentContractTest.cs @@ -108,6 +108,8 @@ public async Task Get_Proposal_Test() getProposal.Output.ProposalId.ShouldBe(proposalId); getProposal.Output.OrganizationAddress.ShouldBe(organizationAddress); getProposal.Output.ToAddress.ShouldBe(TokenContractAddress); + getProposal.Output.Title.ShouldNotBeNullOrEmpty(); + getProposal.Output.Description.ShouldNotBeNullOrEmpty(); var transferParam = TransferInput.Parser.ParseFrom(getProposal.Output.Params); transferParam.Symbol.ShouldBe(transferInput.Symbol); @@ -1541,7 +1543,9 @@ private async Task CreateProposalAsync(ECKeyPair proposalKeyPair, Address ToAddress = TokenContractAddress, Params = transferInput.ToByteString(), ExpiredTime = BlockTimeProvider.GetBlockTime().AddDays(2), - OrganizationAddress = organizationAddress + OrganizationAddress = organizationAddress, + Title = "Token Transfer", + Description = "Transfer 100 ELF to Tester's address", }; var parliamentContractStub = GetParliamentContractTester(proposalKeyPair); var proposal = await parliamentContractStub.CreateProposal.SendAsync(createProposalInput); diff --git a/test/AElf.Contracts.Referendum.Tests/ReferendumContractTest.cs b/test/AElf.Contracts.Referendum.Tests/ReferendumContractTest.cs index d22a250401..81dd45896c 100644 --- a/test/AElf.Contracts.Referendum.Tests/ReferendumContractTest.cs +++ b/test/AElf.Contracts.Referendum.Tests/ReferendumContractTest.cs @@ -110,6 +110,8 @@ public async Task Get_Proposal_Test() getProposal.Output.OrganizationAddress.ShouldBe(organizationAddress); getProposal.Output.ToAddress.ShouldBe(TokenContractAddress); getProposal.Output.Params.ShouldBe(createInput.ToByteString()); + getProposal.Output.Title.ShouldNotBeNullOrEmpty(); + getProposal.Output.Description.ShouldNotBeNullOrEmpty(); } [Fact] @@ -1320,7 +1322,9 @@ private async Task CreateProposalAsync(ECKeyPair proposalKeyPair, Address ToAddress = TokenContractAddress, Params = createInput.ToByteString(), ExpiredTime = timestamp ?? BlockTimeProvider.GetBlockTime().AddSeconds(1000), - OrganizationAddress = organizationAddress + OrganizationAddress = organizationAddress, + Title = "Create token: NEW", + Description = "Create a new token named NEW." }; ReferendumContractStub = GetReferendumContractTester(proposalKeyPair); var proposal = await ReferendumContractStub.CreateProposal.SendAsync(createProposalInput);