Example #1
0
        public async Task CreatePairedAgentsWithRouting()
        {
            var pair = await InProcAgent.CreatePairedWithRoutingAsync();

            var connections1 = await pair.Agent1.Connections.ListAsync(pair.Agent1.Context);

            var invitation1 = connections1.FirstOrDefault(x => x.State == ConnectionState.Invited);

            var connection1 = connections1.FirstOrDefault(x => x.Id != invitation1.Id);
            var connection2 = (await pair.Agent2.Connections.ListAsync(pair.Agent2.Context)).FirstOrDefault();

            var provisioning1 = await pair.Agent1.Host.Services.GetRequiredService <IProvisioningService>()
                                .GetProvisioningAsync(pair.Agent1.Context.Wallet);

            var provisioning2 = await pair.Agent2.Host.Services.GetRequiredService <IProvisioningService>()
                                .GetProvisioningAsync(pair.Agent2.Context.Wallet);

            // Connections exist
            Assert.NotNull(invitation1);
            Assert.NotNull(connection1);
            Assert.NotNull(connection2);

            // The two connections are connected in the correct state
            Assert.Equal(ConnectionState.Connected, connection1.State);
            Assert.Equal(ConnectionState.Connected, connection2.State);

            // Check mediator and edge provisioning record states
            Assert.Equal(provisioning1.GetTag(MediatorProvisioningService.EdgeInvitationTagName), invitation1.Id);
            Assert.Equal(provisioning2.GetTag(EdgeProvisioningService.MediatorConnectionIdTagName), connection2.Id);
        }
Example #2
0
        public async Task InitializeAsync()
        {
            // Agent1 - Mediator
            // Agent2 - Edge
            Pair = await InProcAgent.CreatePairedWithRoutingAsync();

            // WalletService = Pair.Agent2.Host.Services.GetRequiredService<IWalletService>();
            EdgeClient      = Pair.Agent2.Host.Services.GetRequiredService <IEdgeClientService>();
            WalletService   = Pair.Agent2.Host.Services.GetRequiredService <IWalletService>();
            AgentOptions    = Pair.Agent2.Host.Services.GetRequiredService <IOptions <AgentOptions> >().Value;
            EdgeContext     = Pair.Agent2.Context;
            MediatorContext = Pair.Agent1.Context;
        }
Example #3
0
        public async Task IssueCredentialOverConnectionlessTransport()
        {
            var agents = await InProcAgent.CreatePairedAsync(false);

            var issuerProvisioningService = agents.Agent1.Host.Services.GetService <IProvisioningService>();
            var issuerSchemaService       = agents.Agent1.Host.Services.GetService <ISchemaService>();
            var issuerCredentialService   = agents.Agent1.Host.Services.GetService <ICredentialService>();

            var issuerProvisioning = await issuerProvisioningService.GetProvisioningAsync(agents.Agent1.Context.Wallet);

            await PromoteTrustAnchor(issuerProvisioning.IssuerDid, issuerProvisioning.IssuerVerkey);

            var schemaId = await issuerSchemaService.CreateSchemaAsync(
                context : agents.Agent1.Context,
                issuerDid : issuerProvisioning.IssuerDid,
                name : $"test-schema-{Guid.NewGuid()}",
                version : "1.0",
                attributeNames : new[] { "test-attr" });

            var credentialDefinitionId = await issuerSchemaService.CreateCredentialDefinitionAsync(
                context : agents.Agent1.Context,
                schemaId : schemaId,
                issuerDid : issuerProvisioning.IssuerDid,
                tag : "default",
                supportsRevocation : false,
                maxCredentialCount : 0,
                tailsBaseUri : new Uri("https://test"));

            var(offerMessage, issuerRecord) = await issuerCredentialService.CreateOfferAsync(agents.Agent1.Context, new OfferConfiguration
            {
                CredentialDefinitionId    = credentialDefinitionId,
                CredentialAttributeValues = new [] { new CredentialPreviewAttribute("test-attr", "test-value") }
            });

            Assert.NotNull(offerMessage.FindDecorator <ServiceDecorator>(DecoratorNames.ServiceDecorator));
            Assert.Equal(CredentialState.Offered, issuerRecord.State);
            Assert.Null(issuerRecord.ConnectionId);

            var holderCredentialService = agents.Agent2.Host.Services.GetService <ICredentialService>();

            var holderRecord = await holderCredentialService.CreateCredentialAsync(agents.Agent2.Context, offerMessage);

            issuerRecord = await issuerCredentialService.GetAsync(agents.Agent1.Context, issuerRecord.Id);

            Assert.NotNull(holderRecord);
            Assert.Equal(expected: CredentialState.Issued, actual: holderRecord.State);
            Assert.Equal(expected: CredentialState.Issued, actual: issuerRecord.State);
            Assert.NotNull(holderRecord.CredentialAttributesValues);
            Assert.Null(holderRecord.ConnectionId);
        }
Example #4
0
        public async Task CreateInProcAgentsAndConnect()
        {
            var agents = await InProcAgent.CreatePairedAsync(true);

            Assert.NotNull(agents.Agent1);
            Assert.NotNull(agents.Agent2);

            Assert.Equal(ConnectionState.Connected, agents.Connection1.State);
            Assert.Equal(ConnectionState.Connected, agents.Connection2.State);

            await agents.Agent1.DisposeAsync();

            await agents.Agent2.DisposeAsync();
        }
        public async Task CreatePairedAgentsWithRoutingAndMetadata()
        {
            Dictionary <string, string> metaData = new Dictionary <string, string>()
            {
                { "tag", "value" }
            };
            var pair = await InProcAgent.CreatePairedWithRoutingAsync(metaData);

            var connections1 = await pair.Agent1.Connections.ListAsync(pair.Agent1.Context);

            var invitation1 = connections1.FirstOrDefault(x => x.State == ConnectionState.Invited);

            var connection1 = connections1.FirstOrDefault(x => x.Id != invitation1.Id);
            var connection2 = (await pair.Agent2.Connections.ListAsync(pair.Agent2.Context)).FirstOrDefault();

            var provisioning1 = await pair.Agent1.Host.Services.GetRequiredService <IProvisioningService>()
                                .GetProvisioningAsync(pair.Agent1.Context.Wallet);

            var provisioning2 = await pair.Agent2.Host.Services.GetRequiredService <IProvisioningService>()
                                .GetProvisioningAsync(pair.Agent2.Context.Wallet);

            // Connections exist
            invitation1.Should().NotBeNull();
            connection1.Should().NotBeNull();
            connection2.Should().NotBeNull();

            // The two connections are connected in the correct state
            ConnectionState.Connected.Should().Be(connection1.State);
            ConnectionState.Connected.Should().Be(connection2.State);

            // Check mediator and edge provisioning record states
            provisioning1.GetTag(MediatorProvisioningService.EdgeInvitationTagName).Should().Be(invitation1.Id);
            provisioning2.GetTag(EdgeProvisioningService.MediatorConnectionIdTagName).Should().Be(connection2.Id);

            string inboxId = connection1.GetTag("InboxId");
            IWalletRecordService recordService = pair.Agent1.Host.Services.GetRequiredService <IWalletRecordService>();
            InboxRecord          inboxRecord   = await recordService.GetAsync <InboxRecord>(pair.Agent1.Context.Wallet, inboxId);

            inboxRecord.GetTag("tag").Should().Be(metaData["tag"]);
        }
        public async Task TestCredentialIssuanceV1()
        {
            var pair = await InProcAgent.CreatePairedAsync(true);

            var credentialService1 = pair.Agent1.Provider.GetService <ICredentialService>();
            var credentialService2 = pair.Agent2.Provider.GetService <ICredentialService>();
            var proofService1      = pair.Agent1.Provider.GetService <IProofService>();
            var proofService2      = pair.Agent2.Provider.GetService <IProofService>();

            var context1        = pair.Agent1.Context;
            var context2        = pair.Agent2.Context;
            var messageService2 = pair.Agent2.Provider.GetService <IMessageService>();
            var messageService1 = pair.Agent1.Provider.GetRequiredService <IMessageService>();

            // Configure agent1 as issuer
            var issuerConfiguration = await pair.Agent1.Provider.GetRequiredService <IProvisioningService>()
                                      .GetProvisioningAsync(context1.Wallet);

            await PromoteTrustAnchor(issuerConfiguration.IssuerDid, issuerConfiguration.IssuerVerkey);

            var schemaId = await pair.Agent1.Provider.GetRequiredService <ISchemaService>()
                           .CreateSchemaAsync(
                context: context1,
                issuerDid: issuerConfiguration.IssuerDid,
                name: $"test-schema-{Guid.NewGuid()}",
                version: "1.0",
                attributeNames: new[] { "name", "age" });

            var definitionWithRevocationId = await pair.Agent1.Provider.GetRequiredService <ISchemaService>()
                                             .CreateCredentialDefinitionAsync(
                context: context1,
                new CredentialDefinitionConfiguration
            {
                SchemaId                  = schemaId,
                EnableRevocation          = true,
                RevocationRegistryBaseUri = "http://localhost",
                Tag = "revoc"
            });

            var definitionId = await pair.Agent1.Provider.GetRequiredService <ISchemaService>()
                               .CreateCredentialDefinitionAsync(
                context: context1,
                new CredentialDefinitionConfiguration
            {
                SchemaId                  = schemaId,
                EnableRevocation          = false,
                RevocationRegistryBaseUri = "http://localhost",
                Tag = "norevoc"
            });

            // Send offer for two credentials
            var(offer, record) = await credentialService1
                                 .CreateOfferAsync(context1, new OfferConfiguration
            {
                CredentialDefinitionId = definitionWithRevocationId,
                IssuerDid = issuerConfiguration.IssuerDid,
                CredentialAttributeValues = new[]
                {
                    new CredentialPreviewAttribute("name", "random"),
                    new CredentialPreviewAttribute("age", "22")
                }
            });

            await messageService1.SendAsync(context1, offer, pair.Connection1);

            var issuerCredentialWithRevocationId = record.Id;

            (offer, record) = await credentialService1
                              .CreateOfferAsync(context1, new OfferConfiguration
            {
                CredentialDefinitionId = definitionId,
                IssuerDid = issuerConfiguration.IssuerDid,
                CredentialAttributeValues = new[]
                {
                    new CredentialPreviewAttribute("name", "random"),
                    new CredentialPreviewAttribute("age", "22")
                }
            });

            await messageService1
            .SendAsync(context1, offer, pair.Connection1);

            // Find credential for Agent 2 and accept all offers

            var credentials = await credentialService2.ListAsync(context2);

            foreach (var credential in credentials.Where(x => x.State == CredentialState.Offered))
            {
                var(request, _) = await credentialService2.CreateRequestAsync(context2, credential.Id);

                await messageService2.SendAsync(context2, request, pair.Connection2);
            }

            // Issue credential
            credentials = await credentialService1.ListRequestsAsync(context1);

            foreach (var credential in credentials)
            {
                var(issue, _) = await credentialService1.CreateCredentialAsync(context1, credential.Id);

                await messageService1.SendAsync(context1, issue, pair.Connection1);
            }

            // Assert
            foreach (var credential in await credentialService1.ListAsync(context1))
            {
                Assert.Equal(CredentialState.Issued, credential.State);
            }
            foreach (var credential in await credentialService2.ListAsync(context2))
            {
                Assert.Equal(CredentialState.Issued, credential.State);
            }


            // Verification - without revocation
            var(requestPresentationMessage, proofRecordIssuer) = await proofService1
                                                                 .CreateRequestAsync(context1, new ProofRequest
            {
                Name                = "Test Verification",
                Version             = "1.0",
                Nonce               = await AnonCreds.GenerateNonceAsync(),
                RequestedAttributes = new Dictionary <string, ProofAttributeInfo>
                {
                    { "id-verification", new ProofAttributeInfo {
                          Names = new [] { "name", "age" }
                      } }
                }
            });

            var proofRecordHolder = await proofService2.ProcessRequestAsync(context2, requestPresentationMessage, pair.Connection2);

            var availableCredentials = await proofService2.ListCredentialsForProofRequestAsync(context2, proofRecordHolder.RequestJson.ToObject <ProofRequest>(), "id-verification");

            var(presentationMessage, _) = await proofService2.CreatePresentationAsync(
                context2, proofRecordHolder.Id, new RequestedCredentials
            {
                RequestedAttributes = new Dictionary <string, RequestedAttribute>
                {
                    { "id-verification", new RequestedAttribute
                      {
                          CredentialId = availableCredentials.First().CredentialInfo.Referent,
                          Revealed     = true
                      } }
                }
            });

            proofRecordIssuer = await proofService1.ProcessPresentationAsync(context1, presentationMessage);

            var valid = await proofService1.VerifyProofAsync(context1, proofRecordIssuer.Id);

            Assert.True(valid);

            // Verification - with revocation
            var now = (uint)DateTimeOffset.Now.ToUnixTimeSeconds();

            (requestPresentationMessage, proofRecordIssuer) = await proofService1
                                                              .CreateRequestAsync(context1, new ProofRequest
            {
                Name                = "Test Verification",
                Version             = "1.0",
                Nonce               = await AnonCreds.GenerateNonceAsync(),
                RequestedAttributes = new Dictionary <string, ProofAttributeInfo>
                {
                    { "id-verification", new ProofAttributeInfo {
                          Names = new [] { "name", "age" }
                      } }
                },
                NonRevoked = new RevocationInterval
                {
                    From = 0,
                    To   = now
                }
            });

            proofRecordHolder = await proofService2
                                .ProcessRequestAsync(context2, requestPresentationMessage, pair.Connection2);

            availableCredentials = await proofService2
                                   .ListCredentialsForProofRequestAsync(context2, proofRecordHolder.RequestJson.ToObject <ProofRequest>(), "id-verification");

            (presentationMessage, _) = await proofService2
                                       .CreatePresentationAsync(context2, proofRecordHolder.Id, new RequestedCredentials
            {
                RequestedAttributes = new Dictionary <string, RequestedAttribute>
                {
                    { "id-verification", new RequestedAttribute
                      {
                          CredentialId = availableCredentials.First().CredentialInfo.Referent,
                          Revealed     = true
                      } }
                }
            });

            proofRecordIssuer = await proofService1
                                .ProcessPresentationAsync(context1, presentationMessage);

            valid = await proofService1
                    .VerifyProofAsync(context1, proofRecordIssuer.Id);

            Assert.True(valid);

            Assert.False(await proofService2.IsRevokedAsync(context2, availableCredentials.First().CredentialInfo.Referent));

            // Revoke the credential
            await pair.Agent1.Provider.GetService <ICredentialService>()
            .RevokeCredentialAsync(context1, issuerCredentialWithRevocationId);

            await Task.Delay(TimeSpan.FromSeconds(5));

            now = (uint)DateTimeOffset.Now.ToUnixTimeSeconds();

            (requestPresentationMessage, proofRecordIssuer) = await proofService1
                                                              .CreateRequestAsync(context1, new ProofRequest
            {
                Name                = "Test Verification",
                Version             = "1.0",
                Nonce               = await AnonCreds.GenerateNonceAsync(),
                RequestedAttributes = new Dictionary <string, ProofAttributeInfo>
                {
                    { "id-verification", new ProofAttributeInfo {
                          Names = new [] { "name", "age" }
                      } }
                },
                NonRevoked = new RevocationInterval
                {
                    From = 0,
                    To   = now
                }
            });

            proofRecordHolder = await proofService2
                                .ProcessRequestAsync(context2, requestPresentationMessage, pair.Connection2);

            availableCredentials = await proofService2
                                   .ListCredentialsForProofRequestAsync(context2, proofRecordHolder.RequestJson.ToObject <ProofRequest>(), "id-verification");

            (presentationMessage, _) = await proofService2
                                       .CreatePresentationAsync(context2, proofRecordHolder.Id, new RequestedCredentials
            {
                RequestedAttributes = new Dictionary <string, RequestedAttribute>
                {
                    { "id-verification", new RequestedAttribute
                      {
                          CredentialId = availableCredentials.First().CredentialInfo.Referent,
                          Revealed     = true
                      } }
                }
            });

            proofRecordIssuer = await proofService1
                                .ProcessPresentationAsync(context1, presentationMessage);

            valid = await proofService1
                    .VerifyProofAsync(context1, proofRecordIssuer.Id);

            Assert.False(valid);

            Assert.True(await proofService2.IsRevokedAsync(context2, availableCredentials.First().CredentialInfo.Referent));

            // Issue new credential after revoking previous one
            {
                (offer, record) = await credentialService1
                                  .CreateOfferAsync(context1, new OfferConfiguration
                {
                    CredentialDefinitionId = definitionWithRevocationId,
                    IssuerDid = issuerConfiguration.IssuerDid,
                    CredentialAttributeValues = new[]
                    {
                        new CredentialPreviewAttribute("name", "random"),
                        new CredentialPreviewAttribute("age", "22")
                    }
                });

                await messageService1.SendAsync(context1, offer, pair.Connection1);

                string holderCredentialId = null;

                credentials = await credentialService2.ListAsync(context2);

                foreach (var credential in credentials.Where(x => x.State == CredentialState.Offered))
                {
                    var(request, _) = await credentialService2.CreateRequestAsync(context2, credential.Id);

                    holderCredentialId = credential.Id;

                    await messageService2.SendAsync(context2, request, pair.Connection2);
                }

                // Issue credential

                credentials = await credentialService1.ListRequestsAsync(context1);

                foreach (var credential in credentials)
                {
                    var(issue, _) = await credentialService1.CreateCredentialAsync(context1, credential.Id);

                    await messageService1.SendAsync(context1, issue, pair.Connection1);
                }

                await Task.Delay(TimeSpan.FromSeconds(15));

                // Verify latest issued credential with non-revocation proof

                now = (uint)DateTimeOffset.Now.ToUnixTimeSeconds();

                (requestPresentationMessage, proofRecordIssuer) = await proofService1
                                                                  .CreateRequestAsync(context1, new ProofRequest
                {
                    Name                = "Test Verification",
                    Version             = "1.0",
                    Nonce               = await AnonCreds.GenerateNonceAsync(),
                    RequestedAttributes = new Dictionary <string, ProofAttributeInfo>
                    {
                        { "id-verification", new ProofAttributeInfo {
                              Names = new [] { "name", "age" }
                          } }
                    },
                    NonRevoked = new RevocationInterval
                    {
                        From = 0,
                        To   = now
                    }
                });

                proofRecordHolder = await proofService2.ProcessRequestAsync(context2, requestPresentationMessage, pair.Connection2);

                (presentationMessage, _) = await proofService2.CreatePresentationAsync(
                    context2, proofRecordHolder.Id, new RequestedCredentials
                {
                    RequestedAttributes = new Dictionary <string, RequestedAttribute>
                    {
                        { "id-verification", new RequestedAttribute
                          {
                              CredentialId = holderCredentialId,
                              Revealed     = true
                          } }
                    }
                });

                proofRecordIssuer = await proofService1.ProcessPresentationAsync(context1, presentationMessage);

                valid = await proofService1.VerifyProofAsync(context1, proofRecordIssuer.Id);

                Assert.True(valid);
            }
        }
Example #7
0
        public async Task TestCredentialIssuanceV1()
        {
            var pair = await InProcAgent.CreatePairedAsync(true);

            // Configure agent1 as issuer
            var issuerConfiguration = await pair.Agent1.Provider.GetRequiredService <IProvisioningService>()
                                      .GetProvisioningAsync(pair.Agent1.Context.Wallet);

            await PromoteTrustAnchor(issuerConfiguration.IssuerDid, issuerConfiguration.IssuerVerkey);

            var schemaId = await pair.Agent1.Provider.GetRequiredService <ISchemaService>()
                           .CreateSchemaAsync(
                context: pair.Agent1.Context,
                issuerDid: issuerConfiguration.IssuerDid,
                name: $"test-schema-{Guid.NewGuid().ToString()}",
                version: "1.0",
                attributeNames: new[] { "name" });

            var definitionId = await pair.Agent1.Provider.GetRequiredService <ISchemaService>()
                               .CreateCredentialDefinitionAsync(
                context: pair.Agent1.Context,
                schemaId: schemaId,
                issuerDid: issuerConfiguration.IssuerDid,
                tag: "tag",
                supportsRevocation: false,
                maxCredentialCount: 0,
                tailsBaseUri: new Uri("http://localhost"));

            // Send offer
            var(offer, record) = await pair.Agent1.Provider.GetRequiredService <ICredentialService>()
                                 .CreateOfferV1Async(pair.Agent1.Context, new OfferConfiguration
            {
                CredentialDefinitionId = definitionId,
                IssuerDid = issuerConfiguration.IssuerDid,
                CredentialAttributeValues = new []
                {
                    new CredentialPreviewAttribute
                    {
                        Name     = "name",
                        Value    = "random",
                        MimeType = CredentialMimeTypes.TextMimeType
                    }
                }
            });

            await pair.Agent1.Provider.GetRequiredService <IMessageService>()
            .SendAsync(pair.Agent1.Context.Wallet, offer, pair.Connection1);

            // Find credential for Agent 2
            var credentials = await pair.Agent2.Provider.GetService <ICredentialService>()
                              .ListAsync(pair.Agent2.Context);

            var credential = credentials.First();

            // Accept the offer and send request
            var(request, _) = await pair.Agent2.Provider.GetService <ICredentialService>()
                              .CreateRequestAsync(pair.Agent2.Context, credential.Id);

            await pair.Agent2.Provider.GetService <IMessageService>()
            .SendAsync(pair.Agent2.Context.Wallet, request, pair.Connection2);

            // Issue credential
            var(issue, _) = await pair.Agent1.Provider.GetRequiredService <ICredentialService>()
                            .CreateCredentialAsync(pair.Agent1.Context, record.Id);

            await pair.Agent1.Provider.GetService <IMessageService>()
            .SendAsync(pair.Agent1.Context.Wallet, issue, pair.Connection1);

            // Assert
            var credentialHolder = await pair.Agent2.Provider.GetService <ICredentialService>()
                                   .GetAsync(pair.Agent2.Context, credential.Id);

            var credentialIssuer = await pair.Agent1.Provider.GetService <ICredentialService>()
                                   .GetAsync(pair.Agent1.Context, record.Id);

            Assert.Equal(CredentialState.Issued, credentialHolder.State);
            Assert.Equal(CredentialState.Issued, credentialIssuer.State);
        }
        public async Task SendPaymentRequest()
        {
            // Create two agent hosts and establish pairwise connection between them
            var agents = await InProcAgent.CreatePairedAsync(true);

            // Get each agent payment address records
            var paymentAddress1 = await agents.Agent1.Payments.GetDefaultPaymentAddressAsync(agents.Agent1.Context);

            var paymentAddress2 = await agents.Agent2.Payments.GetDefaultPaymentAddressAsync(agents.Agent2.Context);

            // Internal reference number for this payment
            const string paymentReference = "INVOICE# 0001";

            // Setup a basic use case for payments by using basic messages
            // Any AgentMessage can be used to attach payment requests and receipts
            var basicMessage = new BasicMessage {
                Content = "This is payment request"
            };

            // Attach the payment request to the agent message
            var paymentRecord1 = await agents.Agent1.Payments.AttachPaymentRequestAsync(
                context : agents.Agent1.Context,
                agentMessage : basicMessage,
                details : new PaymentDetails
            {
                Id    = paymentReference,
                Items = new List <PaymentItem>
                {
                    new PaymentItem {
                        Label = "Item 1", Amount = 8
                    },
                    new PaymentItem {
                        Label = "Tax", Amount = 2
                    }
                },
                Total = new PaymentItem
                {
                    Amount = new PaymentAmount
                    {
                        Currency = "sov",
                        Value    = 10
                    },
                    Label = "Total"
                }
            },
                payeeAddress : paymentAddress1);

            // PaymentRecord expectations
            Assert.NotNull(paymentRecord1);
            Assert.Equal(10UL, paymentRecord1.Amount);
            Assert.Equal(paymentAddress1.Address, paymentRecord1.Address);
            Assert.Equal(PaymentState.Requested, paymentRecord1.State);

            // Ensure the payment request is attached to the message
            var decorator = basicMessage.FindDecorator <PaymentRequestDecorator>("payment_request");

            Assert.NotNull(decorator);

            // Send the message to agent 2
            await agents.Agent1.Messages.SendAsync(agents.Agent1.Context.Wallet, basicMessage, agents.Connection1);

            // Find the payment record in the context of agent 2
            var search = await agents.Agent2.Records.SearchAsync <PaymentRecord>(
                wallet : agents.Agent2.Context.Wallet,
                query : SearchQuery.And(
                    SearchQuery.Equal(nameof(PaymentRecord.ReferenceId), paymentReference),
                    SearchQuery.Equal(nameof(PaymentRecord.ConnectionId), agents.Connection2.Id)));

            var paymentRecord2 = search.FirstOrDefault();

            Assert.Single(search);
            Assert.NotNull(paymentRecord2);
            Assert.Equal(PaymentState.RequestReceived, paymentRecord2.State);

            // Fund payment address of agent 2 so we can make payment to agent 1
            await FundAccountAsync(50UL, paymentAddress2.Address);

            // Refresh balance to ensure it is funded correctly
            await agents.Agent2.Payments.RefreshBalanceAsync(agents.Agent2.Context, paymentAddress2);

            Assert.Equal(50UL, paymentAddress2.Balance);

            // Make payment for received request
            await agents.Agent2.Payments.MakePaymentAsync(agents.Agent2.Context, paymentRecord2, paymentAddress2);

            Assert.Equal(PaymentState.Paid, paymentRecord2.State);
            Assert.Equal(40UL, paymentAddress2.Balance);
            Assert.NotNull(paymentRecord2.ReceiptId);

            // Send a basic message back to agent 1 and attach a payment receipt
            var message2 = new BasicMessage {
                Content = "Here's a receipt"
            };

            // Attach a payment receipt to the basic message
            // Receipts can be attached to any agent message
            agents.Agent2.Payments.AttachPaymentReceipt(agents.Agent2.Context, message2, paymentRecord2);

            // Send the message to agent 1
            await agents.Agent2.Messages.SendAsync(agents.Agent2.Context.Wallet, message2, agents.Connection2);

            // Fetch payment record 1 again, to refresh state
            paymentRecord1 = await agents.Agent1.Records.GetAsync <PaymentRecord>(agents.Agent1.Context.Wallet, paymentRecord1.Id);

            // Check payment record 1 for receipt

            Assert.Equal(PaymentState.ReceiptReceived, paymentRecord1.State);
            Assert.NotNull(paymentRecord1.ReceiptId);

            // Check agent 1 balance
            await agents.Agent1.Payments.RefreshBalanceAsync(agents.Agent1.Context, paymentAddress1);

            Assert.Equal(10UL, paymentAddress1.Balance);

            // Verify the payment receipt on the ledger
            var verified = await agents.Agent1.Payments.VerifyPaymentAsync(agents.Agent1.Context, paymentRecord1);

            Assert.True(verified);

            // Clean things up and shut down hosts gracefully
            await agents.Agent1.DisposeAsync();

            await agents.Agent2.DisposeAsync();
        }
        public async Task CreateCredentialAndAutoScaleRevocationRegistry()
        {
            var agents = await InProcAgent.CreatePairedAsync(false);

            var issuerProvisioningService = agents.Agent1.Host.Services.GetService <IProvisioningService>();
            var issuerSchemaService       = agents.Agent1.Host.Services.GetService <ISchemaService>();
            var issuerCredentialService   = agents.Agent1.Host.Services.GetService <ICredentialService>();

            var issuerProvisioning = await issuerProvisioningService.GetProvisioningAsync(agents.Agent1.Context.Wallet);

            await PromoteTrustAnchor(issuerProvisioning.IssuerDid, issuerProvisioning.IssuerVerkey);

            var schemaId = await issuerSchemaService.CreateSchemaAsync(
                context : agents.Agent1.Context,
                issuerDid : issuerProvisioning.IssuerDid,
                name : $"test-schema-{Guid.NewGuid()}",
                version : "1.0",
                attributeNames : new[] { "test-attr" });

            string revocationRegistryId1 = null;
            string revocationRegistryId2 = null;
            string revocationRegistryId3 = null;

            var credentialDefinitionId = await issuerSchemaService.CreateCredentialDefinitionAsync(
                context : agents.Agent1.Context,
                new CredentialDefinitionConfiguration
            {
                SchemaId                  = schemaId,
                EnableRevocation          = true,
                RevocationRegistrySize    = 1,
                RevocationRegistryBaseUri = "https://test"
            });

            // First credential - will max out the registry
            {
                var(offerMessage, issuerRecord) = await issuerCredentialService.CreateOfferAsync(agents.Agent1.Context, new OfferConfiguration
                {
                    CredentialDefinitionId    = credentialDefinitionId,
                    CredentialAttributeValues = new[] { new CredentialPreviewAttribute("test-attr", "test-value") }
                });

                Assert.NotNull(offerMessage.FindDecorator <ServiceDecorator>(DecoratorNames.ServiceDecorator));
                Assert.Equal(CredentialState.Offered, issuerRecord.State);
                Assert.Null(issuerRecord.ConnectionId);

                var holderCredentialService = agents.Agent2.Host.Services.GetService <ICredentialService>();

                var holderRecord = await holderCredentialService.CreateCredentialAsync(agents.Agent2.Context, offerMessage);

                issuerRecord = await issuerCredentialService.GetAsync(agents.Agent1.Context, issuerRecord.Id);

                var definitionRecord = await agents.Agent1.Host.Services.GetService <ISchemaService>().GetCredentialDefinitionAsync(agents.Agent1.Context.Wallet, credentialDefinitionId);

                Assert.NotNull(holderRecord);
                Assert.Equal(expected: CredentialState.Issued, actual: holderRecord.State);
                Assert.Equal(expected: CredentialState.Issued, actual: issuerRecord.State);
                Assert.NotNull(holderRecord.CredentialAttributesValues);
                Assert.Null(holderRecord.ConnectionId);
                Assert.Equal(definitionRecord.CurrentRevocationRegistryId, issuerRecord.RevocationRegistryId);
                Assert.Equal(definitionRecord.CurrentRevocationRegistryId, holderRecord.RevocationRegistryId);

                revocationRegistryId1 = definitionRecord.CurrentRevocationRegistryId;
            }

            // Second credential, will auto scale registry
            {
                var(offerMessage, issuerRecord) = await issuerCredentialService.CreateOfferAsync(agents.Agent1.Context, new OfferConfiguration
                {
                    CredentialDefinitionId    = credentialDefinitionId,
                    CredentialAttributeValues = new[] { new CredentialPreviewAttribute("test-attr", "test-value") }
                });

                Assert.NotNull(offerMessage.FindDecorator <ServiceDecorator>(DecoratorNames.ServiceDecorator));
                Assert.Equal(CredentialState.Offered, issuerRecord.State);
                Assert.Null(issuerRecord.ConnectionId);

                var holderCredentialService = agents.Agent2.Host.Services.GetService <ICredentialService>();

                var holderRecord = await holderCredentialService.CreateCredentialAsync(agents.Agent2.Context, offerMessage);

                issuerRecord = await issuerCredentialService.GetAsync(agents.Agent1.Context, issuerRecord.Id);

                var definitionRecord = await agents.Agent1.Host.Services.GetService <ISchemaService>().GetCredentialDefinitionAsync(agents.Agent1.Context.Wallet, credentialDefinitionId);

                Assert.NotNull(holderRecord);
                Assert.Equal(expected: CredentialState.Issued, actual: holderRecord.State);
                Assert.Equal(expected: CredentialState.Issued, actual: issuerRecord.State);
                Assert.NotNull(holderRecord.CredentialAttributesValues);
                Assert.Null(holderRecord.ConnectionId);
                Assert.Equal(definitionRecord.CurrentRevocationRegistryId, issuerRecord.RevocationRegistryId);
                Assert.Equal(definitionRecord.CurrentRevocationRegistryId, holderRecord.RevocationRegistryId);

                revocationRegistryId2 = definitionRecord.CurrentRevocationRegistryId;
            }

            // Third credential, will auto scale registry
            {
                var(offerMessage, issuerRecord) = await issuerCredentialService.CreateOfferAsync(agents.Agent1.Context, new OfferConfiguration
                {
                    CredentialDefinitionId    = credentialDefinitionId,
                    CredentialAttributeValues = new[] { new CredentialPreviewAttribute("test-attr", "test-value") }
                });

                Assert.NotNull(offerMessage.FindDecorator <ServiceDecorator>(DecoratorNames.ServiceDecorator));
                Assert.Equal(CredentialState.Offered, issuerRecord.State);
                Assert.Null(issuerRecord.ConnectionId);

                var holderCredentialService = agents.Agent2.Host.Services.GetService <ICredentialService>();

                var holderRecord = await holderCredentialService.CreateCredentialAsync(agents.Agent2.Context, offerMessage);

                issuerRecord = await issuerCredentialService.GetAsync(agents.Agent1.Context, issuerRecord.Id);

                var definitionRecord = await agents.Agent1.Host.Services.GetService <ISchemaService>().GetCredentialDefinitionAsync(agents.Agent1.Context.Wallet, credentialDefinitionId);

                Assert.NotNull(holderRecord);
                Assert.Equal(expected: CredentialState.Issued, actual: holderRecord.State);
                Assert.Equal(expected: CredentialState.Issued, actual: issuerRecord.State);
                Assert.NotNull(holderRecord.CredentialAttributesValues);
                Assert.Null(holderRecord.ConnectionId);
                Assert.Equal(definitionRecord.CurrentRevocationRegistryId, issuerRecord.RevocationRegistryId);
                Assert.Equal(definitionRecord.CurrentRevocationRegistryId, holderRecord.RevocationRegistryId);

                revocationRegistryId3 = definitionRecord.CurrentRevocationRegistryId;
            }

            Assert.NotEqual(revocationRegistryId1, revocationRegistryId2);
            Assert.NotEqual(revocationRegistryId2, revocationRegistryId3);
            Assert.NotEqual(revocationRegistryId1, revocationRegistryId3);
        }