Skip to content

Commit

Permalink
Refine/Refactor Edge Provisioning (#23)
Browse files Browse the repository at this point in the history
* Introduce alternative EdgeProvisioning flow (#15)

* Introduce alternative EdgeProvisioning flow

Signed-off-by: Johannes Tuerk <[email protected]>

* refactor privisoing process

Signed-off-by: Johannes Tuerk <[email protected]>

---------

Signed-off-by: Johannes Tuerk <[email protected]>

* expose GetMediatorConnection in Interface

Signed-off-by: Johannes Tuerk <[email protected]>

* revert EdgeProvisoningService removal

Signed-off-by: Johannes Tuerk <[email protected]>

* cleaning

Signed-off-by: Johannes Tuerk <[email protected]>

* cleaning

Signed-off-by: Johannes Tuerk <[email protected]>

* cleaning

Signed-off-by: Johannes Tuerk <[email protected]>

* Refactor

Signed-off-by: Johannes Tuerk <[email protected]>

* minor cleaning

Signed-off-by: Johannes Tuerk <[email protected]>

---------

Signed-off-by: Johannes Tuerk <[email protected]>
  • Loading branch information
JoTiTu authored Dec 22, 2023
1 parent c40f487 commit 32666fa
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 147 deletions.
18 changes: 9 additions & 9 deletions src/Hyperledger.Aries.Routing.Edge/EdgeClientService.Backup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public async Task<string> CreateBackupAsync(IAgentContext context, string seed)

File.Delete(path);

await messageService
await _messageService
.SendReceiveAsync<StoreBackupResponseAgentMessage>(context, backupMessage, connection)
.ConfigureAwait(false);
return backupVerkey;
Expand Down Expand Up @@ -115,7 +115,7 @@ public async Task<List<Attachment>> RetrieveBackupAsync(IAgentContext context, s
if (connection == null)
throw new AriesFrameworkException(ErrorCode.RecordNotFound, "Couldn't locate a connection to mediator agent");

var response = await messageService.SendReceiveAsync<RetrieveBackupResponseAgentMessage>(context, retrieveBackupResponseMessage, connection).ConfigureAwait(false);
var response = await _messageService.SendReceiveAsync<RetrieveBackupResponseAgentMessage>(context, retrieveBackupResponseMessage, connection).ConfigureAwait(false);
return response.Payload;
}

Expand All @@ -130,20 +130,20 @@ public async Task<AgentOptions> RestoreFromBackupAsync(IAgentContext sourceConte

await Task.Run(() => File.WriteAllBytes(tempWalletPath, walletToRestoreInBytes));

var oldAgentOptionsString = JsonConvert.SerializeObject(agentoptions);
var oldAgentOptionsString = JsonConvert.SerializeObject(_agentOptions);

var json = new { path = tempWalletPath, key = seed }.ToJson();

agentoptions.WalletConfiguration.Id = Guid.NewGuid().ToString();
agentoptions.WalletCredentials.Key = Utils.GenerateRandomAsync(32);
_agentOptions.WalletConfiguration.Id = Guid.NewGuid().ToString();
_agentOptions.WalletCredentials.Key = Utils.GenerateRandomAsync(32);

await Wallet.ImportAsync(agentoptions.WalletConfiguration.ToJson(), agentoptions.WalletCredentials.ToJson(), json);
await Wallet.ImportAsync(_agentOptions.WalletConfiguration.ToJson(), _agentOptions.WalletCredentials.ToJson(), json);

// Try delete the old wallet
try
{
var oldAgentOptions = JsonConvert.DeserializeObject<AgentOptions>(oldAgentOptionsString);
await walletService.DeleteWalletAsync(oldAgentOptions.WalletConfiguration,
await _walletService.DeleteWalletAsync(oldAgentOptions.WalletConfiguration,
oldAgentOptions.WalletCredentials);
// Add 1 sec delay to allow filesystem to catch up
await Task.Delay(TimeSpan.FromSeconds(1));
Expand All @@ -155,7 +155,7 @@ await walletService.DeleteWalletAsync(oldAgentOptions.WalletConfiguration,

File.Delete(tempWalletPath);

return agentoptions;
return _agentOptions;
}

/// <inheritdoc />
Expand All @@ -172,7 +172,7 @@ public async Task<List<long>> ListBackupsAsync(IAgentContext context)
if (connection == null)
throw new AriesFrameworkException(ErrorCode.RecordNotFound, "Couldn't locate a connection to mediator agent");

var response = await messageService.SendReceiveAsync<ListBackupsResponseAgentMessage>(context, listBackupsMessage, connection).ConfigureAwait(false);
var response = await _messageService.SendReceiveAsync<ListBackupsResponseAgentMessage>(context, listBackupsMessage, connection).ConfigureAwait(false);
return response.BackupList.ToList();
}

Expand Down
55 changes: 25 additions & 30 deletions src/Hyperledger.Aries.Routing.Edge/EdgeClientService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,28 @@ public partial class EdgeClientService : IEdgeClientService
private const string MediatorInboxIdTagName = "MediatorInboxId";
private const string MediatorInboxKeyTagName = "MediatorInboxKey";
private const string MediatorConnectionIdTagName = "MediatorConnectionId";
private readonly IHttpClientFactory httpClientFactory;
private readonly IProvisioningService provisioningService;
private readonly IWalletRecordService recordService;
private readonly IWalletRecordService walletRecordService;
private readonly IWalletService walletService;
private readonly IMessageService messageService;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IProvisioningService _provisioningService;
private readonly IWalletRecordService _recordService;
private readonly IWalletService _walletService;
private readonly IMessageService _messageService;

private readonly AgentOptions agentoptions;
private readonly AgentOptions _agentOptions;

public EdgeClientService(
IHttpClientFactory httpClientFactory,
IProvisioningService provisioningService,
IWalletRecordService recordService,
IMessageService messageService,
IWalletRecordService walletRecordService,
IWalletService walletService,
IOptions<AgentOptions> agentOptions)
{
this.httpClientFactory = httpClientFactory;
this.provisioningService = provisioningService;
this.recordService = recordService;
this.walletRecordService = walletRecordService;
this.walletService = walletService;
this.messageService = messageService;
this.agentoptions = agentOptions.Value;
_httpClientFactory = httpClientFactory;
_provisioningService = provisioningService;
_recordService = recordService;
_walletService = walletService;
_messageService = messageService;
_agentOptions = agentOptions.Value;
}

public virtual async Task AddRouteAsync(IAgentContext agentContext, string routeDestination)
Expand All @@ -50,35 +47,33 @@ public virtual async Task AddRouteAsync(IAgentContext agentContext, string route
if (connection != null)
{
var createInboxMessage = new AddRouteMessage { RouteDestination = routeDestination };
await messageService.SendAsync(agentContext, createInboxMessage, connection);
await _messageService.SendAsync(agentContext, createInboxMessage, connection);
}
}

public virtual async Task CreateInboxAsync(IAgentContext agentContext, Dictionary<string, string> metadata = null)
{
var provisioning = await provisioningService.GetProvisioningAsync(agentContext.Wallet);
var provisioning = await _provisioningService.GetProvisioningAsync(agentContext.Wallet);
if (provisioning.GetTag(MediatorInboxIdTagName) != null)
{
return;
}

var connection = await GetMediatorConnectionAsync(agentContext);

var createInboxMessage = new CreateInboxMessage { Metadata = metadata };
var response = await messageService.SendReceiveAsync<CreateInboxResponseMessage>(agentContext, createInboxMessage, connection);
var response = await _messageService.SendReceiveAsync<CreateInboxResponseMessage>(agentContext, createInboxMessage, connection);

provisioning.SetTag(MediatorInboxIdTagName, response.InboxId);
provisioning.SetTag(MediatorInboxKeyTagName, response.InboxKey);
await recordService.UpdateAsync(agentContext.Wallet, provisioning);
await _recordService.UpdateAsync(agentContext.Wallet, provisioning);
}

internal async Task<ConnectionRecord> GetMediatorConnectionAsync(IAgentContext agentContext)
public async Task<ConnectionRecord> GetMediatorConnectionAsync(IAgentContext agentContext)
{
var provisioning = await provisioningService.GetProvisioningAsync(agentContext.Wallet);
var provisioning = await _provisioningService.GetProvisioningAsync(agentContext.Wallet);
if (provisioning.GetTag(MediatorConnectionIdTagName) == null)
{
return null;
}
var connection = await recordService.GetAsync<ConnectionRecord>(agentContext.Wallet, provisioning.GetTag(MediatorConnectionIdTagName));

var connection = await _recordService.GetAsync<ConnectionRecord>(agentContext.Wallet, provisioning.GetTag(MediatorConnectionIdTagName));
if (connection == null) throw new AriesFrameworkException(ErrorCode.RecordNotFound, "Couldn't locate a connection to mediator agent");
if (connection.State != ConnectionState.Connected) throw new AriesFrameworkException(ErrorCode.RecordInInvalidState, $"You must be connected to the mediator agent. Current state is {connection.State}");

Expand All @@ -87,7 +82,7 @@ internal async Task<ConnectionRecord> GetMediatorConnectionAsync(IAgentContext a

public virtual async Task<AgentPublicConfiguration> DiscoverConfigurationAsync(string agentEndpoint)
{
var httpClient = httpClientFactory.CreateClient();
var httpClient = _httpClientFactory.CreateClient();
var response = await httpClient.GetAsync($"{agentEndpoint}/.well-known/agent-configuration").ConfigureAwait(false);
var responseJson = await response.Content.ReadAsStringAsync();

Expand All @@ -103,7 +98,7 @@ public virtual async Task<AgentPublicConfiguration> DiscoverConfigurationAsync(s
}

var createInboxMessage = new GetInboxItemsMessage();
var response = await messageService.SendReceiveAsync<GetInboxItemsResponseMessage>(agentContext, createInboxMessage, connection);
var response = await _messageService.SendReceiveAsync<GetInboxItemsResponseMessage>(agentContext, createInboxMessage, connection);

var processedItems = new List<string>();
var unprocessedItem = new List<InboxItemMessage>();
Expand All @@ -126,7 +121,7 @@ public virtual async Task<AgentPublicConfiguration> DiscoverConfigurationAsync(s

if (processedItems.Any())
{
await messageService.SendAsync(agentContext, new DeleteInboxItemsMessage { InboxItemIds = processedItems }, connection);
await _messageService.SendAsync(agentContext, new DeleteInboxItemsMessage { InboxItemIds = processedItems }, connection);
}

return (processedItems.Count, unprocessedItem);
Expand All @@ -147,7 +142,7 @@ private async Task SendAgentMessageAsync(IAgentContext agentContext, AgentMessag
var connection = await GetMediatorConnectionAsync(agentContext);
if (connection != null)
{
await messageService.SendAsync(agentContext, message, connection);
await _messageService.SendAsync(agentContext, message, connection);
}
}
}
Expand Down
202 changes: 105 additions & 97 deletions src/Hyperledger.Aries.Routing.Edge/EdgeProvisioningService.cs
Original file line number Diff line number Diff line change
@@ -1,97 +1,105 @@
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Hyperledger.Aries.Configuration;
using Hyperledger.Aries.Features.Handshakes.Connection;
using Hyperledger.Aries.Features.Handshakes.Connection.Models;
using Hyperledger.Aries.Routing;
using Hyperledger.Aries.Storage;
using Hyperledger.Indy.WalletApi;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;

[assembly: InternalsVisibleTo("Hyperledger.Aries.Tests")]

namespace Hyperledger.Aries.Agents.Edge
{
internal class EdgeProvisioningService : IHostedService, IEdgeProvisioningService
{
internal const string MediatorConnectionIdTagName = "MediatorConnectionId";
private const string MediatorInboxIdTagName = "MediatorInboxId";

private readonly IProvisioningService provisioningService;
private readonly IConnectionService connectionService;
private readonly IMessageService messageService;
private readonly IEdgeClientService edgeClientService;
private readonly IWalletRecordService recordService;
private readonly IAgentProvider agentProvider;
private readonly AgentOptions options;

public EdgeProvisioningService(
IProvisioningService provisioningService,
IConnectionService connectionService,
IMessageService messageService,
IEdgeClientService edgeClientService,
IWalletRecordService recordService,
IAgentProvider agentProvider,
IOptions<AgentOptions> options)
{
this.provisioningService = provisioningService;
this.connectionService = connectionService;
this.messageService = messageService;
this.edgeClientService = edgeClientService;
this.recordService = recordService;
this.agentProvider = agentProvider;
this.options = options.Value;
}

public async Task ProvisionAsync(AgentOptions options, CancellationToken cancellationToken = default)
{
var discovery = await edgeClientService.DiscoverConfigurationAsync(options.EndpointUri);

try
{
options.AgentKey = discovery.RoutingKey;
options.EndpointUri = discovery.ServiceEndpoint;

await provisioningService.ProvisionAgentAsync(options);
}
catch(WalletStorageException)
{
// OK
}
catch (WalletExistsException)
{
// OK
}
var agentContext = await agentProvider.GetContextAsync();
var provisioning = await provisioningService.GetProvisioningAsync(agentContext.Wallet);

// Check if connection has been established with mediator agent
if (provisioning.GetTag(MediatorConnectionIdTagName) == null)
{
var (request, record) = await connectionService.CreateRequestAsync(agentContext, discovery.Invitation);
var response = await messageService.SendReceiveAsync<ConnectionResponseMessage>(agentContext, request, record);

await connectionService.ProcessResponseAsync(agentContext, response, record);

// Remove the routing key explicitly as it won't ever be needed.
// Messages will always be sent directly with return routing enabled
record = await connectionService.GetAsync(agentContext, record.Id);
record.Endpoint = new AgentEndpoint(record.Endpoint.Uri, null, null);
await recordService.UpdateAsync(agentContext.Wallet, record);

provisioning.SetTag(MediatorConnectionIdTagName, record.Id);
await recordService.UpdateAsync(agentContext.Wallet, provisioning);
}

await edgeClientService.CreateInboxAsync(agentContext, options.MetaData);
}

public Task ProvisionAsync(CancellationToken cancellationToken = default) => ProvisionAsync(options, cancellationToken);

public Task StartAsync(CancellationToken cancellationToken) => ProvisionAsync(cancellationToken);

public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
}
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Hyperledger.Aries.Configuration;
using Hyperledger.Aries.Features.Handshakes.Connection;
using Hyperledger.Aries.Features.Handshakes.Connection.Models;
using Hyperledger.Aries.Routing;
using Hyperledger.Aries.Storage;
using Hyperledger.Indy.WalletApi;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;

[assembly: InternalsVisibleTo("Hyperledger.Aries.Tests")]

namespace Hyperledger.Aries.Agents.Edge
{
internal class EdgeProvisioningService : IHostedService, IEdgeProvisioningService
{
internal const string MediatorConnectionIdTagName = "MediatorConnectionId";

private readonly IProvisioningService _provisioningService;
private readonly IConnectionService _connectionService;
private readonly IMessageService _messageService;
private readonly IEdgeClientService _edgeClientService;
private readonly IWalletRecordService _recordService;
private readonly IAgentProvider _agentProvider;
private readonly AgentOptions _agentOptions;

public EdgeProvisioningService(
IProvisioningService provisioningService,
IConnectionService connectionService,
IMessageService messageService,
IEdgeClientService edgeClientService,
IWalletRecordService recordService,
IAgentProvider agentProvider,
IOptions<AgentOptions> agentOptions)
{
_provisioningService = provisioningService;
_connectionService = connectionService;
_messageService = messageService;
_agentProvider = agentProvider;
_agentOptions = agentOptions.Value;
_edgeClientService = edgeClientService;
_recordService = recordService;
}

public async Task EnsureMediatorConnectionAndInboxAsync(AgentOptions agentOptions, CancellationToken cancellationToken = default)
{
var agentContext = await _agentProvider.GetContextAsync();
if (_edgeClientService.GetMediatorConnectionAsync(agentContext) != null)
return;

await CreateMediatorConnection(agentContext, agentOptions);

await _edgeClientService.CreateInboxAsync(agentContext, agentOptions.MetaData);
}

private async Task CreateMediatorConnection(IAgentContext agentContext, AgentOptions agentOptions)
{
var discovery = await _edgeClientService.DiscoverConfigurationAsync(agentOptions.EndpointUri);

await _provisioningService.UpdateEndpointAsync(agentContext.Wallet, new AgentEndpoint
{
Uri = discovery.ServiceEndpoint,
Verkey = new[] { discovery.RoutingKey},
Did = agentOptions.AgentDid
});

var (request, record) = await _connectionService.CreateRequestAsync(agentContext, discovery.Invitation);
var response = await _messageService.SendReceiveAsync<ConnectionResponseMessage>(agentContext, request, record);

await _connectionService.ProcessResponseAsync(agentContext, response, record);

// Remove the routing key explicitly as it won't ever be needed.
// Messages will always be sent directly with return routing enabled
record = await _connectionService.GetAsync(agentContext, record.Id);
record.Endpoint = new AgentEndpoint(record.Endpoint.Uri, null, null);
await _recordService.UpdateAsync(agentContext.Wallet, record);

var provisioning = await _provisioningService.GetProvisioningAsync(agentContext.Wallet);
provisioning.SetTag(MediatorConnectionIdTagName, record.Id);
await _recordService.UpdateAsync(agentContext.Wallet, provisioning);
}

public async Task ProvisionAsync(AgentOptions agentOptions, CancellationToken cancellationToken = default)
{
try
{
await _provisioningService.ProvisionAgentAsync(agentOptions);
}
catch (WalletExistsException)
{
// OK
}
}

public Task ProvisionAsync(CancellationToken cancellationToken = default) => ProvisionAsync(_agentOptions, cancellationToken);

public Task EnsureMediatorConnectionAndInboxAsync(CancellationToken cancellationToken = default) => ProvisionAsync(_agentOptions, cancellationToken);

public Task StartAsync(CancellationToken cancellationToken) => ProvisionAsync(cancellationToken);

public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
}
Loading

0 comments on commit 32666fa

Please sign in to comment.