diff --git a/AElf.Client.sln b/AElf.Client.sln new file mode 100644 index 0000000..ce42ea6 --- /dev/null +++ b/AElf.Client.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{940361AC-2167-4D30-A4F6-9A543C608196}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{7CD2B508-C765-4720-B430-94E79135797A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client", "src\AElf.Client\AElf.Client.csproj", "{1AE7844D-B4D9-4F5A-9D25-7E551B61138E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client.Test", "test\AElf.Client.Test\AElf.Client.Test.csproj", "{4D019C99-8B1C-42B0-8630-F1E3661F414B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client.Protobuf", "src\AElf.Client.Protobuf\AElf.Client.Protobuf.csproj", "{01B13EE5-9892-4D2A-8E77-C0A4A9575753}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1AE7844D-B4D9-4F5A-9D25-7E551B61138E} = {940361AC-2167-4D30-A4F6-9A543C608196} + {4D019C99-8B1C-42B0-8630-F1E3661F414B} = {7CD2B508-C765-4720-B430-94E79135797A} + {01B13EE5-9892-4D2A-8E77-C0A4A9575753} = {940361AC-2167-4D30-A4F6-9A543C608196} + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1AE7844D-B4D9-4F5A-9D25-7E551B61138E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1AE7844D-B4D9-4F5A-9D25-7E551B61138E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1AE7844D-B4D9-4F5A-9D25-7E551B61138E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1AE7844D-B4D9-4F5A-9D25-7E551B61138E}.Release|Any CPU.Build.0 = Release|Any CPU + {4D019C99-8B1C-42B0-8630-F1E3661F414B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D019C99-8B1C-42B0-8630-F1E3661F414B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D019C99-8B1C-42B0-8630-F1E3661F414B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D019C99-8B1C-42B0-8630-F1E3661F414B}.Release|Any CPU.Build.0 = Release|Any CPU + {01B13EE5-9892-4D2A-8E77-C0A4A9575753}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01B13EE5-9892-4D2A-8E77-C0A4A9575753}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01B13EE5-9892-4D2A-8E77-C0A4A9575753}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01B13EE5-9892-4D2A-8E77-C0A4A9575753}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/all.sln b/all.sln index 3e3a5d1..9de7f43 100644 --- a/all.sln +++ b/all.sln @@ -10,15 +10,23 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client", "src\AElf.Cli EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client.Protobuf", "src\AElf.Client.Protobuf\AElf.Client.Protobuf.csproj", "{E7DF7523-2FB5-45B3-9EAA-9B759FDDC2EB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TokenManager", "examples\TokenManager\TokenManager.csproj", "{CA0B4594-2549-4DB1-9079-EA5FD739C6B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElectionViewer", "examples\ElectionViewer\ElectionViewer.csproj", "{E678AABE-AF7B-45F4-B931-CF0D77C5AECA}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AEDPoSViewer", "examples\AEDPoSViewer\AEDPoSViewer.csproj", "{BE0E29FA-A9C6-4E03-9E0C-297CCF7C5729}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client.Test", "test\AElf.Client.Test\AElf.Client.Test.csproj", "{4D019C99-8B1C-42B0-8630-F1E3661F414B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client.Abp", "src\AElf.Client.Abp\AElf.Client.Abp.csproj", "{BBE11EF4-5CD6-4918-8E6C-74BF61C1A87F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client.Core", "src\AElf.Client.Core\AElf.Client.Core.csproj", "{BBE11EF4-5CD6-4918-8E6C-74BF61C1A87F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client.TestBase", "test\AElf.Client.TestBase\AElf.Client.TestBase.csproj", "{D542FBDB-DFE1-46C2-8C4E-9A5643F3C401}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client.Genesis", "examples\AElf.Client.Genesis\AElf.Client.Genesis.csproj", "{A6F6AF4C-A783-4EC3-A03F-F1576020BC49}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client.Consensus.AEDPoS", "examples\AElf.Client.Consensus.AEDPoS\AElf.Client.Consensus.AEDPoS.csproj", "{46531278-7E47-4D6F-AD45-A6870E624105}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client.Parliament", "examples\AElf.Client.Parliament\AElf.Client.Parliament.csproj", "{12B17C25-1A00-4BB7-9911-9AC06988C261}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client.Token", "examples\AElf.Client.Token\AElf.Client.Token.csproj", "{0FD6224B-4C4B-49C3-BC25-5C0B1FDBB077}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.Client.CrossChain", "examples\AElf.Client.CrossChain\AElf.Client.CrossChain.csproj", "{0657BEEB-97B1-4250-9252-6685A99B5D10}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -28,11 +36,15 @@ Global GlobalSection(NestedProjects) = preSolution {1AE7844D-B4D9-4F5A-9D25-7E551B61138E} = {940361AC-2167-4D30-A4F6-9A543C608196} {E7DF7523-2FB5-45B3-9EAA-9B759FDDC2EB} = {940361AC-2167-4D30-A4F6-9A543C608196} - {CA0B4594-2549-4DB1-9079-EA5FD739C6B6} = {5D1BDC45-D6F1-4359-868B-CB85CEC4ABD8} - {E678AABE-AF7B-45F4-B931-CF0D77C5AECA} = {5D1BDC45-D6F1-4359-868B-CB85CEC4ABD8} {BE0E29FA-A9C6-4E03-9E0C-297CCF7C5729} = {5D1BDC45-D6F1-4359-868B-CB85CEC4ABD8} {4D019C99-8B1C-42B0-8630-F1E3661F414B} = {7CD2B508-C765-4720-B430-94E79135797A} {BBE11EF4-5CD6-4918-8E6C-74BF61C1A87F} = {940361AC-2167-4D30-A4F6-9A543C608196} + {D542FBDB-DFE1-46C2-8C4E-9A5643F3C401} = {7CD2B508-C765-4720-B430-94E79135797A} + {A6F6AF4C-A783-4EC3-A03F-F1576020BC49} = {5D1BDC45-D6F1-4359-868B-CB85CEC4ABD8} + {46531278-7E47-4D6F-AD45-A6870E624105} = {5D1BDC45-D6F1-4359-868B-CB85CEC4ABD8} + {12B17C25-1A00-4BB7-9911-9AC06988C261} = {5D1BDC45-D6F1-4359-868B-CB85CEC4ABD8} + {0FD6224B-4C4B-49C3-BC25-5C0B1FDBB077} = {5D1BDC45-D6F1-4359-868B-CB85CEC4ABD8} + {0657BEEB-97B1-4250-9252-6685A99B5D10} = {5D1BDC45-D6F1-4359-868B-CB85CEC4ABD8} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1AE7844D-B4D9-4F5A-9D25-7E551B61138E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -43,14 +55,6 @@ Global {E7DF7523-2FB5-45B3-9EAA-9B759FDDC2EB}.Debug|Any CPU.Build.0 = Debug|Any CPU {E7DF7523-2FB5-45B3-9EAA-9B759FDDC2EB}.Release|Any CPU.ActiveCfg = Release|Any CPU {E7DF7523-2FB5-45B3-9EAA-9B759FDDC2EB}.Release|Any CPU.Build.0 = Release|Any CPU - {CA0B4594-2549-4DB1-9079-EA5FD739C6B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CA0B4594-2549-4DB1-9079-EA5FD739C6B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CA0B4594-2549-4DB1-9079-EA5FD739C6B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CA0B4594-2549-4DB1-9079-EA5FD739C6B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E678AABE-AF7B-45F4-B931-CF0D77C5AECA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E678AABE-AF7B-45F4-B931-CF0D77C5AECA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E678AABE-AF7B-45F4-B931-CF0D77C5AECA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E678AABE-AF7B-45F4-B931-CF0D77C5AECA}.Release|Any CPU.Build.0 = Release|Any CPU {BE0E29FA-A9C6-4E03-9E0C-297CCF7C5729}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BE0E29FA-A9C6-4E03-9E0C-297CCF7C5729}.Debug|Any CPU.Build.0 = Debug|Any CPU {BE0E29FA-A9C6-4E03-9E0C-297CCF7C5729}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -63,5 +67,29 @@ Global {BBE11EF4-5CD6-4918-8E6C-74BF61C1A87F}.Debug|Any CPU.Build.0 = Debug|Any CPU {BBE11EF4-5CD6-4918-8E6C-74BF61C1A87F}.Release|Any CPU.ActiveCfg = Release|Any CPU {BBE11EF4-5CD6-4918-8E6C-74BF61C1A87F}.Release|Any CPU.Build.0 = Release|Any CPU + {D542FBDB-DFE1-46C2-8C4E-9A5643F3C401}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D542FBDB-DFE1-46C2-8C4E-9A5643F3C401}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D542FBDB-DFE1-46C2-8C4E-9A5643F3C401}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D542FBDB-DFE1-46C2-8C4E-9A5643F3C401}.Release|Any CPU.Build.0 = Release|Any CPU + {A6F6AF4C-A783-4EC3-A03F-F1576020BC49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6F6AF4C-A783-4EC3-A03F-F1576020BC49}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6F6AF4C-A783-4EC3-A03F-F1576020BC49}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6F6AF4C-A783-4EC3-A03F-F1576020BC49}.Release|Any CPU.Build.0 = Release|Any CPU + {46531278-7E47-4D6F-AD45-A6870E624105}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {46531278-7E47-4D6F-AD45-A6870E624105}.Debug|Any CPU.Build.0 = Debug|Any CPU + {46531278-7E47-4D6F-AD45-A6870E624105}.Release|Any CPU.ActiveCfg = Release|Any CPU + {46531278-7E47-4D6F-AD45-A6870E624105}.Release|Any CPU.Build.0 = Release|Any CPU + {12B17C25-1A00-4BB7-9911-9AC06988C261}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {12B17C25-1A00-4BB7-9911-9AC06988C261}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12B17C25-1A00-4BB7-9911-9AC06988C261}.Release|Any CPU.ActiveCfg = Release|Any CPU + {12B17C25-1A00-4BB7-9911-9AC06988C261}.Release|Any CPU.Build.0 = Release|Any CPU + {0FD6224B-4C4B-49C3-BC25-5C0B1FDBB077}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0FD6224B-4C4B-49C3-BC25-5C0B1FDBB077}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0FD6224B-4C4B-49C3-BC25-5C0B1FDBB077}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0FD6224B-4C4B-49C3-BC25-5C0B1FDBB077}.Release|Any CPU.Build.0 = Release|Any CPU + {0657BEEB-97B1-4250-9252-6685A99B5D10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0657BEEB-97B1-4250-9252-6685A99B5D10}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0657BEEB-97B1-4250-9252-6685A99B5D10}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0657BEEB-97B1-4250-9252-6685A99B5D10}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 464e1d0..3f22733 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -9,7 +9,7 @@ jobs: - task: UseDotNet@2 displayName: 'Install .NET Core SDK' inputs: - version: 3.1.101 + version: 6.0.300 - powershell: ./scripts/aelf-node/start-window.ps1 displayName: 'Build and Test' # All tasks on Linux @@ -25,7 +25,7 @@ jobs: steps: - task: DotNetCoreInstaller@0 inputs: - version: '3.1.101' + version: '6.0.300' - script: cd scripts/aelf-node && bash start.sh displayName: 'Deploy a full node' - script: bash build.sh -target=test @@ -40,7 +40,7 @@ jobs: steps: - task: DotNetCoreInstaller@0 inputs: - version: '3.1.101' + version: '6.0.300' - script: cd scripts/aelf-node && bash start-mac.sh displayName: 'Deploy a full node' - script: bash build.sh -target=test diff --git a/build.config b/build.config index 803c029..015ef8e 100644 --- a/build.config +++ b/build.config @@ -1,3 +1,3 @@ #!/usr/bin/env bash CAKE_VERSION=0.37.0 -DOTNET_VERSION=3.1.101 +DOTNET_VERSION=6.0.300 diff --git a/examples/AEDPoSViewer/AEDPoSViewer.csproj b/examples/AEDPoSViewer/AEDPoSViewer.csproj index a356a80..005dac9 100644 --- a/examples/AEDPoSViewer/AEDPoSViewer.csproj +++ b/examples/AEDPoSViewer/AEDPoSViewer.csproj @@ -8,7 +8,7 @@ - + @@ -18,6 +18,7 @@ + diff --git a/examples/AEDPoSViewer/AEDPoSViewerHostedService.cs b/examples/AEDPoSViewer/AEDPoSViewerHostedService.cs index bdb0b9c..c1f2491 100644 --- a/examples/AEDPoSViewer/AEDPoSViewerHostedService.cs +++ b/examples/AEDPoSViewer/AEDPoSViewerHostedService.cs @@ -1,5 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -34,8 +32,8 @@ public async Task StartAsync(CancellationToken cancellationToken) await _abpApplication.InitializeAsync(); - var helloWorldService = _abpApplication.ServiceProvider.GetRequiredService(); - await helloWorldService.RunAsync(); + var viewerService = _abpApplication.ServiceProvider.GetRequiredService(); + await viewerService.RunAsync(); } public async Task StopAsync(CancellationToken cancellationToken) diff --git a/examples/AEDPoSViewer/AEDPoSViewerModule.cs b/examples/AEDPoSViewer/AEDPoSViewerModule.cs index 1bd743a..48c582d 100644 --- a/examples/AEDPoSViewer/AEDPoSViewerModule.cs +++ b/examples/AEDPoSViewer/AEDPoSViewerModule.cs @@ -1,4 +1,4 @@ -using AElf.Client.Abp; +using AElf.Client.Core; using Volo.Abp.Modularity; namespace AEDPoSViewer; @@ -8,5 +8,8 @@ namespace AEDPoSViewer; )] public class AEDPoSViewerModule : AbpModule { + public override void ConfigureServices(ServiceConfigurationContext context) + { + } } \ No newline at end of file diff --git a/examples/AEDPoSViewer/AEDPoSViewerService.cs b/examples/AEDPoSViewer/AEDPoSViewerService.cs index 6dafa91..68e2deb 100644 --- a/examples/AEDPoSViewer/AEDPoSViewerService.cs +++ b/examples/AEDPoSViewer/AEDPoSViewerService.cs @@ -1,10 +1,13 @@ -using AElf.Client; +using AEDPoSViewer.Data; +using AElf; +using AElf.Client.Core; +using AElf.Client.Core.Options; using AElf.Contracts.Consensus.AEDPoS; -using Google.Protobuf; +using AElf.Types; using Google.Protobuf.WellKnownTypes; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Spectre.Console; using Volo.Abp.DependencyInjection; namespace AEDPoSViewer; @@ -13,26 +16,74 @@ public class AEDPoSViewerService : ITransientDependency { private IServiceScopeFactory ServiceScopeFactory { get; } - public ILogger Logger { get; set; } - public AEDPoSViewerService(IServiceScopeFactory serviceScopeFactory) { ServiceScopeFactory = serviceScopeFactory; - - Logger = NullLogger.Instance; } public async Task RunAsync() + { + var currentRound = await GetCurrentRoundAsync(); + for (var i = currentRound.RoundNumber - 4; i < currentRound.RoundNumber; i++) + { + DisplayRound(await GetRoundAsync(i)); + } + + DisplayRound(currentRound); + } + + private async Task GetRoundAsync(long roundNumber) { using var scope = ServiceScopeFactory.CreateScope(); + var clientConfig = scope.ServiceProvider.GetRequiredService>(); + var clientService = scope.ServiceProvider.GetRequiredService(); - var result = await clientService.ViewSystemAsync(AEDPoSViewerConstants.ConsensusSmartContractName, - "GetCurrentRoundInformation", new Empty(), EndpointType.MainNetSidechain.ToString(), "Ean"); + return await clientService.ViewSystemAsync(AEDPoSViewerConstants.ConsensusSmartContractName, + "GetRoundInformation", new Int64Value { Value = roundNumber }, clientConfig.Value.ClientAlias); + } + + private async Task GetCurrentRoundAsync() + { + using var scope = ServiceScopeFactory.CreateScope(); + + var clientConfig = scope.ServiceProvider.GetRequiredService>(); + + var clientService = scope.ServiceProvider.GetRequiredService(); + + return await clientService.ViewSystemAsync(AEDPoSViewerConstants.ConsensusSmartContractName, + "GetCurrentRoundInformation", new Empty(), clientConfig.Value.ClientAlias); + } + + private void DisplayRound(Round round) + { + //AnsiConsole.MarkupLine($"[blue]Term:[/]\t{round.TermNumber}"); + AnsiConsole.MarkupLine($"[blue]Round:[/]\t{round.RoundNumber}"); + //AnsiConsole.MarkupLine($"[blue]Days:[/]\t{new TimeSpan(0, 0, 0, (int)round.BlockchainAge).TotalDays}"); + + var table = new Table(); + table.AddColumn(new TableColumn(new Markup("[blue]Pubkey[/]"))); + table.AddColumn(new TableColumn("[blue]Address[/]")); + table.AddColumn(new TableColumn("[blue]Order[/]")); + table.AddColumn(new TableColumn("[blue]Produced[/]")); + table.AddColumn(new TableColumn("[blue]Missed Slots[/]")); + foreach (var info in round.RealTimeMinersInformation.OrderBy(i => i.Value.Order)) + { + var pubkey = info.Key; + var minerInRound = info.Value; + var address = Address.FromPublicKey(ByteArrayHelper.HexStringToByteArray(pubkey)).ToBase58(); + BlockProducer.Map.TryGetValue(pubkey, out var displayPubkey); + var actualMiningTimes = minerInRound.ActualMiningTimes.Count; + var isAlarm = actualMiningTimes == 0; + table.AddRow( + (isAlarm ? "[red]" : "") + (displayPubkey ?? pubkey[..10]) + (isAlarm ? "[/]" : ""), + address, + minerInRound.Order.ToString(), + (isAlarm ? "[red]" : "") + actualMiningTimes + (isAlarm ? "[/]" : ""), + minerInRound.MissedTimeSlots.ToString()); + } - var round = new Round(); - round.MergeFrom(result); - Logger.LogInformation($"Current round: {round}"); + AnsiConsole.Write(table); } } \ No newline at end of file diff --git a/examples/AEDPoSViewer/Data/BlockProducerMap.cs b/examples/AEDPoSViewer/Data/BlockProducerMap.cs new file mode 100644 index 0000000..dcd134e --- /dev/null +++ b/examples/AEDPoSViewer/Data/BlockProducerMap.cs @@ -0,0 +1,47 @@ +namespace AEDPoSViewer.Data; + +public static class BlockProducer +{ + public static Dictionary Map; + + static BlockProducer() + { + Map = new Dictionary + { + ["048a8e7562f14207a5e9cc41163a8b947cf826bd1628fa79b4e5345e014bd3cb139617ccbb1fed60a4cdd691a238f9d30f30bf50452d5a4d9b050de73d16ccd606"] = + "FBG", + ["04f34af28e68d7c9bcfc7a9797a4d894ee9a9d3d142e275cd254b6495ed7ec95f95411c9819950c23c36fd00a2c610947487bd6e0b3e3ed8d2c90f089fa4f0c02a"] = + "RockX", + ["04d92f55aa3ea457c24836b263aca894b7aa890eb17176b8d46b17d35d5a417e491a024c8974c1bfc7b586c15f329e5d7e707f888841ba984f32dec7631efd891a"] = + "CoinHuntingCommunity", + ["04cfe24816b93a4936d564c63fd7cdcc3e77c8f26037dec96b8ab5f209b161c6567df2a317d1bb5b5494eac41d8c457ac6b8242dff9af0147beabe0ecb28ea2b79"] = + "LumosSocial", + ["0454c2e605e790e8c6e295604eeb6e169af97e25862882f44cbff19502096e29460b425e7cb50da1215b5931057fa84dd812a66a15ead46a66a9972d3e3c3fe38b"] = + "bountyblok", + ["0476d07226df54bc3f0cbbf6ab86f5c9d2324860e9e3c36db2886555d7832eed89214f73dfe8c43e7efdd7157dfbb148966944fbcb1ad0bf812b6d3350858e9104"] = + "ChainExplorers", + ["04a45c95d121c659d9f90ce7c43236f0204b87e5d180a700d6a040f2ad60a4b7cb7f22aed7e2ed7205376edcabfd2d1efb011e46e45ec94eed23cad48b794ce89e"] = + "marbleGameFI", + ["040bcbc49d65a89e49dc7152b2720f5769e6132a2c2d8742d0b7d6efb7bc1a977355f5dbd05f565e3c8848cbce8c8a0250a8a2296dc74b3d4fa0335637de5b1ec7"] = + "Genesis5", + ["04c7132a91d3b3924d1afcf99b255dde5fc0fbb437e8cb3b0d3858e9bbbf7f30e2e39e3819ebb9f034963b6a7ed3e3c12e8528cfce6210f39cf12803f89877a890"] = + "OKX", + ["042f7cab8272e1f4e90e59e91bbe2c211f1a0e15a5b670aff517a02f625d678607a96723b6ce3232fcf6ad0fc988e3e198cdedebcc10cd4d86b4dddfd8a99edcf0"] = + "aelfin", + ["04f7760c46da886f7e06e8e665cbab7d63cfe825c680dce990fcb2c3b7a01c7236e0ae40aacb32bbc4fffc57ae45bc1fe5344461efeed2b4660aef3ec3f9e35b15"] = + "HuobiPool", + ["0458ad2ec4d8944bff7f3ab7b56a90ffca784b0632bdf8c4a952da153b24b3fbbda5432f5ef293ab7ced791969f5fe02b0b5e6bc5af7ce074a9dc386c8dab0e6db"] = + "MetaDefy", + ["04e6b61a5904cfa1e242051bd71a6adba572eaf325213f41a0ae4d0139fe63627d130e6e525c3d23732ae64f3bfc8959ca4174f6aef8ea7901b4c436f5cfc81371"] = + "Genesis2", + ["04ace35149bf24792b0a4c9ea327ef44061e4a6dffa598a24be054b8068d173e0c59f4a070417b7f544057c447f27678d6aa6b531b14ee996af9728ff2fabe7f42"] = + "Genesis3", + ["04dfc5398e6c0485cb4ee98f6202f81fd49c971fa21ef9f62e309a4b983cffae9149c13cf9a8d00c10153f45855943e1ec5ca399b7143cf336cc901c457e905236"] = + "Genesis1", + ["0485bc2868f15dc6ff07ec85e3ee6f5900bb3bf3c6c50c3f40d377a203c476fdade815ab5bba1ade2eefb20de26085ab1ab84317ba07b093aa622a536f32d35682"] = + "CoinMultiDex", + ["04c1b4a75fd9ba37e0a84b9916e517e9c591c5b9efacabf1feb1a3d34f38920a250454dc9dce0f811956d210804c904959be96a75a9d9b1410aa25f7d9e1b6f69c"] = + "Genesis4", + }; + } +} \ No newline at end of file diff --git a/examples/AEDPoSViewer/appsettings.json b/examples/AEDPoSViewer/appsettings.json index ec1136b..f0b02d7 100644 --- a/examples/AEDPoSViewer/appsettings.json +++ b/examples/AEDPoSViewer/appsettings.json @@ -17,7 +17,7 @@ } ] }, - "AElfAccount":{ + "AElfAccount": { "KeyDirectory": "", "AccountConfigList": [ { @@ -36,5 +36,10 @@ "PrivateKey": "5e2f12d13e4527ad1128e07db00f1614ec6b8b51662e68d4fdb42125ab384195", } ] + }, + "AElfClientConfig": { + "ClientAlias": "MainNetMainChain", + "AccountAlias": "Ean", + "CamelCase": false } } \ No newline at end of file diff --git a/examples/TokenManager/TokenManager.csproj b/examples/AElf.Client.Consensus.AEDPoS/AElf.Client.Consensus.AEDPoS.csproj similarity index 50% rename from examples/TokenManager/TokenManager.csproj rename to examples/AElf.Client.Consensus.AEDPoS/AElf.Client.Consensus.AEDPoS.csproj index b9de063..b1de127 100644 --- a/examples/TokenManager/TokenManager.csproj +++ b/examples/AElf.Client.Consensus.AEDPoS/AElf.Client.Consensus.AEDPoS.csproj @@ -1,10 +1,17 @@ - Exe net6.0 enable enable + + + + + + + + diff --git a/examples/AElf.Client.Consensus.AEDPoS/AElfClientAEDPoSModule.cs b/examples/AElf.Client.Consensus.AEDPoS/AElfClientAEDPoSModule.cs new file mode 100644 index 0000000..63f4b0f --- /dev/null +++ b/examples/AElf.Client.Consensus.AEDPoS/AElfClientAEDPoSModule.cs @@ -0,0 +1,12 @@ +using AElf.Client.Core; +using Volo.Abp.Modularity; + +namespace AElf.Client.Consensus.AEDPoS; + +[DependsOn( + typeof(AElfClientModule), + typeof(CoreAElfModule) +)] +public class AElfClientAEDPoSModule : AbpModule +{ +} \ No newline at end of file diff --git a/examples/AElf.Client.Consensus.AEDPoS/AElfConsensusConstants.cs b/examples/AElf.Client.Consensus.AEDPoS/AElfConsensusConstants.cs new file mode 100644 index 0000000..fcc333a --- /dev/null +++ b/examples/AElf.Client.Consensus.AEDPoS/AElfConsensusConstants.cs @@ -0,0 +1,6 @@ +namespace AElf.Client.Consensus.AEDPoS; + +public class AElfConsensusConstants +{ + public const string ConsensusSmartContractName = "AElf.ContractNames.Consensus"; +} \ No newline at end of file diff --git a/examples/AElf.Client.Consensus.AEDPoS/ConsensusService.Send.cs b/examples/AElf.Client.Consensus.AEDPoS/ConsensusService.Send.cs new file mode 100644 index 0000000..8c0e783 --- /dev/null +++ b/examples/AElf.Client.Consensus.AEDPoS/ConsensusService.Send.cs @@ -0,0 +1,19 @@ +using AElf.Client.Core; +using AElf.Client.Core.Options; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace AElf.Client.Consensus.AEDPoS; + +public partial class ConsensusService : ContractServiceBase, IConsensusService, ITransientDependency +{ + private readonly IAElfClientService _clientService; + private readonly AElfClientConfigOptions _clientConfigOptions; + + public ConsensusService(IAElfClientService clientService, IOptionsSnapshot clientConfigOptions) + : base(clientService, AElfConsensusConstants.ConsensusSmartContractName) + { + _clientService = clientService; + _clientConfigOptions = clientConfigOptions.Value; + } +} \ No newline at end of file diff --git a/examples/AElf.Client.Consensus.AEDPoS/ConsensusService.View.cs b/examples/AElf.Client.Consensus.AEDPoS/ConsensusService.View.cs new file mode 100644 index 0000000..8e8cf61 --- /dev/null +++ b/examples/AElf.Client.Consensus.AEDPoS/ConsensusService.View.cs @@ -0,0 +1,15 @@ +using AElf.Contracts.Consensus.AEDPoS; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; + +namespace AElf.Client.Consensus.AEDPoS; + +public partial class ConsensusService +{ + public async Task GetCurrentMinerList() + { + var useClientAlias = _clientConfigOptions.ClientAlias; + return await _clientService.ViewSystemAsync(AElfConsensusConstants.ConsensusSmartContractName, + "GetCurrentMinerList", new Empty(), useClientAlias); + } +} \ No newline at end of file diff --git a/examples/AElf.Client.Consensus.AEDPoS/IConsensusService.cs b/examples/AElf.Client.Consensus.AEDPoS/IConsensusService.cs new file mode 100644 index 0000000..2f0a120 --- /dev/null +++ b/examples/AElf.Client.Consensus.AEDPoS/IConsensusService.cs @@ -0,0 +1,8 @@ +using AElf.Contracts.Consensus.AEDPoS; + +namespace AElf.Client.Consensus.AEDPoS; + +public interface IConsensusService +{ + Task GetCurrentMinerList(); +} \ No newline at end of file diff --git a/examples/ElectionViewer/ElectionViewer.csproj b/examples/AElf.Client.CrossChain/AElf.Client.CrossChain.csproj similarity index 64% rename from examples/ElectionViewer/ElectionViewer.csproj rename to examples/AElf.Client.CrossChain/AElf.Client.CrossChain.csproj index b9de063..fa15dcd 100644 --- a/examples/ElectionViewer/ElectionViewer.csproj +++ b/examples/AElf.Client.CrossChain/AElf.Client.CrossChain.csproj @@ -1,10 +1,13 @@ - Exe net6.0 enable enable + + + + diff --git a/examples/AElf.Client.CrossChain/AElfClientCrossChainModule.cs b/examples/AElf.Client.CrossChain/AElfClientCrossChainModule.cs new file mode 100644 index 0000000..f53ed5f --- /dev/null +++ b/examples/AElf.Client.CrossChain/AElfClientCrossChainModule.cs @@ -0,0 +1,12 @@ +using AElf.Client.Core; +using Volo.Abp.Modularity; + +namespace AElf.Client.CrossChain; + +[DependsOn( + typeof(AElfClientModule), + typeof(CoreAElfModule) +)] +public class AElfClientCrossChainModule : AbpModule +{ +} \ No newline at end of file diff --git a/examples/AElf.Client.CrossChain/AElfCrossChainConstants.cs b/examples/AElf.Client.CrossChain/AElfCrossChainConstants.cs new file mode 100644 index 0000000..3bc30cd --- /dev/null +++ b/examples/AElf.Client.CrossChain/AElfCrossChainConstants.cs @@ -0,0 +1,6 @@ +namespace AElf.Client.CrossChain; + +public class AElfCrossChainConstants +{ + public const string CrossChainSmartContractName = "AElf.ContractNames.CrossChain"; +} \ No newline at end of file diff --git a/examples/AElf.Client.CrossChain/CrossChainService.cs b/examples/AElf.Client.CrossChain/CrossChainService.cs new file mode 100644 index 0000000..f0a4f72 --- /dev/null +++ b/examples/AElf.Client.CrossChain/CrossChainService.cs @@ -0,0 +1,37 @@ +using AElf.Client.Core; +using AElf.Client.Core.Options; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace AElf.Client.CrossChain; + +public class CrossChainService : ContractServiceBase, ICrossChainService, ITransientDependency +{ + private readonly IAElfClientService _clientService; + private readonly AElfClientConfigOptions _clientConfigOptions; + + public CrossChainService(IAElfClientService clientService, + IOptionsSnapshot clientConfigOptions) : base(clientService, + AElfCrossChainConstants.CrossChainSmartContractName) + { + _clientService = clientService; + _clientConfigOptions = clientConfigOptions.Value; + } + + public async Task GetSyncedHeightByChainId(int chainId) + { + var useClientAlias = _clientConfigOptions.ClientAlias; + var height = chainId == AElfClientConstants.MainChainId + ? await _clientService.ViewSystemAsync(AElfCrossChainConstants.CrossChainSmartContractName, + "GetParentChainHeight", + new Empty(), useClientAlias) + : await _clientService.ViewSystemAsync(AElfCrossChainConstants.CrossChainSmartContractName, + "GetSideChainHeight", new Int32Value + { + Value = chainId + }, useClientAlias); + return height.Value; + } +} \ No newline at end of file diff --git a/examples/AElf.Client.CrossChain/ICrossChainService.cs b/examples/AElf.Client.CrossChain/ICrossChainService.cs new file mode 100644 index 0000000..8537c8d --- /dev/null +++ b/examples/AElf.Client.CrossChain/ICrossChainService.cs @@ -0,0 +1,6 @@ +namespace AElf.Client.CrossChain; + +public interface ICrossChainService +{ + Task GetSyncedHeightByChainId(int chainId); +} \ No newline at end of file diff --git a/examples/AElf.Client.Genesis/AElf.Client.Genesis.csproj b/examples/AElf.Client.Genesis/AElf.Client.Genesis.csproj new file mode 100644 index 0000000..9d89cf5 --- /dev/null +++ b/examples/AElf.Client.Genesis/AElf.Client.Genesis.csproj @@ -0,0 +1,20 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + diff --git a/examples/AElf.Client.Genesis/AElfClientGenesisModule.cs b/examples/AElf.Client.Genesis/AElfClientGenesisModule.cs new file mode 100644 index 0000000..8b33541 --- /dev/null +++ b/examples/AElf.Client.Genesis/AElfClientGenesisModule.cs @@ -0,0 +1,22 @@ +using AElf.Client.Consensus.AEDPoS; +using AElf.Client.Core; +using AElf.Client.Core.Options; +using AElf.Client.Parliament; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace AElf.Client.Genesis; + +[DependsOn(typeof(AElfClientModule), + typeof(CoreAElfModule), + typeof(AElfClientParliamentModule), + typeof(AElfClientAEDPoSModule))] +public class AElfClientGenesisModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + Configure(options => { configuration.GetSection("AElfMinerAccount").Bind(options); }); + } +} \ No newline at end of file diff --git a/examples/AElf.Client.Genesis/DeployContractService.cs b/examples/AElf.Client.Genesis/DeployContractService.cs new file mode 100644 index 0000000..c4523f8 --- /dev/null +++ b/examples/AElf.Client.Genesis/DeployContractService.cs @@ -0,0 +1,168 @@ +using AElf.Client.Consensus.AEDPoS; +using AElf.Client.Core.Infrastructure; +using AElf.Client.Core.Options; +using AElf.Client.Parliament; +using AElf.Standards.ACS0; +using AElf.Standards.ACS3; +using AElf.Types; +using Google.Protobuf; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace AElf.Client.Genesis; + +public class DeployContractService : IDeployContractService, ITransientDependency +{ + private readonly IGenesisService _genesisService; + private readonly IParliamentService _parliamentService; + private readonly IConsensusService _consensusService; + + private readonly IKeyDirectoryProvider _keyDirectoryProvider; + private readonly AElfContractOptions _contractOptions; + + private ILogger Logger { get; set; } + + public DeployContractService( + IGenesisService genesisService, + IConsensusService consensusService, + IParliamentService parliamentService, + IKeyDirectoryProvider keyDirectoryProvider, + IOptionsSnapshot contractOption) + { + _parliamentService = parliamentService; + _genesisService = genesisService; + _consensusService = consensusService; + _keyDirectoryProvider = keyDirectoryProvider; + _contractOptions = contractOption.Value; + + Logger = NullLogger.Instance; + } + + public async Task DeployContractAsync(string contractFileName) + { + Logger.LogInformation("Deploying contract: {ContractFileName}", contractFileName); + var input = await GetContractDeploymentInputAsync(contractFileName); + var proposalNewContact = await _genesisService.ProposeNewContract(input); + Logger.LogInformation("ProposalNewContact: {Result}", proposalNewContact.TransactionResult); + if (proposalNewContact.TransactionResult.Status != TransactionResultStatus.Mined) return null; + + var proposalNewLogs = proposalNewContact.TransactionResult.Logs; + var proposalId = ProposalCreated.Parser + .ParseFrom(proposalNewLogs.First(l => l.Name.Contains(nameof(ProposalCreated))).NonIndexed) + .ProposalId; + var proposalHash = ContractProposed.Parser + .ParseFrom(proposalNewLogs.First(l => l.Name.Contains(nameof(ContractProposed))).NonIndexed) + .ProposedContractInputHash; + + var toBeRelease = await ApproveThroughMinersAsync(proposalId); + if (!toBeRelease) return null; + var releaseApprovedInput = new ReleaseContractInput + { + ProposalId = proposalId, + ProposedContractInputHash = proposalHash + }; + var releaseApprovedContract = await _genesisService.ReleaseApprovedContract(releaseApprovedInput); + Logger.LogInformation("ReleaseApprovedContract: {Result}", releaseApprovedContract.TransactionResult); + if (releaseApprovedContract.TransactionResult.Status != TransactionResultStatus.Mined) return null; + + var releaseApprovedLogs = releaseApprovedContract.TransactionResult.Logs; + var deployProposalId = ProposalCreated.Parser + .ParseFrom(releaseApprovedLogs.First(l => l.Name.Contains(nameof(ProposalCreated))).NonIndexed) + .ProposalId; + + var releaseCodeCheckedInput = new ReleaseContractInput + { + ProposalId = deployProposalId, + ProposedContractInputHash = proposalHash + }; + + if (await CheckProposalAsync(deployProposalId)) + { + var releaseCodeCheckedResult = await _genesisService.ReleaseCodeCheckedContract(releaseCodeCheckedInput); + Logger.LogInformation("ReleaseCodeCheckedResult: {Result}", releaseCodeCheckedResult.TransactionResult); + if (releaseCodeCheckedResult.TransactionResult.Status != TransactionResultStatus.Mined) return null; + var deployAddress = CodeUpdated.Parser.ParseFrom(releaseCodeCheckedResult.TransactionResult.Logs + .First(l => l.Name.Contains(nameof(CodeUpdated))).Indexed + .First()).Address; + Logger.LogInformation($"Contract deploy passed authority, contract address: {deployAddress}"); + return deployAddress; + } + + Logger.LogError("Contract code didn't pass the code check"); + return null; + } + + private async Task GetContractDeploymentInputAsync(string name) + { + var contractPath = GetFileFullPath(name, _contractOptions.ContractDirectory); + var code = await File.ReadAllBytesAsync(contractPath); + var checkCode = await CheckCodeAsync(code); + + if (checkCode) + { + var input = new ContractDeploymentInput + { + Category = 0, + Code = ByteString.CopyFrom(code) + }; + return input; + } + + Logger.LogError("The code is already deployed."); + return new ContractDeploymentInput(); + } + + private async Task ApproveThroughMinersAsync(Hash proposalId) + { + var miners = await _consensusService.GetCurrentMinerList(); + foreach (var minersPubkey in miners.Pubkeys) + { + var miner = Address.FromPublicKey(minersPubkey.ToByteArray()); + var approveResult = await _parliamentService.ApproveAsync(proposalId, miner.ToBase58()); + Logger.LogInformation("Approve: {Result}", approveResult.TransactionResult); + } + var toBeRelease = (await _parliamentService.CheckProposal(proposalId)).ToBeReleased; + return toBeRelease; + } + + private async Task CheckProposalAsync(Hash proposalId) + { + var toBeReleased = false; + var sleepTimes = 0; + while (!toBeReleased && sleepTimes < 20) + { + Thread.Sleep(1000); + toBeReleased = (await _parliamentService.CheckProposal(proposalId)).ToBeReleased; + Console.Write("\rCheck proposal status"); + sleepTimes++; + } + + return toBeReleased; + } + + private string GetFileFullPath(string contractName, string contractFileDirectory) + { + var dirPath = GetDirectoryPath(contractFileDirectory); + var filePath = Path.Combine(dirPath, $"{contractName}.dll"); + var filePathWithExtension = File.Exists(filePath) + ? filePath + : Path.Combine(dirPath, $"{contractName}.dll.patched"); + return filePathWithExtension; + } + + private string GetDirectoryPath(string? configuredKeyDirectory) + { + return string.IsNullOrWhiteSpace(configuredKeyDirectory) + ? Path.Combine(_keyDirectoryProvider.GetAppDataPath(), "contracts") + : configuredKeyDirectory; + } + + private async Task CheckCodeAsync(byte[] code) + { + var hash = HashHelper.ComputeFrom(code); + var registration = await _genesisService.GetSmartContractRegistrationByCodeHash(hash); + return registration.Equals(new SmartContractRegistration()); + } +} \ No newline at end of file diff --git a/examples/AElf.Client.Genesis/GenesisService.Send.cs b/examples/AElf.Client.Genesis/GenesisService.Send.cs new file mode 100644 index 0000000..da4bdd7 --- /dev/null +++ b/examples/AElf.Client.Genesis/GenesisService.Send.cs @@ -0,0 +1,73 @@ +using AElf.Client.Core; +using AElf.Client.Core.Options; +using AElf.Standards.ACS0; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; + +namespace AElf.Client.Genesis; + +public partial class GenesisService : ContractServiceBase, IGenesisService, ITransientDependency +{ + private readonly IAElfClientService _clientService; + private readonly AElfClientConfigOptions _clientConfigOptions; + private readonly string _contractAddress; + + public GenesisService(IAElfClientService clientService, + IOptionsSnapshot clientConfigOptions) : base(clientService, + AElfClientCoreConstants.GenesisSmartContractName) + { + _clientService = clientService; + _clientConfigOptions = clientConfigOptions.Value; + _contractAddress = AsyncHelper.RunSync(async () => + (await _clientService.GetGenesisContractAddressAsync(_clientConfigOptions.ClientAlias)).ToBase58()); + } + + public async Task ProposeNewContract(ContractDeploymentInput contractDeploymentInput) + { + var clientAlias = _clientConfigOptions.ClientAlias; + var tx = await _clientService.SendAsync(_contractAddress, "ProposeNewContract", contractDeploymentInput, + clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias) + }; + } + + public async Task ProposeUpdateContract(ContractUpdateInput contractUpdateInput) + { + var clientAlias = _clientConfigOptions.ClientAlias; + var tx = await _clientService.SendAsync(_contractAddress, "ProposeUpdateContract", contractUpdateInput, + clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias) + }; + } + + public async Task ReleaseApprovedContract(ReleaseContractInput releaseContractInput) + { + var clientAlias = _clientConfigOptions.ClientAlias; + var tx = await _clientService.SendAsync(_contractAddress, "ReleaseApprovedContract", releaseContractInput, + clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias) + }; + } + + public async Task ReleaseCodeCheckedContract(ReleaseContractInput releaseContractInput) + { + var clientAlias = _clientConfigOptions.ClientAlias; + var tx = await _clientService.SendAsync(_contractAddress, "ReleaseCodeCheckedContract", releaseContractInput, + clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias) + }; + } +} \ No newline at end of file diff --git a/examples/AElf.Client.Genesis/GenesisService.View.cs b/examples/AElf.Client.Genesis/GenesisService.View.cs new file mode 100644 index 0000000..461c331 --- /dev/null +++ b/examples/AElf.Client.Genesis/GenesisService.View.cs @@ -0,0 +1,29 @@ +using AElf.Standards.ACS0; +using AElf.Types; +using Google.Protobuf.WellKnownTypes; + +namespace AElf.Client.Genesis; + +public partial class GenesisService +{ + public async Task GetContractDeploymentController() + { + var useClientAlias = _clientConfigOptions.ClientAlias; + return await _clientService.ViewAsync(_contractAddress, "GetContractDeploymentController", + new Empty(), useClientAlias); + } + + public async Task GetSmartContractRegistrationByCodeHash(Hash codeHash) + { + var useClientAlias = _clientConfigOptions.ClientAlias; + return await _clientService.ViewAsync(_contractAddress, + "GetSmartContractRegistrationByCodeHash", codeHash, useClientAlias); + } + + public async Task GetContractInfo(Address contractAddress) + { + var useClientAlias = _clientConfigOptions.ClientAlias; + return await _clientService.ViewAsync(_contractAddress, "GetContractInfo", + contractAddress, useClientAlias); + } +} \ No newline at end of file diff --git a/examples/AElf.Client.Genesis/IDeployContractService.cs b/examples/AElf.Client.Genesis/IDeployContractService.cs new file mode 100644 index 0000000..1a5526c --- /dev/null +++ b/examples/AElf.Client.Genesis/IDeployContractService.cs @@ -0,0 +1,8 @@ +using AElf.Types; + +namespace AElf.Client.Genesis; + +public interface IDeployContractService +{ + Task DeployContractAsync(string contractFileName); +} \ No newline at end of file diff --git a/examples/AElf.Client.Genesis/IGenesisService.cs b/examples/AElf.Client.Genesis/IGenesisService.cs new file mode 100644 index 0000000..178c6e2 --- /dev/null +++ b/examples/AElf.Client.Genesis/IGenesisService.cs @@ -0,0 +1,21 @@ +using AElf.Client.Core; +using AElf.Standards.ACS0; +using AElf.Types; + +namespace AElf.Client.Genesis; + +public interface IGenesisService +{ + Task ProposeNewContract(ContractDeploymentInput contractDeploymentInput); + + Task ProposeUpdateContract(ContractUpdateInput contractUpdateInput); + + Task ReleaseApprovedContract(ReleaseContractInput releaseContractInput); + + Task ReleaseCodeCheckedContract(ReleaseContractInput releaseContractInput); + + + Task GetContractDeploymentController(); + Task GetContractInfo(Address contractAddress); + Task GetSmartContractRegistrationByCodeHash(Hash codeHash); +} \ No newline at end of file diff --git a/examples/AElf.Client.Parliament/AElf.Client.Parliament.csproj b/examples/AElf.Client.Parliament/AElf.Client.Parliament.csproj new file mode 100644 index 0000000..08725e2 --- /dev/null +++ b/examples/AElf.Client.Parliament/AElf.Client.Parliament.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + diff --git a/examples/AElf.Client.Parliament/AElfClientParliamentModule.cs b/examples/AElf.Client.Parliament/AElfClientParliamentModule.cs new file mode 100644 index 0000000..4e2a703 --- /dev/null +++ b/examples/AElf.Client.Parliament/AElfClientParliamentModule.cs @@ -0,0 +1,12 @@ +using AElf.Client.Core; +using Volo.Abp.Modularity; + +namespace AElf.Client.Parliament; + +[DependsOn( + typeof(AElfClientModule), + typeof(CoreAElfModule) +)] +public class AElfClientParliamentModule : AbpModule +{ +} \ No newline at end of file diff --git a/examples/AElf.Client.Parliament/AElfParliamentConstants.cs b/examples/AElf.Client.Parliament/AElfParliamentConstants.cs new file mode 100644 index 0000000..1b56bf5 --- /dev/null +++ b/examples/AElf.Client.Parliament/AElfParliamentConstants.cs @@ -0,0 +1,6 @@ +namespace AElf.Client.Parliament; + +public class AElfParliamentConstants +{ + public const string ParliamentSmartContractName = "AElf.ContractNames.Parliament"; +} \ No newline at end of file diff --git a/examples/AElf.Client.Parliament/IParliamentService.cs b/examples/AElf.Client.Parliament/IParliamentService.cs new file mode 100644 index 0000000..6676ecc --- /dev/null +++ b/examples/AElf.Client.Parliament/IParliamentService.cs @@ -0,0 +1,16 @@ +using AElf.Client.Core; +using AElf.Types; +using AElf.Standards.ACS3; +using AElf.Contracts.Parliament; + +namespace AElf.Client.Parliament; + +public interface IParliamentService +{ + Task ApproveAsync(Hash proposalId, string accountAddress); + + Task CheckProposal(Hash proposalId); + + Task GetOrganization(Address organizationAddress); + +} \ No newline at end of file diff --git a/examples/AElf.Client.Parliament/Options/AElfMinerAccountOptions.cs b/examples/AElf.Client.Parliament/Options/AElfMinerAccountOptions.cs new file mode 100644 index 0000000..3be3b5d --- /dev/null +++ b/examples/AElf.Client.Parliament/Options/AElfMinerAccountOptions.cs @@ -0,0 +1,6 @@ +namespace AElf.Client.Core.Options; + +public class AElfMinerAccountOptions +{ + public string DefaultPassword { get; set; } +} \ No newline at end of file diff --git a/examples/AElf.Client.Parliament/ParliamentService.Send.cs b/examples/AElf.Client.Parliament/ParliamentService.Send.cs new file mode 100644 index 0000000..0bfc3ac --- /dev/null +++ b/examples/AElf.Client.Parliament/ParliamentService.Send.cs @@ -0,0 +1,37 @@ +using AElf.Client.Core; +using AElf.Client.Core.Options; +using AElf.Types; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace AElf.Client.Parliament; + +public partial class ParliamentService : ContractServiceBase, IParliamentService, ITransientDependency +{ + private readonly IAElfClientService _clientService; + private readonly AElfClientConfigOptions _clientConfigOptions; + private readonly IAElfAccountProvider _aelfAccountProvider; + + public ParliamentService(IAElfClientService clientService, IAElfAccountProvider aelfAccountProvider, + IOptionsSnapshot clientConfigOptions) + : base(clientService, AElfParliamentConstants.ParliamentSmartContractName) + { + _clientService = clientService; + _aelfAccountProvider = aelfAccountProvider; + _clientConfigOptions = clientConfigOptions.Value; + } + + public async Task ApproveAsync(Hash proposalId, string accountAddress) + { + var useClientAlias = _clientConfigOptions.ClientAlias; + _aelfAccountProvider.AddAccountByDefaultPassword(accountAddress); + var accountAlias = _aelfAccountProvider.GetAliasByAddress(accountAddress); + var tx = await _clientService.SendSystemAsync(AElfParliamentConstants.ParliamentSmartContractName, "Approve", + proposalId, useClientAlias, accountAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), useClientAlias) + }; + } +} \ No newline at end of file diff --git a/examples/AElf.Client.Parliament/ParliamentService.View.cs b/examples/AElf.Client.Parliament/ParliamentService.View.cs new file mode 100644 index 0000000..54d0cf9 --- /dev/null +++ b/examples/AElf.Client.Parliament/ParliamentService.View.cs @@ -0,0 +1,23 @@ +using AElf.Contracts.Parliament; +using AElf.Standards.ACS3; +using AElf.Types; +using Google.Protobuf; + +namespace AElf.Client.Parliament; + +public partial class ParliamentService +{ + public async Task CheckProposal(Hash proposalId) + { + var useClientAlias = _clientConfigOptions.ClientAlias; + return await _clientService.ViewSystemAsync(AElfParliamentConstants.ParliamentSmartContractName, + "GetProposal", proposalId, useClientAlias); + } + + public async Task GetOrganization(Address organizationAddress) + { + var useClientAlias = _clientConfigOptions.ClientAlias; + return await _clientService.ViewSystemAsync(AElfParliamentConstants.ParliamentSmartContractName, + "GetOrganization", organizationAddress, useClientAlias); + } +} \ No newline at end of file diff --git a/examples/AElf.Client.Token/AElf.Client.Token.csproj b/examples/AElf.Client.Token/AElf.Client.Token.csproj new file mode 100644 index 0000000..13d8a42 --- /dev/null +++ b/examples/AElf.Client.Token/AElf.Client.Token.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + enable + AElf.Client.Token + AElf token manager abp module. + AElf + true + + + + + + + + diff --git a/examples/AElf.Client.Token/AElfClientTokenModule.cs b/examples/AElf.Client.Token/AElfClientTokenModule.cs new file mode 100644 index 0000000..33131ce --- /dev/null +++ b/examples/AElf.Client.Token/AElfClientTokenModule.cs @@ -0,0 +1,32 @@ +using AElf.Client.Core; +using AElf.Client.Core.Options; +using AElf.Client.CrossChain; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; +using Volo.Abp.Modularity; + +namespace AElf.Client.Token; + +[DependsOn( + typeof(AElfClientModule), + typeof(CoreAElfModule), + typeof(AElfClientCrossChainModule) +)] +public class AElfClientTokenModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + Configure(options => { configuration.GetSection("AElfContract").Bind(options); }); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var taskQueueManager = context.ServiceProvider.GetService(); + taskQueueManager?.CreateQueue(AElfTokenConstants.SyncTokenInfoQueueName, + AElfTokenConstants.DefaultMaxDegreeOfParallelism); + taskQueueManager?.CreateQueue(AElfTokenConstants.CrossChainTransferQueueName, + AElfTokenConstants.DefaultMaxDegreeOfParallelism); + } +} \ No newline at end of file diff --git a/examples/AElf.Client.Token/AElfTokenConstants.cs b/examples/AElf.Client.Token/AElfTokenConstants.cs new file mode 100644 index 0000000..4b6f692 --- /dev/null +++ b/examples/AElf.Client.Token/AElfTokenConstants.cs @@ -0,0 +1,11 @@ +namespace AElf.Client.Token; + +public class AElfTokenConstants +{ + public const string TokenSmartContractName = "AElf.ContractNames.Token"; + public const string SyncTokenInfoQueueName = "SyncTokenInfo"; + public const string CrossChainTransferQueueName = "CrossChainTransfer"; + public const int DefaultMaxDegreeOfParallelism = int.MaxValue; + public const int TenSeconds = 10_000; + public const string TestNetSideChain1MultiTokenContractAddress = "7RzVGiuVWkvL4VfVHdZfQF2Tri3sgLe9U991bohHFfSRZXuGX"; +} \ No newline at end of file diff --git a/examples/AElf.Client.Token/CrossChainTransfer/CrossChainTransferQueueService.cs b/examples/AElf.Client.Token/CrossChainTransfer/CrossChainTransferQueueService.cs new file mode 100644 index 0000000..ebf6679 --- /dev/null +++ b/examples/AElf.Client.Token/CrossChainTransfer/CrossChainTransferQueueService.cs @@ -0,0 +1,28 @@ +using AElf.Types; +using Volo.Abp.DependencyInjection; + +namespace AElf.Client.Token.CrossChainTransfer; + +public class CrossChainTransferQueueService : ICrossChainTransferQueueService, ITransientDependency +{ + private readonly ICrossChainTransferService _crossChainTransferService; + private readonly ITaskQueueManager _taskQueueManager; + + public CrossChainTransferQueueService(ICrossChainTransferService crossChainTransferService, + ITaskQueueManager taskQueueManager) + { + _crossChainTransferService = crossChainTransferService; + _taskQueueManager = taskQueueManager; + } + + public void Enqueue(Address to, string symbol, long amount, string fromClientAlias, string toClientAlias) + { + _taskQueueManager.Enqueue( + async () => + { + await _crossChainTransferService.CrossChainTransferAsync(to, symbol, amount, fromClientAlias, + toClientAlias); + }, + AElfTokenConstants.CrossChainTransferQueueName); + } +} \ No newline at end of file diff --git a/examples/AElf.Client.Token/CrossChainTransfer/CrossChainTransferService.cs b/examples/AElf.Client.Token/CrossChainTransfer/CrossChainTransferService.cs new file mode 100644 index 0000000..4fedc66 --- /dev/null +++ b/examples/AElf.Client.Token/CrossChainTransfer/CrossChainTransferService.cs @@ -0,0 +1,75 @@ +using AElf.Client.Core; +using AElf.Client.Core.Options; +using AElf.Contracts.MultiToken; +using AElf.Types; +using Google.Protobuf; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace AElf.Client.Token.CrossChainTransfer; + +public class CrossChainTransferService : ICrossChainTransferService, ITransientDependency +{ + private readonly ITokenService _tokenService; + private readonly IAElfClientService _clientService; + private readonly AElfClientConfigOptions _clientConfigOptions; + public ILogger Logger { get; set; } + + public CrossChainTransferService(ITokenService tokenService, IAElfClientService clientService, + IOptionsSnapshot clientConfigOptions) + { + _tokenService = tokenService; + _clientService = clientService; + _clientConfigOptions = clientConfigOptions.Value; + } + + public async Task CrossChainTransferAsync(Address to, string symbol, long amount, string fromClientAlias, + string toClientAlias) + { + var fromChainStatus = await _clientService.GetChainStatusAsync(fromClientAlias); + var toChainStatus = await _clientService.GetChainStatusAsync(toClientAlias); + var tokenInfo = await _tokenService.GetTokenInfoAsync(symbol); + var crossChainTransferInput = new CrossChainTransferInput + { + To = to, + Symbol = symbol, + Amount = amount, + ToChainId = ChainHelper.ConvertBase58ToChainId(toChainStatus.ChainId), + IssueChainId = tokenInfo.IssueChainId + }; + var transferResult = await _tokenService.CrossChainTransferAsync(crossChainTransferInput, fromClientAlias); + Logger.LogInformation("CrossChainTransfer: {Result}", transferResult.TransactionResult); + if (transferResult.TransactionResult.Status == TransactionResultStatus.Mined) + { + while (true) + { + var chainStatus = + await _clientService.GetChainStatusAsync(fromClientAlias); + Logger.LogInformation( + "From chain lib height: {LibHeight}, Transfer tx package height: {TransferHeight}", + chainStatus.LastIrreversibleBlockHeight, transferResult.TransactionResult.BlockNumber); + if (chainStatus.LastIrreversibleBlockHeight - transferResult.TransactionResult.BlockNumber > 300) + break; + await Task.Delay(AElfTokenConstants.TenSeconds); + } + + var merklePath = await _clientService.GetMerklePathByTransactionIdAsync( + transferResult.TransactionResult.TransactionId.ToHex(), + fromClientAlias); + var crossChainReceiveTokenInput = new CrossChainReceiveTokenInput + { + FromChainId = ChainHelper.ConvertBase58ToChainId(fromChainStatus.ChainId), + MerklePath = merklePath, + ParentChainHeight = transferResult.TransactionResult.BlockNumber, + TransferTransactionBytes = + ByteString.CopyFrom( + ByteArrayHelper.HexStringToByteArray(transferResult.Transaction.ToByteArray().ToHex())), + }; + + var crossChainReceiveTokenResult = + await _tokenService.CrossChainReceiveTokenAsync(crossChainReceiveTokenInput, toClientAlias); + Logger.LogInformation("CrossChainReceiveToken: {Result}", crossChainReceiveTokenResult.TransactionResult); + } + } +} \ No newline at end of file diff --git a/examples/AElf.Client.Token/CrossChainTransfer/ICrossChainTransferQueueService.cs b/examples/AElf.Client.Token/CrossChainTransfer/ICrossChainTransferQueueService.cs new file mode 100644 index 0000000..794a13e --- /dev/null +++ b/examples/AElf.Client.Token/CrossChainTransfer/ICrossChainTransferQueueService.cs @@ -0,0 +1,8 @@ +using AElf.Types; + +namespace AElf.Client.Token.CrossChainTransfer; + +public interface ICrossChainTransferQueueService +{ + void Enqueue(Address to, string symbol, long amount, string fromClientAlias, string toClientAlias); +} \ No newline at end of file diff --git a/examples/AElf.Client.Token/CrossChainTransfer/ICrossChainTransferService.cs b/examples/AElf.Client.Token/CrossChainTransfer/ICrossChainTransferService.cs new file mode 100644 index 0000000..bcfdb65 --- /dev/null +++ b/examples/AElf.Client.Token/CrossChainTransfer/ICrossChainTransferService.cs @@ -0,0 +1,8 @@ +using AElf.Types; + +namespace AElf.Client.Token.CrossChainTransfer; + +public interface ICrossChainTransferService +{ + Task CrossChainTransferAsync(Address to, string symbol, long amount, string fromClientAlias, string toClientAlias); +} \ No newline at end of file diff --git a/examples/AElf.Client.Token/ITokenService.cs b/examples/AElf.Client.Token/ITokenService.cs new file mode 100644 index 0000000..f971e55 --- /dev/null +++ b/examples/AElf.Client.Token/ITokenService.cs @@ -0,0 +1,33 @@ +using AElf.Client.Core; +using AElf.Contracts.Bridge; +using AElf.Contracts.MultiToken; +using AElf.Contracts.NFT; +using AElf.Types; +using TransferInput = AElf.Contracts.MultiToken.TransferInput; + +namespace AElf.Client.Token; + +public interface ITokenService +{ + Task CreateTokenAsync(Contracts.MultiToken.CreateInput createInput); + Task CreateNFTProtocolAsync(Contracts.NFT.CreateInput createInput); + Task MintNFTAsync(MintInput mintInput); + Task ValidateTokenInfoExistsAsync(ValidateTokenInfoExistsInput validateTokenInfoExistsInput); + Task CrossChainCreateTokenAsync(CrossChainCreateTokenInput crossChainCreateTokenInput); + Task CrossChainTransferAsync(CrossChainTransferInput crossChainTransferInput, + string clientAlias); + Task CrossChainReceiveTokenAsync(CrossChainReceiveTokenInput crossChainReceiveTokenInput, + string clientAlias); + + Task CrossChainCreateNFTProtocolAsync(CrossChainCreateInput crossChainCreateInput); + + Task TransferAsync(TransferInput transferInput); + + Task AddMintersAsync(AddMintersInput addMintersInput); + Task SwapTokenAsync(SwapTokenInput swapTokenInput); + + Task GetTokenInfoAsync(string symbol); + Task GetTokenBalanceAsync(string symbol, Address owner); + Task GetTokenAllowanceAsync(string symbol, Address owner, Address spender); + Task GetCalculateFeeCoefficientsForSenderAsync(); +} \ No newline at end of file diff --git a/examples/AElf.Client.Token/README.md b/examples/AElf.Client.Token/README.md new file mode 100644 index 0000000..a7c2a45 --- /dev/null +++ b/examples/AElf.Client.Token/README.md @@ -0,0 +1,4 @@ +# AElf Client TokenManager +Some useful methods to interact with: +- [AElf MultiToken Contract](https://github.com/AElfProject/AElf/tree/dev/contract/AElf.Contracts.MultiToken) +- [AElf NFT Contract](https://github.com/AElfProject/AElf/tree/dev/contract/AElf.Contracts.NFT) diff --git a/examples/AElf.Client.Token/SyncTokenInfo/ISyncTokenInfoQueueService.cs b/examples/AElf.Client.Token/SyncTokenInfo/ISyncTokenInfoQueueService.cs new file mode 100644 index 0000000..6f28907 --- /dev/null +++ b/examples/AElf.Client.Token/SyncTokenInfo/ISyncTokenInfoQueueService.cs @@ -0,0 +1,6 @@ +namespace AElf.Client.Token.SyncTokenInfo; + +public interface ISyncTokenInfoQueueService +{ + void Enqueue(string symbol); +} \ No newline at end of file diff --git a/examples/AElf.Client.Token/SyncTokenInfo/ISyncTokenInfoService.cs b/examples/AElf.Client.Token/SyncTokenInfo/ISyncTokenInfoService.cs new file mode 100644 index 0000000..8ebb3ff --- /dev/null +++ b/examples/AElf.Client.Token/SyncTokenInfo/ISyncTokenInfoService.cs @@ -0,0 +1,6 @@ +namespace AElf.Client.Token.SyncTokenInfo; + +public interface ISyncTokenInfoService +{ + Task SyncTokenInfoAsync(string symbol); +} \ No newline at end of file diff --git a/examples/AElf.Client.Token/SyncTokenInfo/SyncTokenInfoQueueService.cs b/examples/AElf.Client.Token/SyncTokenInfo/SyncTokenInfoQueueService.cs new file mode 100644 index 0000000..c6d506e --- /dev/null +++ b/examples/AElf.Client.Token/SyncTokenInfo/SyncTokenInfoQueueService.cs @@ -0,0 +1,22 @@ +using Volo.Abp.DependencyInjection; + +namespace AElf.Client.Token.SyncTokenInfo; + +public class SyncTokenInfoQueueService : ISyncTokenInfoQueueService, ITransientDependency +{ + private readonly ISyncTokenInfoService _syncTokenInfoService; + private readonly ITaskQueueManager _taskQueueManager; + + public SyncTokenInfoQueueService(ISyncTokenInfoService syncTokenInfoService, + ITaskQueueManager taskQueueManager) + { + _syncTokenInfoService = syncTokenInfoService; + _taskQueueManager = taskQueueManager; + } + + public void Enqueue(string symbol) + { + _taskQueueManager.Enqueue(async () => { await _syncTokenInfoService.SyncTokenInfoAsync(symbol); }, + AElfTokenConstants.SyncTokenInfoQueueName); + } +} \ No newline at end of file diff --git a/examples/AElf.Client.Token/SyncTokenInfo/SyncTokenInfoService.cs b/examples/AElf.Client.Token/SyncTokenInfo/SyncTokenInfoService.cs new file mode 100644 index 0000000..930c8e5 --- /dev/null +++ b/examples/AElf.Client.Token/SyncTokenInfo/SyncTokenInfoService.cs @@ -0,0 +1,86 @@ +using AElf.Client.Core; +using AElf.Client.Core.Options; +using AElf.Client.CrossChain; +using AElf.Contracts.MultiToken; +using AElf.Types; +using Google.Protobuf; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace AElf.Client.Token.SyncTokenInfo; + +public class SyncTokenInfoService : ISyncTokenInfoService, ITransientDependency +{ + private readonly ITokenService _tokenService; + private readonly ICrossChainService _crossChainService; + private readonly IAElfClientService _clientService; + private readonly AElfClientConfigOptions _clientConfigOptions; + public ILogger Logger { get; set; } + + public SyncTokenInfoService(ITokenService tokenService, ICrossChainService crossChainService, + IAElfClientService clientService, IOptionsSnapshot clientConfigOptions) + { + _tokenService = tokenService; + _crossChainService = crossChainService; + _clientService = clientService; + _clientConfigOptions = clientConfigOptions.Value; + + Logger = NullLogger.Instance; + } + + public async Task SyncTokenInfoAsync(string symbol) + { + var tokenInfo = await _tokenService.GetTokenInfoAsync(symbol); + var validateInput = new ValidateTokenInfoExistsInput + { + Symbol = tokenInfo.Symbol, + TokenName = tokenInfo.TokenName, + Decimals = tokenInfo.Decimals, + IsBurnable = tokenInfo.IsBurnable, + IssueChainId = tokenInfo.IssueChainId, + Issuer = tokenInfo.Issuer, + TotalSupply = tokenInfo.TotalSupply, + ExternalInfo = { tokenInfo.ExternalInfo.Value } + }; + + var validateResult = await _tokenService.ValidateTokenInfoExistsAsync(validateInput); + var packagedBlockHeight = validateResult.TransactionResult.BlockNumber; + Logger.LogInformation("ValidateTokenInfoExists: {Result}", validateResult.TransactionResult); + if (validateResult.TransactionResult.Status == TransactionResultStatus.Mined) + { + while (true) + { + var syncedMainChainHeight = + await _crossChainService.GetSyncedHeightByChainId(AElfClientConstants.MainChainId); + Logger.LogInformation( + "Synced main chain height: {MainChainHeight}, Validate tx package height: {ValidateHeight}", + syncedMainChainHeight, validateResult.TransactionResult.BlockNumber); + if (syncedMainChainHeight >= packagedBlockHeight) + { + break; + } + + await Task.Delay(AElfTokenConstants.TenSeconds); + } + + var merklePath = await _clientService.GetMerklePathByTransactionIdAsync( + validateResult.TransactionResult.TransactionId.ToHex(), + _clientConfigOptions.MainChainClientAlias); + var crossChainCreateTokenInput = new CrossChainCreateTokenInput + { + FromChainId = AElfClientConstants.MainChainId, + ParentChainHeight = validateResult.TransactionResult.BlockNumber, + TransactionBytes = + ByteString.CopyFrom( + ByteArrayHelper.HexStringToByteArray(validateResult.Transaction.ToByteArray().ToHex())), + MerklePath = merklePath + }; + + var crossChainCreateTokenResult = + await _tokenService.CrossChainCreateTokenAsync(crossChainCreateTokenInput); + Logger.LogInformation("CrossChainCreateToken: {Result}", crossChainCreateTokenResult.TransactionResult); + } + } +} \ No newline at end of file diff --git a/examples/AElf.Client.Token/TokenService.Send.cs b/examples/AElf.Client.Token/TokenService.Send.cs new file mode 100644 index 0000000..15cfcfc --- /dev/null +++ b/examples/AElf.Client.Token/TokenService.Send.cs @@ -0,0 +1,177 @@ +using AElf.Client.Core; +using AElf.Client.Core.Options; +using AElf.Contracts.Bridge; +using AElf.Contracts.MultiToken; +using AElf.Contracts.NFT; +using AElf.Types; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; +using CreateInput = AElf.Contracts.MultiToken.CreateInput; +using TransferInput = AElf.Contracts.MultiToken.TransferInput; + +namespace AElf.Client.Token; + +public partial class TokenService : ContractServiceBase, ITokenService, ITransientDependency +{ + private readonly IAElfClientService _clientService; + private readonly AElfContractOptions _contractOptions; + private readonly AElfClientConfigOptions _clientConfigOptions; + + public TokenService(IAElfClientService clientService, IOptionsSnapshot clientConfigOptions, + IOptionsSnapshot contractOptions) : base(clientService, + AElfTokenConstants.TokenSmartContractName) + { + _clientService = clientService; + _contractOptions = contractOptions.Value; + _clientConfigOptions = clientConfigOptions.Value; + } + + public async Task CreateTokenAsync(CreateInput createInput) + { + var clientAlias = PreferGetUseMainChainClientAlias(); + var tx = await PerformSendTransactionAsync("Create", createInput, clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias) + }; + } + + public async Task CreateNFTProtocolAsync(Contracts.NFT.CreateInput createInput) + { + var clientAlias = PreferGetUseMainChainClientAlias(); + var tx = await PerformSendTransactionAsync("Create", createInput, clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias) + }; + } + + public async Task MintNFTAsync(MintInput mintInput) + { + var clientAlias = _clientConfigOptions.ClientAlias; + var tx = await PerformSendTransactionAsync("Mint", mintInput, clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias) + }; + } + + public async Task ValidateTokenInfoExistsAsync( + ValidateTokenInfoExistsInput validateTokenInfoExistsInput) + { + var clientAlias = PreferGetUseMainChainClientAlias(); + var tx = await PerformSendTransactionAsync("ValidateTokenInfoExists", validateTokenInfoExistsInput, + clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias) + }; + } + + public async Task CrossChainCreateTokenAsync( + CrossChainCreateTokenInput crossChainCreateTokenInput) + { + var clientAlias = PreferGetUseSideChainClientAlias(); + var tx = await PerformSendTransactionAsync("CrossChainCreateToken", crossChainCreateTokenInput, clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias) + }; + } + + public async Task CrossChainTransferAsync(CrossChainTransferInput crossChainTransferInput, + string clientAlias) + { + var tx = await _clientService.SendSystemAsync(AElfTokenConstants.TokenSmartContractName, "CrossChainTransfer", + crossChainTransferInput, clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias) + }; + } + + public async Task CrossChainReceiveTokenAsync( + CrossChainReceiveTokenInput crossChainReceiveTokenInput, string clientAlias) + { + var tx = await PerformSendTransactionAsync("CrossChainReceiveToken", crossChainReceiveTokenInput, + clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias) + }; + } + + public async Task CrossChainCreateNFTProtocolAsync( + CrossChainCreateInput crossChainCreateInput) + { + var clientAlias = PreferGetUseSideChainClientAlias(); + ContractAddress = Address.FromBase58(_contractOptions.ContractAddressList["NFTContract"]); + var tx = await PerformSendTransactionAsync("CrossChainCreate", crossChainCreateInput, + clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias) + }; + } + + public async Task TransferAsync(TransferInput transferInput) + { + var clientAlias = _clientConfigOptions.ClientAlias; + var tx = await PerformSendTransactionAsync("Transfer", transferInput, + clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias) + }; + } + + public async Task AddMintersAsync(AddMintersInput addMintersInput) + { + var clientAlias = _clientConfigOptions.ClientAlias; + ContractAddress = Address.FromBase58(_contractOptions.ContractAddressList["NFTContract"]); + var tx = await PerformSendTransactionAsync("AddMinters", addMintersInput, + clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias) + }; + } + + public async Task SwapTokenAsync(SwapTokenInput swapTokenInput) + { + var clientAlias = _clientConfigOptions.ClientAlias; + ContractAddress = Address.FromBase58(_contractOptions.ContractAddressList["BridgeContract"]); + var tx = await PerformSendTransactionAsync("SwapToken", swapTokenInput, + clientAlias); + var txResult = await PerformGetTransactionResultAsync(tx.GetHash().ToHex(), clientAlias); + return new SendTransactionResult + { + Transaction = tx, + TransactionResult = txResult + }; + } + + private string PreferGetUseMainChainClientAlias() + { + return !string.IsNullOrEmpty(_clientConfigOptions.MainChainClientAlias) + ? _clientConfigOptions.MainChainClientAlias + : _clientConfigOptions.ClientAlias; + } + + private string PreferGetUseSideChainClientAlias() + { + return !string.IsNullOrEmpty(_clientConfigOptions.SideChainClientAlias) + ? _clientConfigOptions.SideChainClientAlias + : _clientConfigOptions.ClientAlias; + } +} \ No newline at end of file diff --git a/examples/AElf.Client.Token/TokenService.View.cs b/examples/AElf.Client.Token/TokenService.View.cs new file mode 100644 index 0000000..a98257b --- /dev/null +++ b/examples/AElf.Client.Token/TokenService.View.cs @@ -0,0 +1,50 @@ +using AElf.Contracts.MultiToken; +using AElf.Types; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; +using Microsoft.Extensions.Logging; + +namespace AElf.Client.Token; + +public partial class TokenService +{ + public async Task GetTokenInfoAsync(string symbol) + { + var useClientAlias = _clientConfigOptions.ClientAlias; + return await _clientService.ViewSystemAsync(AElfTokenConstants.TokenSmartContractName, + "GetTokenInfo", new GetTokenInfoInput + { + Symbol = symbol + }, useClientAlias); + } + + public async Task GetTokenBalanceAsync(string symbol, Address owner) + { + var useClientAlias = _clientConfigOptions.ClientAlias; + return await _clientService.ViewSystemAsync(AElfTokenConstants.TokenSmartContractName, + "GetBalance", new GetBalanceInput + { + Owner = owner, + Symbol = symbol + }, useClientAlias); + } + + public async Task GetTokenAllowanceAsync(string symbol, Address owner, Address spender) + { + var useClientAlias = _clientConfigOptions.ClientAlias; + return await _clientService.ViewSystemAsync(AElfTokenConstants.TokenSmartContractName, + "GetAllowance", new GetAllowanceInput + { + Owner = owner, + Spender = spender, + Symbol = symbol + }, useClientAlias); + } + + public async Task GetCalculateFeeCoefficientsForSenderAsync() + { + var useClientAlias = _clientConfigOptions.ClientAlias; + return await _clientService.ViewSystemAsync(AElfTokenConstants.TokenSmartContractName, + "GetCalculateFeeCoefficientsForSender", new Empty(), useClientAlias); + } +} \ No newline at end of file diff --git a/examples/ElectionViewer/Program.cs b/examples/ElectionViewer/Program.cs deleted file mode 100644 index e5dff12..0000000 --- a/examples/ElectionViewer/Program.cs +++ /dev/null @@ -1,3 +0,0 @@ -// See https://aka.ms/new-console-template for more information - -Console.WriteLine("Hello, World!"); \ No newline at end of file diff --git a/examples/TokenManager/Program.cs b/examples/TokenManager/Program.cs deleted file mode 100644 index e5dff12..0000000 --- a/examples/TokenManager/Program.cs +++ /dev/null @@ -1,3 +0,0 @@ -// See https://aka.ms/new-console-template for more information - -Console.WriteLine("Hello, World!"); \ No newline at end of file diff --git a/src/AElf.Client.Abp/AElfClientService.cs b/src/AElf.Client.Abp/AElfClientService.cs deleted file mode 100644 index 734cbb7..0000000 --- a/src/AElf.Client.Abp/AElfClientService.cs +++ /dev/null @@ -1,54 +0,0 @@ -using AElf.Client.Dto; -using Google.Protobuf; -using Volo.Abp.DependencyInjection; - -namespace AElf.Client.Abp; - -public class AElfClientService : IAElfClientService, ITransientDependency -{ - private readonly IAElfClientProvider _aelfClientProvider; - private readonly IAElfAccountProvider _aelfAccountProvider; - - public AElfClientService(IAElfClientProvider aelfClientProvider, IAElfAccountProvider aelfAccountProvider) - { - _aelfClientProvider = aelfClientProvider; - _aelfAccountProvider = aelfAccountProvider; - } - - public async Task ViewAsync(string contractAddress, string methodName, IMessage parameter, string clientAlias, - string accountAlias = "Default") - { - var aelfClient = _aelfClientProvider.GetClient(alias: clientAlias); - var aelfAccount = _aelfAccountProvider.GetPrivateKey(alias: accountAlias); - var tx = new TransactionBuilder(aelfClient) - .UsePrivateKey(aelfAccount) - .UseContract(contractAddress) - .UseMethod(methodName) - .UseParameter(parameter) - .Build(); - return await PerformViewAsync(aelfClient, tx); - } - - public async Task ViewSystemAsync(string systemContractName, string methodName, IMessage parameter, string clientAlias, - string accountAlias = "Default") - { - var aelfClient = _aelfClientProvider.GetClient(alias: clientAlias); - var privateKey = _aelfAccountProvider.GetPrivateKey(alias: accountAlias); - var tx = new TransactionBuilder(aelfClient) - .UsePrivateKey(privateKey) - .UseSystemContract(systemContractName) - .UseMethod(methodName) - .UseParameter(parameter) - .Build(); - return await PerformViewAsync(aelfClient, tx); - } - - private async Task PerformViewAsync(AElfClient aelfClient, Transaction tx) - { - var result = await aelfClient.ExecuteTransactionAsync(new ExecuteTransactionDto - { - RawTransaction = tx.ToByteArray().ToHex() - }); - return ByteArrayHelper.HexStringToByteArray(result); - } -} \ No newline at end of file diff --git a/src/AElf.Client.Abp/IAElfClientService.cs b/src/AElf.Client.Abp/IAElfClientService.cs deleted file mode 100644 index 3d126f5..0000000 --- a/src/AElf.Client.Abp/IAElfClientService.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Google.Protobuf; - -namespace AElf.Client; - -public interface IAElfClientService -{ - Task ViewAsync(string contractAddress, string methodName, IMessage parameter, string clientAlias, - string accountAlias = "Default"); - - Task ViewSystemAsync(string systemContractName, string methodName, IMessage parameter, - string clientAlias, string accountAlias = "Default"); -} \ No newline at end of file diff --git a/src/AElf.Client.Abp/AElf.Client.Abp.csproj b/src/AElf.Client.Core/AElf.Client.Core.csproj similarity index 53% rename from src/AElf.Client.Abp/AElf.Client.Abp.csproj rename to src/AElf.Client.Core/AElf.Client.Core.csproj index 2f6815f..f048d05 100644 --- a/src/AElf.Client.Abp/AElf.Client.Abp.csproj +++ b/src/AElf.Client.Core/AElf.Client.Core.csproj @@ -4,18 +4,21 @@ net6.0 enable enable - AElf.Client.Abp - C# sdk with abp module. + AElf.Client.Core + AElf C# sdk with abp module. AElf true + AElf.Client.Core - + + + - + - + diff --git a/src/AElf.Client.Core/AElfClientCoreConstants.cs b/src/AElf.Client.Core/AElfClientCoreConstants.cs new file mode 100644 index 0000000..a090699 --- /dev/null +++ b/src/AElf.Client.Core/AElfClientCoreConstants.cs @@ -0,0 +1,7 @@ +namespace AElf.Client.Core; + +public class AElfClientCoreConstants +{ + public const int DefaultWaitMilliseconds = 100; + public const string GenesisSmartContractName = "AElf.ContractNames.Genesis"; +} \ No newline at end of file diff --git a/src/AElf.Client.Abp/AElfClientModule.cs b/src/AElf.Client.Core/AElfClientModule.cs similarity index 50% rename from src/AElf.Client.Abp/AElfClientModule.cs rename to src/AElf.Client.Core/AElfClientModule.cs index 95cfe53..700356e 100644 --- a/src/AElf.Client.Abp/AElfClientModule.cs +++ b/src/AElf.Client.Core/AElfClientModule.cs @@ -1,13 +1,15 @@ -using AElf.Client.Options; +using AElf.Client.Core.Options; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Autofac; +using Volo.Abp.AutoMapper; using Volo.Abp.Modularity; -namespace AElf.Client.Abp; +namespace AElf.Client.Core; [DependsOn( - typeof(AbpAutofacModule) + typeof(AbpAutofacModule), + typeof(AbpAutoMapperModule) )] public class AElfClientModule : AbpModule { @@ -16,5 +18,14 @@ public override void ConfigureServices(ServiceConfigurationContext context) var configuration = context.Services.GetConfiguration(); Configure(options => { configuration.GetSection("AElfClient").Bind(options); }); Configure(options => { configuration.GetSection("AElfAccount").Bind(options); }); + Configure(options => { configuration.GetSection("AElfContract").Bind(options); }); + Configure(options => { configuration.GetSection("AElfClientConfig").Bind(options); }); + + context.Services.AddAutoMapperObjectMapper(); + + Configure(options => + { + options.AddMaps(); + }); } } \ No newline at end of file diff --git a/src/AElf.Client.Core/AElfClientService.Blockchain.cs b/src/AElf.Client.Core/AElfClientService.Blockchain.cs new file mode 100644 index 0000000..5256706 --- /dev/null +++ b/src/AElf.Client.Core/AElfClientService.Blockchain.cs @@ -0,0 +1,63 @@ +using AElf.Client.Dto; +using Google.Protobuf; +using Google.Protobuf.Reflection; +using Microsoft.Extensions.Logging; + +namespace AElf.Client.Core; + +public partial class AElfClientService +{ + public async Task GetTransactionResultAsync(string transactionId, string clientAlias) + { + var aelfClient = _aelfClientProvider.GetClient(alias: clientAlias); + var result = await aelfClient.GetTransactionResultAsync(transactionId); + var i = 0; + while (i < 10) + { + if (result!.Status == TransactionResultStatus.Mined.ToString().ToUpper()) + { + break; + } + + if (result.Status == TransactionResultStatus.Failed.ToString().ToUpper() || + result.Status == TransactionResultStatus.NodeValidationFailed.ToString().ToUpper()) + { + break; + } + + await Task.Delay(AElfClientCoreConstants.DefaultWaitMilliseconds); + result = await aelfClient.GetTransactionResultAsync(transactionId); + i++; + } + + return _objectMapper.Map(result!); + } + + public async Task GetChainStatusAsync(string clientAlias) + { + var aelfClient = _aelfClientProvider.GetClient(alias: clientAlias); + return await aelfClient.GetChainStatusAsync(); + } + + public async Task GetMerklePathByTransactionIdAsync(string transactionId, string clientAlias) + { + var aelfClient = _aelfClientProvider.GetClient(alias: clientAlias); + var merklePathDto = await aelfClient.GetMerklePathByTransactionIdAsync(transactionId); + if (merklePathDto == null) + { + Logger.LogError("Cannot get merkle path of transaction {TransactionId}", transactionId); + merklePathDto = new MerklePathDto(); + } + + return _objectMapper.Map(merklePathDto); + } + + public async Task GetContractFileDescriptorSetAsync(string contractAddress, string clientAlias) + { + var aelfClient = _aelfClientProvider.GetClient(alias: clientAlias); + var result = await aelfClient.GetContractFileDescriptorSetAsync(contractAddress); + var fileDescriptorSet = new FileDescriptorSet(); + fileDescriptorSet.MergeFrom(result); + return fileDescriptorSet; + } +} \ No newline at end of file diff --git a/src/AElf.Client.Core/AElfClientService.Send.cs b/src/AElf.Client.Core/AElfClientService.Send.cs new file mode 100644 index 0000000..6cd9395 --- /dev/null +++ b/src/AElf.Client.Core/AElfClientService.Send.cs @@ -0,0 +1,57 @@ +using AElf.Client.Dto; +using AElf.Client.Services; +using Google.Protobuf; + +namespace AElf.Client.Core; + +public partial class AElfClientService +{ + public async Task SendAsync(string contractAddress, string methodName, IMessage parameter, + string clientAlias, string? accountAlias = null) + { + var aelfClient = _aelfClientProvider.GetClient(alias: clientAlias); + var aelfAccount = _aelfAccountProvider.GetPrivateKey(alias: _clientConfigOptions.AccountAlias); + var tx = new TransactionBuilder(aelfClient) + .UsePrivateKey(aelfAccount) + .UseContract(contractAddress) + .UseMethod(methodName) + .UseParameter(parameter) + .Build(); + await PerformSendAsync(aelfClient, tx); + return tx; + } + + public async Task SendSystemAsync(string systemContractName, string methodName, IMessage parameter, + string clientAlias, string? accountAlias = null) + { + if (!systemContractName.StartsWith("AElf.ContractNames.")) + { + throw new ArgumentException("Invalid system contract name."); + } + + if (systemContractName == AElfClientCoreConstants.GenesisSmartContractName) + { + return await SendAsync((await GetGenesisContractAddressAsync(clientAlias)).ToBase58(), methodName, + parameter, clientAlias, accountAlias); + } + + var aelfClient = _aelfClientProvider.GetClient(alias: clientAlias); + var aelfAccount = _aelfAccountProvider.GetPrivateKey(alias: _clientConfigOptions.AccountAlias); + var tx = new TransactionBuilder(aelfClient) + .UsePrivateKey(aelfAccount) + .UseSystemContract(systemContractName) + .UseMethod(methodName) + .UseParameter(parameter) + .Build(); + await PerformSendAsync(aelfClient, tx); + return tx; + } + + private static async Task PerformSendAsync(ITransactionAppService aelfClient, Transaction tx) + { + await aelfClient.SendTransactionAsync(new SendTransactionInput + { + RawTransaction = tx.ToByteArray().ToHex() + }); + } +} \ No newline at end of file diff --git a/src/AElf.Client.Core/AElfClientService.View.cs b/src/AElf.Client.Core/AElfClientService.View.cs new file mode 100644 index 0000000..278ff3b --- /dev/null +++ b/src/AElf.Client.Core/AElfClientService.View.cs @@ -0,0 +1,87 @@ +using AElf.Client.Dto; +using AElf.Client.Core.Options; +using AElf.Client.Services; +using Google.Protobuf; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; +using Volo.Abp.ObjectMapping; + +namespace AElf.Client.Core; + +public partial class AElfClientService : IAElfClientService, ITransientDependency +{ + private readonly IAElfClientProvider _aelfClientProvider; + private readonly IAElfAccountProvider _aelfAccountProvider; + private readonly IObjectMapper _objectMapper; + private readonly AElfClientConfigOptions _clientConfigOptions; + + public ILogger Logger { get; set; } + + public AElfClientService(IAElfClientProvider aelfClientProvider, IAElfAccountProvider aelfAccountProvider, + IObjectMapper objectMapper, IOptionsSnapshot clientConfigOptions) + { + _aelfClientProvider = aelfClientProvider; + _aelfAccountProvider = aelfAccountProvider; + _objectMapper = objectMapper; + _clientConfigOptions = clientConfigOptions.Value; + + Logger = NullLogger.Instance; + } + + public async Task ViewAsync(string contractAddress, string methodName, IMessage parameter, string clientAlias, + string accountAlias = "Default") where T : IMessage, new() + { + var aelfClient = _aelfClientProvider.GetClient(alias: clientAlias); + var aelfAccount = _aelfAccountProvider.GetPrivateKey(alias: accountAlias); + var tx = new TransactionBuilder(aelfClient) + .UsePrivateKey(aelfAccount) + .UseContract(contractAddress) + .UseMethod(methodName) + .UseParameter(parameter) + .Build(); + var returnValue = await PerformViewAsync(aelfClient, tx); + var result = new T(); + result.MergeFrom(returnValue); + return result; + } + + + public async Task ViewSystemAsync(string systemContractName, string methodName, IMessage parameter, + string clientAlias, string accountAlias = "Default") where T : IMessage, new() + { + if (!systemContractName.StartsWith("AElf.ContractNames.")) + { + throw new ArgumentException("Invalid system contract name."); + } + + var aelfClient = _aelfClientProvider.GetClient(alias: clientAlias); + var aelfAccount = _aelfAccountProvider.GetPrivateKey(alias: accountAlias); + var tx = new TransactionBuilder(aelfClient) + .UsePrivateKey(aelfAccount) + .UseSystemContract(systemContractName) + .UseMethod(methodName) + .UseParameter(parameter) + .Build(); + var returnValue = await PerformViewAsync(aelfClient, tx); + var result = new T(); + result.MergeFrom(returnValue); + return result; + } + + public async Task
GetGenesisContractAddressAsync(string clientAlias) + { + var chainStatus = await GetChainStatusAsync(clientAlias); + return Address.FromBase58(chainStatus.GenesisContractAddress); + } + + private async Task PerformViewAsync(ITransactionAppService aelfClient, Transaction tx) + { + var result = await aelfClient.ExecuteTransactionAsync(new ExecuteTransactionDto + { + RawTransaction = tx.ToByteArray().ToHex() + }); + return ByteArrayHelper.HexStringToByteArray(result); + } +} \ No newline at end of file diff --git a/src/AElf.Client.Core/ContractServiceBase.cs b/src/AElf.Client.Core/ContractServiceBase.cs new file mode 100644 index 0000000..2f1901d --- /dev/null +++ b/src/AElf.Client.Core/ContractServiceBase.cs @@ -0,0 +1,61 @@ +using Google.Protobuf; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Threading; + +namespace AElf.Client.Core; + +public class ContractServiceBase +{ + private readonly IAElfClientService _clientService; + protected string? SmartContractName { get; } + protected Address? ContractAddress { get; set; } + + public ILogger Logger { get; set; } + + protected ContractServiceBase(IAElfClientService clientService, string smartContractName) + { + _clientService = clientService; + SmartContractName = smartContractName; + + Logger = NullLogger.Instance; + } + + protected ContractServiceBase(IAElfClientService clientService, Address contractAddress) + { + _clientService = clientService; + ContractAddress = contractAddress; + + Logger = NullLogger.Instance; + } + + protected async Task PerformSendTransactionAsync(string methodName, IMessage parameter, + string clientAlias) + { + if (ContractAddress != null) + { + return await _clientService.SendAsync(ContractAddress.ToBase58(), methodName, parameter, clientAlias); + } + + if (SmartContractName != null) + { + return await _clientService.SendSystemAsync(SmartContractName, methodName, parameter, clientAlias); + } + + Logger.LogError($"Neither ContractAddress nor SmartContractName is set."); + return null; + } + + protected async Task PerformGetTransactionResultAsync(string transactionId, + string clientAlias) + { + TransactionResult txResult; + do + { + txResult = await _clientService.GetTransactionResultAsync(transactionId, clientAlias); + } while (txResult.Status == TransactionResultStatus.Pending); + + Logger.LogInformation("{TxResult}", txResult); + return txResult; + } +} \ No newline at end of file diff --git a/src/AElf.Client.Abp/GlobalUsings.cs b/src/AElf.Client.Core/GlobalUsings.cs similarity index 100% rename from src/AElf.Client.Abp/GlobalUsings.cs rename to src/AElf.Client.Core/GlobalUsings.cs diff --git a/src/AElf.Client.Abp/IAElfAccountProvider.cs b/src/AElf.Client.Core/IAElfAccountProvider.cs similarity index 55% rename from src/AElf.Client.Abp/IAElfAccountProvider.cs rename to src/AElf.Client.Core/IAElfAccountProvider.cs index f3a1809..74d02cc 100644 --- a/src/AElf.Client.Abp/IAElfAccountProvider.cs +++ b/src/AElf.Client.Core/IAElfAccountProvider.cs @@ -1,5 +1,5 @@ -using AElf.Client.Abp.Infrastructure; -using AElf.Client.Options; +using AElf.Client.Core.Infrastructure; +using AElf.Client.Core.Options; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; @@ -7,17 +7,23 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.Threading; -namespace AElf.Client.Abp; +namespace AElf.Client.Core; public interface IAElfAccountProvider { byte[] GetPrivateKey(string? alias = null, string? address = null); - void SetPrivateKey(byte[] privateKey, string? alias = null, string? address = null); + void SetPrivateKey(byte[] privateKey, string? accountAlias = null); + void SetPrivateKey(string address, string password, string? accountAlias = null); + string GetAliasByAddress(string address); + void AddAccountByDefaultPassword(string address); } public class AElfAccountProvider : Dictionary, IAElfAccountProvider, ISingletonDependency { private readonly IKeyDirectoryProvider _keyDirectoryProvider; + private readonly AElfAccountOptions _aelfAccountOptions; + + private readonly KeyStoreService _keyStoreService; public ILogger Logger { get; set; } @@ -26,10 +32,10 @@ public AElfAccountProvider(IKeyDirectoryProvider keyDirectoryProvider, { Logger = NullLogger.Instance; _keyDirectoryProvider = keyDirectoryProvider; + _aelfAccountOptions = aelfAccountOptions.Value; var defaultPrivateKey = ByteArrayHelper.HexStringToByteArray(AElfClientConstants.DefaultPrivateKey); - SetPrivateKey(defaultPrivateKey, "Default", Address.FromPublicKey(defaultPrivateKey).ToBase58()); - - var keyStoreService = new KeyStoreService(); + SetPrivateKey(defaultPrivateKey, "Default"); + _keyStoreService = new KeyStoreService(); foreach (var accountConfig in aelfAccountOptions.Value.AccountConfigList) { @@ -40,14 +46,14 @@ public AElfAccountProvider(IKeyDirectoryProvider keyDirectoryProvider, { using var textReader = File.OpenText(keyFilePath); var json = textReader.ReadToEnd(); - return keyStoreService.DecryptKeyStoreFromJson(accountConfig.Password, json); + return _keyStoreService.DecryptKeyStoreFromJson(accountConfig.Password, json); })); - SetPrivateKey(privateKey, accountConfig.Alias, accountConfig.Address); + SetPrivateKey(privateKey, accountConfig.Alias); } else { var privateKey = ByteArrayHelper.HexStringToByteArray(accountConfig.PrivateKey); - SetPrivateKey(privateKey, accountConfig.Alias, Address.FromPublicKey(privateKey).ToBase58()); + SetPrivateKey(privateKey, accountConfig.Alias); } } } @@ -66,15 +72,56 @@ public byte[] GetPrivateKey(string? alias = null, string? address = null) return this[keys.Single()]; } - public void SetPrivateKey(byte[] privateKey, string? alias = null, string? address = null) + public void SetPrivateKey(byte[] privateKey, string? accountAlias = null) { TryAdd(new AElfAccountInfo { - Alias = alias, + Alias = accountAlias, + Address = Address.FromPublicKey(privateKey).ToBase58() + }, privateKey); + } + + public void SetPrivateKey(string address, string password, string? accountAlias = null) + { + var keyFilePath = GetKeyFileFullPath(address, _aelfAccountOptions.KeyDirectory); + var privateKey = AsyncHelper.RunSync(() => Task.Run(() => + { + using var textReader = File.OpenText(keyFilePath); + var json = textReader.ReadToEnd(); + return _keyStoreService.DecryptKeyStoreFromJson(password, json); + })); + + var keys = Keys + .WhereIf(!accountAlias.IsNullOrWhiteSpace(), a => a.Alias == accountAlias) + .WhereIf(!address.IsNullOrWhiteSpace(), a => a.Address == address) + .ToList(); + + if (keys.Count == 1) return; + TryAdd(new AElfAccountInfo + { + Alias = accountAlias, Address = address }, privateKey); } + public string GetAliasByAddress(string address) + { + var keys = Keys + .WhereIf(!address.IsNullOrWhiteSpace(), a => a.Address == address) + .ToList(); + if (keys.Count != 1) + { + throw new AElfClientException($"Failed to get alias of {address}."); + } + + return keys.First().Alias!; + } + + public void AddAccountByDefaultPassword(string address) + { + SetPrivateKey(address, _aelfAccountOptions.DefaultPassword); + } + private string GetKeyFileFullPath(string address, string configuredKeyDirectory) { var dirPath = GetKeystoreDirectoryPath(configuredKeyDirectory); diff --git a/src/AElf.Client.Abp/IAElfClientProvider.cs b/src/AElf.Client.Core/IAElfClientProvider.cs similarity index 73% rename from src/AElf.Client.Abp/IAElfClientProvider.cs rename to src/AElf.Client.Core/IAElfClientProvider.cs index d138778..a0f1794 100644 --- a/src/AElf.Client.Abp/IAElfClientProvider.cs +++ b/src/AElf.Client.Core/IAElfClientProvider.cs @@ -1,8 +1,8 @@ -using AElf.Client.Options; +using AElf.Client.Core.Options; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; -namespace AElf.Client; +namespace AElf.Client.Core; public interface IAElfClientProvider { @@ -14,19 +14,21 @@ void SetClient(AElfClient client, string? environment = null, int? chainId = nul public class AElfClientProvider : Dictionary, IAElfClientProvider, ISingletonDependency { - public AElfClientProvider(IOptionsSnapshot aelfClientOptions) + public AElfClientProvider(IOptionsSnapshot aelfClientOptions, + IOptionsSnapshot aelfClientConfigOptions) { + var useCamelCase = aelfClientConfigOptions.Value.CamelCase; var clientBuilder = new AElfClientBuilder(); - SetClient(clientBuilder.UsePublicEndpoint(EndpointType.MainNetMainChain).Build(), "MainNet", - AElfClientConstants.MainChainId, "MainChain", EndpointType.MainNetMainChain.ToString()); - SetClient(clientBuilder.UsePublicEndpoint(EndpointType.MainNetSidechain).Build(), "MainNet", - AElfClientConstants.SidechainId, "Sidechain", EndpointType.MainNetSidechain.ToString()); - SetClient(clientBuilder.UsePublicEndpoint(EndpointType.TestNetMainChain).Build(), "TestNet", - AElfClientConstants.MainChainId, "MainChain", EndpointType.TestNetMainChain.ToString()); - SetClient(clientBuilder.UsePublicEndpoint(EndpointType.TestNetSidechain).Build(), "MainNet", - AElfClientConstants.SidechainId, "Sidechain", EndpointType.TestNetSidechain.ToString()); - SetClient(clientBuilder.UsePublicEndpoint(EndpointType.Local).Build(), "Local", AElfClientConstants.MainChainId, - "MainChain", EndpointType.Local.ToString()); + SetClient(clientBuilder.UsePublicEndpoint(EndpointType.MainNetMainChain).UseCamelCase(useCamelCase).Build(), + "MainNet", AElfClientConstants.MainChainId, "MainChain", EndpointType.MainNetMainChain.ToString()); + SetClient(clientBuilder.UsePublicEndpoint(EndpointType.MainNetSideChain1).UseCamelCase(useCamelCase).Build(), + "MainNet", AElfClientConstants.SideChainId2, "SideChain", EndpointType.MainNetSideChain1.ToString()); + SetClient(clientBuilder.UsePublicEndpoint(EndpointType.TestNetMainChain).UseCamelCase(useCamelCase).Build(), + "TestNet", AElfClientConstants.MainChainId, "MainChain", EndpointType.TestNetMainChain.ToString()); + SetClient(clientBuilder.UsePublicEndpoint(EndpointType.TestNetSideChain2).UseCamelCase(useCamelCase).Build(), + "MainNet", AElfClientConstants.SideChainId2, "SideChain", EndpointType.TestNetSideChain2.ToString()); + SetClient(clientBuilder.UsePublicEndpoint(EndpointType.Local).UseCamelCase(useCamelCase).Build(), "Local", + AElfClientConstants.MainChainId, "MainChain", EndpointType.Local.ToString()); foreach (var clientConfig in aelfClientOptions.Value.ClientConfigList) { diff --git a/src/AElf.Client.Core/IAElfClientService.cs b/src/AElf.Client.Core/IAElfClientService.cs new file mode 100644 index 0000000..980bee3 --- /dev/null +++ b/src/AElf.Client.Core/IAElfClientService.cs @@ -0,0 +1,72 @@ +using AElf.Client.Dto; +using Google.Protobuf; +using Google.Protobuf.Reflection; + +namespace AElf.Client.Core; + +public interface IAElfClientService +{ + /// + /// Build a transaction to view contract state and return the result. + /// + /// Transaction.To + /// Transaction.MethodName + /// Transaction.Parameter + /// Which client to use + /// Which account to use + /// Only IMessage type + /// Contract call result + Task ViewAsync(string contractAddress, string methodName, IMessage parameter, string clientAlias, + string accountAlias = "Default") where T : IMessage, new(); + + /// + /// Build a transaction to view system contract state and return the result. + /// + /// The System Contract Name with the format AElf.ContractNames.* + /// Transaction.MethodName + /// Transaction.Parameter + /// Which client to use + /// Which account to use + /// Only IMessage type + /// Contract call result + /// Throw Invalid system contract name if systemContractName + /// is not starts with AElf.ContractNames. + Task ViewSystemAsync(string systemContractName, string methodName, IMessage parameter, + string clientAlias, string accountAlias = "Default") where T : IMessage, new(); + + /// + /// Send a transaction to a Contract. + /// + /// Transaction.tO + /// Transaction.MethodName + /// Transaction.Parameter + /// Which client to use + /// Which account to use + /// Contract call result + Task SendAsync(string contractAddress, string methodName, IMessage parameter, + string clientAlias, string? accountAlias = null); + + /// + /// Send a transaction to a System Contract. + /// + /// The System Contract Name with the format AElf.ContractNames.* + /// Transaction.MethodName + /// Transaction.Parameter + /// Which client to use + /// Which account to use + /// Contract call result + /// Throw Invalid system contract name if systemContractName + /// is not starts with AElf.ContractNames. + Task SendSystemAsync(string systemContractName, string methodName, IMessage parameter, + string clientAlias, string? accountAlias = null); + + Task GetTransactionResultAsync(string transactionId, string clientAlias); + + Task GetChainStatusAsync(string clientAlias); + + Task
GetGenesisContractAddressAsync(string clientAlias); + + Task GetMerklePathByTransactionIdAsync(string transactionId, string clientAlias); + + Task GetContractFileDescriptorSetAsync(string contractAddress, string clientAlias); +} \ No newline at end of file diff --git a/src/AElf.Client.Abp/Infrastructure/IKeyDirectoryProvider.cs b/src/AElf.Client.Core/Infrastructure/IKeyDirectoryProvider.cs similarity index 62% rename from src/AElf.Client.Abp/Infrastructure/IKeyDirectoryProvider.cs rename to src/AElf.Client.Core/Infrastructure/IKeyDirectoryProvider.cs index a89a5ee..77ef341 100644 --- a/src/AElf.Client.Abp/Infrastructure/IKeyDirectoryProvider.cs +++ b/src/AElf.Client.Core/Infrastructure/IKeyDirectoryProvider.cs @@ -1,4 +1,4 @@ -namespace AElf.Client.Abp.Infrastructure; +namespace AElf.Client.Core.Infrastructure; public interface IKeyDirectoryProvider { diff --git a/src/AElf.Client.Abp/Infrastructure/KeyDirectoryProvider.cs b/src/AElf.Client.Core/Infrastructure/KeyDirectoryProvider.cs similarity index 93% rename from src/AElf.Client.Abp/Infrastructure/KeyDirectoryProvider.cs rename to src/AElf.Client.Core/Infrastructure/KeyDirectoryProvider.cs index 99dd09b..789672b 100644 --- a/src/AElf.Client.Abp/Infrastructure/KeyDirectoryProvider.cs +++ b/src/AElf.Client.Core/Infrastructure/KeyDirectoryProvider.cs @@ -1,6 +1,6 @@ using Volo.Abp.DependencyInjection; -namespace AElf.Client.Abp.Infrastructure; +namespace AElf.Client.Core.Infrastructure; public class KeyDirectoryProvider : IKeyDirectoryProvider, ISingletonDependency { diff --git a/src/AElf.Client.Abp/Options/AElfAccountOptions.cs b/src/AElf.Client.Core/Options/AElfAccountOptions.cs similarity index 80% rename from src/AElf.Client.Abp/Options/AElfAccountOptions.cs rename to src/AElf.Client.Core/Options/AElfAccountOptions.cs index 310f2f5..2df14ba 100644 --- a/src/AElf.Client.Abp/Options/AElfAccountOptions.cs +++ b/src/AElf.Client.Core/Options/AElfAccountOptions.cs @@ -1,9 +1,10 @@ -namespace AElf.Client.Options; +namespace AElf.Client.Core.Options; public class AElfAccountOptions { public string KeyDirectory { get; set; } public List AccountConfigList { get; set; } = new(); + public string DefaultPassword { get; set; } } public class AccountConfig diff --git a/src/AElf.Client.Core/Options/AElfClientConfigOptions.cs b/src/AElf.Client.Core/Options/AElfClientConfigOptions.cs new file mode 100644 index 0000000..816a4ef --- /dev/null +++ b/src/AElf.Client.Core/Options/AElfClientConfigOptions.cs @@ -0,0 +1,10 @@ +namespace AElf.Client.Core.Options; + +public class AElfClientConfigOptions +{ + public string ClientAlias { get; set; } = "TestNetSideChain2"; + public string MainChainClientAlias { get; set; } = "TestNetMainChain"; + public string SideChainClientAlias { get; set; } = "TestNetSideChain2"; + public string AccountAlias { get; set; } = "Default"; + public bool CamelCase { get; set; } = false; +} \ No newline at end of file diff --git a/src/AElf.Client.Abp/Options/AElfClientOptions.cs b/src/AElf.Client.Core/Options/AElfClientOptions.cs similarity index 68% rename from src/AElf.Client.Abp/Options/AElfClientOptions.cs rename to src/AElf.Client.Core/Options/AElfClientOptions.cs index 1caf518..ae612dd 100644 --- a/src/AElf.Client.Abp/Options/AElfClientOptions.cs +++ b/src/AElf.Client.Core/Options/AElfClientOptions.cs @@ -1,4 +1,4 @@ -namespace AElf.Client.Options; +namespace AElf.Client.Core.Options; public class AElfClientOptions { @@ -9,7 +9,7 @@ public class ClientConfig { public string Alias { get; set; } public string Endpoint { get; set; } - public string UserName { get; set; } - public string Password { get; set; } + public string? UserName { get; set; } + public string? Password { get; set; } public int Timeout { get; set; } = 60; } \ No newline at end of file diff --git a/src/AElf.Client.Core/Options/AElfContractOptions.cs b/src/AElf.Client.Core/Options/AElfContractOptions.cs new file mode 100644 index 0000000..25f3e7a --- /dev/null +++ b/src/AElf.Client.Core/Options/AElfContractOptions.cs @@ -0,0 +1,7 @@ +namespace AElf.Client.Core.Options; + +public class AElfContractOptions +{ + public Dictionary ContractAddressList { get; set; } + public string ContractDirectory { get; set; } +} \ No newline at end of file diff --git a/src/AElf.Client.Core/Profile/CommonProfile.cs b/src/AElf.Client.Core/Profile/CommonProfile.cs new file mode 100644 index 0000000..3bacaa9 --- /dev/null +++ b/src/AElf.Client.Core/Profile/CommonProfile.cs @@ -0,0 +1,27 @@ +using Google.Protobuf; + +namespace AElf.Client.Core; + +public class CommonProfile : AutoMapper.Profile +{ + public CommonProfile() + { + CreateMap() + .ConvertUsing(s => s == null ? null : s.ToHex()); + + CreateMap() + .ConvertUsing(s => Hash.LoadFromHex(s)); + + CreateMap() + .ConvertUsing(s => s.ToBase58()); + + CreateMap() + .ConvertUsing(s => Address.FromBase58(s)); + + CreateMap() + .ConvertUsing(s => s.ToBase64()); + + CreateMap() + .ConvertUsing(s => ByteString.CopyFromUtf8(s)); + } +} \ No newline at end of file diff --git a/src/AElf.Client.Core/Profile/MerklePathProfile.cs b/src/AElf.Client.Core/Profile/MerklePathProfile.cs new file mode 100644 index 0000000..8a9bf65 --- /dev/null +++ b/src/AElf.Client.Core/Profile/MerklePathProfile.cs @@ -0,0 +1,16 @@ +using AElf.Client.Dto; +using AutoMapper; + +namespace AElf.Client.Core; + +public class MerklePathProfile : Profile +{ + public MerklePathProfile() + { + CreateMap(); + CreateMap(); + + CreateMap(); + CreateMap(); + } +} \ No newline at end of file diff --git a/src/AElf.Client.Core/Profile/TransactionProfile.cs b/src/AElf.Client.Core/Profile/TransactionProfile.cs new file mode 100644 index 0000000..f776623 --- /dev/null +++ b/src/AElf.Client.Core/Profile/TransactionProfile.cs @@ -0,0 +1,78 @@ +using AElf.Client.Dto; +using AutoMapper; +using Google.Protobuf; +using Volo.Abp.AutoMapper; + +namespace AElf.Client.Core; + +public class TransactionProfile : Profile +{ + public const string ErrorTrace = "WithMetrics"; + + public TransactionProfile() + { + CreateMap(); + CreateMap(); + + CreateMap() + .ForMember(d => d.ReturnValue, opt => opt.MapFrom(s => s.ReturnValue.ToHex(false))) + .ForMember(d => d.Bloom, + opt => opt.MapFrom(s => + s.Status == TransactionResultStatus.NotExisted + ? null + : s.Bloom.Length == 0 + ? ByteString.CopyFrom(new byte[256]).ToBase64() + : s.Bloom.ToBase64())) + .ForMember(d => d.Status, opt => opt.MapFrom(s => s.Status.ToString().ToUpper())) + .ForMember(d => d.Error, opt => opt.MapFrom()) + .Ignore(d => d.Transaction) + .Ignore(d => d.TransactionSize); + + TransactionResultStatus status; + CreateMap() + .ForMember(d => d.ReturnValue, + opt => opt.MapFrom(s => ByteString.CopyFrom(ByteArrayHelper.HexStringToByteArray(s.ReturnValue)))) + .ForMember(d => d.BlockHash, opt => opt.MapFrom(s => Hash.LoadFromHex(s.BlockHash))) + .ForMember(d => d.Bloom, opt => opt.MapFrom(s => + s.Status.ToUpper() == TransactionResultStatus.NotExisted.ToString().ToUpper() + ? null + : string.IsNullOrEmpty(s.Bloom) + ? ByteString.Empty + : ByteString.FromBase64(s.Bloom))) + .ForMember(d => d.Status, + opt => opt.MapFrom(s => + Enum.TryParse($"{s.Status[0]}{s.Status.Substring(1).ToLower()}", out status) + ? status + : TransactionResultStatus.NotExisted)) + .ForMember(d => d.Logs, opt => opt.MapFrom(s => s.Logs)) + .Ignore(d => d.Error) + .Ignore(d => d.Bloom); + + CreateMap() + .ForMember(d => d.Indexed, opt => opt.MapFrom(s => s.Indexed.Select(ByteString.FromBase64))) + .ForMember(d => d.NonIndexed, opt => opt.MapFrom(s => ByteString.FromBase64(s.NonIndexed))); + CreateMap(); + } +} + +public class TransactionErrorResolver : IValueResolver +{ + public string Resolve(TransactionResult source, TransactionResultDto destination, string destMember, + ResolutionContext context) + { + var errorTraceNeeded = (bool)context.Items[TransactionProfile.ErrorTrace]; + return TakeErrorMessage(source.Error, errorTraceNeeded); + } + + public static string TakeErrorMessage(string transactionResultError, bool errorTraceNeeded) + { + if (string.IsNullOrWhiteSpace(transactionResultError)) + return null; + + if (errorTraceNeeded) + return transactionResultError; + + using var stringReader = new StringReader(transactionResultError); + return stringReader.ReadLine(); + } +} \ No newline at end of file diff --git a/src/AElf.Client.Core/SendTransactionResult.cs b/src/AElf.Client.Core/SendTransactionResult.cs new file mode 100644 index 0000000..057d94e --- /dev/null +++ b/src/AElf.Client.Core/SendTransactionResult.cs @@ -0,0 +1,7 @@ +namespace AElf.Client.Core; + +public class SendTransactionResult +{ + public TransactionResult TransactionResult { get; set; } + public Transaction Transaction { get; set; } +} \ No newline at end of file diff --git a/src/AElf.Client.Protobuf/AElf.Client.Protobuf.csproj b/src/AElf.Client.Protobuf/AElf.Client.Protobuf.csproj index a582f80..c9b869f 100644 --- a/src/AElf.Client.Protobuf/AElf.Client.Protobuf.csproj +++ b/src/AElf.Client.Protobuf/AElf.Client.Protobuf.csproj @@ -26,21 +26,57 @@ MSBuild:Compile + + MSBuild:Compile + MSBuild:Compile MSBuild:Compile + + MSBuild:Compile + + + MSBuild:Compile + MSBuild:Compile + + MSBuild:Compile + MSBuild:Compile MSBuild:Compile + + MSBuild:Compile + + + MSBuild:Compile + + + MSBuild:Compile + + + MSBuild:Compile + + + MSBuild:Compile + + + MSBuild:Compile + + + MSBuild:Compile + + + MSBuild:Compile + diff --git a/src/AElf.Client.Protobuf/Protobuf/acs13.proto b/src/AElf.Client.Protobuf/Protobuf/acs13.proto new file mode 100644 index 0000000..2f80e12 --- /dev/null +++ b/src/AElf.Client.Protobuf/Protobuf/acs13.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package acs13; +import "aelf/options.proto"; +import "aelf/core.proto"; +import "google/protobuf/wrappers.proto"; + +option (aelf.identity) = "acs13"; +option csharp_namespace = "AElf.Standards.ACS13"; + +service OracleAggregatorContract { + rpc Aggregate (AggregateInput) returns (google.protobuf.StringValue) { + } +} + + +message AggregateInput { + repeated string results = 1; + repeated int32 frequencies = 2; + int32 aggregate_option = 3; +} \ No newline at end of file diff --git a/src/AElf.Client.Protobuf/Protobuf/bridge_contract.proto b/src/AElf.Client.Protobuf/Protobuf/bridge_contract.proto new file mode 100644 index 0000000..47e8672 --- /dev/null +++ b/src/AElf.Client.Protobuf/Protobuf/bridge_contract.proto @@ -0,0 +1,207 @@ +syntax = "proto3"; + +import "aelf/core.proto"; +import "aelf/options.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/wrappers.proto"; +import "callback_input.proto"; +import "acs13.proto"; +import "receipt_maker.proto"; + +option csharp_namespace = "AElf.Contracts.Bridge"; + +service BridgeContract { + option (aelf.csharp_state) = "AElf.Contracts.Bridge.BridgeContractState"; + option (aelf.base) = "acs13.proto"; + option (aelf.base) = "receipt_maker.proto"; + + rpc Initialize (InitializeInput) returns (google.protobuf.Empty) {} + + rpc CreateSwap (CreateSwapInput) returns (aelf.Hash) { + } + rpc SwapToken (SwapTokenInput) returns (google.protobuf.Empty) { + } + rpc ChangeSwapRatio (ChangeSwapRatioInput) returns (google.protobuf.Empty) { + } + rpc Deposit (DepositInput) returns (google.protobuf.Empty) { + } + rpc Withdraw (WithdrawInput) returns (google.protobuf.Empty) { + } + rpc UpdateMerkleTree (UpdateMerkleTreeInput) returns (google.protobuf.Empty) { + } + rpc RecordReceiptHash (CallbackInput) returns (google.protobuf.Empty) { + } + + rpc ChangeMaximalLeafCount (google.protobuf.Int32Value) returns (google.protobuf.Empty) { + } + + // view methods + + rpc GetSwapInfo (aelf.Hash) returns (SwapInfo) { + option (aelf.is_view) = true; + } + rpc GetSwapPair (GetSwapPairInput) returns (SwapPair) { + option (aelf.is_view) = true; + } + rpc GetSwapAmounts (GetSwapAmountsInput) returns (SwapAmounts) { + option (aelf.is_view) = true; + } + rpc GetRegimentAddressByRecorderId (google.protobuf.Int64Value) returns (aelf.Address) { + option (aelf.is_view) = true; + } + rpc GetSwappedReceiptIdList (GetSwappedReceiptIdListInput) returns (ReceiptIdList) { + option (aelf.is_view) = true; + } + rpc GetSwappedReceiptInfoList (GetSwappedReceiptInfoListInput) returns (ReceiptInfoList) { + option (aelf.is_view) = true; + } +} + +message InitializeInput { + aelf.Address oracle_contract_address = 1; + aelf.Address merkle_tree_recorder_contract_address = 2; + aelf.Address regiment_contract_address = 3; + aelf.Address merkle_tree_generator_contract_address = 4; + int32 merkle_tree_leaf_limit = 5; +} + +message CreateSwapInput { + int32 origin_token_size_in_byte = 1; + bool origin_token_numeric_big_endian = 2; + repeated SwapTargetToken swap_target_token_list = 3; + aelf.Address regiment_address = 4; +} + +message SwapTargetToken { + string target_token_symbol = 1; + SwapRatio swap_ratio = 2; + int64 deposit_amount = 3; +} + +message SwapInfo { + aelf.Hash swap_id = 1; + int32 origin_token_size_in_byte = 2; + bool origin_token_numeric_big_endian = 3; + map swap_target_token_map = 4; + aelf.Address regiment_address = 5; + int64 recorder_id = 6; +} + +message SwapPair { + aelf.Hash swap_id = 1; + int32 origin_token_size_in_byte = 2; + bool origin_token_numeric_big_endian = 3; + string target_token_symbol = 4; + SwapRatio swap_ratio = 5; + int64 swapped_amount = 6; + int64 swapped_times = 7; + int64 deposit_amount = 8; +} + +message SwapRatio { + int64 origin_share = 1; + int64 target_share = 2; +} + +message ChangeSwapRatioInput { + aelf.Hash swap_id = 1; + SwapRatio swap_ratio = 2; + string target_token_symbol = 3; +} + +message SwapTokenInput { + aelf.Hash swap_id = 1; + int64 receipt_id = 2; + string origin_amount = 3; +} + +message DepositInput { + aelf.Hash swap_id = 1; + string target_token_symbol = 2; + int64 amount = 3; +} + +message GetSwapPairInput { + aelf.Hash swap_id = 1; + string target_token_symbol = 2; +} + +message GetSwapAmountsInput { + aelf.Hash swap_id = 1; + int64 receipt_id = 2; +} + +message SwapAmounts { + aelf.Address receiver = 1; + map received_amounts = 2; +} + +message WithdrawInput { + aelf.Hash swap_id = 1; + string target_token_symbol = 2; + int64 amount = 3; +} + +message ReceiptHashMap { + map value = 1; + int64 recorder_id = 2; +} + +message UpdateMerkleTreeInput { + int64 recorder_id = 1; + aelf.Address regiment_address = 2; +} + +message ReceiptIdList { + repeated int64 value = 1; +} + +message GetSwappedReceiptIdListInput { + aelf.Hash swap_id = 1; + aelf.Address receiver_address = 2; +} + +message GetSwappedReceiptInfoListInput { + aelf.Hash swap_id = 1; + aelf.Address receiving_address = 2; +} + +message ReceiptInfo { + int64 receipt_id = 1; + aelf.Hash receiving_tx_id = 2; + google.protobuf.Timestamp receiving_time = 3; + int64 amount = 4; + map amount_map = 5; +} + +message ReceiptInfoList { + repeated ReceiptInfo value = 1; +} + +message SendingInfo { + int64 receipt_id = 1; + string sending_tx_id = 2; + string sending_time = 3; +} + +// Events + +message SwapRatioChanged { + option (aelf.is_event) = true; + aelf.Hash swap_id = 1; + SwapRatio new_swap_ratio = 2; + string target_token_symbol = 3; +} + +message TokenSwapped { + option (aelf.is_event) = true; + aelf.Address address = 1; + int64 amount = 2; + string symbol = 3; +} + +message SwapPairAdded { + option (aelf.is_event) = true; + aelf.Hash swap_id = 1; +} \ No newline at end of file diff --git a/src/AElf.Client.Protobuf/Protobuf/callback_input.proto b/src/AElf.Client.Protobuf/Protobuf/callback_input.proto new file mode 100644 index 0000000..c389829 --- /dev/null +++ b/src/AElf.Client.Protobuf/Protobuf/callback_input.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +import "aelf/core.proto"; + +message CallbackInput { + aelf.Hash query_id = 1; + bytes result = 2; + repeated aelf.Address oracle_nodes = 3; +} \ No newline at end of file diff --git a/src/AElf.Client.Protobuf/Protobuf/nft_contract.proto b/src/AElf.Client.Protobuf/Protobuf/nft_contract.proto new file mode 100644 index 0000000..b3521f6 --- /dev/null +++ b/src/AElf.Client.Protobuf/Protobuf/nft_contract.proto @@ -0,0 +1,505 @@ +/** + * NFT contract. + */ +syntax = "proto3"; + +package nft; + +import "aelf/core.proto"; +import "aelf/options.proto"; +import "acs1.proto"; +import "transaction_fee.proto"; +import "authority_info.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; + +option csharp_namespace = "AElf.Contracts.NFT"; + +service NFTContract { + option (aelf.csharp_state) = "AElf.Contracts.NFT.NFTContractState"; + option (aelf.base) = "acs1.proto"; + + // Create a new nft protocol. + rpc Create (CreateInput) returns (google.protobuf.StringValue) { + } + rpc CrossChainCreate (CrossChainCreateInput) returns (google.protobuf.Empty) { + } + // Mint (Issue) an amount of nft. + rpc Mint (MintInput) returns (aelf.Hash) { + } + // Transfer nft to another address. + rpc Transfer (TransferInput) returns (google.protobuf.Empty) { + } + // Transfer nft from one address to another. + rpc TransferFrom (TransferFromInput) returns (google.protobuf.Empty) { + } + // Approve another address to transfer nft from own account. + rpc Approve (ApproveInput) returns (google.protobuf.Empty) { + } + // De-approve. + rpc UnApprove (UnApproveInput) returns (google.protobuf.Empty) { + } + // Approve or de-approve another address as the operator of all NFTs of a certain protocol. + rpc ApproveProtocol (ApproveProtocolInput) returns (google.protobuf.Empty) { + } + // Destroy nfts. + rpc Burn (BurnInput) returns (google.protobuf.Empty) { + } + // Lock several nfts and fts to mint one nft. + rpc Assemble (AssembleInput) returns (aelf.Hash) { + } + // Disassemble one assembled nft to get locked nfts and fts back. + rpc Disassemble (DisassembleInput) returns (google.protobuf.Empty) { + } + // Modify metadata of one nft. + rpc Recast (RecastInput) returns (google.protobuf.Empty) { + } + + rpc AddMinters (AddMintersInput) returns (google.protobuf.Empty) { + } + rpc RemoveMinters (RemoveMintersInput) returns (google.protobuf.Empty) { + } + + rpc AddNFTType (AddNFTTypeInput) returns (google.protobuf.Empty) { + } + rpc RemoveNFTType (google.protobuf.StringValue) returns (google.protobuf.Empty) { + } + + rpc GetNFTProtocolInfo (google.protobuf.StringValue) returns (NFTProtocolInfo) { + option (aelf.is_view) = true; + } + rpc GetNFTInfo (GetNFTInfoInput) returns (NFTInfo) { + option (aelf.is_view) = true; + } + rpc GetNFTInfoByTokenHash (aelf.Hash) returns (NFTInfo) { + option (aelf.is_view) = true; + } + rpc GetBalance (GetBalanceInput) returns (GetBalanceOutput) { + option (aelf.is_view) = true; + } + rpc GetBalanceByTokenHash (GetBalanceByTokenHashInput) returns (GetBalanceOutput) { + option (aelf.is_view) = true; + } + rpc GetAllowance (GetAllowanceInput) returns (GetAllowanceOutput) { + option (aelf.is_view) = true; + } + rpc GetAllowanceByTokenHash (GetAllowanceByTokenHashInput) returns (GetAllowanceOutput) { + option (aelf.is_view) = true; + } + rpc GetMinterList (google.protobuf.StringValue) returns (MinterList) { + option (aelf.is_view) = true; + } + rpc CalculateTokenHash (CalculateTokenHashInput) returns (aelf.Hash) { + option (aelf.is_view) = true; + } + rpc GetNFTTypes (google.protobuf.Empty) returns (NFTTypes) { + option (aelf.is_view) = true; + } + rpc GetOperatorList (GetOperatorListInput) returns (AddressList) { + option (aelf.is_view) = true; + } +} + +// Structs +message NFTTypes { + map value = 1; +} + +// Inputs +message CreateInput { + // The type of this nft protocol. + string nft_type = 1; + // The name of this nft protocol. + string protocol_name = 2; + // The total supply of the token. + int64 total_supply = 3; + // The address that created the token. + aelf.Address creator = 4; + // A flag indicating if this token is burnable. + bool is_burnable = 5; + // The chain id of the token. + int32 issue_chain_id = 6; + // The metadata of the token. + Metadata metadata = 7; + // Base Uri. + string base_uri = 8; + // Is token id can be reused. + bool is_token_id_reuse = 9; + // Initial minter list (creator will be added automatically) + MinterList minter_list = 10; +} + +message CrossChainCreateInput { + string symbol = 1; +} + +message TransferInput { + aelf.Address to = 1; + string symbol = 2; + int64 token_id = 3; + string memo = 4; + int64 amount = 5; +} + +message TransferFromInput { + aelf.Address from = 1; + aelf.Address to = 2; + string symbol = 3; + int64 token_id = 4; + string memo = 5; + int64 amount = 6; +} + +message ApproveInput { + aelf.Address spender = 1; + string symbol = 2; + int64 token_id = 3; + int64 amount = 4; +} + +message UnApproveInput { + aelf.Address spender = 1; + string symbol = 2; + int64 token_id = 3; + int64 amount = 4; +} + +message ApproveProtocolInput { + aelf.Address operator = 1; + string symbol = 2; + bool approved = 3; +} + +message AddressList { + repeated aelf.Address value = 1; +} + +message GetOperatorListInput { + string symbol = 1; + aelf.Address owner = 2; +} + +message BurnInput { + string symbol = 1; + int64 token_id = 2; + int64 amount = 3; +} + +message AssembleInput { + string symbol = 1; + aelf.Address owner = 2; + string uri = 3; + string alias = 4; + Metadata metadata = 5; + AssembledNfts assembled_nfts = 6; + AssembledFts assembled_fts = 7; + int64 token_id = 8; +} + +message DisassembleInput { + string symbol = 1; + int64 token_id = 2; + aelf.Address owner = 3; +} + +message MinterList { + repeated aelf.Address value = 1; +} + +message MintInput { + string symbol = 1; + aelf.Address owner = 2; + string uri = 3; + string alias = 4; + Metadata metadata = 5; + int64 quantity = 6; + int64 token_id = 7; +} + +message GetBalanceInput { + aelf.Address owner = 1; + string symbol = 2; + int64 token_id = 3; +} + +message GetBalanceByTokenHashInput { + aelf.Address owner = 1; + aelf.Hash token_hash = 2; +} + +message GetBalanceOutput { + aelf.Address owner = 1; + aelf.Hash token_hash = 2; + int64 balance = 3; +} + +message GetAllowanceInput { + string symbol = 1; + int64 token_id = 2; + aelf.Address owner = 3; + aelf.Address spender = 4; +} + +message GetAllowanceByTokenHashInput { + aelf.Hash token_hash = 1; + aelf.Address owner = 2; + aelf.Address spender = 3; +} + +message GetAllowanceOutput { + aelf.Hash token_hash = 1; + int64 allowance = 2; + aelf.Address owner = 3; + aelf.Address spender = 4; +} + +message CalculateTokenHashInput { + string symbol = 1; + int64 token_id = 2; +} + +message NFTProtocolInfo { + // The symbol of the token. + string symbol = 1; + // The minted number of the token. + int64 supply = 2; + // The total number of the token. + int64 total_supply = 3; + // The address that creat the token. + aelf.Address creator = 4; + // Base Uri. + string base_uri = 5; + // A flag indicating if this token is burnable. + bool is_burnable = 6; + // The chain to mint this token. + int32 issue_chain_id = 7; + // The metadata of the token. + Metadata metadata = 8; + // NFT Type. + string nft_type = 9; + // Protocol name, aka token name in MultiToken Contract. + string protocol_name = 10; + // Is token id can be reused. + bool is_token_id_reuse = 11; + int64 issued = 12; +} + +message NFTInfo { + // The symbol of the protocol this nft belongs to. + string symbol = 1; + // The name of the protocol this nft belongs to. + string protocol_name = 2; + // Actually is the order of this token. + int64 token_id = 3; + // The address that creat the base token. + aelf.Address creator = 4; + // The addresses that mint this token. + repeated aelf.Address minters = 5; + // The metadata of the token. + Metadata metadata = 6; + // Minted amount. + int64 quantity = 7; + // Token Uri. + string uri = 8; + // Base Uri. + string base_uri = 9; + // Alias + string alias = 10; + // Is burned. + bool is_burned = 11; + // NFT Type + string nft_type = 12; +} + +enum NFTType { + ANY = 0; + ART = 1; + MUSIC = 2; + DOMAIN_NAMES = 3; + VIRTUAL_WORLDS = 4; + TRADING_CARDS = 5; + COLLECTABLES = 6; + SPORTS = 7; + UTILITY = 8; + BADGES = 9; +} + +message Metadata { + map value = 1; +} + +message AddMintersInput { + MinterList minter_list = 1; + string symbol = 2; +} + +message RemoveMintersInput { + MinterList minter_list = 1; + string symbol = 2; +} + +message GetNFTInfoInput { + string symbol = 1; + int64 token_id = 2; +} + +message RecastInput { + string symbol = 1; + int64 token_id = 2; + string uri = 3; + string alias = 4; + Metadata metadata = 5; +} + +message AssembledNfts { + map value = 1; +} + +message AssembledFts { + map value = 1; +} + +message AddNFTTypeInput { + string full_name = 1; + string short_name = 2; +} + +// Events + +message NFTProtocolCreated { + option (aelf.is_event) = true; + // The symbol of this protocol. + string symbol = 1; + // The name of this protocol. + string protocol_name = 2; + // The total supply of the token. + int64 total_supply = 3; + // The address that created the token. + aelf.Address creator = 4; + // A flag indicating if this token is burnable. + bool is_burnable = 5; + // The chain id of the token. + int32 issue_chain_id = 6; + // The metadata of the token. + Metadata metadata = 7; + // Base Uri. + string base_uri = 8; + // Is token id can be reused. + bool is_token_id_reuse = 9; + string nft_type = 10; +} + +message NFTMinted { + option (aelf.is_event) = true; + // The symbol of this protocol. + string symbol = 1; + // The name of this protocol. + string protocol_name = 2; + // Actually is the order of this token. + int64 token_id = 3; + // The address that creat the base token. + aelf.Address creator = 4; + // The address that mint this token. + aelf.Address minter = 5; + // The metadata of the token. + Metadata metadata = 6; + // The current owner of this nft. + aelf.Address owner = 7; + // Token Uri. + string uri = 8; + // Base Uri. + string base_uri = 9; + // Alias + string alias = 10; + // NFT Type + string nft_type = 11; + // Quantity + int64 quantity = 12; + int64 total_quantity = 13; + aelf.Hash token_hash = 14; +} + +message Transferred { + option (aelf.is_event) = true; + aelf.Address from = 1 [(aelf.is_indexed) = true]; + aelf.Address to = 2 [(aelf.is_indexed) = true]; + string symbol = 3 [(aelf.is_indexed) = true]; + int64 token_id = 4 [(aelf.is_indexed) = true]; + int64 amount = 5; + string memo = 6; +} + +message Approved { + option (aelf.is_event) = true; + aelf.Address owner = 1 [(aelf.is_indexed) = true]; + aelf.Address spender = 2 [(aelf.is_indexed) = true]; + string symbol = 3 [(aelf.is_indexed) = true]; + int64 token_id = 4 [(aelf.is_indexed) = true]; + int64 amount = 5; +} + +message UnApproved { + option (aelf.is_event) = true; + aelf.Address owner = 1 [(aelf.is_indexed) = true]; + aelf.Address spender = 2 [(aelf.is_indexed) = true]; + string symbol = 3 [(aelf.is_indexed) = true]; + int64 token_id = 4 [(aelf.is_indexed) = true]; + int64 current_allowance = 5; +} + +message Burned { + option (aelf.is_event) = true; + aelf.Address burner = 1 [(aelf.is_indexed) = true]; + string symbol = 2 [(aelf.is_indexed) = true]; + int64 token_id = 3 [(aelf.is_indexed) = true]; + int64 amount = 4; +} + +message Recasted { + option (aelf.is_event) = true; + string symbol = 1 [(aelf.is_indexed) = true]; + int64 token_id = 2 [(aelf.is_indexed) = true]; + Metadata old_metadata = 3 [(aelf.is_indexed) = true]; + Metadata new_metadata = 4 [(aelf.is_indexed) = true]; + string alias = 5 [(aelf.is_indexed) = true]; + string uri = 6 [(aelf.is_indexed) = true]; +} + +message Assembled { + option (aelf.is_event) = true; + string symbol = 1 [(aelf.is_indexed) = true]; + int64 token_id = 2 [(aelf.is_indexed) = true]; + AssembledNfts assembled_nfts = 3 [(aelf.is_indexed) = true]; + AssembledFts assembled_fts = 4 [(aelf.is_indexed) = true]; +} + +message Disassembled { + option (aelf.is_event) = true; + string symbol = 1 [(aelf.is_indexed) = true]; + int64 token_id = 2 [(aelf.is_indexed) = true]; + AssembledNfts disassembled_nfts = 3 [(aelf.is_indexed) = true]; + AssembledFts disassembled_fts = 4 [(aelf.is_indexed) = true]; +} + +message NFTTypeAdded { + option (aelf.is_event) = true; + string full_name = 1; + string short_name = 2; +} + +message NFTTypeRemoved { + option (aelf.is_event) = true; + string short_name = 1; +} + +message MinterListAdded { + option (aelf.is_event) = true; + MinterList minter_list = 1; + string symbol = 2; +} + +message MinterListRemoved { + option (aelf.is_event) = true; + MinterList minter_list = 1; + string symbol = 2; +} \ No newline at end of file diff --git a/src/AElf.Client.Protobuf/Protobuf/receipt_maker.proto b/src/AElf.Client.Protobuf/Protobuf/receipt_maker.proto new file mode 100644 index 0000000..3adefd5 --- /dev/null +++ b/src/AElf.Client.Protobuf/Protobuf/receipt_maker.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; + +import "aelf/core.proto"; +import "aelf/options.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; + +option csharp_namespace = "AElf.Contracts.ReceiptMakerContract"; + +service ReceiptMakerContract { + rpc GetReceiptCount(google.protobuf.Int64Value) returns (google.protobuf.Int64Value) { + option (aelf.is_view) = true; + } + rpc GetReceiptHash(GetReceiptHashInput) returns (aelf.Hash) { + option (aelf.is_view) = true; + } + rpc GetReceiptHashList(GetReceiptHashListInput) returns (GetReceiptHashListOutput) { + option (aelf.is_view) = true; + } +} + +message GetReceiptHashInput { + int64 recorder_id = 1; + int64 receipt_id = 2; +} + +message GetReceiptHashListInput { + int64 first_leaf_index = 1; + int64 last_leaf_index = 2; + int64 recorder_id = 3; +} + +message GetReceiptHashListOutput { + repeated aelf.Hash receipt_hash_list = 1; +} \ No newline at end of file diff --git a/src/AElf.Client.Protobuf/Protobuf/token_contract.proto b/src/AElf.Client.Protobuf/Protobuf/token_contract.proto index 93d1433..90c1945 100644 --- a/src/AElf.Client.Protobuf/Protobuf/token_contract.proto +++ b/src/AElf.Client.Protobuf/Protobuf/token_contract.proto @@ -124,7 +124,15 @@ service TokenContract { // This method is used to initialize the governance organization for some functions, // including: the coefficient of the user transaction fee calculation formula, // the coefficient of the contract developer resource fee calculation formula, and the side chain rental fee. - rpc InitializeAuthorizedController(google.protobuf.Empty) returns (google.protobuf.Empty){ + rpc InitializeAuthorizedController (google.protobuf.Empty) returns (google.protobuf.Empty){ + } + + rpc ResetExternalInfo (ResetExternalInfoInput) returns (google.protobuf.Empty){ + } + + rpc AddAddressToCreateTokenWhiteList (aelf.Address) returns (google.protobuf.Empty) { + } + rpc RemoveAddressFromCreateTokenWhiteList (aelf.Address) returns (google.protobuf.Empty) { } // Query token information. @@ -200,6 +208,12 @@ service TokenContract { rpc IsTokenAvailableForMethodFee (google.protobuf.StringValue) returns (google.protobuf.BoolValue) { option (aelf.is_view) = true; } + rpc IsInCreateTokenWhiteList (aelf.Address) returns (google.protobuf.BoolValue) { + option (aelf.is_view) = true; + } + rpc GetReservedExternalInfoKeyList (google.protobuf.Empty) returns (StringList) { + option (aelf.is_view) = true; + } } message TokenInfo { @@ -221,6 +235,12 @@ message TokenInfo { int32 issue_chain_id = 8; // The amount of issued tokens. int64 issued = 9; + // The external information of the token. + ExternalInfo external_info = 10; +} + +message ExternalInfo { + map value = 1; } message CreateInput { @@ -240,6 +260,8 @@ message CreateInput { repeated aelf.Address lock_white_list = 7; // The chain id of the token. int32 issue_chain_id = 8; + // The external information of the token. + ExternalInfo external_info = 9; } message SetPrimaryTokenSymbolInput { @@ -462,6 +484,11 @@ message ChargeTransactionFeesOutput { string charging_information = 2; } +message CallbackInfo { + aelf.Address contract_address = 1; + string method_name = 2; +} + message ExtraTokenListModified { option (aelf.is_event) = true; // Transaction fee token information. @@ -592,6 +619,15 @@ message ChangeTokenIssuerInput aelf.Address new_token_Issuer = 2; } +message ResetExternalInfoInput { + string symbol = 1; + ExternalInfo external_info = 2; +} + +message StringList { + repeated string value = 1; +} + message Transferred { option (aelf.is_event) = true; // The source address of the transferred token. @@ -647,7 +683,6 @@ message ChainPrimaryTokenSymbolSet { string token_symbol = 1; } - message CalculateFeeAlgorithmUpdated { option (aelf.is_event) = true; // All calculate fee coefficients after modification. @@ -686,6 +721,8 @@ message TokenCreated { bool is_burnable = 6; // The chain id of the token. int32 issue_chain_id = 7; + // The external information of the token. + ExternalInfo external_info = 8; } message Issued { @@ -736,4 +773,10 @@ message CrossChainReceived { int32 issue_chain_id = 7; // The parent chain height of the transfer transaction. int64 parent_chain_height = 8; +} + +message ExternalInfoChanged { + option (aelf.is_event) = true; + string symbol = 1; + ExternalInfo external_info = 2; } \ No newline at end of file diff --git a/src/AElf.Client.Protobuf/Protobuf/token_contract_impl.proto b/src/AElf.Client.Protobuf/Protobuf/token_contract_impl.proto new file mode 100644 index 0000000..cfc91fc --- /dev/null +++ b/src/AElf.Client.Protobuf/Protobuf/token_contract_impl.proto @@ -0,0 +1,240 @@ +/** + * MultiToken contract. + * + * The MultiToken contract is mainly used to manage the user's account and transaction fees related Settings. + * + * Implement AElf Standards ACS1 and ACS2. + */ +syntax = "proto3"; + +package tokenimpl; + +import "aelf/core.proto"; +import "acs1.proto"; +import "acs2.proto"; +import "token_contract.proto"; +// Because implementation uses this proto file. +import "transaction_fee.proto"; +import "authority_info.proto"; + +option csharp_namespace = "AElf.Contracts.MultiToken"; + +service TokenContractImpl { + option (aelf.csharp_state) = "AElf.Contracts.MultiToken.TokenContractState"; + option (aelf.base) = "acs1.proto"; + option (aelf.base) = "acs2.proto"; + option (aelf.base) = "token_contract.proto"; + + // Transfer resource tokens to designated contract address. + rpc AdvanceResourceToken (AdvanceResourceTokenInput) returns (google.protobuf.Empty) { + } + + // Take token from contract address. + rpc TakeResourceTokenBack (TakeResourceTokenBackInput) returns (google.protobuf.Empty) { + } + + // Register the token contract address for cross chain. + rpc RegisterCrossChainTokenContractAddress (RegisterCrossChainTokenContractAddressInput) returns (google.protobuf.Empty) { + } + + // Set the receiver address of the side chain transaction fee. + rpc SetFeeReceiver (aelf.Address) returns (google.protobuf.Empty) { + } + + // Validates if the token exist. + rpc ValidateTokenInfoExists(ValidateTokenInfoExistsInput) returns (google.protobuf.Empty){ + } + + // Update the rental unit price of the side chain. + rpc UpdateRental (UpdateRentalInput) returns (google.protobuf.Empty) { + } + + // Set the amount of resources fee per minute for the side chain. + rpc UpdateRentedResources (UpdateRentedResourcesInput) returns (google.protobuf.Empty) { + } + + // Transfer Token to the specified contract. + rpc TransferToContract (TransferToContractInput) returns (google.protobuf.Empty) { + } + + // Change the governance organization of side chain rental. + rpc ChangeSideChainRentalController (AuthorityInfo) returns (google.protobuf.Empty) { + } + + // Change the governance organization for tokens to pay transaction fees. + rpc ChangeSymbolsToPayTXSizeFeeController(AuthorityInfo) returns (google.protobuf.Empty){ + } + + // Change the governance organization for cross-chain token contract address registration. + rpc ChangeCrossChainTokenContractRegistrationController (AuthorityInfo) returns (google.protobuf.Empty) { + } + + // Change the governance organization of the coefficient of the user transaction fee calculation formula. + rpc ChangeUserFeeController (AuthorityInfo) returns (google.protobuf.Empty) { + } + + // Change the governance organization of the coefficient of the developer's transaction resource fee calculation formula. + rpc ChangeDeveloperController (AuthorityInfo) returns (google.protobuf.Empty) { + } + + // Get the address of fee receiver. + rpc GetFeeReceiver (google.protobuf.Empty) returns (aelf.Address){ + option (aelf.is_view) = true; + } + + // Query the amount of resources usage currently. + rpc GetResourceUsage (google.protobuf.Empty) returns (ResourceUsage) { + option (aelf.is_view) = true; + } + + // Query the governance organization for tokens to pay transaction fees. + rpc GetSymbolsToPayTXSizeFeeController(google.protobuf.Empty) returns (AuthorityInfo){ + option (aelf.is_view) = true; + } + + // Query the governance organization of the + rpc GetCrossChainTokenContractRegistrationController (google.protobuf.Empty) returns (AuthorityInfo) { + option (aelf.is_view) = true; + } + + // Query the governance organization that calculates the formula coefficient + // for the transaction cost the user sends the contract. + rpc GetUserFeeController(google.protobuf.Empty) returns (UserFeeController){ + option (aelf.is_view) = true; + } + + // Query the governing organization of the formula coefficients for calculating developer contract transaction fee. + rpc GetDeveloperFeeController (google.protobuf.Empty) returns (DeveloperFeeController) { + option (aelf.is_view) = true; + } + + // Query the organization that governs the side chain rental fee. + rpc GetSideChainRentalControllerCreateInfo (google.protobuf.Empty) returns (AuthorityInfo) { + option (aelf.is_view) = true; + } + + // Compute the virtual address for locking. + rpc GetVirtualAddressForLocking (GetVirtualAddressForLockingInput) returns (aelf.Address) { + option (aelf.is_view) = true; + } + + // Query how much resource tokens should be paid currently. + rpc GetOwningRental (google.protobuf.Empty) returns (OwningRental) { + option (aelf.is_view) = true; + } + + // Query the unit price of the side chain resource cost, resource cost = unit price * quantity, + // the quantity can be queried through GetResourceUsage. + rpc GetOwningRentalUnitValue (google.protobuf.Empty) returns (OwningRentalUnitValue) { + option (aelf.is_view) = true; + } +} + +message AdvanceResourceTokenInput { + // The contract address to transfer. + aelf.Address contract_address = 1; + // The resource token symbol to transfer. + string resource_token_symbol = 2; + // The amount of resource token to transfer. + int64 amount = 3; +} + +message TakeResourceTokenBackInput { + // The contract address to take back. + aelf.Address contract_address = 1; + // The resource token symbol to take back. + string resource_token_symbol = 2; + // The amount of resource token to take back. + int64 amount = 3; +} + +message RegisterCrossChainTokenContractAddressInput{ + // The source chain id. + int32 from_chain_id = 1; + // The parent chain height of the transaction. + int64 parent_chain_height = 2; + // The raw bytes of the transfer transaction. + bytes transaction_bytes = 3; + // The merkle path created from the transaction. + aelf.MerklePath merkle_path = 4; + // The token contract address. + aelf.Address token_contract_address = 5; +} + +message ValidateTokenInfoExistsInput{ + // The symbol of the token. + string symbol = 1; + // The full name of the token. + string token_name = 2; + // The total supply of the token. + int64 total_supply = 3; + // The precision of the token. + int32 decimals = 4; + // The address that created the token. + aelf.Address issuer = 5; + // A flag indicating if this token is burnable. + bool is_burnable = 6; + // The chain id of the token. + int32 issue_chain_id = 7; + // The external information of the token. + map external_info = 8; +} + +message UpdateRentalInput { + // The unit price of resource tokens, symbol -> unit price. + map rental = 1; +} + +message UpdateRentedResourcesInput { + // Amount of resource tokens consumed per minute, symbol -> resource consumption. + map resource_amount = 1; +} + +message ResourceUsage { + // The amount of resource tokens usage, symbol -> amount. + map value = 1; +} + +message GetVirtualAddressForLockingInput { + // The address of the lock. + aelf.Address address = 1; + // The id of the lock. + aelf.Hash lock_id = 2; +} + +message OwningRental { + // The amount of resource tokens owed, symbol -> amount. + map resource_amount = 1; +} + +message OwningRentalUnitValue { + // Resource unit price, symbol -> unit price. + map resource_unit_value = 1; +} + +message TransferToContractInput { + // The symbol of token. + string symbol = 1; + // The amount of token. + int64 amount = 2; + // The memo. + string memo = 3; +} + +message UserFeeController{ + // The association that governs the organization. + AuthorityInfo root_controller = 1; + // The parliament organization of members. + AuthorityInfo parliament_controller = 2; + // The referendum organization of members. + AuthorityInfo referendum_controller = 3; +} + +message DeveloperFeeController { + // The association that governs the organization. + AuthorityInfo root_controller = 1; + // The parliament organization of members. + AuthorityInfo parliament_controller = 2; + // The developer organization of members. + AuthorityInfo developer_controller = 3; +} diff --git a/src/AElf.Client.Protobuf/Protobuf/whitelist_contract.proto b/src/AElf.Client.Protobuf/Protobuf/whitelist_contract.proto new file mode 100644 index 0000000..37ce7af --- /dev/null +++ b/src/AElf.Client.Protobuf/Protobuf/whitelist_contract.proto @@ -0,0 +1,581 @@ +syntax = "proto3"; + +package whitelist; + +import "aelf/core.proto"; +import "aelf/options.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; +import "acs1.proto"; + +option csharp_namespace = "AElf.Contracts.Whitelist"; + +service WhitelistContract { + option (aelf.csharp_state) = "AElf.Contracts.Whitelist.WhitelistContractState"; + option (aelf.base) = "acs1.proto"; + + rpc Initialize (google.protobuf.Empty) returns (google.protobuf.Empty) { + } + + //For Managers. + //Create whitelist. + rpc CreateWhitelist (CreateWhitelistInput) returns (aelf.Hash){ + } + + //Add tag info or extraInfo. + rpc AddExtraInfo (AddExtraInfoInput) returns (aelf.Hash){ + } + + //Add multiple addresses to an existing whitelist. + rpc AddAddressInfoListToWhitelist (AddAddressInfoListToWhitelistInput) returns (google.protobuf.Empty){ + } + + //Remove multiple addresses from an existing whitelist. + rpc RemoveAddressInfoListFromWhitelist (RemoveAddressInfoListFromWhitelistInput) returns (google.protobuf.Empty){ + } + + //Remove tag info. + rpc RemoveTagInfo (RemoveTagInfoInput) returns (google.protobuf.Empty){ + } + + //Disable whitelist according to the whitelist_id. + rpc DisableWhitelist (aelf.Hash) returns (google.protobuf.Empty){ + } + + //Re-enable whitelist according to the whitelist_id. + rpc EnableWhitelist (aelf.Hash) returns (google.protobuf.Empty){ + } + + //Update state: Whether the whitelist is allowed to be cloned. + rpc ChangeWhitelistCloneable(ChangeWhitelistCloneableInput) returns (google.protobuf.Empty){ + } + + //Update whitelist extraInfo according to the whitelist_id and extraInfo. + rpc UpdateExtraInfo (UpdateExtraInfoInput) returns (google.protobuf.Empty){ + } + + //Transfer Manager. + rpc TransferManager (TransferManagerInput) returns (google.protobuf.Empty){ + } + + //Add manager. + rpc AddManagers (AddManagersInput) returns (google.protobuf.Empty){ + } + + //Remove manager. + rpc RemoveManagers (RemoveManagersInput) returns (google.protobuf.Empty){ + } + + //Reset whitelist according to the whitelist_id and project_id. + rpc ResetWhitelist (ResetWhitelistInput) returns (google.protobuf.Empty){ + } + + + //For Subscribers. + //Subscribe whitelist. + rpc SubscribeWhitelist (SubscribeWhitelistInput) returns (aelf.Hash){ + } + + // Cancel subscribe according to the subscribe_id. + rpc UnsubscribeWhitelist (aelf.Hash) returns (google.protobuf.Empty){ + } + + //After used,address and extra info will be added into the consumedList. + rpc ConsumeWhitelist (ConsumeWhitelistInput) returns (google.protobuf.Empty){ + } + + //Clone whitelist. + rpc CloneWhitelist (CloneWhitelistInput) returns (aelf.Hash){ + } + + //Add subscribe whitelist manager. + rpc AddSubscribeManagers (AddSubscribeManagersInput) returns (google.protobuf.Empty){ + } + + //Remove subscribe whitelist manager. + rpc RemoveSubscribeManagers (RemoveSubscribeManagersInput) returns (google.protobuf.Empty){ + } + + + //Views. + //Get whitelist_id list according to the manager. + rpc GetWhitelistByManager (aelf.Address) returns (WhitelistIdList){ + option (aelf.is_view) = true; + } + + //Get existing whitelist according to the whitelist_id. + rpc GetWhitelist (aelf.Hash) returns (WhitelistInfo){ + option (aelf.is_view) = true; + } + + //Get whitelist detail extraInfo according to the whitelist_id. + rpc GetWhitelistDetail (aelf.Hash) returns(ExtraInfoList){ + option (aelf.is_view) = true; + } + + //Get whitelist id by project_id. + rpc GetWhitelistByProject(aelf.Hash) returns (WhitelistIdList){ + option (aelf.is_view) = true; + } + + //Get extraInfo according to the tag_id. + rpc GetExtraInfoByTag (GetExtraInfoByTagInput) returns (ExtraInfo){ + option (aelf.is_view) = true; + } + + //Get tag info according to the tag_info_id. + rpc GetTagInfoByHash (aelf.Hash) returns (TagInfo){ + option (aelf.is_view) = true; + } + + //Get tagInfoId list according to the whitelist_id and project_id. + rpc GetExtraInfoIdList(GetExtraInfoIdListInput) returns (HashList){ + option (aelf.is_view) = true; + } + + //Get tagId according to the whitelist_id and address. + rpc GetTagIdByAddress(GetTagIdByAddressInput) returns (aelf.Hash){ + option (aelf.is_view) = true; + } + + //Get TagInfo according to the address and whitelist_id. + rpc GetExtraInfoByAddress(GetExtraInfoByAddressInput) returns (TagInfo){ + option (aelf.is_view) = true; + } + + //Whether the address exists in the whitelist according to the whitelist_id and address. + rpc GetAddressFromWhitelist(GetAddressFromWhitelistInput) returns (google.protobuf.BoolValue){ + option (aelf.is_view) = true; + } + + //Whether the extraInfo (address+TagInfoId) exists in the whitelist according to the whitelist_id and address. + rpc GetExtraInfoFromWhitelist(GetExtraInfoFromWhitelistInput) returns (google.protobuf.BoolValue){ + option (aelf.is_view) = true; + } + + //Whether the tagInfo exists in the whitelist according to the whitelist_id,project_id,tagInfo(tagName,info). + rpc GetTagInfoFromWhitelist(GetTagInfoFromWhitelistInput) returns (google.protobuf.BoolValue){ + option (aelf.is_view) = true; + } + + //Get manager list according to the whitelist_id. + rpc GetManagerList(aelf.Hash) returns (AddressList){ + option (aelf.is_view) = true; + } + + //Get subscribe manager list according to the subscribe_id. + rpc GetSubscribeManagerList(aelf.Hash) returns (AddressList){ + option (aelf.is_view) = true; + } + + //Whether manager exist in whitelist. + rpc GetManagerExistFromWhitelist(GetManagerExistFromWhitelistInput) returns (google.protobuf.BoolValue){ + option (aelf.is_view) = true; + } + + //Get subscribe whitelist info according to the subscribe_id. + rpc GetSubscribeWhitelist (aelf.Hash) returns (SubscribeWhitelistInfo){ + option (aelf.is_view) = true; + } + + //Get subscribe_id list according to the manager. + rpc GetSubscribeIdByManager (aelf.Address) returns (HashList){ + option (aelf.is_view) = true; + } + + //Get consumed list according to the subscribe_id. + rpc GetConsumedList (aelf.Hash) returns (ConsumedList){ + option (aelf.is_view) = true; + } + + //After consumed,get available whitelist according to the subscribe_id. + rpc GetAvailableWhitelist (aelf.Hash) returns (ExtraInfoIdList){ + option (aelf.is_view) = true; + } + + //Whether the extraInfo exist in the available whitelist. + rpc GetFromAvailableWhitelist (GetFromAvailableWhitelistInput) returns (google.protobuf.BoolValue){ + option (aelf.is_view) = true; + } + + +} + +//Structs. + +message WhitelistInfo { + //The whitelist id. + aelf.Hash whitelist_id = 1; + //The project id. + aelf.Hash project_id = 2; + //The list of address and extra info in this whitelist. + ExtraInfoIdList extra_info_id_list = 3; + //Whether the whitelist is available. + bool is_available = 4; + //Whether the whiteList can be cloned. + bool is_cloneable = 5; + string remark = 6; + aelf.Hash clone_from = 7; + aelf.Address creator = 8; + AddressList manager = 9; + StrategyType strategy_type = 10; +} + +//Pricing strategy +message PriceTag{ + string symbol = 1; + int64 amount = 2; +} + +enum StrategyType{ + Basic = 0; + Price = 1; + Customize = 2; +} + +message ExtraInfoIdList { + repeated ExtraInfoId value = 1; +} + +message ExtraInfoId { + AddressList address_list = 1; + aelf.Hash id = 2; +} + +message ExtraInfoList { + repeated ExtraInfo value = 1; +} + +message ExtraInfo { + AddressList address_list = 1; + TagInfo info = 2; +} + +message TagInfo { + string tag_name = 1; + bytes info = 2; +} + +message TagInfoList{ + repeated TagInfo value = 1; +} + +message WhitelistIdList{ + repeated aelf.Hash whitelist_id = 1; +} + +message SubscribeWhitelistInfo { + //The subscribe id. + aelf.Hash subscribe_id = 1; + //The project id. + aelf.Hash project_id = 2; + //The whitelist id. + aelf.Hash whitelist_id = 3; + aelf.Address subscriber = 4; + //Manager list. + AddressList manager_list = 5; +} + +message ConsumedList { + //The subscribe id. + aelf.Hash subscribe_id = 1; + //The whitelist id. + aelf.Hash whitelist_id = 2; + //The consumed address and extra info list in this whitelist. + ExtraInfoIdList extra_info_id_list = 3; +} + +message AddressList { + repeated aelf.Address value = 1; +} + +message HashList{ + repeated aelf.Hash value = 1; +} + +//Inputs. + +//message InitializeInput{ +// +//} + +message CreateWhitelistInput { + ExtraInfoList extra_info_list = 1; + bool is_cloneable = 2; + string remark = 3; + aelf.Address creator = 4; + AddressList manager_list = 5; + aelf.Hash project_id = 6; + StrategyType strategy_type = 7; +} + +message AddAddressInfoListToWhitelistInput { + aelf.Hash whitelist_id = 1; + ExtraInfoIdList extra_info_id_list = 2; +} + +message RemoveAddressInfoListFromWhitelistInput{ + aelf.Hash whitelist_id = 1; + ExtraInfoIdList extra_info_id_list = 2; +} + +message ChangeWhitelistCloneableInput{ + aelf.Hash whitelist_id = 1; + bool is_cloneable = 2; +} + +message AddExtraInfoInput{ + aelf.Hash whitelist_id = 1; + aelf.Hash project_id = 2; + TagInfo tag_info = 3; + AddressList address_list = 4; +} + +message RemoveTagInfoInput{ + aelf.Hash whitelist_id = 1; + aelf.Hash project_id = 2; + aelf.Hash tag_id = 3; +} + +message UpdateExtraInfoInput{ + aelf.Hash whitelist_id = 1; + ExtraInfoId extra_info_list = 2; +} + +message SubscribeWhitelistInput{ + //The project id. + aelf.Hash project_id = 1; + //The whitelist id. + aelf.Hash whitelist_id = 2; + //Subscriber. + aelf.Address subscriber = 3; + //Manager list. + AddressList manager_list = 4; +} + +message ConsumeWhitelistInput{ + aelf.Hash subscribe_id = 1; + aelf.Hash whitelist_id = 2; + ExtraInfoId extra_info_id = 3; +} + +message CloneWhitelistInput{ + aelf.Hash whitelist_id = 1; + aelf.Address creator = 2; + AddressList manager_list = 3; + aelf.Hash project_id = 4; +} + +message TransferManagerInput{ + aelf.Hash whitelist_id = 1; + aelf.Address manager = 2; +} + +message GetFromAvailableWhitelistInput{ + aelf.Hash subscribe_id = 1; + ExtraInfoId extra_info_id = 2; +} + +message AddManagersInput{ + aelf.Hash whitelist_id = 1; + AddressList manager_list = 2; +} + +message AddSubscribeManagersInput{ + aelf.Hash subscribe_id = 1; + AddressList manager_list = 2; +} + +message RemoveManagersInput{ + aelf.Hash whitelist_id = 1; + AddressList manager_list = 2; +} + +message RemoveSubscribeManagersInput{ + aelf.Hash subscribe_id = 1; + AddressList manager_list = 2; +} + +message GetTagIdByAddressInput{ + aelf.Hash whitelist_id = 1; + aelf.Address address = 2; +} + +message GetExtraInfoByAddressInput{ + aelf.Hash whitelist_id = 1; + aelf.Address address = 2; +} + +message GetExtraInfoIdListInput{ + aelf.Hash whitelist_id = 1; + aelf.Hash project_id = 2; +} + +message GetExtraInfoByTagInput{ + aelf.Hash whitelist_id = 1; + aelf.Hash tag_info_id = 2; +} + +message GetAddressFromWhitelistInput{ + aelf.Hash whitelist_id = 1; + aelf.Address address = 2; +} + +message GetExtraInfoFromWhitelistInput{ + aelf.Hash whitelist_id = 1; + ExtraInfoId extra_info_id = 2; +} + +message GetTagInfoFromWhitelistInput{ + aelf.Hash whitelist_id = 1; + aelf.Hash project_id = 2; + TagInfo tag_info = 3; +} + +message GetManagerExistFromWhitelistInput{ + aelf.Hash whitelist_id = 1; + aelf.Address manager = 2; +} + +message ResetWhitelistInput{ + aelf.Hash whitelist_id = 1; + aelf.Hash project_id = 2; +} + +//Events. + +message WhitelistCreated { + option (aelf.is_event) = true; + //The whitelist id. + aelf.Hash whitelist_id = 1; + //The list of address and extra info in this whitelist. + ExtraInfoIdList extra_info_id_list = 2; + //Whether the whitelist is available. + bool is_available = 3; + bool is_cloneable = 4; + string remark = 5; + aelf.Hash clone_from = 6; + aelf.Address creator = 7; + AddressList manager = 8; + aelf.Hash project_id = 9; + StrategyType strategy_type = 10; + +} + +message WhitelistSubscribed { + option (aelf.is_event) = true; + //The subscribe id. + aelf.Hash subscribe_id = 1; + //The project id. + aelf.Hash project_id = 2; + //The whitelist id. + aelf.Hash whitelist_id = 3; + //Subscriber. + aelf.Address subscriber = 4; + //Manager list. + AddressList manager_list = 5; +} + +message WhitelistUnsubscribed{ + option (aelf.is_event) = true; + aelf.Hash subscribe_id = 1; + aelf.Hash project_id = 2; + aelf.Hash whitelist_id = 3; +} + +message WhitelistAddressInfoAdded { + option (aelf.is_event) = true; + aelf.Hash whitelist_id = 1; + ExtraInfoIdList extra_info_id_list = 2; +} + +message WhitelistAddressInfoRemoved { + option (aelf.is_event) = true; + aelf.Hash whitelist_id = 1; + ExtraInfoIdList extra_info_id_list = 2; +} + +message WhitelistDisabled { + option (aelf.is_event) = true; + aelf.Hash whitelist_id = 1; + bool is_available = 2; +} + +message WhitelistReenable{ + option (aelf.is_event) = true; + aelf.Hash whitelist_id = 1; + bool is_available = 2; +} + +message ConsumedListAdded { + option (aelf.is_event) = true; + aelf.Hash subscribe_id = 1; + aelf.Hash whitelist_id = 2; + ExtraInfoIdList extra_info_id_list = 3; +} + +message IsCloneableChanged{ + option (aelf.is_event) = true; + aelf.Hash whitelist_id = 1; + bool is_cloneable = 2; +} + +message TagInfoAdded{ + option (aelf.is_event) = true; + aelf.Hash tag_info_id = 1; + TagInfo tag_info = 2; + aelf.Hash project_id = 3; + aelf.Hash whitelist_id = 4; +} + +message TagInfoRemoved{ + option (aelf.is_event) = true; + aelf.Hash tag_info_id = 1; + TagInfo tag_info = 2; + aelf.Hash project_id = 3; + aelf.Hash whitelist_id = 4; +} + +message ExtraInfoUpdated { + option (aelf.is_event) = true; + aelf.Hash whitelist_id = 1; + ExtraInfoId extra_info_id_before = 2; + ExtraInfoId extra_info_id_after = 3; +} + +message ManagerTransferred{ + option (aelf.is_event) = true; + aelf.Hash whitelist_id = 1; + aelf.Address transfer_from = 2; + aelf.Address transfer_to = 3; +} + +message ManagerAdded{ + option (aelf.is_event) = true; + aelf.Hash whitelist_id = 1; + AddressList manager_list = 2; +} + +message ManagerRemoved{ + option (aelf.is_event) = true; + aelf.Hash whitelist_id = 1; + AddressList manager_list = 2; +} + +message WhitelistReset{ + option (aelf.is_event) = true; + aelf.Hash whitelist_id = 1; + aelf.Hash project_id = 2; +} + +message SubscribeManagerAdded{ + option (aelf.is_event) = true; + aelf.Hash subscribe_id = 1; + AddressList manager_list = 2; +} + +message SubscribeManagerRemoved{ + option (aelf.is_event) = true; + aelf.Hash subscribe_id = 1; + AddressList manager_list = 2; +} \ No newline at end of file diff --git a/src/AElf.Client/AElf.Client.csproj b/src/AElf.Client/AElf.Client.csproj index ffb9f1b..e6ccb5c 100644 --- a/src/AElf.Client/AElf.Client.csproj +++ b/src/AElf.Client/AElf.Client.csproj @@ -15,9 +15,10 @@ - - - + + + + diff --git a/src/AElf.Client/AElfClient.Chain.cs b/src/AElf.Client/AElfClient.Chain.cs index e42eb0f..eeeb460 100644 --- a/src/AElf.Client/AElfClient.Chain.cs +++ b/src/AElf.Client/AElfClient.Chain.cs @@ -32,7 +32,7 @@ public async Task GetContractFileDescriptorSetAsync(string? address) var set = await _httpService.GetResponseAsync(url); if (set == null) { - throw new AElfClientException("Failed to get chain status"); + throw new AElfClientException("Failed to get method descriptor"); } return set; diff --git a/src/AElf.Client/AElfClient.Client.cs b/src/AElf.Client/AElfClient.Client.cs index c187395..fd455c2 100644 --- a/src/AElf.Client/AElfClient.Client.cs +++ b/src/AElf.Client/AElfClient.Client.cs @@ -12,257 +12,254 @@ namespace AElf.Client; public partial class AElfClient : IClientService { /// - /// Verify whether this sdk successfully connects the chain. - /// - /// IsConnected or not - public async Task IsConnectedAsync() + /// Verify whether this sdk successfully connects the chain. + /// + /// IsConnected or not + public async Task IsConnectedAsync() + { + try { - try - { - var chainStatus = await GetChainStatusAsync(); - return chainStatus != null; - } - catch (Exception) - { - return false; - } + await GetChainStatusAsync(); + return true; } - - /// - /// Get the address of genesis contract. - /// - /// Address - public async Task GetGenesisContractAddressAsync() + catch (Exception) { - var statusDto = await GetChainStatusAsync(); - var genesisAddress = statusDto?.GenesisContractAddress; - return genesisAddress; + return false; } + } + + /// + /// Get the address of genesis contract. + /// + /// Address + public async Task GetGenesisContractAddressAsync() + { + var statusDto = await GetChainStatusAsync(); + var genesisAddress = statusDto.GenesisContractAddress; + return genesisAddress; + } - /// - /// Get address of a contract by given contractNameHash. - /// - /// - /// Address - public async Task
GetContractAddressByNameAsync(Hash contractNameHash) + /// + /// Get address of a contract by given contractNameHash. + /// + /// + /// Address + public async Task
GetContractAddressByNameAsync(Hash contractNameHash) + { + var from = GetAddressFromPrivateKey(AElfClientConstants.DefaultPrivateKey); + var to = await GetGenesisContractAddressAsync(); + var transaction = await GenerateTransactionAsync(from, to, "GetContractAddressByName", contractNameHash); + var txWithSig = SignTransaction(AElfClientConstants.DefaultPrivateKey, transaction); + + var response = await ExecuteTransactionAsync(new ExecuteTransactionDto { - var from = GetAddressFromPrivateKey(AElfClientConstants.DefaultPrivateKey); - var to = await GetGenesisContractAddressAsync(); - var transaction = await GenerateTransactionAsync(from, to, "GetContractAddressByName", contractNameHash); - var txWithSig = SignTransaction(AElfClientConstants.DefaultPrivateKey, transaction); + RawTransaction = txWithSig.ToByteArray().ToHex() + }); + var byteArray = ByteArrayHelper.HexStringToByteArray(response); + var address = Address.Parser.ParseFrom(byteArray); - var response = await ExecuteTransactionAsync(new ExecuteTransactionDto + return address; + } + + /// + /// Build a transaction from the input parameters. + /// + /// + /// + /// + /// + /// Transaction unsigned + public async Task GenerateTransactionAsync(string? from, string? to, + string methodName, IMessage input) + { + try + { + AssertValidAddress(to); + var chainStatus = await GetChainStatusAsync(); + var transaction = new Transaction { - RawTransaction = txWithSig.ToByteArray().ToHex() - }); - var byteArray = ByteArrayHelper.HexStringToByteArray(response); - var address = Address.Parser.ParseFrom(byteArray); + From = from.ToAddress(), + To = Address.FromBase58(to), + MethodName = methodName, + Params = input.ToByteString(), + RefBlockNumber = chainStatus.BestChainHeight, + RefBlockPrefix = ByteString.CopyFrom(Hash.LoadFromHex(chainStatus.BestChainHash).Value + .Take(4).ToArray()) + }; - return address; + return transaction; } - - /// - /// Build a transaction from the input parameters. - /// - /// - /// - /// - /// - /// Transaction unsigned - public async Task GenerateTransactionAsync(string? from, string? to, - string methodName, IMessage input) + catch (Exception ex) { - try - { - AssertValidAddress(to); - var chainStatus = await GetChainStatusAsync(); - var transaction = new Transaction - { - From = from.ToAddress(), - To = Address.FromBase58(to), - MethodName = methodName, - Params = input.ToByteString(), - RefBlockNumber = chainStatus.BestChainHeight, - RefBlockPrefix = ByteString.CopyFrom(Hash.LoadFromHex(chainStatus.BestChainHash).Value - .Take(4).ToArray()) - }; - - return transaction; - } - catch (Exception ex) - { - throw new AElfClientException($"Failed to generate transaction: {ex.Message}"); - } + throw new AElfClientException($"Failed to generate transaction: {ex.Message}"); } + } - /// - /// Convert the Address to the displayed string:symbol_base58-string_base58-string-chain-id - /// - /// - /// - public async Task GetFormattedAddressAsync(Address address) + /// + /// Convert the Address to the displayed string:symbol_base58-string_base58-string-chain-id + /// + /// + /// + public async Task GetFormattedAddressAsync(Address address) + { + var tokenContractAddress = + await GetContractAddressByNameAsync(HashHelper.ComputeFrom("AElf.ContractNames.Token")); + var fromAddress = GetAddressFromPrivateKey(AElfClientConstants.DefaultPrivateKey); + var toAddress = tokenContractAddress.ToBase58(); + var methodName = "GetPrimaryTokenSymbol"; + var param = new Empty(); + + var transaction = await GenerateTransactionAsync(fromAddress, toAddress, methodName, param); + var txWithSign = SignTransaction(AElfClientConstants.DefaultPrivateKey, transaction); + + var result = await ExecuteTransactionAsync(new ExecuteTransactionDto { - var tokenContractAddress = await GetContractAddressByNameAsync(HashHelper.ComputeFrom("AElf.ContractNames.Token")); - var fromAddress = GetAddressFromPrivateKey(AElfClientConstants.DefaultPrivateKey); - var toAddress = tokenContractAddress.ToBase58(); - var methodName = "GetPrimaryTokenSymbol"; - var param = new Empty(); + RawTransaction = txWithSign.ToByteArray().ToHex() + }); - var transaction = await GenerateTransactionAsync(fromAddress, toAddress, methodName, param); - var txWithSign = SignTransaction(AElfClientConstants.DefaultPrivateKey, transaction); + var symbol = StringValue.Parser.ParseFrom(ByteArrayHelper.HexStringToByteArray(result)); + var chainIdString = (await GetChainStatusAsync()).ChainId; - var result = await ExecuteTransactionAsync(new ExecuteTransactionDto - { - RawTransaction = txWithSign.ToByteArray().ToHex() - }); - - var symbol = StringValue.Parser.ParseFrom(ByteArrayHelper.HexStringToByteArray(result)); - var chainIdString = (await GetChainStatusAsync())?.ChainId; + return $"{symbol.Value}_{address.ToBase58()}_{chainIdString}"; + } - return $"{symbol.Value}_{address.ToBase58()}_{chainIdString}"; - } + /// + /// Sign a transaction using private key. + /// + /// + /// + /// Transaction signed + public Transaction SignTransaction(string? privateKeyHex, Transaction transaction) + { + var transactionData = transaction.GetHash().ToByteArray(); - /// - /// Sign a transaction using private key. - /// - /// - /// - /// Transaction signed - public Transaction SignTransaction(string? privateKeyHex, Transaction transaction) - { - var transactionData = transaction.GetHash().ToByteArray(); + privateKeyHex ??= AElfClientConstants.DefaultPrivateKey; - privateKeyHex ??= AElfClientConstants.DefaultPrivateKey; + // Sign the hash + var privateKey = ByteArrayHelper.HexStringToByteArray(privateKeyHex); + var signature = CryptoHelper.SignWithPrivateKey(privateKey, transactionData); + transaction.Signature = ByteString.CopyFrom(signature); - // Sign the hash - var privateKey = ByteArrayHelper.HexStringToByteArray(privateKeyHex); - var signature = CryptoHelper.SignWithPrivateKey(privateKey, transactionData); - transaction.Signature = ByteString.CopyFrom(signature); + return transaction; + } - return transaction; - } - - /// - /// Sign a transaction using private key. - /// - /// - /// - /// Transaction signed - public Transaction SignTransaction(byte[]? privateKey, Transaction transaction) - { - var transactionData = transaction.GetHash().ToByteArray(); + /// + /// Sign a transaction using private key. + /// + /// + /// + /// Transaction signed + public Transaction SignTransaction(byte[]? privateKey, Transaction transaction) + { + var transactionData = transaction.GetHash().ToByteArray(); - privateKey ??= ByteArrayHelper.HexStringToByteArray(AElfClientConstants.DefaultPrivateKey); + privateKey ??= ByteArrayHelper.HexStringToByteArray(AElfClientConstants.DefaultPrivateKey); - // Sign the hash - var signature = CryptoHelper.SignWithPrivateKey(privateKey, transactionData); - transaction.Signature = ByteString.CopyFrom(signature); + // Sign the hash + var signature = CryptoHelper.SignWithPrivateKey(privateKey, transactionData); + transaction.Signature = ByteString.CopyFrom(signature); - return transaction; - } + return transaction; + } - /// - /// Get the account address through the public key. - /// - /// - /// Account - public string GetAddressFromPubKey(string pubKey) - { - var publicKey = ByteArrayHelper.HexStringToByteArray(pubKey); - var address = Address.FromPublicKey(publicKey); - return address.ToBase58(); - } - - /// - /// Get the account address through the private key. - /// - /// - /// - public string? GetAddressFromPrivateKey(string? privateKeyHex) - { - var address = Address.FromPublicKey(GetAElfKeyPair(privateKeyHex).PublicKey); - return address.ToBase58(); - } + /// + /// Get the account address through the public key. + /// + /// + /// Account + public string GetAddressFromPubKey(string pubKey) + { + var publicKey = ByteArrayHelper.HexStringToByteArray(pubKey); + var address = Address.FromPublicKey(publicKey); + return address.ToBase58(); + } - public Address GetBase58String(string base58String) + /// + /// Get the account address through the private key. + /// + /// + /// + public string? GetAddressFromPrivateKey(string? privateKeyHex) + { + var address = Address.FromPublicKey(GetAElfKeyPair(privateKeyHex).PublicKey); + return address.ToBase58(); + } + + public KeyPairInfo GenerateKeyPairInfo() + { + var keyPair = CryptoHelper.GenerateKeyPair(); + var privateKey = keyPair.PrivateKey.ToHex(); + var publicKey = keyPair.PublicKey.ToHex(); + var address = GetAddressFromPrivateKey(privateKey); + + return new KeyPairInfo { - return Address.FromBase58(base58String); - } - - public KeyPairInfo GenerateKeyPairInfo() + PrivateKey = privateKey, + PublicKey = publicKey, + Address = address + }; + } + + #region private methods + + private ECKeyPair GetAElfKeyPair(string? privateKeyHex) + { + var privateKey = ByteArrayHelper.HexStringToByteArray(privateKeyHex); + var keyPair = CryptoHelper.FromPrivateKey(privateKey); + + return keyPair; + } + + private string GetRequestUrl(string baseUrl, string relativeUrl) + { + var uri = new Uri(baseUrl + (baseUrl.EndsWith("/") ? "" : "/")); + return new Uri(uri, relativeUrl).ToString(); + } + + private void AssertValidAddress(params string?[] addresses) + { + try { - var keyPair = CryptoHelper.GenerateKeyPair(); - var privateKey = keyPair.PrivateKey.ToHex(); - var publicKey = keyPair.PublicKey.ToHex(); - var address = GetAddressFromPrivateKey(privateKey); - - return new KeyPairInfo + foreach (var address in addresses) { - PrivateKey = privateKey, - PublicKey = publicKey, - Address = address - }; - } - - #region private methods - - private ECKeyPair GetAElfKeyPair(string? privateKeyHex) - { - var privateKey = ByteArrayHelper.HexStringToByteArray(privateKeyHex); - var keyPair = CryptoHelper.FromPrivateKey(privateKey); - - return keyPair; + Address.FromBase58(address); + } } - - private string GetRequestUrl(string baseUrl, string relativeUrl) + catch (Exception) { - return new Uri(new Uri(baseUrl + (baseUrl.EndsWith("/") ? "" : "/")), relativeUrl).ToString(); + throw new AElfClientException(Error.Message[Error.InvalidAddress]); } + } - private void AssertValidAddress(params string?[] addresses) + private void AssertValidHash(params string[] hashes) + { + try { - try + foreach (var hash in hashes) { - foreach (var address in addresses) - { - Address.FromBase58(address); - } - } - catch (Exception) - { - throw new AElfClientException(Error.Message[Error.InvalidAddress]); + Hash.LoadFromHex(hash); } } - - private void AssertValidHash(params string[] hashes) + catch (Exception) { - try - { - foreach (var hash in hashes) - { - Hash.LoadFromHex(hash); - } - } - catch (Exception) - { - throw new AElfClientException(Error.Message[Error.InvalidBlockHash]); - } + throw new AElfClientException(Error.Message[Error.InvalidBlockHash]); } + } - private void AssertValidTransactionId(params string[] transactionIds) + private void AssertValidTransactionId(params string[] transactionIds) + { + try { - try + foreach (var transactionId in transactionIds) { - foreach (var transactionId in transactionIds) - { - Hash.LoadFromHex(transactionId); - } - } - catch (Exception) - { - throw new AElfClientException(Error.Message[Error.InvalidTransactionId]); + Hash.LoadFromHex(transactionId); } } + catch (Exception) + { + throw new AElfClientException(Error.Message[Error.InvalidTransactionId]); + } + } - #endregion + #endregion } \ No newline at end of file diff --git a/src/AElf.Client/AElfClient.Net.cs b/src/AElf.Client/AElfClient.Net.cs index 89b2f5b..5b4d4fb 100644 --- a/src/AElf.Client/AElfClient.Net.cs +++ b/src/AElf.Client/AElfClient.Net.cs @@ -9,72 +9,72 @@ namespace AElf.Client; public partial class AElfClient : INetAppService { /// - /// Attempt to add a node to the connected network nodes.Input parameter contains the ipAddress of the node. - /// - /// - /// - /// - /// Add successfully or not - public async Task AddPeerAsync(string ipAddress, string userName, string password) + /// Attempt to add a node to the connected network nodes.Input parameter contains the ipAddress of the node. + /// + /// + /// + /// + /// Add successfully or not + public async Task AddPeerAsync(string ipAddress, string? userName, string? password) + { + if (!EndpointHelper.TryParse(ipAddress, out var endpoint)) { - if (!EndpointHelper.TryParse(ipAddress, out var endpoint)) - { - return false; - } - - var url = GetRequestUrl(_baseUrl, "api/net/peer"); - var parameters = new Dictionary - { - {"address", endpoint?.ToString() ?? AElfClientConstants.LocalEndpoint} - }; - - return await _httpService.PostResponseAsync(url, parameters, - authenticationHeaderValue: GetAuthenticationHeaderValue()); + return false; } - /// - /// Attempt to remove a node from the connected network nodes by given the ipAddress. - /// - /// - /// - /// - /// Delete successfully or not - public async Task RemovePeerAsync(string ipAddress, string userName, string password) + var url = GetRequestUrl(_baseUrl, "api/net/peer"); + var parameters = new Dictionary { - if (!EndpointHelper.TryParse(ipAddress, out var endpoint)) - { - return false; - } + { "address", endpoint?.ToString() ?? AElfClientConstants.LocalEndpoint }, + }; - var url = GetRequestUrl(_baseUrl, $"api/net/peer?address={endpoint}"); - return await _httpService.DeleteResponseAsObjectAsync(url, - authenticationHeaderValue: GetAuthenticationHeaderValue()); - } + return await _httpService.PostResponseAsync(url, parameters, + authenticationHeaderValue: GetAuthenticationHeaderValue(userName, password)); + } - /// - /// Gets information about the peer nodes of the current node.Optional whether to include metrics. - /// - /// - /// Information about the peer nodes - public async Task?> GetPeersAsync(bool withMetrics) + /// + /// Attempt to remove a node from the connected network nodes by given the ipAddress. + /// + /// + /// + /// + /// Delete successfully or not + public async Task RemovePeerAsync(string ipAddress, string? userName, string? password) + { + if (!EndpointHelper.TryParse(ipAddress, out var endpoint)) { - var url = GetRequestUrl(_baseUrl, $"api/net/peers?withMetrics={withMetrics}"); - return await _httpService.GetResponseAsync>(url); + return false; } - /// - /// Get the node's network information. - /// - /// Network information - public async Task GetNetworkInfoAsync() - { - var url = GetRequestUrl(_baseUrl, "api/net/networkInfo"); - return await _httpService.GetResponseAsync(url); - } + var url = GetRequestUrl(_baseUrl, $"api/net/peer?address={endpoint}"); + return await _httpService.DeleteResponseAsObjectAsync(url, + authenticationHeaderValue: GetAuthenticationHeaderValue(userName, password)); + } - private AuthenticationHeaderValue GetAuthenticationHeaderValue() - { - var byteArray = Encoding.ASCII.GetBytes($"{_userName}:{_password}"); - return new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); - } + /// + /// Gets information about the peer nodes of the current node.Optional whether to include metrics. + /// + /// + /// Information about the peer nodes + public async Task?> GetPeersAsync(bool withMetrics) + { + var url = GetRequestUrl(_baseUrl, $"api/net/peers?withMetrics={withMetrics}"); + return await _httpService.GetResponseAsync>(url); + } + + /// + /// Get the node's network information. + /// + /// Network information + public async Task GetNetworkInfoAsync() + { + var url = GetRequestUrl(_baseUrl, "api/net/networkInfo"); + return await _httpService.GetResponseAsync(url); + } + + private AuthenticationHeaderValue GetAuthenticationHeaderValue(string? userName, string? password) + { + var byteArray = Encoding.ASCII.GetBytes($"{userName ?? _userName}:{password ?? _password}"); + return new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); + } } \ No newline at end of file diff --git a/src/AElf.Client/AElfClient.Transaction.cs b/src/AElf.Client/AElfClient.Transaction.cs index d1b6b4a..5afbc16 100644 --- a/src/AElf.Client/AElfClient.Transaction.cs +++ b/src/AElf.Client/AElfClient.Transaction.cs @@ -6,160 +6,160 @@ namespace AElf.Client; public partial class AElfClient : ITransactionAppService { /// - /// Get information about the current transaction pool. - /// - /// TransactionPoolStatusOutput - public async Task GetTransactionPoolStatusAsync() - { - var url = GetRequestUrl(_baseUrl, "api/blockChain/transactionPoolStatus"); - return await _httpService.GetResponseAsync(url); - } + /// Get information about the current transaction pool. + /// + /// TransactionPoolStatusOutput + public async Task GetTransactionPoolStatusAsync() + { + var url = GetRequestUrl(_baseUrl, "api/blockChain/transactionPoolStatus"); + return await _httpService.GetResponseAsync(url); + } - /// - /// Call a read-only method of a contract. - /// - /// - /// - public async Task ExecuteTransactionAsync(ExecuteTransactionDto input) + /// + /// Call a read-only method of a contract. + /// + /// + /// + public async Task ExecuteTransactionAsync(ExecuteTransactionDto input) + { + var url = GetRequestUrl(_baseUrl, "api/blockChain/executeTransaction"); + var parameters = new Dictionary { - var url = GetRequestUrl(_baseUrl, "api/blockChain/executeTransaction"); - var parameters = new Dictionary - { - {"RawTransaction", input.RawTransaction} - }; - - var result = await _httpService.PostResponseAsync(url, parameters); + { "RawTransaction", input.RawTransaction } + }; - if (result == null) - { - throw new AElfClientException("Failed to execute tx."); - } + var result = await _httpService.PostResponseAsync(url, parameters); - return result; - } - - /// - /// Call a method of a contract by given serialized strings. - /// - /// - /// Serialized result - public async Task ExecuteRawTransactionAsync(ExecuteRawTransactionDto input) + if (result == null) { - var url = GetRequestUrl(_baseUrl, "api/blockChain/executeRawTransaction"); - var parameters = new Dictionary - { - {"RawTransaction", input.RawTransaction}, - {"Signature", input.Signature} - }; - - return await _httpService.PostResponseAsync(url, parameters); + throw new AElfClientException("Failed to execute tx."); } - /// - /// Creates an unsigned serialized transaction. - /// - /// - /// CreateRawTransactionOutput - public async Task CreateRawTransactionAsync(CreateRawTransactionInput input) - { - var url = GetRequestUrl(_baseUrl, "api/blockChain/rawTransaction"); - var parameters = new Dictionary - { - {"From", input.From}, - {"To", input.To}, - {"RefBlockNumber", input.RefBlockNumber.ToString()}, - {"RefBlockHash", input.RefBlockHash}, - {"MethodName", input.MethodName}, - {"Params", input.Params} - }; - - return await _httpService.PostResponseAsync(url, parameters); - } + return result; + } - /// - /// Broadcast a serialized transaction. - /// - /// - /// SendRawTransactionOutput - public async Task SendRawTransactionAsync(SendRawTransactionInput input) + /// + /// Call a method of a contract by given serialized strings. + /// + /// + /// Serialized result + public async Task ExecuteRawTransactionAsync(ExecuteRawTransactionDto input) + { + var url = GetRequestUrl(_baseUrl, "api/blockChain/executeRawTransaction"); + var parameters = new Dictionary { - var url = GetRequestUrl(_baseUrl, "api/blockChain/sendRawTransaction"); - var parameters = new Dictionary - { - {"Transaction", input.Transaction}, - {"Signature", input.Signature}, - {"ReturnTransaction", input.ReturnTransaction ? "true" : "false"} - }; - return await _httpService.PostResponseAsync(url, parameters); - } + { "RawTransaction", input.RawTransaction }, + { "Signature", input.Signature } + }; - /// - /// Broadcast a transaction. - /// - /// - /// TransactionId - public async Task SendTransactionAsync(SendTransactionInput input) - { - var url = GetRequestUrl(_baseUrl, "api/blockChain/sendTransaction"); - var parameters = new Dictionary - { - {"RawTransaction", input.RawTransaction} - }; - return await _httpService.PostResponseAsync(url, parameters); - } + return await _httpService.PostResponseAsync(url, parameters); + } - /// - /// Broadcast volume transactions. - /// - /// - /// TransactionIds - public async Task SendTransactionsAsync(SendTransactionsInput input) + /// + /// Creates an unsigned serialized transaction. + /// + /// + /// CreateRawTransactionOutput + public async Task CreateRawTransactionAsync(CreateRawTransactionInput input) + { + var url = GetRequestUrl(_baseUrl, "api/blockChain/rawTransaction"); + var parameters = new Dictionary { - var url = GetRequestUrl(_baseUrl, "api/blockChain/sendTransactions"); - var parameters = new Dictionary - { - {"RawTransactions", input.RawTransactions} - }; - return await _httpService.PostResponseAsync(url, parameters); - } + { "From", input.From }, + { "To", input.To }, + { "RefBlockNumber", input.RefBlockNumber.ToString() }, + { "RefBlockHash", input.RefBlockHash }, + { "MethodName", input.MethodName }, + { "Params", input.Params } + }; - /// - /// Gets the result of transaction execution by the given transactionId. - /// - /// - /// TransactionResultDto - public async Task GetTransactionResultAsync(string transactionId) + return await _httpService.PostResponseAsync(url, parameters); + } + + /// + /// Broadcast a serialized transaction. + /// + /// + /// SendRawTransactionOutput + public async Task SendRawTransactionAsync(SendRawTransactionInput input) + { + var url = GetRequestUrl(_baseUrl, "api/blockChain/sendRawTransaction"); + var parameters = new Dictionary { - AssertValidTransactionId(transactionId); - var url = GetRequestUrl(_baseUrl, $"api/blockChain/transactionResult?transactionId={transactionId}"); - return await _httpService.GetResponseAsync(url); - } + { "Transaction", input.Transaction }, + { "Signature", input.Signature }, + { "ReturnTransaction", input.ReturnTransaction ? "true" : "false" } + }; + return await _httpService.PostResponseAsync(url, parameters); + } - /// - /// Get results of multiple transactions by specified blockHash and the offset. - /// - /// - /// - /// - /// TransactionResultDtos - public async Task?> GetTransactionResultsAsync(string blockHash, int offset = 0, - int limit = 10) + /// + /// Broadcast a transaction. + /// + /// + /// TransactionId + public async Task SendTransactionAsync(SendTransactionInput input) + { + var url = GetRequestUrl(_baseUrl, "api/blockChain/sendTransaction"); + var parameters = new Dictionary { - AssertValidHash(blockHash); - var url = GetRequestUrl(_baseUrl, - $"api/blockChain/transactionResults?blockHash={blockHash}&offset={offset}&limit={limit}"); - return await _httpService.GetResponseAsync>(url); - } + { "RawTransaction", input.RawTransaction } + }; + return await _httpService.PostResponseAsync(url, parameters); + } - /// - /// Get merkle path of a transaction. - /// - /// - /// MerklePathDto - public async Task GetMerklePathByTransactionIdAsync(string transactionId) + /// + /// Broadcast volume transactions. + /// + /// + /// TransactionIds + public async Task SendTransactionsAsync(SendTransactionsInput input) + { + var url = GetRequestUrl(_baseUrl, "api/blockChain/sendTransactions"); + var parameters = new Dictionary { - AssertValidTransactionId(transactionId); - var url = GetRequestUrl(_baseUrl, $"api/blockChain/merklePathByTransactionId?transactionId={transactionId}"); - return await _httpService.GetResponseAsync(url); - } + { "RawTransactions", input.RawTransactions } + }; + return await _httpService.PostResponseAsync(url, parameters); + } + + /// + /// Gets the result of transaction execution by the given transactionId. + /// + /// + /// TransactionResultDto + public async Task GetTransactionResultAsync(string transactionId) + { + AssertValidTransactionId(transactionId); + var url = GetRequestUrl(_baseUrl, $"api/blockChain/transactionResult?transactionId={transactionId}"); + return await _httpService.GetResponseAsync(url); + } + + /// + /// Get results of multiple transactions by specified blockHash and the offset. + /// + /// + /// + /// + /// TransactionResultDtos + public async Task?> GetTransactionResultsAsync(string blockHash, int offset = 0, + int limit = 10) + { + AssertValidHash(blockHash); + var url = GetRequestUrl(_baseUrl, + $"api/blockChain/transactionResults?blockHash={blockHash}&offset={offset}&limit={limit}"); + return await _httpService.GetResponseAsync>(url); + } + + /// + /// Get merkle path of a transaction. + /// + /// + /// MerklePathDto + public async Task GetMerklePathByTransactionIdAsync(string transactionId) + { + AssertValidTransactionId(transactionId); + var url = GetRequestUrl(_baseUrl, $"api/blockChain/merklePathByTransactionId?transactionId={transactionId}"); + return await _httpService.GetResponseAsync(url); + } } \ No newline at end of file diff --git a/src/AElf.Client/AElfClient.cs b/src/AElf.Client/AElfClient.cs index a45bb29..f5cd32d 100644 --- a/src/AElf.Client/AElfClient.cs +++ b/src/AElf.Client/AElfClient.cs @@ -1,18 +1,19 @@ -using System.Text.Json; -using AElf.Client.Service; +using AElf.Client.Services; namespace AElf.Client; public partial class AElfClient : IDisposable { private readonly IHttpService _httpService; + // aelf node endpoint. private readonly string _baseUrl; - private readonly string _userName; - private readonly string _password; + private string? _userName; + private string? _password; - public AElfClient(string baseUrl, int timeOut = 60, string userName = null, string password = null) + public AElfClient(string baseUrl, int timeOut = 60, string? userName = null, string? password = null, + bool useCamelCase = false) { - _httpService = new HttpService(timeOut); + _httpService = new HttpService(timeOut, useCamelCase); _baseUrl = baseUrl; _userName = userName; _password = password; diff --git a/src/AElf.Client/AElfClientBuilder.cs b/src/AElf.Client/AElfClientBuilder.cs index c2a04fe..91444fd 100644 --- a/src/AElf.Client/AElfClientBuilder.cs +++ b/src/AElf.Client/AElfClientBuilder.cs @@ -5,8 +5,10 @@ public sealed class AElfClientBuilder private string NodeEndpoint { get; set; } private int Timeout { get; set; } - private string UserName { get; set; } - private string Password { get; set; } + private string? UserName { get; set; } + private string? Password { get; set; } + + private bool IsUseCamelCase { get; set; } public AElfClientBuilder() { @@ -27,14 +29,14 @@ public AElfClientBuilder UsePublicEndpoint(EndpointType endpointType) case EndpointType.MainNetMainChain: NodeEndpoint = AElfClientConstants.MainNetMainChain; break; - case EndpointType.MainNetSidechain: - NodeEndpoint = AElfClientConstants.MainNetSidechain; + case EndpointType.MainNetSideChain1: + NodeEndpoint = AElfClientConstants.MainNetSideChain1; break; case EndpointType.TestNetMainChain: NodeEndpoint = AElfClientConstants.TestNetMainChain; break; - case EndpointType.TestNetSidechain: - NodeEndpoint = AElfClientConstants.TestNetSidechain; + case EndpointType.TestNetSideChain2: + NodeEndpoint = AElfClientConstants.TestNetSideChain2; break; case EndpointType.Local: default: @@ -51,15 +53,21 @@ public AElfClientBuilder SetHttpTimeout(int timeout) return this; } - public AElfClientBuilder ManagePeerInfo(string userName, string password) + public AElfClientBuilder ManagePeerInfo(string? userName, string? password) { UserName = userName; Password = password; return this; } + public AElfClientBuilder UseCamelCase(bool isUseCamelCase) + { + IsUseCamelCase = isUseCamelCase; + return this; + } + public AElfClient Build() { - return new AElfClient(NodeEndpoint, Timeout, UserName, Password); + return new AElfClient(NodeEndpoint, Timeout, UserName, Password, IsUseCamelCase); } } \ No newline at end of file diff --git a/src/AElf.Client/AElfClientConstants.cs b/src/AElf.Client/AElfClientConstants.cs index 6c1efc9..b55f7e8 100644 --- a/src/AElf.Client/AElfClientConstants.cs +++ b/src/AElf.Client/AElfClientConstants.cs @@ -3,11 +3,13 @@ namespace AElf.Client; public class AElfClientConstants { public const string DefaultPrivateKey = "09da44778f8db2e602fb484334f37df19e221c84c4582ce5b7770ccfbc3ddbef"; - public const string LocalEndpoint = "http://127.0.0.1:1235"; + public const string LocalEndpoint = "http://127.0.0.1:1726"; public const string MainNetMainChain = "https://aelf-public-node.aelf.io"; - public const string MainNetSidechain = "https://tdvv-public-node.aelf.io"; + public const string MainNetSideChain1 = "https://tdvv-public-node.aelf.io"; public const string TestNetMainChain = "https://aelf-test-node.aelf.io"; - public const string TestNetSidechain = "https://tdvv-test-node.aelf.io"; + public const string TestNetSideChain1 = "https://tdvv-test-node.aelf.io"; + public const string TestNetSideChain2 = "https://tdvw-test-node.aelf.io"; public const int MainChainId = 9992731; - public const int SidechainId = 9992731; + public const int SideChainId1 = 1866392; + public const int SideChainId2 = 1931928; } \ No newline at end of file diff --git a/src/AElf.Client/Dto/BlockDto.cs b/src/AElf.Client/Dto/BlockDto.cs index 2c25367..a155653 100644 --- a/src/AElf.Client/Dto/BlockDto.cs +++ b/src/AElf.Client/Dto/BlockDto.cs @@ -7,4 +7,6 @@ public class BlockDto public BlockHeaderDto Header { get; set; } public BlockBodyDto Body { get; set; } + + public int BlockSize { get; set; } } \ No newline at end of file diff --git a/src/AElf.Client/Dto/ChainStatusDto.cs b/src/AElf.Client/Dto/ChainStatusDto.cs index 2cb6580..45feb0d 100644 --- a/src/AElf.Client/Dto/ChainStatusDto.cs +++ b/src/AElf.Client/Dto/ChainStatusDto.cs @@ -1,26 +1,28 @@ +using System.Text.Json.Serialization; + namespace AElf.Client.Dto; public class ChainStatusDto { public string ChainId { get; set; } - - public Dictionary Branches { get; set; } - - public Dictionary NotLinkedBlocks { get; set; } - + + public Dictionary Branches { get; set; } + + public Dictionary NotLinkedBlocks { get; set; } + public long LongestChainHeight { get; set; } - + public string LongestChainHash { get; set; } public string GenesisBlockHash { get; set; } - + public string GenesisContractAddress { get; set; } - + public string LastIrreversibleBlockHash { get; set; } - + public long LastIrreversibleBlockHeight { get; set; } - + public string BestChainHash { get; set; } - + public long BestChainHeight { get; set; } } \ No newline at end of file diff --git a/src/AElf.Client/Dto/MerklePathDto.cs b/src/AElf.Client/Dto/MerklePathDto.cs index ee0f9d1..dbd8994 100644 --- a/src/AElf.Client/Dto/MerklePathDto.cs +++ b/src/AElf.Client/Dto/MerklePathDto.cs @@ -2,7 +2,7 @@ namespace AElf.Client.Dto; public class MerklePathDto { - public List MerklePathNodes; + public List MerklePathNodes { get; set; } } public class MerklePathNodeDto diff --git a/src/AElf.Client/Dto/PeerDto.cs b/src/AElf.Client/Dto/PeerDto.cs index 411c2df..d7ba842 100644 --- a/src/AElf.Client/Dto/PeerDto.cs +++ b/src/AElf.Client/Dto/PeerDto.cs @@ -1,3 +1,5 @@ +using Google.Protobuf.WellKnownTypes; + namespace AElf.Client.Dto; public class PeerDto @@ -18,5 +20,5 @@ public class RequestMetric public long RoundTripTime { get; set; } public string MethodName { get; set; } public string Info { get; set; } - public string RequestTime { get; set; } + public Timestamp RequestTime { get; set; } } \ No newline at end of file diff --git a/src/AElf.Client/Dto/TransactionResultDto.cs b/src/AElf.Client/Dto/TransactionResultDto.cs index 30ff3fa..815aa47 100644 --- a/src/AElf.Client/Dto/TransactionResultDto.cs +++ b/src/AElf.Client/Dto/TransactionResultDto.cs @@ -3,20 +3,22 @@ namespace AElf.Client.Dto; public class TransactionResultDto { public string TransactionId { get; set; } - + public string Status { get; set; } - + public LogEventDto[] Logs { get; set; } - + public string Bloom { get; set; } - + public long BlockNumber { get; set; } - + public string BlockHash { get; set; } - + public TransactionDto Transaction { get; set; } - + public string ReturnValue { get; set; } public string Error { get; set; } + + public int TransactionSize { get; set; } } \ No newline at end of file diff --git a/src/AElf.Client/EndpointType.cs b/src/AElf.Client/EndpointType.cs index 0b132fa..43011e0 100644 --- a/src/AElf.Client/EndpointType.cs +++ b/src/AElf.Client/EndpointType.cs @@ -3,8 +3,8 @@ namespace AElf.Client; public enum EndpointType { MainNetMainChain, - MainNetSidechain, + MainNetSideChain1, TestNetMainChain, - TestNetSidechain, + TestNetSideChain2, Local } \ No newline at end of file diff --git a/src/AElf.Client/Services/HttpService.cs b/src/AElf.Client/Services/HttpService.cs index 74bbdde..388d59c 100644 --- a/src/AElf.Client/Services/HttpService.cs +++ b/src/AElf.Client/Services/HttpService.cs @@ -1,13 +1,9 @@ -using System; -using System.Collections.Generic; using System.Net; -using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Text.Json; -using System.Threading.Tasks; -namespace AElf.Client.Service; +namespace AElf.Client.Services; public interface IHttpService { @@ -20,16 +16,18 @@ public interface IHttpService Task DeleteResponseAsObjectAsync(string url, string? version = null, HttpStatusCode expectedStatusCode = HttpStatusCode.OK, - AuthenticationHeaderValue authenticationHeaderValue = null); + AuthenticationHeaderValue? authenticationHeaderValue = null); } public class HttpService : IHttpService { - private HttpClient Client { get; set; } + private readonly bool _useCamelCase; + private HttpClient? Client { get; set; } private int TimeoutSeconds { get; } - public HttpService(int timeoutSeconds) + public HttpService(int timeoutSeconds, bool useCamelCase = false) { + _useCamelCase = useCamelCase; TimeoutSeconds = timeoutSeconds; } @@ -46,7 +44,13 @@ public HttpService(int timeoutSeconds) { var response = await GetResponseAsync(url, version, expectedStatusCode); var stream = await response.Content.ReadAsStreamAsync(); - return await JsonSerializer.DeserializeAsync(stream); + var jsonSerializerOptions = _useCamelCase + ? new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + } + : new JsonSerializerOptions(); + return await JsonSerializer.DeserializeAsync(stream, jsonSerializerOptions); } /// @@ -67,7 +71,13 @@ public HttpService(int timeoutSeconds) var response = await PostResponseAsync(url, parameters, version, true, expectedStatusCode, authenticationHeaderValue); var stream = await response.Content.ReadAsStreamAsync(); - return await JsonSerializer.DeserializeAsync(stream); + var jsonSerializerOptions = _useCamelCase + ? new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + } + : new JsonSerializerOptions(); + return await JsonSerializer.DeserializeAsync(stream, jsonSerializerOptions); } /// @@ -76,16 +86,23 @@ public HttpService(int timeoutSeconds) /// /// /// + /// /// /// /// public async Task DeleteResponseAsObjectAsync(string url, string? version = null, HttpStatusCode expectedStatusCode = HttpStatusCode.OK, - AuthenticationHeaderValue authenticationHeaderValue = null) + AuthenticationHeaderValue? authenticationHeaderValue = null) { var response = await DeleteResponseAsync(url, version, expectedStatusCode, authenticationHeaderValue); var stream = await response.Content.ReadAsStreamAsync(); - return await JsonSerializer.DeserializeAsync(stream); + var jsonSerializerOptions = _useCamelCase + ? new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + } + : new JsonSerializerOptions(); + return await JsonSerializer.DeserializeAsync(stream, jsonSerializerOptions); } #region GetResponse @@ -162,7 +179,7 @@ private async Task PostResponseAsync(string url, private async Task DeleteResponseAsStringAsync(string url, string? version = null, HttpStatusCode expectedStatusCode = HttpStatusCode.OK, - AuthenticationHeaderValue authenticationHeaderValue = null) + AuthenticationHeaderValue? authenticationHeaderValue = null) { var response = await DeleteResponseAsync(url, version, expectedStatusCode, authenticationHeaderValue); return await response.Content.ReadAsStringAsync(); @@ -170,7 +187,7 @@ private async Task DeleteResponseAsStringAsync(string url, string? versi private async Task DeleteResponseAsync(string url, string? version = null, HttpStatusCode expectedStatusCode = HttpStatusCode.OK, - AuthenticationHeaderValue authenticationHeaderValue = null) + AuthenticationHeaderValue? authenticationHeaderValue = null) { version = !string.IsNullOrWhiteSpace(version) ? $";v={version}" : string.Empty; var client = GetHttpClient(version); diff --git a/src/AElf.Client/Services/IChainAppService.cs b/src/AElf.Client/Services/IChainAppService.cs index 48d9b61..1685b1d 100644 --- a/src/AElf.Client/Services/IChainAppService.cs +++ b/src/AElf.Client/Services/IChainAppService.cs @@ -1,5 +1,4 @@ using AElf.Client.Dto; -using AElf.Kernel; namespace AElf.Client; diff --git a/src/AElf.Client/TransactionBuilder.cs b/src/AElf.Client/TransactionBuilder.cs index beb357f..2b352b6 100644 --- a/src/AElf.Client/TransactionBuilder.cs +++ b/src/AElf.Client/TransactionBuilder.cs @@ -1,5 +1,6 @@ using AElf.Cryptography; using Google.Protobuf; +using Volo.Abp.Threading; namespace AElf.Client; @@ -8,9 +9,9 @@ public class TransactionBuilder private readonly AElfClient _aelfClient; private byte[] PrivateKey { get; set; } - private string ContractAddress { get; set; } - private string MethodName { get; set; } - private IMessage Parameter { get; set; } + private string? ContractAddress { get; set; } + private string? MethodName { get; set; } + private IMessage? Parameter { get; set; } public TransactionBuilder(AElfClient aelfClient) { @@ -26,8 +27,8 @@ public TransactionBuilder UsePrivateKey(byte[] privateKey) public TransactionBuilder UseSystemContract(string systemContractName) { - ContractAddress = _aelfClient.GetContractAddressByNameAsync(HashHelper.ComputeFrom(systemContractName)).Result - .ToBase58(); + ContractAddress = AsyncHelper.RunSync(async () => + (await _aelfClient.GetContractAddressByNameAsync(HashHelper.ComputeFrom(systemContractName))).ToBase58()); return this; } diff --git a/test/AElf.Client.Test/AElf.Client.Test.csproj b/test/AElf.Client.Test/AElf.Client.Test.csproj index f6a8729..647ea1f 100644 --- a/test/AElf.Client.Test/AElf.Client.Test.csproj +++ b/test/AElf.Client.Test/AElf.Client.Test.csproj @@ -3,18 +3,29 @@ net6.0 false + enable + + false + - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + - + diff --git a/test/AElf.Client.Test/BasicTests.cs b/test/AElf.Client.Test/BasicTests.cs new file mode 100644 index 0000000..a3e3dec --- /dev/null +++ b/test/AElf.Client.Test/BasicTests.cs @@ -0,0 +1,15 @@ +using AElf.Types; +using Shouldly; + +namespace AElf.Client.Test; + +public class BasicTests +{ + [Theory] + [InlineData("04c1b4a75fd9ba37e0a84b9916e517e9c591c5b9efacabf1feb1a3d34f38920a250454dc9dce0f811956d210804c904959be96a75a9d9b1410aa25f7d9e1b6f69c")] + public void PubkeyToAddressTest(string pubkey) + { + var address = Address.FromPublicKey(ByteArrayHelper.HexStringToByteArray(pubkey)); + address.ShouldNotBeNull(); + } +} \ No newline at end of file diff --git a/test/AElf.Client.Test/ClientTest.cs b/test/AElf.Client.Test/ClientTest.cs index 4127c7f..888d971 100644 --- a/test/AElf.Client.Test/ClientTest.cs +++ b/test/AElf.Client.Test/ClientTest.cs @@ -8,7 +8,7 @@ using AElf.Types; using AElf.Client.Dto; using AElf.Client.Extensions; -using AElf.Client.Service; +using AElf.Client.Services; using AElf.Contracts.MultiToken; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; @@ -25,7 +25,7 @@ namespace AElf.Client.Test; public class ClientTest { - private const string BaseUrl = "http://192.168.196.116:8000"; + private const string BaseUrl = "https://tdvw-test-node.aelf.io"; private string _genesisAddress; private string GenesisAddress => GetGenesisContractAddress(); @@ -84,7 +84,7 @@ public async Task GetBlock_Test() public async Task GetBlockByHeight_Failed_Test() { const int timeOut = 60; - var httpService = new HttpService(timeOut); + var httpService = new HttpService(timeOut, false); const int heightNotExist = int.MaxValue; var errorResponse = await httpService.GetResponseAsync( $"{BaseUrl}/api/blockChain/blockByHeight?blockHeight={heightNotExist}&includeTransactions=false", @@ -160,6 +160,15 @@ public async Task GetPeers_Test() var peersInfo = JsonConvert.SerializeObject(peers, Formatting.Indented); _testOutputHelper.WriteLine(peersInfo); } + + [Fact] + public async Task GetPeersWithMetrics_Test() + { + var peers = await Client.GetPeersAsync(true); + Assert.True(peers != null); + var peersInfo = JsonConvert.SerializeObject(peers, Formatting.Indented); + _testOutputHelper.WriteLine(peersInfo); + } [Fact] public async Task GetNetworkInfo_Test() diff --git a/test/AElf.Client.Test/Usings.cs b/test/AElf.Client.Test/Usings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/test/AElf.Client.Test/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/test/AElf.Client.TestBase/AElf.Client.TestBase.csproj b/test/AElf.Client.TestBase/AElf.Client.TestBase.csproj new file mode 100644 index 0000000..866a363 --- /dev/null +++ b/test/AElf.Client.TestBase/AElf.Client.TestBase.csproj @@ -0,0 +1,44 @@ + + + + net6.0 + enable + enable + false + + + + + + PreserveNewest + true + PreserveNewest + + + + PreserveNewest + Always + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + diff --git a/test/AElf.Client.TestBase/AElfClientAbpTestBase.cs b/test/AElf.Client.TestBase/AElfClientAbpTestBase.cs new file mode 100644 index 0000000..6640b9d --- /dev/null +++ b/test/AElf.Client.TestBase/AElfClientAbpTestBase.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; +using Volo.Abp.Modularity; +using Volo.Abp.Testing; + +namespace AElf.Client.TestBase; + +public abstract class AElfClientAbpTestBase : AbpIntegratedTest + where TStartupModule : IAbpModule +{ + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + + protected override void BeforeAddApplication(IServiceCollection services) + { + var builder = new ConfigurationBuilder(); + builder.AddJsonFile("appsettings.json", false); + builder.AddJsonFile("appsettings.secrets.json", true); + services.ReplaceConfiguration(builder.Build()); + } +} \ No newline at end of file diff --git a/test/AElf.Client.TestBase/AElfClientAbpTestBaseModule.cs b/test/AElf.Client.TestBase/AElfClientAbpTestBaseModule.cs new file mode 100644 index 0000000..9f9adb7 --- /dev/null +++ b/test/AElf.Client.TestBase/AElfClientAbpTestBaseModule.cs @@ -0,0 +1,14 @@ +using Volo.Abp; +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace AElf.Client.TestBase; + +[DependsOn( + typeof(AbpAutofacModule), + typeof(AbpTestBaseModule) +)] +public class AElfClientAbpTestBaseModule : AbpModule +{ + +} \ No newline at end of file diff --git a/test/AElf.Client.TestBase/IgnoreOnCIFact.cs b/test/AElf.Client.TestBase/IgnoreOnCIFact.cs new file mode 100644 index 0000000..08a9df4 --- /dev/null +++ b/test/AElf.Client.TestBase/IgnoreOnCIFact.cs @@ -0,0 +1,14 @@ +namespace AElf.Client.TestBase; + +public sealed class IgnoreOnCIFact : FactAttribute +{ + public IgnoreOnCIFact() + { + if (IsOnCI()) Skip = "Ignore on CI running to save execution time."; + } + + private static bool IsOnCI() + { + return Environment.GetEnvironmentVariable("CI_TEST") != null; + } +} \ No newline at end of file diff --git a/test/AElf.Client.TestBase/IgnoreOnCITheory.cs b/test/AElf.Client.TestBase/IgnoreOnCITheory.cs new file mode 100644 index 0000000..4378f6f --- /dev/null +++ b/test/AElf.Client.TestBase/IgnoreOnCITheory.cs @@ -0,0 +1,14 @@ +namespace AElf.Client.TestBase; + +public sealed class IgnoreOnCITheory : TheoryAttribute +{ + public IgnoreOnCITheory() + { + if (IsOnCI()) Skip = "Ignore on CI running to save execution time."; + } + + private static bool IsOnCI() + { + return Environment.GetEnvironmentVariable("CI_TEST") != null; + } +} \ No newline at end of file diff --git a/test/AElf.Client.TestBase/Usings.cs b/test/AElf.Client.TestBase/Usings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/test/AElf.Client.TestBase/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/test/AElf.Client.TestBase/appsettings.json b/test/AElf.Client.TestBase/appsettings.json new file mode 100644 index 0000000..6bd9fdb --- /dev/null +++ b/test/AElf.Client.TestBase/appsettings.json @@ -0,0 +1,60 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + }, + "AElfClient": { + "ClientConfigList": [ + { + "Alias": "Example", + "Endpoint": "http://192.168.0.31:6800", + "UserName": "", + "Password": "", + "Timeout": 100 + } + ] + }, + "AElfAccount": { + "KeyDirectory": "", + "AccountConfigList": [ + { + "Alias": "Ean", + "PrivateKey": "8cc296e44d9e8881942e75a21ebc116ed3f29e39e0eaace1c92dc21e86d215c3", + "Address": "2AiXjNszZwUMdonm2RYb3GsB3aLUU3hkD1fxoazMwqPAamerLQ", + "Password": "aelftest" + }, + { + "Alias": "Test1", + "Address": "215tht8WyakoxNK4SvsR132jChydxE27RtJN8HSk1UXxuDQnmM", + "Password": "aelftest" + }, + { + "Alias": "Test2", + "PrivateKey": "5e2f12d13e4527ad1128e07db00f1614ec6b8b51662e68d4fdb42125ab384195" + }, + { + "Alias": "eanz", + "Address": "2HeW7S9HZrbRJZeivMppUuUY3djhWdfVnP5zrDsz8wqq6hKMfT", + "Password": "zhaoyiqi" + } + ] + }, + "AElfContract": { + "ContractDirectory": "", + "ContractAddressList": { + "BridgeContract": "2RHf2fxsnEaM3wb6N1yGqPupNZbcCY98LgWbGSFWmWzgEs5Sjo", + "GenesisContract": "2UKQnHcQvhBT6X6ULtfnuh3b9PVRvVMEroHHkcK4YfcoH1Z1x2", + "NFTContract": "2ZpYFeE4yWjrcKLBoj1iwbfYnbo9hK7exvfGTdqcq77QSxpzNH" + } + }, + "AElfClientConfig": { + "ClientAlias": "MainNetMainChain", + "MainChainClientAlias": "TestNetMainChain", + "SideChainClientAlias": "TestNetSideChain2", + "AccountAlias": "eanz", + "CamelCase": false + } +} \ No newline at end of file diff --git a/test/AElf.Client.TestBase/appsettings.secrets.json b/test/AElf.Client.TestBase/appsettings.secrets.json new file mode 100644 index 0000000..8e6b25d --- /dev/null +++ b/test/AElf.Client.TestBase/appsettings.secrets.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + }, + "AElfMinerAccount": { + "DefaultPassword": "" + } +} \ No newline at end of file