public async Task <ActionResult <IEnumerable <AttributeValue> > > IssueIdpAttributes(string issuer, [FromBody] IssueAttributesRequestDTO request) { if (request is null) { throw new ArgumentNullException(nameof(request)); } AccountDescriptor account = _accountsService.GetByPublicKey(issuer.HexStringToByteArray()); StatePersistency statePersistency = _executionContextManager.ResolveStateExecutionServices(account.AccountId); IEnumerable <AttributeDefinition> attributeDefinitions = _dataAccessService.GetAttributesSchemeByIssuer(issuer, true) .Select(a => new AttributeDefinition { SchemeId = a.IdentitiesSchemeId, AttributeName = a.AttributeName, SchemeName = a.AttributeSchemeName, Alias = a.Alias, Description = a.Description, IsActive = a.IsActive, IsRoot = a.CanBeRoot }); if (!attributeDefinitions.Any(a => a.IsRoot)) { throw new NoRootAttributeSchemeDefinedException(issuer); } var issuanceInputDetails = GetValidatedIssuanceDetails(request, attributeDefinitions); Identity identity = CreateIdentityInDb(account, issuanceInputDetails); IssuanceDetailsDto issuanceDetails; if (!string.IsNullOrEmpty(request.PublicSpendKey) && !string.IsNullOrEmpty(request.PublicViewKey)) { ConfidentialAccount targetAccount = new ConfidentialAccount { PublicSpendKey = request.PublicSpendKey.HexStringToByteArray(), PublicViewKey = request.PublicViewKey.HexStringToByteArray() }; issuanceDetails = await IssueIdpAttributesAsRoot(issuer, request.Protection, identity, issuanceInputDetails, account, targetAccount, statePersistency).ConfigureAwait(false); } else { issuanceDetails = await IssueIdpAttributesAsAssociated(issuer, identity, issuanceInputDetails, statePersistency).ConfigureAwait(false); } await _idenitiesHubContext.Clients.Group(account.AccountId.ToString()).SendAsync("RequestForIssuance", issuanceDetails); IIntegrationIdP integrationService = GetIntegrationService(account.AccountId); if (integrationService != null) { IssuanceDetails issuanceIntegration = new IssuanceDetails { RootAttribute = new IssuanceDetails.IssuanceDetailsRoot { AttributeName = issuanceDetails.RootAttribute.AttributeName, AssetCommitment = issuanceDetails.RootAttribute.AssetCommitment.HexStringToByteArray(), OriginatingCommitment = issuanceDetails.RootAttribute.OriginatingCommitment.HexStringToByteArray(), SurjectionProof = issuanceDetails.RootAttribute.SurjectionProof.HexStringToByteArray() }, AssociatedAttributes = issuanceDetails.AssociatedAttributes .Select(a => new IssuanceDetails.IssuanceDetailsAssociated { AttributeName = a.AttributeName, AssetCommitment = a.AssetCommitment.HexStringToByteArray(), BindingToRootCommitment = a.BindingToRootCommitment.HexStringToByteArray() }).ToList() }; integrationService.IssueAttributes(account.AccountId, issuanceIntegration); } var attributeValues = FillAttributeValues(request.Attributes, attributeDefinitions); return(Ok(attributeValues)); #region Internal Functions IReadOnlyCollection <AttributeValue> FillAttributeValues(Dictionary <string, IssueAttributesRequestDTO.AttributeValue> attributes, IEnumerable <AttributeDefinition> attributeDefinitions) { List <AttributeValue> attributeValues = new List <AttributeValue>(); var protectionAttrDefinition = attributeDefinitions.FirstOrDefault(a => a.SchemeName == AttributesSchemes.ATTR_SCHEME_NAME_PASSWORD); foreach (var attributeName in attributes.Keys.Where(a => protectionAttrDefinition?.AttributeName != a)) { string content = attributes[attributeName].Value; AttributeValue attributeValue = new AttributeValue { Value = content, Definition = attributeDefinitions.FirstOrDefault(d => d.AttributeName == attributeName) }; attributeValues.Add(attributeValue); } return(new ReadOnlyCollection <AttributeValue>(attributeValues)); } async Task <IssuanceDetailsDto> IssueIdpAttributesAsRoot( string issuer, IssuanceProtection protection, Identity identity, IEnumerable <AttributeIssuanceDetails> attributeIssuanceDetails, AccountDescriptor account, ConfidentialAccount targetAccount, StatePersistency statePersistency) { IssuanceDetailsDto issuanceDetails = new IssuanceDetailsDto(); IEnumerable <IdentitiesScheme> identitiesSchemes = _dataAccessService.GetAttributesSchemeByIssuer(issuer, true); var rootAttributeDetails = attributeIssuanceDetails.First(a => a.Definition.IsRoot); byte[] rootAssetId = await _assetsService.GenerateAssetId(rootAttributeDetails.Definition.SchemeName, rootAttributeDetails.Value.Value, issuer).ConfigureAwait(false); IdentityAttribute rootAttribute = identity.Attributes.FirstOrDefault(a => a.AttributeName == rootAttributeDetails.Definition.AttributeName); if (!CreateRootAttributeIfNeeded(statePersistency, rootAttribute, rootAssetId)) { var protectionAttribute = identity.Attributes.FirstOrDefault(a => a.AttributeName == AttributesSchemes.ATTR_SCHEME_NAME_PASSWORD); bool res = VerifyProtectionAttribute(protectionAttribute, protection.SignatureE.HexStringToByteArray(), protection.SignatureS.HexStringToByteArray(), protection.SessionCommitment.HexStringToByteArray()); if (!res) { _logger.Warning($"[{account.AccountId}]: Failed to verify Surjection Proofs of the Protection Attribute"); throw new ProtectionAttributeVerificationFailedException(); } } else { issuanceDetails.AssociatedAttributes = await IssueAssociatedAttributes( attributeIssuanceDetails.Where(a => !a.Definition.IsRoot) .ToDictionary(d => identity.Attributes.First(a => a.AttributeName == d.Definition.AttributeName).AttributeId, d => d), statePersistency.TransactionsService, issuer, rootAssetId).ConfigureAwait(false); } var packet = TransferAssetToUtxo(statePersistency.TransactionsService, targetAccount, rootAssetId); if (packet == null) { _logger.Error($"[{account.AccountId}]: failed to transfer Root Attribute"); throw new RootAttributeTransferFailedException(); } _dataAccessService.AddOrUpdateIdentityTarget(identity.IdentityId, targetAccount.PublicSpendKey.ToHexString(), targetAccount.PublicViewKey.ToHexString()); issuanceDetails.RootAttribute = new IssuanceDetailsDto.IssuanceDetailsRoot { AttributeName = rootAttribute.AttributeName, OriginatingCommitment = packet.SurjectionProof.AssetCommitments[0].ToHexString(), AssetCommitment = packet.TransferredAsset.AssetCommitment.ToHexString(), SurjectionProof = $"{packet.SurjectionProof.Rs.E.ToHexString()}{packet.SurjectionProof.Rs.S[0].ToHexString()}" }; return(issuanceDetails); } async Task <IssuanceDetailsDto> IssueIdpAttributesAsAssociated( string issuer, Identity identity, IEnumerable <AttributeIssuanceDetails> attributeIssuanceDetails, StatePersistency statePersistency) { IssuanceDetailsDto issuanceDetails = new IssuanceDetailsDto(); IdentitiesScheme rootScheme = _dataAccessService.GetRootIdentityScheme(issuer); IEnumerable <IdentitiesScheme> identitiesSchemes = _dataAccessService.GetAttributesSchemeByIssuer(issuer, true); var rootAttributeDetails = attributeIssuanceDetails.First(a => a.Definition.IsRoot); var packet = await IssueAssociatedAttribute( rootScheme.AttributeSchemeName, rootAttributeDetails.Value.Value, rootAttributeDetails.Value.BlindingPointValue, rootAttributeDetails.Value.BlindingPointRoot, issuer, statePersistency.TransactionsService).ConfigureAwait(false); _dataAccessService.UpdateIdentityAttributeCommitment(identity.Attributes.FirstOrDefault(a => a.AttributeName == rootScheme.AttributeName).AttributeId, packet.AssetCommitment); byte[] rootAssetId = _assetsService.GenerateAssetId(rootScheme.IdentitiesSchemeId, rootAttributeDetails.Value.Value); issuanceDetails.AssociatedAttributes = await IssueAssociatedAttributes( attributeIssuanceDetails .ToDictionary(d => identity.Attributes.First(a => a.AttributeName == d.Definition.AttributeName).AttributeId, d => d), statePersistency.TransactionsService, issuer, rootAssetId).ConfigureAwait(false); return(issuanceDetails); }
public async Task <ActionStatus> IssueAttributes(long accountId, IssuanceDetails issuanceDetails) { ActionStatus actionStatus = new ActionStatus { IntegrationType = Key, IntegrationAction = nameof(IssueAttributes), ActionSucceeded = true }; await _semaphoreSlim.WaitAsync(); try { var privateKey = _dataAccessService.GetAccountKeyValue(accountId, "RskSecretKey"); if (string.IsNullOrEmpty(privateKey)) { actionStatus.ActionSucceeded = false; actionStatus.ErrorMsg = $"Account {accountId} has no integration with {Key}"; } else { var account = new Account(privateKey); actionStatus.IntegrationAddress = account.Address; var web3 = new Web3(account, _integrationConfiguration.RpcUri); var o10IdentityService = new O10IdentityService(web3, _integrationConfiguration.ContractAddress); var issuers = await o10IdentityService.GetAllIssuersQueryAsync().ConfigureAwait(false); if (!issuers.ReturnValue1.Any(issuers => issuers.Address.Equals(account.Address, StringComparison.InvariantCultureIgnoreCase))) { actionStatus.ActionSucceeded = false; actionStatus.ErrorMsg = $"Account with Id {accountId} not registered as an Identity Provider"; } else { List <AttributeRecord> attributeRecords = new List <AttributeRecord> { new AttributeRecord { AttributeName = issuanceDetails.RootAttribute.AttributeName, AssetCommitment = issuanceDetails.RootAttribute.AssetCommitment, BindingCommitment = issuanceDetails.RootAttribute.OriginatingCommitment } }; foreach (var attr in issuanceDetails.AssociatedAttributes) { attributeRecords.Add(new AttributeRecord { AttributeName = attr.AttributeName, AssetCommitment = attr.AssetCommitment, BindingCommitment = attr.BindingToRootCommitment, AttributeId = new BigInteger(0), Version = new BigInteger(0) }); } var balance = await web3.Eth.GetBalance.SendRequestAsync(account.Address).ConfigureAwait(false); var functionIssueAttributesHandler = web3.Eth.GetContractTransactionHandler <IssueAttributesFunction>(); IssueAttributesFunction func = new IssueAttributesFunction { AttributeRecords = attributeRecords }; var esimatedGas = await functionIssueAttributesHandler.EstimateGasAsync(_integrationConfiguration.ContractAddress, func).ConfigureAwait(false); func.Gas = new BigInteger(esimatedGas.ToLong() * 100); if (balance < func.Gas) { actionStatus.ActionSucceeded = false; actionStatus.ErrorMsg = "Not enough funds"; } else { var receipt = await o10IdentityService.IssueAttributesRequestAndWaitForReceiptAsync(func.AttributeRecords).ConfigureAwait(false); if (receipt.Failed()) { actionStatus.ActionSucceeded = false; actionStatus.ErrorMsg = $"Transaction with hash {receipt.TransactionHash} failed"; } } } } } catch (Exception ex) { _logger.Error($"{nameof(IssueAttributes)} failed for the acccount {accountId}", ex); actionStatus.ActionSucceeded = false; actionStatus.ErrorMsg = ex.Message; } finally { _semaphoreSlim.Release(); } return(actionStatus); }