From ec0afbb67d60846ef20d1149e2a8dc4946332fee Mon Sep 17 00:00:00 2001 From: "zecong.nian" Date: Wed, 22 Nov 2023 20:35:04 +0800 Subject: [PATCH 01/75] 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 02/75] 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 03/75] 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 1a520c37196d9b90963df781ec6c3f8fc943dbc8 Mon Sep 17 00:00:00 2001 From: "zecong.nian" Date: Wed, 13 Dec 2023 11:36:04 +0800 Subject: [PATCH 04/75] 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 05/75] 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 06/75] 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 07/75] 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 cc24fabea3e20078e9e80b46651b58c08ad52b7c Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Mon, 5 Feb 2024 18:33:35 +0800 Subject: [PATCH 08/75] 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 09/75] 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 10/75] 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 11/75] 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 12/75] 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 3a8eef1ea21daa95f989f6ac74ee4d3cc0891b0e Mon Sep 17 00:00:00 2001 From: "louis.li" Date: Mon, 26 Feb 2024 14:00:54 +0800 Subject: [PATCH 13/75] 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 fb34cee2f3359ddeef2725bcb05cff3fe54d1f3d Mon Sep 17 00:00:00 2001 From: eanzhao Date: Wed, 6 Mar 2024 17:12:34 +0800 Subject: [PATCH 14/75] 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 7cf51056216a6f7963df685739ef60a2c49d9735 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Thu, 21 Mar 2024 13:08:39 +0800 Subject: [PATCH 15/75] 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 16/75] 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 17/75] 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 18/75] 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 19/75] 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 20/75] 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 21/75] 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 22/75] 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 23/75] 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 24/75] 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 25/75] 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 26/75] 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 27/75] 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 28/75] 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 29/75] 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 30/75] 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 31/75] 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 32/75] 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 33/75] 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 34/75] 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 35/75] 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 36/75] 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 37/75] 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 38/75] 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 39/75] 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 40/75] 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 41/75] 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 42/75] 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 43/75] 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 44/75] 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 45/75] 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 46/75] 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 47/75] 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 48/75] 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 49/75] 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 50/75] 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 51/75] 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 52/75] 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 53/75] 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 54/75] 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 55/75] 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 56/75] 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 57/75] 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 58/75] 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 59/75] 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 60/75] 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 61/75] 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 62/75] 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 63/75] 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 64/75] 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 65/75] 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 66/75] 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 67/75] 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 68/75] 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 69/75] 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 70/75] 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 71/75] 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 72/75] 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 73/75] 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 fbd962034a479206ab30bebc442d6b9ee8962398 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Tue, 21 May 2024 12:58:47 +0800 Subject: [PATCH 74/75] Fix test case: CompareConsumptions --- .../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 8f91a6ac2eb7002a0a9edbbbd39bef0e6aa21938 Mon Sep 17 00:00:00 2001 From: eanzhao Date: Tue, 21 May 2024 13:01:47 +0800 Subject: [PATCH 75/75] 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"]);