diff --git a/DashSync/shared/DSDashSharedCore.m b/DashSync/shared/DSDashSharedCore.m index d19ab369..71e08eab 100644 --- a/DashSync/shared/DSDashSharedCore.m +++ b/DashSync/shared/DSDashSharedCore.m @@ -23,6 +23,7 @@ #import "DSChain+Wallet.h" #import "DSChainLock.h" #import "DSChainManager+Protected.h" +#import "DSDashPlatform.h" #import "DSDashSharedCore.h" #import "DSIdentity+Protected.h" #import "DSKeyManager.h" @@ -132,6 +133,10 @@ - (instancetype)initOnChain:(DSChain *)chain { UpdateMasternodesAddressUsage update_address_usage_of_masternodes = { .caller = &update_address_usage_of_masternodes_caller }; + Fn_ARGS_std_os_raw_c_void_data_contracts_SystemDataContract_RTRN_dpp_data_contract_DataContract get_data_contract_from_cache = { + .caller = &get_data_contract_from_cache_caller, + .destructor = &get_data_contract_from_cache_dtor + }; NSArray *addresses = @[@"127.0.0.1"]; switch (chain.chainType->tag) { @@ -152,7 +157,7 @@ - (instancetype)initOnChain:(DSChain *)chain { } Vec_ *address_list = [NSArray ffi_to_vec:addresses]; dash_spv_masternode_processor_processing_processor_DiffConfig *diff_config = [chain createDiffConfig]; - self.core = dash_spv_apple_bindings_DashSPVCore_with_callbacks(chain.chainType, diff_config, address_list, get_data_contract, get_platform_activation_height, callback_signer, callback_can_sign, get_block_height_by_hash, get_block_hash_by_height, update_address_usage_of_masternodes, context); + self.core = dash_spv_apple_bindings_DashSPVCore_with_callbacks(chain.chainType, diff_config, address_list, get_data_contract, get_platform_activation_height, callback_signer, callback_can_sign, get_data_contract_from_cache, get_block_height_by_hash, get_block_hash_by_height, update_address_usage_of_masternodes, context); return self; } @@ -171,6 +176,18 @@ - (void)dealloc { } void get_data_contract_dtor(MaybeDataContract *result) {} +DDataContract *get_data_contract_from_cache_caller(const void *context, data_contracts_SystemDataContract *ty) { + DSDashSharedCore *core = AS_OBJC(context); + switch (ty[0]) { + case data_contracts_SystemDataContract_DPNS: + return [DSDashPlatform sharedInstanceForChain:core.chain].dpnsRawContract; + case data_contracts_SystemDataContract_Dashpay: + return [DSDashPlatform sharedInstanceForChain:core.chain].dashPayRawContract; + default: return NULL; + } +} +void get_data_contract_from_cache_dtor(DDataContract *result) {} + MaybeSignedData *callback_signer_caller(const void *context, DIdentityPublicKey *identity_public_key, Vec_u8 *data) { DSDashSharedCore *core = AS_OBJC(context); DBinaryData *ok = NULL; @@ -184,7 +201,7 @@ void get_data_contract_dtor(MaybeDataContract *result) {} ok = DBinaryDataCtor(DOpaqueKeyHashAndSign(maybe_key->ok, data)); } DMaybeOpaqueKeyDtor(maybe_key); - dpp_identity_identity_public_key_IdentityPublicKey_destroy(identity_public_key); + DIdentityPublicKeyDtor(identity_public_key); bytes_dtor(data); return Result_ok_platform_value_types_binary_data_BinaryData_err_dpp_errors_protocol_error_ProtocolError_ctor(ok, error); diff --git a/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Platform.h b/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Platform.h index 9e740be1..cdfbed8a 100644 --- a/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Platform.h +++ b/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Platform.h @@ -45,4 +45,8 @@ NS_ASSUME_NONNULL_BEGIN + (NSError *)ffi_from_quorum_validation_error:(dashcore_sml_quorum_validation_error_QuorumValidationError *)ffi_ref; @end +@interface NSError (dash_spv_platform_identity_username_registration_error_UsernameRegistrationError) ++ (NSError *)ffi_from_username_registration_error:(dash_spv_platform_identity_username_registration_error_UsernameRegistrationError *)ffi_ref; +@end + NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Platform.m b/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Platform.m index ffefde31..3f23157b 100644 --- a/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Platform.m +++ b/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Platform.m @@ -15,6 +15,7 @@ // limitations under the License. // +#import "NSArray+Dash.h" #import "NSData+Dash.h" #import "NSError+Dash.h" #import "NSError+Platform.h" @@ -32,6 +33,8 @@ + (nonnull NSError *)ffi_from_platform_error:(nonnull DPlatformError *)ffi_ref { case dash_spv_platform_error_Error_MaxRetryExceeded: case dash_spv_platform_error_Error_InstantSendSignatureVerificationError: return [NSError errorWithCode:0 localizedDescriptionKey:NSStringFromPtr(ffi_ref->instant_send_signature_verification_error)]; + case dash_spv_platform_error_Error_UsernameRegistrationError: + return [NSError ffi_from_username_registration_error:ffi_ref->username_registration_error]; } } @@ -248,5 +251,19 @@ + (NSError *)ffi_from_sml_error:(dashcore_sml_error_SmlError *)ffi_ref { } @end +@implementation NSError (dash_spv_platform_identity_username_registration_error_UsernameRegistrationError) ++ (NSError *)ffi_from_username_registration_error:(dash_spv_platform_identity_username_registration_error_UsernameRegistrationError *)ffi_ref { + + switch (ffi_ref->tag) { + case dash_spv_platform_identity_username_registration_error_UsernameRegistrationError_NoUsernameFullPathsWithStatus: + return [NSError errorWithCode:0 descriptionKey:DSLocalizedFormat(@"UsernameRegistrationError::NoUsernameFullPathsWithStatus(%u)", nil, DUsernameStatusIndex(ffi_ref->no_username_full_paths_with_status))]; + case dash_spv_platform_identity_username_registration_error_UsernameRegistrationError_NoUsernamePreorderDocuments: + return [NSError errorWithCode:0 descriptionKey:DSLocalizedFormat(@"UsernameRegistrationError::NoUsernamePreorderDocuments(%u, %@)", nil, DUsernameStatusIndex(ffi_ref->no_username_preorder_documents._0), [NSArray ffi_from_vec_of_string:ffi_ref->no_username_preorder_documents._1])]; + case dash_spv_platform_identity_username_registration_error_UsernameRegistrationError_NotSupported: + return [NSError errorWithCode:0 descriptionKey:DSLocalizedFormat(@"UsernameRegistrationError::NotSupported(%u)", nil, DUsernameStatusIndex(ffi_ref->not_supported))]; + } +} +@end + diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 8789098c..3f8662c4 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -244,7 +244,7 @@ - (void)stopAsync { - (void)dealloc { if (_options != NULL) { - free(_options); + dash_spv_coinjoin_models_coinjoin_client_options_CoinJoinClientOptions_destroy(_options); } } @@ -733,7 +733,7 @@ - (double)getMixingProgress { } + (BOOL)isDenominatedAmount:(uint64_t)amount { - return is_denominated_amount(amount); + return dash_spv_coinjoin_coinjoin_CoinJoin_is_denominated_amount(amount); } - (BOOL)isCoinJoinOutput:(DSTransactionOutput *)output utxo:(DSUTXO)utxo { diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 12966147..ba2f504f 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -364,6 +364,7 @@ bool hasChainLock(const void *context, uint32_t block_height) { @synchronized (context) { DSCoinJoinWrapper *wrapper = AS_OBJC(context); DSTransaction *transaction = [wrapper.chain transactionForHash:txHash]; + DSLog(@"[DSCoinJoinWrapper] getTransaction: %@ == %@ (%@)", uint256_hex(txHash), transaction, [wrapper.chain transactionForHash:uint256_reverse(txHash)]); if (transaction) tx = [transaction ffi_malloc:wrapper.chain.chainType]; } diff --git a/DashSync/shared/Models/Derivation Paths/DSDerivationPathFactory.m b/DashSync/shared/Models/Derivation Paths/DSDerivationPathFactory.m index dcb0864c..233933a7 100644 --- a/DashSync/shared/Models/Derivation Paths/DSDerivationPathFactory.m +++ b/DashSync/shared/Models/Derivation Paths/DSDerivationPathFactory.m @@ -279,8 +279,6 @@ - (DSAuthenticationKeysDerivationPath *)identityECDSAKeysDerivationPathForWallet [mArray addObject:fundsDerivationPath]; } } - if (account.coinJoinDerivationPath && ![account.coinJoinDerivationPath hasExtendedPublicKey]) - [mArray addObject:account.coinJoinDerivationPath]; } return [mArray copy]; diff --git a/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m b/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m index b6c074cc..3674f88c 100644 --- a/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m +++ b/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m @@ -156,7 +156,7 @@ - (NSArray *)registerAddressesWithSettings:(DSGapLimit *)settings inContext:(NSManagedObjectContext *)context { DSGapLimitFunds *gapLimitSettings = (DSGapLimitFunds *)settings; uintptr_t gapLimit = gapLimitSettings.gapLimit; - BOOL internal = (gapLimitSettings.direction & DSGapLimitFundsDirection_Internal) == DSGapLimitFundsDirection_Internal; + BOOL internal = FLAG_IS_SET(gapLimitSettings.direction, DSGapLimitFundsDirection_Internal); if (!self.account.wallet.isTransient) { NSAssert(self.addressesLoaded, @"addresses must be loaded before calling this function"); } diff --git a/DashSync/shared/Models/Entities/DSDashpayUserEntity+CoreDataClass.h b/DashSync/shared/Models/Entities/DSDashpayUserEntity+CoreDataClass.h index cce63d32..b7c5a42a 100644 --- a/DashSync/shared/Models/Entities/DSDashpayUserEntity+CoreDataClass.h +++ b/DashSync/shared/Models/Entities/DSDashpayUserEntity+CoreDataClass.h @@ -45,7 +45,7 @@ NS_ASSUME_NONNULL_BEGIN - (NSDictionary *)friendsWithActivityForType:(DSDashpayUserEntityFriendActivityType)activityType count:(NSUInteger)count ascending:(BOOL)ascending; -- (NSError *)applyTransientDashpayUser:(DSTransientDashpayUser *)transientDashpayUser +- (NSError *)applyTransientDashpayUser:(DTransientUser *)transientDashpayUser save:(BOOL)save; - (void)sendAmount:(uint64_t)amount fromAccount:(DSAccount *)account toFriendWithIdentityIdentifier:(UInt256)identityIdentifier requestingAdditionalInfo:(DSTransactionCreationRequestingAdditionalInfoBlock)additionalInfoRequest diff --git a/DashSync/shared/Models/Entities/DSDashpayUserEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSDashpayUserEntity+CoreDataClass.m index 9a1c756a..fa549655 100644 --- a/DashSync/shared/Models/Entities/DSDashpayUserEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSDashpayUserEntity+CoreDataClass.m @@ -101,20 +101,21 @@ - (NSString *)username { return username.stringValue; } -- (NSError *)applyTransientDashpayUser:(DSTransientDashpayUser *)transientDashpayUser save:(BOOL)save { +- (NSError *)applyTransientDashpayUser:(DTransientUser *)transientDashpayUser save:(BOOL)save { if (!self.documentIdentifier) { - self.documentIdentifier = transientDashpayUser.documentIdentifier; + self.documentIdentifier = NSDataFromPtr(transientDashpayUser->document_identifier->_0->_0); } else if (self.documentIdentifier) { return [NSError errorWithCode:500 localizedDescriptionKey:@"Error when updating profile information"]; } - self.localProfileDocumentRevision = transientDashpayUser.revision; - self.remoteProfileDocumentRevision = transientDashpayUser.revision; - self.avatarPath = transientDashpayUser.avatarPath; - self.publicMessage = transientDashpayUser.publicMessage; - self.displayName = transientDashpayUser.displayName; - - self.createdAt = transientDashpayUser.createdAt; - self.updatedAt = transientDashpayUser.updatedAt; + dpp_prelude_Revision *revision = transientDashpayUser->revision; + self.localProfileDocumentRevision = revision->_0; + self.remoteProfileDocumentRevision = revision->_0; + self.avatarPath = NSStringFromPtr(transientDashpayUser->avatar_url); + self.publicMessage = NSStringFromPtr(transientDashpayUser->public_message); + self.displayName = NSStringFromPtr(transientDashpayUser->display_name); + + self.createdAt = transientDashpayUser->created_at->_0; + self.updatedAt = transientDashpayUser->updated_at->_0; if (save) { [self.managedObjectContext ds_save]; diff --git a/DashSync/shared/Models/Entities/DSInstantSendLockEntity+CoreDataProperties.h b/DashSync/shared/Models/Entities/DSInstantSendLockEntity+CoreDataProperties.h index 9b260c48..6c6866f5 100644 --- a/DashSync/shared/Models/Entities/DSInstantSendLockEntity+CoreDataProperties.h +++ b/DashSync/shared/Models/Entities/DSInstantSendLockEntity+CoreDataProperties.h @@ -17,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nullable, nonatomic, retain) NSData *signature; @property (nullable, nonatomic, retain) NSData *cycleHash; -@property (assign, nonatomic) uint8_t version; +@property (assign, nonatomic) uint16_t version; @property (assign, nonatomic) BOOL validSignature; @property (nullable, nonatomic, retain) DSTransactionEntity *transaction; diff --git a/DashSync/shared/Models/Identity/DSIdentity+ContactRequest.m b/DashSync/shared/Models/Identity/DSIdentity+ContactRequest.m index 05125f73..e1f04731 100644 --- a/DashSync/shared/Models/Identity/DSIdentity+ContactRequest.m +++ b/DashSync/shared/Models/Identity/DSIdentity+ContactRequest.m @@ -45,6 +45,32 @@ #define ERROR_DERIVATION_FRIENDSHIP [NSError errorWithCode:500 localizedDescriptionKey:@"Could not create friendship derivation path"] #define ERROR_CONTACT_REQUEST_KEY_ENCRYPTION [NSError errorWithCode:500 localizedDescriptionKey:@"Contact request extended public key is incorrectly encrypted."] +#define AS_OBJC(context) ((__bridge DSIdentity *)(context)) +#define AS_RUST(context) ((__bridge void *)(context)) + +BOOL has_contact_request_with_id_caller(const void *context, BOOL incoming, u256 *contact_request_id, const void *storage_context) { + DSIdentity *identity = AS_OBJC(context); + NSManagedObjectContext *ctx = ((__bridge NSManagedObjectContext *)(storage_context)); + NSData *identifier = NSDataFromPtr(contact_request_id); + u256_dtor(contact_request_id); + __block BOOL exist = NO; + [ctx performBlockAndWait:^{ + exist = [DSFriendRequestEntity countObjectsInContext:ctx + matching: incoming + ? @"destinationContact == %@ && sourceContact.associatedBlockchainIdentity.uniqueID == %@" + : @"sourceContact == %@ && destinationContact.associatedBlockchainIdentity.uniqueID == %@", [identity matchingDashpayUserInContext:ctx], identifier]; + }]; + return exist; +} +void has_contact_request_with_id_dtor(BOOL result) { + +} +Fn_ARGS_std_os_raw_c_void_bool_Arr_u8_32_std_os_raw_c_void_RTRN_bool has_contact_request_with_id = { + .caller = &has_contact_request_with_id_caller, + .destructor = &has_contact_request_with_id_dtor, +}; + + @implementation DSIdentity (ContactRequest) - (void)fetchContactRequests:(void (^)(BOOL success, NSArray *errors))completion { @@ -108,11 +134,13 @@ - (void)fetchIncomingContactRequestsInContext:(NSManagedObjectContext *)context if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[error ? error : ERROR_IDENTITY_NOT_ACTIVATED]); }); return; } - u256 *user_id = u256_ctor_u(self.uniqueID); - uint64_t since = self.lastCheckedIncomingContactsTimestamp ? (self.lastCheckedIncomingContactsTimestamp - HOUR_TIME_INTERVAL) : 0; +// u256 *user_id = u256_ctor_u(self.uniqueID); + uint64_t lastCheckedIncomingContactsTimestamp = DIdentityModelLastCheckedIncomingContactsTimestamp(self.model); + uint64_t since = lastCheckedIncomingContactsTimestamp ? (lastCheckedIncomingContactsTimestamp - HOUR_TIME_INTERVAL) : 0; Vec_u8 *start_after = startAfter ? bytes_ctor(startAfter) : nil; - __weak typeof(self) weakSelf = self; - DMaybeContactRequests *result = dash_spv_platform_document_contact_request_ContactRequestManager_stream_incoming_contact_requests_with_contract(self.chain.sharedRuntime, self.chain.sharedContactsObj, user_id, since, start_after, dashpayContract.raw_contract, DRetryLinear(5), dash_spv_platform_document_contact_request_ContactRequestValidator_None_ctor(), 1000); + + DMaybeContactRequests *result = DFetchIncomingContactRequests(self, dashpayContract.raw_contract, since, start_after, ((__bridge void *)(context)), has_contact_request_with_id); + if (result->error) { NSError *error = [NSError ffi_from_platform_error:result->error]; DMaybeContactRequestsDtor(result); @@ -121,79 +149,134 @@ - (void)fetchIncomingContactRequestsInContext:(NSManagedObjectContext *)context if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[error]); }); return; } - - dispatch_async(self.identityQueue, ^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - [debugInfo appendFormat:@" : ERROR: Lost self context"]; - DSLog(@"%@", debugInfo); - if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[ERROR_MEM_ALLOC]); }); - return; - } - DContactRequests *documents = result->ok; - NSAssert(completionQueue == self.identityQueue, @"we should be on identity queue"); - __block NSMutableArray *incomingNewRequests = [NSMutableArray array]; - __block NSMutableArray *rErrors = [NSMutableArray array]; - [context performBlockAndWait:^{ - for (int i = 0; i < documents->count; i++) { - DContactRequestKind *kind = documents->values[i]; - switch (kind->tag) { - case dash_spv_platform_models_contact_request_ContactRequestKind_Incoming: { - NSData *identifier = NSDataFromPtr(kind->incoming->owner_id); - //we are the recipient, this is an incoming request - DSFriendRequestEntity *exist = [DSFriendRequestEntity anyObjectInContext:context matching:@"destinationContact == %@ && sourceContact.associatedBlockchainIdentity.uniqueID == %@", [self matchingDashpayUserInContext:context], identifier]; - - // TODO: memory - if (!exist) - [incomingNewRequests addObject:[NSValue valueWithPointer:dash_spv_platform_document_contact_request_as_incoming_request(kind)]]; - break; - } - default: { - //we should not have received this - NSAssert(FALSE, @"the contact request needs to be either outgoing or incoming"); - break; - } - } + DContactRequests *requests = result->ok; + __block BOOL succeeded = YES; + __block NSMutableArray *rErrors = [NSMutableArray array]; + dispatch_group_t dispatchGroup = dispatch_group_create(); + if (requests->count > 0) { + dispatch_group_enter(dispatchGroup); + [self handleIncomingRequests:requests + context:context + completion:^(BOOL success, NSArray *errors) { + if (!success) { + succeeded = NO; + [rErrors addObjectsFromArray:errors]; } - }]; - __block BOOL succeeded = YES; - dispatch_group_t dispatchGroup = dispatch_group_create(); - if ([incomingNewRequests count]) { - dispatch_group_enter(dispatchGroup); - [self handleIncomingRequests:incomingNewRequests - context:context - completion:^(BOOL success, NSArray *errors) { - if (!success) { - succeeded = NO; - [rErrors addObjectsFromArray:errors]; - } - dispatch_group_leave(dispatchGroup); - } - onCompletionQueue:completionQueue]; + dispatch_group_leave(dispatchGroup); } - dispatch_group_notify(dispatchGroup, completionQueue, ^{ - BOOL hasMore = documents->count == DAPI_DOCUMENT_RESPONSE_COUNT_LIMIT; - if (!hasMore) - [self.platformContext performBlockAndWait:^{ - self.lastCheckedIncomingContactsTimestamp = [[NSDate date] timeIntervalSince1970]; - }]; - [debugInfo appendFormat:@" : OK: %u: %@", succeeded, rErrors]; - DSLog(@"%@", debugInfo); - __block NSData * hasMoreStartAfter = nil; - if (documents->count > 0) { - DContactRequestKind *last = documents->values[documents->count-1]; - if (last->incoming) - hasMoreStartAfter = NSDataFromPtr(last->incoming->id); - } - if (succeeded && hasMoreStartAfter) - [self fetchIncomingContactRequestsInContext:context - startAfter:hasMoreStartAfter - withCompletion:completion - onCompletionQueue:completionQueue]; - else if (completion) - completion(succeeded, [rErrors copy]); - }); + onCompletionQueue:completionQueue]; + } + + dispatch_group_notify(dispatchGroup, completionQueue, ^{ + BOOL hasMore = requests->count == DAPI_DOCUMENT_RESPONSE_COUNT_LIMIT; + if (!hasMore) + [self.platformContext performBlockAndWait:^{ + DIdentityModelSetLastCheckedIncomingContactsTimestamp(self.model, [[NSDate date] timeIntervalSince1970]); + }]; + [debugInfo appendFormat:@" : OK: %u: %@", succeeded, rErrors]; + DSLog(@"%@", debugInfo); + __block NSData * hasMoreStartAfter = nil; + if (requests->count > 0) { + DContactRequest *last = requests->values[requests->count-1]; + if (last) + hasMoreStartAfter = NSDataFromPtr(last->id); + } + DMaybeContactRequestsDtor(result); + if (succeeded && hasMoreStartAfter) + [self fetchIncomingContactRequestsInContext:context + startAfter:hasMoreStartAfter + withCompletion:completion + onCompletionQueue:completionQueue]; + else if (completion) + completion(succeeded, [rErrors copy]); }); + + + + +// __weak typeof(self) weakSelf = self; +// DMaybeContactRequests *result = dash_spv_platform_document_contact_request_ContactRequestManager_stream_incoming_contact_requests_with_contract(self.chain.sharedRuntime, self.chain.sharedContactsObj, user_id, since, start_after, dashpayContract.raw_contract, DRetryLinear(5), dash_spv_platform_document_contact_request_ContactRequestValidator_None_ctor(), 1000); +// if (result->error) { +// NSError *error = [NSError ffi_from_platform_error:result->error]; +// DMaybeContactRequestsDtor(result); +// [debugInfo appendFormat:@" : ERROR: %@", error]; +// DSLog(@"%@", debugInfo); +// if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[error]); }); +// return; +// } + +// dispatch_async(self.identityQueue, ^{ +// __strong typeof(weakSelf) strongSelf = weakSelf; +// if (!strongSelf) { +// [debugInfo appendFormat:@" : ERROR: Lost self context"]; +// DSLog(@"%@", debugInfo); +// if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[ERROR_MEM_ALLOC]); }); +// return; +// } +// DContactRequests *documents = result->ok; +//// NSAssert(completionQueue == self.identityQueue, @"we should be on identity queue"); +// __block NSMutableArray *incomingNewRequests = [NSMutableArray array]; +// __block NSMutableArray *rErrors = [NSMutableArray array]; +// [context performBlockAndWait:^{ +// for (int i = 0; i < documents->count; i++) { +// DContactRequestKind *kind = documents->values[i]; +// switch (kind->tag) { +// case dash_spv_platform_models_contact_request_ContactRequestKind_Incoming: { +// NSData *identifier = NSDataFromPtr(kind->incoming->owner_id); +// //we are the recipient, this is an incoming request +// DSFriendRequestEntity *exist = [DSFriendRequestEntity anyObjectInContext:context matching:@"destinationContact == %@ && sourceContact.associatedBlockchainIdentity.uniqueID == %@", [self matchingDashpayUserInContext:context], identifier]; +// +// // TODO: memory +// if (!exist) +// [incomingNewRequests addObject:[NSValue valueWithPointer:dash_spv_platform_document_contact_request_as_incoming_request(kind)]]; +// break; +// } +// default: { +// //we should not have received this +// NSAssert(FALSE, @"the contact request needs to be either outgoing or incoming"); +// break; +// } +// } +// } +// }]; +// __block BOOL succeeded = YES; +// dispatch_group_t dispatchGroup = dispatch_group_create(); +// if ([incomingNewRequests count]) { +// dispatch_group_enter(dispatchGroup); +// [self handleIncomingRequests:incomingNewRequests +// context:context +// completion:^(BOOL success, NSArray *errors) { +// if (!success) { +// succeeded = NO; +// [rErrors addObjectsFromArray:errors]; +// } +// dispatch_group_leave(dispatchGroup); +// } +// onCompletionQueue:completionQueue]; +// } +// dispatch_group_notify(dispatchGroup, completionQueue, ^{ +// BOOL hasMore = documents->count == DAPI_DOCUMENT_RESPONSE_COUNT_LIMIT; +// if (!hasMore) +// [self.platformContext performBlockAndWait:^{ +// DIdentityModelSetLastCheckedIncomingContactsTimestamp(self.model, [[NSDate date] timeIntervalSince1970]); +// }]; +// [debugInfo appendFormat:@" : OK: %u: %@", succeeded, rErrors]; +// DSLog(@"%@", debugInfo); +// __block NSData * hasMoreStartAfter = nil; +// if (documents->count > 0) { +// DContactRequestKind *last = documents->values[documents->count-1]; +// if (last->incoming) +// hasMoreStartAfter = NSDataFromPtr(last->incoming->id); +// } +// if (succeeded && hasMoreStartAfter) +// [self fetchIncomingContactRequestsInContext:context +// startAfter:hasMoreStartAfter +// withCompletion:completion +// onCompletionQueue:completionQueue]; +// else if (completion) +// completion(succeeded, [rErrors copy]); +// }); +// }); } - (void)fetchOutgoingContactRequests:(void (^)(BOOL success, NSArray *errors))completion { @@ -208,6 +291,7 @@ - (void)fetchOutgoingContactRequestsInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(BOOL success, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { NSMutableString *debugInfo = [NSMutableString stringWithFormat:@"%@ Fetch Outgoing Contact Requests: (after: %@)", self.logPrefix, startAfter ? startAfter.hexString : @"NULL"]; + DSLog(@"%@", debugInfo); DPContract *dashpayContract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; if (dashpayContract.contractState != DPContractState_Registered) { [debugInfo appendFormat:@" : ERROR: DashPay Contract State: %lu", dashpayContract.contractState]; @@ -219,95 +303,149 @@ - (void)fetchOutgoingContactRequestsInContext:(NSManagedObjectContext *)context if (![self activePrivateKeysAreLoadedWithFetchingError:&error]) { [debugInfo appendFormat:@" : ERROR: Active private keys are loaded with error: %@", error]; DSLog(@"%@", debugInfo); - //The blockchain identity hasn't been intialized on the device, ask the user to activate the blockchain user, this action allows private keys to be cached on the blockchain identity level + //The identity hasn't been intialized on the device, ask the user to activate the blockchain user, this action allows private keys to be cached on the identity level if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[error ? error : ERROR_IDENTITY_NOT_ACTIVATED]); }); return; } - __weak typeof(self) weakSelf = self; - Vec_u8 *start_after = startAfter ? bytes_ctor(startAfter) : NULL; - uint64_t since = self.lastCheckedOutgoingContactsTimestamp ? (self.lastCheckedOutgoingContactsTimestamp - HOUR_TIME_INTERVAL) : 0; - u256 *user_id = u256_ctor_u(self.uniqueID); - DMaybeContactRequests *result = dash_spv_platform_document_contact_request_ContactRequestManager_stream_outgoing_contact_requests_with_contract(self.chain.sharedRuntime, self.chain.sharedContactsObj, user_id, since, start_after, dashpayContract.raw_contract, DRetryLinear(5), dash_spv_platform_document_contact_request_ContactRequestValidator_None_ctor(), 1000); + uint64_t lastCheckedOutgoingContactsTimestamp = DIdentityModelLastCheckedOutgoingContactsTimestamp(self.model); + uint64_t since = lastCheckedOutgoingContactsTimestamp ? (lastCheckedOutgoingContactsTimestamp - HOUR_TIME_INTERVAL) : 0; + DMaybeContactRequests *result = DFetchOutgoingContactRequests(self, dashpayContract.raw_contract, since, start_after, ((__bridge void *)(context)), has_contact_request_with_id); if (result->error) { NSError *error = [NSError ffi_from_platform_error:result->error]; DMaybeContactRequestsDtor(result); [debugInfo appendFormat:@" : ERROR: %@", error]; DSLog(@"%@", debugInfo); + if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[error]); }); return; } - - dispatch_async(self.identityQueue, ^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - [debugInfo appendFormat:@" : ERROR: Lost self context"]; - DSLog(@"%@", debugInfo); - if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[ERROR_MEM_ALLOC]); }); - return; - } - DContactRequests *documents = result->ok; - NSAssert(completionQueue == self.identityQueue, @"we should be on identity queue"); - __block NSMutableArray *outgoingNewRequests = [NSMutableArray array]; - __block NSMutableArray *rErrors = [NSMutableArray array]; - [context performBlockAndWait:^{ - for (int i = 0; i < documents->count; i++) { - DContactRequestKind *kind = documents->values[i]; - switch (kind->tag) { - case dash_spv_platform_models_contact_request_ContactRequestKind_Outgoing: { - NSData *identifier = NSDataFromPtr(kind->outgoing->recipient); - - BOOL exist = [DSFriendRequestEntity countObjectsInContext:context matching:@"sourceContact == %@ && destinationContact.associatedBlockchainIdentity.uniqueID == %@", [self matchingDashpayUserInContext:context], identifier]; - // TODO: memory - if (!exist) [outgoingNewRequests addObject:[NSValue valueWithPointer:dash_spv_platform_document_contact_request_as_outgoing_request(kind)]]; - break; - } - default: { - //we should not have received this - NSAssert(FALSE, @"the contact request needs to be either outgoing or incoming"); - break; - } - } - } - }]; - __block BOOL succeeded = YES; - dispatch_group_t dispatchGroup = dispatch_group_create(); - if ([outgoingNewRequests count]) { - dispatch_group_enter(dispatchGroup); - [self handleOutgoingRequests:outgoingNewRequests - context:context - completion:^(BOOL success, NSArray *errors) { - if (!success) { - succeeded = NO; - [rErrors addObjectsFromArray:errors]; - } - dispatch_group_leave(dispatchGroup); + DContactRequests *requests = result->ok; + + __block BOOL succeeded = YES; + __block NSMutableArray *rErrors = [NSMutableArray array]; + dispatch_group_t dispatchGroup = dispatch_group_create(); + if (requests->count > 0) { + dispatch_group_enter(dispatchGroup); + [self handleOutgoingRequests:requests + context:context + completion:^(BOOL success, NSArray *errors) { + if (!success) { + succeeded = NO; + [rErrors addObjectsFromArray:errors]; } - onCompletionQueue:completionQueue]; + dispatch_group_leave(dispatchGroup); } - dispatch_group_notify(dispatchGroup, completionQueue, ^{ - BOOL hasMore = documents->count == DAPI_DOCUMENT_RESPONSE_COUNT_LIMIT; - if (!hasMore) - [self.platformContext performBlockAndWait:^{ - self.lastCheckedOutgoingContactsTimestamp = [[NSDate date] timeIntervalSince1970]; - }]; - [debugInfo appendFormat:@" : %@: %@", succeeded ? @"OK" : @"No", [rErrors count] ? rErrors.description : @""]; - DSLog(@"%@", debugInfo); - __block NSData * hasMoreStartAfter = nil; - if (documents->count > 0) { - DContactRequestKind *last = documents->values[documents->count-1]; - if (last->outgoing) - hasMoreStartAfter = NSDataFromPtr(last->outgoing->id); - } - if (succeeded && hasMoreStartAfter) - [self fetchOutgoingContactRequestsInContext:context - startAfter:hasMoreStartAfter - withCompletion:completion - onCompletionQueue:completionQueue]; - else if (completion) - completion(succeeded, [rErrors copy]); - }); + onCompletionQueue:completionQueue]; + } + dispatch_group_notify(dispatchGroup, completionQueue, ^{ + BOOL hasMore = requests->count == DAPI_DOCUMENT_RESPONSE_COUNT_LIMIT; + if (!hasMore) + [self.platformContext performBlockAndWait:^{ + DIdentityModelSetLastCheckedOutgoingContactsTimestamp(self.model, [[NSDate date] timeIntervalSince1970]); + }]; + [debugInfo appendFormat:@" : %@: %@", succeeded ? @"OK" : @"No", [rErrors count] ? rErrors.description : @""]; + DSLog(@"%@", debugInfo); + __block NSData * hasMoreStartAfter = nil; + if (requests->count > 0) { + DContactRequest *last = requests->values[requests->count-1]; + if (last) + hasMoreStartAfter = NSDataFromPtr(last->id); + } + DMaybeContactRequestsDtor(result); + if (succeeded && hasMoreStartAfter) + [self fetchOutgoingContactRequestsInContext:context + startAfter:hasMoreStartAfter + withCompletion:completion + onCompletionQueue:completionQueue]; + else if (completion) + completion(succeeded, [rErrors copy]); }); + + + + +// u256 *user_id = u256_ctor_u(self.uniqueID); +// DMaybeContactRequests *result = dash_spv_platform_document_contact_request_ContactRequestManager_stream_outgoing_contact_requests_with_contract(self.chain.sharedRuntime, self.chain.sharedContactsObj, user_id, since, start_after, dashpayContract.raw_contract, DRetryLinear(5), dash_spv_platform_document_contact_request_ContactRequestValidator_None_ctor(), 1000); +// if (result->error) { +// NSError *error = [NSError ffi_from_platform_error:result->error]; +// DMaybeContactRequestsDtor(result); +// [debugInfo appendFormat:@" : ERROR: %@", error]; +// DSLog(@"%@", debugInfo); +// if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[error]); }); +// return; +// } +// +//// dispatch_async(self.identityQueue, ^{ +//// __strong typeof(weakSelf) strongSelf = weakSelf; +//// if (!strongSelf) { +//// [debugInfo appendFormat:@" : ERROR: Lost self context"]; +//// DSLog(@"%@", debugInfo); +//// if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[ERROR_MEM_ALLOC]); }); +//// return; +//// } +// DContactRequests *documents = result->ok; +//// NSAssert(completionQueue == self.identityQueue, @"we should be on identity queue"); +// __block NSMutableArray *outgoingNewRequests = [NSMutableArray array]; +// __block NSMutableArray *rErrors = [NSMutableArray array]; +// [context performBlockAndWait:^{ +// for (int i = 0; i < documents->count; i++) { +// DContactRequestKind *kind = documents->values[i]; +// switch (kind->tag) { +// case dash_spv_platform_models_contact_request_ContactRequestKind_Outgoing: { +// NSData *identifier = NSDataFromPtr(kind->outgoing->recipient); +// +// BOOL exist = [DSFriendRequestEntity countObjectsInContext:context matching:@"sourceContact == %@ && destinationContact.associatedBlockchainIdentity.uniqueID == %@", [self matchingDashpayUserInContext:context], identifier]; +// // TODO: memory +// if (!exist) [outgoingNewRequests addObject:[NSValue valueWithPointer:dash_spv_platform_document_contact_request_as_outgoing_request(kind)]]; +// break; +// } +// default: { +// //we should not have received this +// NSAssert(FALSE, @"the contact request needs to be either outgoing or incoming"); +// break; +// } +// } +// } +// }]; +// __block BOOL succeeded = YES; +// dispatch_group_t dispatchGroup = dispatch_group_create(); +// if ([outgoingNewRequests count]) { +// dispatch_group_enter(dispatchGroup); +// [self handleOutgoingRequests:outgoingNewRequests +// context:context +// completion:^(BOOL success, NSArray *errors) { +// if (!success) { +// succeeded = NO; +// [rErrors addObjectsFromArray:errors]; +// } +// dispatch_group_leave(dispatchGroup); +// } +// onCompletionQueue:completionQueue]; +// } +// dispatch_group_notify(dispatchGroup, completionQueue, ^{ +// BOOL hasMore = documents->count == DAPI_DOCUMENT_RESPONSE_COUNT_LIMIT; +// if (!hasMore) +// [self.platformContext performBlockAndWait:^{ +// DIdentityModelSetLastCheckedOutgoingContactsTimestamp(self.model, [[NSDate date] timeIntervalSince1970]); +// }]; +// [debugInfo appendFormat:@" : %@: %@", succeeded ? @"OK" : @"No", [rErrors count] ? rErrors.description : @""]; +// DSLog(@"%@", debugInfo); +// __block NSData * hasMoreStartAfter = nil; +// if (documents->count > 0) { +// DContactRequestKind *last = documents->values[documents->count-1]; +// if (last->outgoing) +// hasMoreStartAfter = NSDataFromPtr(last->outgoing->id); +// } +// if (succeeded && hasMoreStartAfter) +// [self fetchOutgoingContactRequestsInContext:context +// startAfter:hasMoreStartAfter +// withCompletion:completion +// onCompletionQueue:completionQueue]; +// else if (completion) +// completion(succeeded, [rErrors copy]); +// }); +//// }); } @@ -325,7 +463,7 @@ - (NSData *)decryptedPublicKeyDataWithKey:(DOpaqueKey *)key return data; } -- (void)handleIncomingRequests:(NSArray *)incomingRequests +- (void)handleIncomingRequests:(Vec_dash_spv_platform_models_contact_request_ContactRequest *)incomingRequests context:(NSManagedObjectContext *)context completion:(void (^)(BOOL success, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { @@ -337,9 +475,8 @@ - (void)handleIncomingRequests:(NSArray *)incomingRequests __block BOOL succeeded = YES; __block NSMutableArray *errors = [NSMutableArray array]; dispatch_group_t dispatchGroup = dispatch_group_create(); - - for (NSValue *contactRequest in incomingRequests) { - DContactRequest *request = contactRequest.pointerValue; + for (int i = 0; i < incomingRequests->count; i++) { + DContactRequest *request = incomingRequests->values[i]; NSData *senderIdentityUniqueId = NSDataFromPtr(request->owner_id); DSBlockchainIdentityEntity *externalIdentityEntity = [DSBlockchainIdentityEntity anyObjectInContext:context matching:@"uniqueID == %@", senderIdentityUniqueId]; if (!externalIdentityEntity) { @@ -401,12 +538,12 @@ - (void)handleIncomingRequests:(NSArray *)incomingRequests } else { DSIdentity *sourceIdentity = [[DSIdentity alloc] initWithIdentityEntity:externalIdentityEntity]; NSAssert(sourceIdentity, @"This should not be null"); - if ([sourceIdentity activeKeyCount] > 0 && [sourceIdentity keyAtIndex:request->sender_key_index]) { + if ([sourceIdentity activeKeyCount] > 0 && [sourceIdentity hasKeyAtIndex:request->sender_key_index]) { //the contact already existed, and has an encryption public key set, create the incoming friend request, add a friendship if an outgoing friend request also exists DOpaqueKey *key = [sourceIdentity keyAtIndex:request->sender_key_index]; NSData *decryptedExtendedPublicKeyData = [self decryptedPublicKeyDataWithKey:key request:request]; NSAssert(decryptedExtendedPublicKeyData, @"Data should be decrypted"); - DMaybeOpaqueKey *extendedPublicKey = [DSKeyManager keyWithExtendedPublicKeyData:decryptedExtendedPublicKeyData ofType:DKeyKindECDSA()]; + DMaybeOpaqueKey *extendedPublicKey = [DSKeyManager keyWithExtendedPublicKeyData:decryptedExtendedPublicKeyData ofType:DOpaqueKeyKind(key)]; if (!extendedPublicKey) { succeeded = FALSE; [errors addObject:ERROR_CONTACT_REQUEST_KEY_ENCRYPTION]; @@ -455,17 +592,19 @@ - (void)handleIncomingRequests:(NSArray *)incomingRequests } } } + + dispatch_group_notify(dispatchGroup, completionQueue, ^{ - for (NSValue *request in incomingRequests) { - DContactRequestDtor(request.pointerValue); - } +// for (NSValue *request in incomingRequests) { +// DContactRequestDtor(request.pointerValue); +// } if (completion) completion(succeeded, [errors copy]); }); }]; } -- (void)handleOutgoingRequests:(NSArray *)outgoingRequests +- (void)handleOutgoingRequests:(Vec_dash_spv_platform_models_contact_request_ContactRequest *)outgoingRequests context:(NSManagedObjectContext *)context completion:(void (^)(BOOL success, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { @@ -477,8 +616,8 @@ - (void)handleOutgoingRequests:(NSArray *)outgoingRequests __block NSMutableArray *errors = [NSMutableArray array]; __block BOOL succeeded = YES; dispatch_group_t dispatchGroup = dispatch_group_create(); - for (NSValue *contactRequest in outgoingRequests) { - DContactRequest *request = contactRequest.pointerValue; + for (int i = 0; i < outgoingRequests->count; i++) { + DContactRequest *request = outgoingRequests->values[i]; DSBlockchainIdentityEntity *recipientIdentityEntity = [DSBlockchainIdentityEntity anyObjectInContext:context matching:@"uniqueID == %@", NSDataFromPtr(request->recipient)]; if (!recipientIdentityEntity) { @@ -539,9 +678,9 @@ - (void)handleOutgoingRequests:(NSArray *)outgoingRequests } } dispatch_group_notify(dispatchGroup, completionQueue, ^{ - for (NSValue *request in outgoingRequests) { - DContactRequestDtor(request.pointerValue); - } +// for (NSValue *request in outgoingRequests) { +// DContactRequestDtor(request.pointerValue); +// } if (completion) completion(succeeded, [errors copy]); }); diff --git a/DashSync/shared/Models/Identity/DSIdentity+Friendship.m b/DashSync/shared/Models/Identity/DSIdentity+Friendship.m index ea4f4c98..9ba7869a 100644 --- a/DashSync/shared/Models/Identity/DSIdentity+Friendship.m +++ b/DashSync/shared/Models/Identity/DSIdentity+Friendship.m @@ -58,17 +58,19 @@ - (void)sendNewFriendRequestToIdentity:(DSIdentity *)identity completion:(void (^)(BOOL success, NSArray *_Nullable errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { if (identity.isTransient) { - identity.isTransient = FALSE; + dash_spv_platform_identity_model_IdentityModel_set_is_transient(identity.model, NO); [self.identitiesManager registerForeignIdentity:identity]; - if (identity.transientDashpayUser) { - [identity applyProfileChanges:identity.transientDashpayUser + DTransientUser *user = dash_spv_platform_identity_model_IdentityModel_maybe_user(identity.model); + if (user) { + [identity applyProfileChanges:user inContext:context saveContext:YES completion:^(BOOL success, NSError *_Nullable error) { + DTransientUserDtor(user); if (success && !error) { DSDashpayUserEntity *dashpayUser = [identity matchingDashpayUserInContext:context]; - if (identity.transientDashpayUser.revision == dashpayUser.remoteProfileDocumentRevision) - identity.transientDashpayUser = nil; + if (dash_spv_platform_identity_model_IdentityModel_if_user_matches_revision(identity.model, dashpayUser.remoteProfileDocumentRevision)) + dash_spv_platform_identity_model_IdentityModel_set_user(identity.model, NULL); } } onCompletionQueue:self.identityQueue]; diff --git a/DashSync/shared/Models/Identity/DSIdentity+Profile.h b/DashSync/shared/Models/Identity/DSIdentity+Profile.h index 7a487f43..a7af3190 100644 --- a/DashSync/shared/Models/Identity/DSIdentity+Profile.h +++ b/DashSync/shared/Models/Identity/DSIdentity+Profile.h @@ -89,7 +89,7 @@ NS_ASSUME_NONNULL_BEGIN withCompletion:(void (^)(BOOL success, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue; -- (void)applyProfileChanges:(DSTransientDashpayUser *)transientDashpayUser +- (void)applyProfileChanges:(DTransientUser *)transientDashpayUser inContext:(NSManagedObjectContext *)context saveContext:(BOOL)saveContext completion:(void (^_Nullable)(BOOL success, NSError *_Nullable error))completion diff --git a/DashSync/shared/Models/Identity/DSIdentity+Profile.m b/DashSync/shared/Models/Identity/DSIdentity+Profile.m index adc9eee8..da2360f2 100644 --- a/DashSync/shared/Models/Identity/DSIdentity+Profile.m +++ b/DashSync/shared/Models/Identity/DSIdentity+Profile.m @@ -35,56 +35,78 @@ @implementation DSIdentity (Profile) - (NSString *)avatarPath { - if (self.transientDashpayUser) { - return self.transientDashpayUser.avatarPath; + char *maybe_result = dash_spv_platform_identity_model_IdentityModel_maybe_avatar_path(self.model); + if (maybe_result) { + NSString *result = NSStringFromPtr(maybe_result); + str_destroy(maybe_result); + return result; } else { return self.matchingDashpayUserInViewContext.avatarPath; } } - (NSData *)avatarFingerprint { - if (self.transientDashpayUser) { - return self.transientDashpayUser.avatarFingerprint; + Vec_u8 *maybe_result = dash_spv_platform_identity_model_IdentityModel_maybe_avatar_fingerprint(self.model); + if (maybe_result) { + NSData *result = NSDataFromPtr(maybe_result); + bytes_dtor(maybe_result); + return result; } else { return self.matchingDashpayUserInViewContext.avatarFingerprint; } } - (NSData *)avatarHash { - if (self.transientDashpayUser) { - return self.transientDashpayUser.avatarHash; + u256 *maybe_result = dash_spv_platform_identity_model_IdentityModel_maybe_avatar_hash(self.model); + if (maybe_result) { + NSData *result = NSDataFromPtr(maybe_result); + u256_dtor(maybe_result); + return result; } else { return self.matchingDashpayUserInViewContext.avatarHash; } } - (NSString *)displayName { - if (self.transientDashpayUser) { - return self.transientDashpayUser.displayName; + char *maybe_result = dash_spv_platform_identity_model_IdentityModel_maybe_display_name(self.model); + if (maybe_result) { + NSString *result = NSStringFromPtr(maybe_result); + str_destroy(maybe_result); + return result; } else { return self.matchingDashpayUserInViewContext.displayName; } } - (NSString *)publicMessage { - if (self.transientDashpayUser) { - return self.transientDashpayUser.publicMessage; + char *maybe_result = dash_spv_platform_identity_model_IdentityModel_maybe_public_message(self.model); + if (maybe_result) { + NSString *result = NSStringFromPtr(maybe_result); + str_destroy(maybe_result); + return result; } else { return self.matchingDashpayUserInViewContext.publicMessage; } + } - (uint64_t)dashpayProfileUpdatedAt { - if (self.transientDashpayUser) { - return self.transientDashpayUser.updatedAt; + uint64_t *maybe_result = dash_spv_platform_identity_model_IdentityModel_maybe_profile_updated_at(self.model); + if (maybe_result) { + uint64_t result = maybe_result[0]; + u64_destroy(maybe_result); + return result; } else { return self.matchingDashpayUserInViewContext.updatedAt; } } - (uint64_t)dashpayProfileCreatedAt { - if (self.transientDashpayUser) { - return self.transientDashpayUser.createdAt; + uint64_t *maybe_result = dash_spv_platform_identity_model_IdentityModel_maybe_profile_created_at(self.model); + if (maybe_result) { + uint64_t result = maybe_result[0]; + u64_destroy(maybe_result); + return result; } else { return self.matchingDashpayUserInViewContext.createdAt; } @@ -473,16 +495,26 @@ - (void)fetchProfileInContext:(NSManagedObjectContext *)context if (completion) dispatch_async(completionQueue, ^{ completion(NO, ERROR_DASHPAY_CONTRACT_NOT_REGISTERED); }); return; } - [self.identitiesManager fetchProfileForIdentity:self - withCompletion:^(BOOL success, DSTransientDashpayUser *_Nullable dashpayUserInfo, NSError *_Nullable error) { - if (!success || error || dashpayUserInfo == nil) { - if (completion) dispatch_async(completionQueue, ^{ completion(success, error); }); - return; - } - [self applyProfileChanges:dashpayUserInfo + + NSMutableString *debugString = [NSMutableString stringWithFormat:@"%@ Fetch Profile for: %@", self.logPrefix, self]; + DSLog(@"%@", debugString); + DMaybeTransientUser *result = dash_spv_platform_document_manager_DocumentsManager_fetch_profile(self.chain.sharedRuntime, self.chain.sharedDocumentsObj, self.model, dashpayContract.raw_contract); + + if (result->error) { + NSError *error = [NSError ffi_from_platform_error:result->error]; + DSLog(@"%@: ERROR: %@", debugString, error); + if (completion) dispatch_async(completionQueue, ^{ completion(NO, error); }); + DMaybeTransientUserDtor(result); + return; + } + + dispatch_async(self.identityQueue, ^{ + [self applyProfileChanges:result->ok inContext:context saveContext:YES completion:^(BOOL success, NSError *_Nullable error) { + DMaybeTransientUserDtor(result); + if (!success) { [self fetchUsernamesInContext:context withCompletion:completion @@ -495,12 +527,42 @@ - (void)fetchProfileInContext:(NSManagedObjectContext *)context } onCompletionQueue:self.identityQueue]; - } - onCompletionQueue:self.identityQueue]; + }); + + +// DSTransientDashpayUser *transientDashpayUser = [[DSTransientDashpayUser alloc] initWithDocument:result->ok]; +// DSLog(@"%@: OK: %@", debugString, transientDashpayUser); +// dispatch_async(completionQueue, ^{ if (completion) completion(YES, transientDashpayUser, nil); }); + + +// [self.identitiesManager fetchProfileForIdentity:self +// withCompletion:^(BOOL success, DSTransientDashpayUser *_Nullable dashpayUserInfo, NSError *_Nullable error) { +// if (!success || error || dashpayUserInfo == nil) { +// if (completion) dispatch_async(completionQueue, ^{ completion(success, error); }); +// return; +// } +// [self applyProfileChanges:dashpayUserInfo +// inContext:context +// saveContext:YES +// completion:^(BOOL success, NSError *_Nullable error) { +// if (!success) { +// [self fetchUsernamesInContext:context +// withCompletion:completion +// onCompletionQueue:completionQueue]; +// +// } else if (completion) { +// dispatch_async(completionQueue, ^{ completion(success, error); }); +// } +// +// +// } +// onCompletionQueue:self.identityQueue]; +// } +// onCompletionQueue:self.identityQueue]; } -- (void)applyProfileChanges:(DSTransientDashpayUser *)transientDashpayUser +- (void)applyProfileChanges:(DTransientUser *)transientDashpayUser inContext:(NSManagedObjectContext *)context saveContext:(BOOL)saveContext completion:(void (^)(BOOL success, NSError *error))completion @@ -525,7 +587,7 @@ - (void)applyProfileChanges:(DSTransientDashpayUser *)transientDashpayUser NSAssert(contact, @"It is weird to get here"); if (!contact) contact = [DSDashpayUserEntity anyObjectInContext:context matching:@"associatedBlockchainIdentity.uniqueID == %@", self.uniqueIDData]; - if (!contact || transientDashpayUser.updatedAt > contact.updatedAt) { + if (!contact || dash_spv_platform_models_transient_dashpay_user_TransientDashPayUser_is_updated_after(transientDashpayUser, contact.updatedAt)) { if (!contact) { contact = [DSDashpayUserEntity managedObjectInBlockedContext:context]; contact.chain = [strongSelf.wallet.chain chainEntityInContext:context]; diff --git a/DashSync/shared/Models/Identity/DSIdentity+Protected.h b/DashSync/shared/Models/Identity/DSIdentity+Protected.h index 97d190bb..36a9a1d3 100644 --- a/DashSync/shared/Models/Identity/DSIdentity+Protected.h +++ b/DashSync/shared/Models/Identity/DSIdentity+Protected.h @@ -27,10 +27,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, weak) DSWallet *wallet; @property (nonatomic, readonly) DSBlockchainIdentityEntity *identityEntity; -@property (nullable, nonatomic, strong) DSTransientDashpayUser *transientDashpayUser; +@property (nullable, nonatomic) DTransientUser *transientDashpayUser; @property (nonatomic, weak) DSInvitation *associatedInvitation; @property (nonatomic, assign) DMaybeOpaqueKey *registrationFundingPrivateKey; -@property (nonatomic, assign) BOOL isLocal; @property (nonatomic, assign) UInt256 registrationAssetLockTransactionHash; @property (nonatomic, readonly) NSManagedObjectContext *platformContext; @@ -41,12 +40,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) uint32_t currentMainKeyIndex; @property (nonatomic, readonly) uint32_t keysCreated; -@property (nonatomic, assign) IdentityModel *identity_model; - -@property (nonatomic, assign) BOOL isTransient; - -@property (nonatomic, assign) uint64_t lastCheckedIncomingContactsTimestamp; -@property (nonatomic, assign) uint64_t lastCheckedOutgoingContactsTimestamp; +@property (nonatomic, assign) IdentityModel *model; - (BOOL)isDashpayReady; - (void)saveProfileTimestamp; @@ -55,6 +49,9 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithIdentityEntity:(DSBlockchainIdentityEntity *)entity; +- (instancetype)initWithModel:(IdentityModel *)model + onChain:(DSChain *)chain; + //This one is called for a local identity that is being recreated from the network - (instancetype)initAtIndex:(uint32_t)index withUniqueId:(UInt256)uniqueId @@ -84,9 +81,9 @@ NS_ASSUME_NONNULL_BEGIN withLockedOutpoint:(DSUTXO)lockedOutpoint inWallet:(DSWallet *)wallet; -- (instancetype)initAtIndex:(uint32_t)index - withUniqueId:(UInt256)uniqueId - inWallet:(DSWallet *)wallet; +//- (instancetype)initAtIndex:(uint32_t)index +// withUniqueId:(UInt256)uniqueId +// inWallet:(DSWallet *)wallet; - (instancetype)initAtIndex:(uint32_t)index uniqueId:(UInt256)uniqueId @@ -139,6 +136,24 @@ NS_ASSUME_NONNULL_BEGIN - (UInt256)contractIdIfRegistered:(DDataContract *)contract; +- (DSAuthenticationKeysDerivationPath *)derivationPathForType:(DKeyKind *)type; +- (BOOL)createNewKey:(DOpaqueKey *)key + forIdentityEntity:(DSBlockchainIdentityEntity *)identityEntity + atPath:(NSIndexPath *)path + withStatus:(DIdentityKeyStatus *)status + withSecurityLevel:(DSecurityLevel *)security_level + withPurpose:(DPurpose *)purpose + fromDerivationPath:(DSDerivationPath *)derivationPath + inContext:(NSManagedObjectContext *)context; +- (BOOL)saveNewKeyForCurrentEntity:(DOpaqueKey *)key + atPath:(NSIndexPath *)path + withStatus:(DIdentityKeyStatus *)status + withSecurityLevel:(DSecurityLevel *)security_level + withPurpose:(DPurpose *)purpose + fromDerivationPath:(DSDerivationPath *)derivationPath + inContext:(NSManagedObjectContext *)context; + + @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Identity/DSIdentity+Username.h b/DashSync/shared/Models/Identity/DSIdentity+Username.h index 590fab4b..f1b5ba4e 100644 --- a/DashSync/shared/Models/Identity/DSIdentity+Username.h +++ b/DashSync/shared/Models/Identity/DSIdentity+Username.h @@ -33,14 +33,20 @@ NS_ASSUME_NONNULL_BEGIN - (void)applyUsernameEntitiesFromIdentityEntity:(DSBlockchainIdentityEntity *)identityEntity; - (void)collectUsernameEntitiesIntoIdentityEntityInContext:(DSBlockchainIdentityEntity *)identityEntity context:(NSManagedObjectContext *)context; - +- (void)addDashpayUsername:(NSString *)username; - (void)addDashpayUsername:(NSString *)username save:(BOOL)save; +- (void)addConfirmedUsername:(NSString *)username + inDomain:(NSString *)domain; + +- (void)addUsername:(NSString *)username + inDomain:(NSString *)domain + status:(DUsernameStatus)status; - (void)addUsername:(NSString *)username inDomain:(NSString *)domain save:(BOOL)save; - (void)addUsername:(NSString *)username inDomain:(NSString *)domain - status:(DUsernameStatus *)status + status:(DUsernameStatus)status save:(BOOL)save registerOnNetwork:(BOOL)registerOnNetwork; - (DUsernameStatus *_Nullable)statusOfUsername:(NSString *)username @@ -52,12 +58,14 @@ NS_ASSUME_NONNULL_BEGIN withCompletion:(void (^)(BOOL success, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue; -- (void)setAndSaveUsernameFullPaths:(NSArray *)usernameFullPaths - toStatus:(DUsernameStatus *)status - inContext:(NSManagedObjectContext *)context; +//- (void)setAndSaveUsernameFullPaths:(NSArray *)usernameFullPaths +// toStatus:(DUsernameStatus *)status +// inContext:(NSManagedObjectContext *)context; - (BOOL)hasDashpayUsername:(NSString *)username; +- (void)notifyUsernameUpdate:(nullable NSDictionary *)userInfo; + @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Identity/DSIdentity+Username.m b/DashSync/shared/Models/Identity/DSIdentity+Username.m index df01ee8d..f1f6e3e9 100644 --- a/DashSync/shared/Models/Identity/DSIdentity+Username.m +++ b/DashSync/shared/Models/Identity/DSIdentity+Username.m @@ -20,13 +20,11 @@ #import "DSIdentity+Username.h" #import "DSChainManager.h" #import "DSDashPlatform.h" -#import "DSUsernameFullPathSaveContext.h" #import "DSWallet.h" #import "NSArray+Dash.h" #import "NSError+Dash.h" #import "NSError+Platform.h" #import "NSManagedObject+Sugar.h" -#import #define DEFAULT_FETCH_USERNAMES_RETRY_COUNT 5 @@ -34,52 +32,30 @@ #define ERROR_TRANSITION_SIGNING [NSError errorWithCode:501 localizedDescriptionKey:@"Unable to sign transition"] #define ERROR_UNSUPPORTED_DOCUMENT_VERSION(version) [NSError errorWithCode:500 descriptionKey:DSLocalizedFormat(@"Unsupported document version: %u", nil, version)] -void usernames_save_context_caller(const void *context, DUsernameStatus *status) { - DSUsernameFullPathSaveContext *saveContext = ((__bridge DSUsernameFullPathSaveContext *)(context)); - switch (*status) { - case dash_spv_platform_document_usernames_UsernameStatus_PreorderRegistrationPending: { - [saveContext setAndSaveUsernameFullPaths:dash_spv_platform_document_usernames_UsernameStatus_PreorderRegistrationPending_ctor()]; - break; - } - case dash_spv_platform_document_usernames_UsernameStatus_Preordered: - [saveContext setAndSaveUsernameFullPaths:dash_spv_platform_document_usernames_UsernameStatus_Preordered_ctor()]; - break; - case dash_spv_platform_document_usernames_UsernameStatus_RegistrationPending: - [saveContext setAndSaveUsernameFullPaths:dash_spv_platform_document_usernames_UsernameStatus_RegistrationPending_ctor()]; - break; - case dash_spv_platform_document_usernames_UsernameStatus_Confirmed: - [saveContext setAndSaveUsernameFullPaths:dash_spv_platform_document_usernames_UsernameStatus_Confirmed_ctor()]; - break; - default: - break; - } - DUsernameStatusDtor(status); -} - @implementation DSIdentity (Username) - (void)applyUsernameEntitiesFromIdentityEntity:(DSBlockchainIdentityEntity *)identityEntity { for (DSBlockchainIdentityUsernameEntity *usernameEntity in identityEntity.usernames) { - NSData *salt = usernameEntity.salt; + NSData *saltData = usernameEntity.salt; NSString *domain = usernameEntity.domain; NSString *username = usernameEntity.stringValue; DUsernameStatus *status = DUsernameStatusFromIndex(usernameEntity.status); - if (salt) { - Vec_u8 *salt_bytes = bytes_ctor(salt); - dash_spv_platform_identity_model_IdentityModel_add_username_with_salt(self.identity_model, DChar(username), DChar(domain), status, salt_bytes); - DAddSaltForUsername(self.identity_model, username, salt_bytes); + if (saltData) { + u256 *salt = u256_ctor(saltData); + dash_spv_platform_identity_model_IdentityModel_add_username_with_salt(self.model, DChar(username), DChar(domain), status, salt); + DAddSaltForUsername(self.model, username, salt); } else { - DUsernameAdd(self.identity_model, username, domain ? domain : @"", status); + DUsernameAdd(self.model, username, domain ? domain : @"", status); } } } - (void)collectUsernameEntitiesIntoIdentityEntityInContext:(DSBlockchainIdentityEntity *)identityEntity context:(NSManagedObjectContext *)context { - DUsernameStatuses *username_statuses = dash_spv_platform_identity_model_IdentityModel_username_statuses(self.identity_model); + DUsernameStatuses *username_statuses = dash_spv_platform_identity_model_IdentityModel_username_statuses(self.model); for (int i = 0; i < username_statuses->count; i++) { char *username_full_path = username_statuses->keys[i]; - dash_spv_platform_identity_model_UsernameStatusInfo *info = username_statuses->values[i]; + DUsernameStatusInfo *info = username_statuses->values[i]; DSBlockchainIdentityUsernameEntity *usernameEntity = [DSBlockchainIdentityUsernameEntity managedObjectInBlockedContext:context]; NSString *usernameFullPath = NSStringFromPtr(username_full_path); usernameEntity.status = DUsernameStatusIndex(info->status); @@ -94,11 +70,16 @@ - (void)collectUsernameEntitiesIntoIdentityEntityInContext:(DSBlockchainIdentity // MARK: Usernames +- (void)addDashpayUsername:(NSString *)username { + [self addUsername:username + inDomain:@"dash" + status:dash_spv_platform_document_usernames_UsernameStatus_Initial]; +} - (void)addDashpayUsername:(NSString *)username save:(BOOL)save { [self addUsername:username inDomain:@"dash" - status:dash_spv_platform_document_usernames_UsernameStatus_Initial_ctor() + status:dash_spv_platform_document_usernames_UsernameStatus_Initial save:save registerOnNetwork:YES]; } @@ -108,50 +89,89 @@ - (void)addUsername:(NSString *)username save:(BOOL)save { [self addUsername:username inDomain:domain - status:dash_spv_platform_document_usernames_UsernameStatus_Initial_ctor() + status:dash_spv_platform_document_usernames_UsernameStatus_Initial save:save registerOnNetwork:YES]; } +- (void)addConfirmedUsername:(NSString *)username + inDomain:(NSString *)domain { + [self addUsername:username + inDomain:domain + status:dash_spv_platform_document_usernames_UsernameStatus_Confirmed]; +} - (void)addUsername:(NSString *)username inDomain:(NSString *)domain - status:(DUsernameStatus *)status + status:(DUsernameStatus)status { + DUsernameAdd(self.model, username, domain, &status); + +} + +- (void)addUsername:(NSString *)username + inDomain:(NSString *)domain + status:(DUsernameStatus)status save:(BOOL)save registerOnNetwork:(BOOL)registerOnNetwork { - DUsernameAdd(self.identity_model, username, domain, status); + [self addUsername:username inDomain:domain status:status]; if (save) dispatch_async(self.identityQueue, ^{ - [self saveNewUsername:username - inDomain:domain - status:dash_spv_platform_document_usernames_UsernameStatus_Initial - inContext:self.platformContext]; - if (registerOnNetwork && self.registered && !dash_spv_platform_document_usernames_UsernameStatus_is_confirmed(status)) + NSAssert([username containsString:@"."] == FALSE, @"This is most likely an error"); + NSAssert(domain, @"Domain must not be nil"); + if (self.isTransient || !self.isActive) return; + NSManagedObjectContext *storageContext = self.platformContext; + [self.platformContext performBlockAndWait:^{ + DSBlockchainIdentityEntity *entity = [self identityEntityInContext:self.platformContext]; + DSBlockchainIdentityUsernameEntity *usernameEntity = [DSBlockchainIdentityUsernameEntity managedObjectInBlockedContext:storageContext]; + usernameEntity.status = status; + usernameEntity.stringValue = username; + NSString *usernameFullPath = [self fullPathForUsername:username inDomain:domain]; + BOOL is_initial = dash_spv_platform_identity_model_IdentityModel_status_of_username_full_path_is_initial(self.model, DChar(usernameFullPath)); + u256 *maybe_salt = DSaltForUsername(self.model, usernameFullPath); + if (is_initial || !maybe_salt) { + NSData *salt = uint256_data(uint256_random); + usernameEntity.salt = salt; + DAddSaltForUsername(self.model, usernameFullPath, u256_ctor(salt)); + } else { + usernameEntity.salt = NSDataFromPtr(maybe_salt); + } + if (maybe_salt) + u256_dtor(maybe_salt); + usernameEntity.domain = domain; + [entity addUsernamesObject:usernameEntity]; + [entity setDashpayUsername:usernameEntity]; + [storageContext ds_save]; + [self notifyUsernameUpdate:@{ + DSChainManagerNotificationChainKey: self.chain, + DSIdentityKey: self + }]; + }]; + if (registerOnNetwork && self.registered && status != dash_spv_platform_document_usernames_UsernameStatus_Confirmed) [self registerUsernamesWithCompletion:^(BOOL success, NSArray *errors) {}]; }); } - (DUsernameStatus *_Nullable)statusOfUsername:(NSString *)username inDomain:(NSString *)domain { - return username ? dash_spv_platform_identity_model_IdentityModel_status_of_username(self.identity_model, DChar(username), DChar(domain)) : nil; + return username ? dash_spv_platform_identity_model_IdentityModel_status_of_username(self.model, DChar(username), DChar(domain)) : nil; } - (DUsernameStatus *_Nullable)statusOfDashpayUsername:(NSString *)username { - return username ? dash_spv_platform_identity_model_IdentityModel_status_of_dashpay_username(self.identity_model, DChar(username)) : nil; + return username ? dash_spv_platform_identity_model_IdentityModel_status_of_dashpay_username(self.model, DChar(username)) : nil; } - (DUsernameStatus *)statusOfUsernameFullPath:(NSString *)usernameFullPath { - return dash_spv_platform_identity_model_IdentityModel_status_of_username_full_path(self.identity_model, DChar(usernameFullPath)); + return dash_spv_platform_identity_model_IdentityModel_status_of_username_full_path(self.model, DChar(usernameFullPath)); } - (NSString *)usernameOfUsernameFullPath:(NSString *)usernameFullPath { - char *result = dash_spv_platform_identity_model_IdentityModel_username_of_username_full_path(self.identity_model, DChar(usernameFullPath)); + char *result = dash_spv_platform_identity_model_IdentityModel_username_of_username_full_path(self.model, DChar(usernameFullPath)); NSString *res = NSStringFromPtr(result); DCharDtor(result); return res; } - (NSString *)domainOfUsernameFullPath:(NSString *)usernameFullPath { - char *result = dash_spv_platform_identity_model_IdentityModel_domain_of_username_full_path(self.identity_model, DChar(usernameFullPath)); + char *result = dash_spv_platform_identity_model_IdentityModel_domain_of_username_full_path(self.model, DChar(usernameFullPath)); NSString *res = NSStringFromPtr(result); DCharDtor(result); return res; @@ -162,19 +182,19 @@ - (NSString *)fullPathForUsername:(NSString *)username return [[username lowercaseString] stringByAppendingFormat:@".%@", [domain lowercaseString]]; } - (NSUInteger)dashpayUsernameCount { - return dash_spv_platform_identity_model_IdentityModel_dashpay_username_count(self.identity_model); + return dash_spv_platform_identity_model_IdentityModel_dashpay_username_count(self.model); } - (NSArray *)dashpayUsernameFullPaths { - Vec_String *result = dash_spv_platform_identity_model_IdentityModel_dashpay_username_full_paths(self.identity_model); + Vec_String *result = dash_spv_platform_identity_model_IdentityModel_dashpay_username_full_paths(self.model); NSArray*arr = [NSArray ffi_from_vec_of_string:result]; Vec_String_destroy(result); return arr; } - (BOOL)hasDashpayUsername:(NSString *)username { - return dash_spv_platform_identity_model_IdentityModel_has_dashpay_username(self.identity_model, DChar(username)); + return dash_spv_platform_identity_model_IdentityModel_has_dashpay_username(self.model, DChar(username)); } - (NSArray *)dashpayUsernames { - Vec_String *result = dash_spv_platform_identity_model_IdentityModel_dashpay_usernames(self.identity_model); + Vec_String *result = dash_spv_platform_identity_model_IdentityModel_dashpay_usernames(self.model); NSArray*arr = [NSArray ffi_from_vec_of_string:result]; Vec_String_destroy(result); return arr; @@ -183,156 +203,158 @@ - (BOOL)hasDashpayUsername:(NSString *)username { // MARK: Username Helpers -- (NSMutableDictionary *)saltedDomainHashesForUsernameFullPaths:(NSArray *)usernameFullPaths - inContext:(NSManagedObjectContext *)context { - NSMutableDictionary *mSaltedDomainHashes = [NSMutableDictionary dictionary]; - for (NSString *unregisteredUsernameFullPath in usernameFullPaths) { - NSMutableData *saltedDomain = [NSMutableData data]; - NSData *salt; - BOOL is_initial = dash_spv_platform_identity_model_IdentityModel_status_of_username_full_path_is_initial(self.identity_model, DChar(unregisteredUsernameFullPath)); - Vec_u8 *maybe_salt = DSaltForUsername(self.identity_model, unregisteredUsernameFullPath); - if (is_initial || !maybe_salt) { - salt = uint256_data(uint256_random); - DAddSaltForUsername(self.identity_model, unregisteredUsernameFullPath, bytes_ctor(salt)); - [self saveUsername:[self usernameOfUsernameFullPath:unregisteredUsernameFullPath] - inDomain:[self domainOfUsernameFullPath:unregisteredUsernameFullPath] - status:DUsernameStatusIndex([self statusOfUsernameFullPath:unregisteredUsernameFullPath]) - salt:salt - commitSave:YES - inContext:context]; - } else { - salt = NSDataFromPtr(maybe_salt); - Vec_u8_destroy(maybe_salt); - } - - [saltedDomain appendData:salt]; - [saltedDomain appendData:[unregisteredUsernameFullPath dataUsingEncoding:NSUTF8StringEncoding]]; - mSaltedDomainHashes[unregisteredUsernameFullPath] = uint256_data([saltedDomain SHA256_2]); - DAddSaltForUsername(self.identity_model, unregisteredUsernameFullPath, bytes_ctor(salt)); - } - return [mSaltedDomainHashes copy]; -} - -- (void)saveNewUsername:(NSString *)username - inDomain:(NSString *)domain - status:(uint16_t)status - inContext:(NSManagedObjectContext *)context { - NSAssert([username containsString:@"."] == FALSE, @"This is most likely an error"); - NSAssert(domain, @"Domain must not be nil"); - if (self.isTransient || !self.isActive) return; - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *entity = [self identityEntityInContext:context]; - DSBlockchainIdentityUsernameEntity *usernameEntity = [DSBlockchainIdentityUsernameEntity managedObjectInBlockedContext:context]; - usernameEntity.status = status; - usernameEntity.stringValue = username; - NSString *usernameFullPath = [self fullPathForUsername:username inDomain:domain]; - BOOL is_initial = dash_spv_platform_identity_model_IdentityModel_status_of_username_full_path_is_initial(self.identity_model, DChar(usernameFullPath)); - Vec_u8 *maybe_salt = DSaltForUsername(self.identity_model, usernameFullPath); - if (is_initial || !maybe_salt) { - NSData *salt = uint256_data(uint256_random); - usernameEntity.salt = salt; - DAddSaltForUsername(self.identity_model, usernameFullPath, bytes_ctor(salt)); - } else { - usernameEntity.salt = NSDataFromPtr(maybe_salt); - Vec_u8_destroy(maybe_salt); - } - usernameEntity.domain = domain; - [entity addUsernamesObject:usernameEntity]; - [entity setDashpayUsername:usernameEntity]; - [context ds_save]; - [self notifyUsernameUpdate:@{ - DSChainManagerNotificationChainKey: self.chain, - DSIdentityKey: self - }]; - }]; -} - -- (void)setAndSaveUsernameFullPaths:(NSArray *)usernameFullPaths - toStatus:(DUsernameStatus *)status - inContext:(NSManagedObjectContext *)context { - Vec_String *username_full_paths = [NSArray ffi_to_vec_of_string:usernameFullPaths]; - dash_spv_platform_identity_model_IdentityModel_set_username_full_paths(self.identity_model, username_full_paths, status); - if (self.isTransient || !self.isActive) return; - Vec_Tuple_String_String *result = dash_spv_platform_identity_model_IdentityModel_usernames_and_domains(self.identity_model, username_full_paths); - [context performBlockAndWait:^{ - for (int i = 0; i < result->count; i++) { - Tuple_String_String *pair = result->values[i]; - NSString *username = NSStringFromPtr(pair->o_0); - NSString *domain = NSStringFromPtr(pair->o_1); - [self saveUsername:username - inDomain:domain - status:DUsernameStatusIndex(status) - salt:nil - commitSave:NO - inContext:context]; - } - [context ds_save]; - }]; - Vec_Tuple_String_String_destroy(result); -} - -- (void)saveUsernameFullPath:(NSString *)usernameFullPath - status:(DUsernameStatus *)status - salt:(NSData *)salt - commitSave:(BOOL)commitSave - inContext:(NSManagedObjectContext *)context { - if (self.isTransient || !self.isActive) return; - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *entity = [self identityEntityInContext:context]; - NSSet *usernamesPassingTest = [entity.usernames objectsPassingTest:^BOOL(DSBlockchainIdentityUsernameEntity *_Nonnull obj, BOOL *_Nonnull stop) { - BOOL isEqual = [[self fullPathForUsername:obj.stringValue inDomain:obj.domain] isEqualToString:usernameFullPath]; - if (isEqual) *stop = YES; - return isEqual; - }]; - if ([usernamesPassingTest count]) { - NSAssert([usernamesPassingTest count] == 1, @"There should never be more than 1"); - DSBlockchainIdentityUsernameEntity *usernameEntity = [usernamesPassingTest anyObject]; - usernameEntity.status = DUsernameStatusIndex(status); - if (salt) - usernameEntity.salt = salt; - if (commitSave) - [context ds_save]; - [self notifyUsernameUpdate:@{ - DSChainManagerNotificationChainKey: self.chain, - DSIdentityKey: self, - DSIdentityUsernameKey: usernameEntity.stringValue, - DSIdentityUsernameDomainKey: usernameEntity.domain - }]; - } - }]; -} - -- (void)saveUsername:(NSString *)username - inDomain:(NSString *)domain - status:(uint16_t)status - salt:(NSData *)salt - commitSave:(BOOL)commitSave - inContext:(NSManagedObjectContext *)context { - if (self.isTransient || !self.isActive) return; - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *entity = [self identityEntityInContext:context]; - NSSet *usernamesPassingTest = [entity.usernames objectsPassingTest:^BOOL(DSBlockchainIdentityUsernameEntity *_Nonnull obj, BOOL *_Nonnull stop) { - BOOL isEqual = [obj.stringValue isEqualToString:username]; - if (isEqual) *stop = YES; - return isEqual; - }]; - if ([usernamesPassingTest count]) { - NSAssert([usernamesPassingTest count] == 1, @"There should never be more than 1"); - DSBlockchainIdentityUsernameEntity *usernameEntity = [usernamesPassingTest anyObject]; - usernameEntity.status = status; - if (salt) - usernameEntity.salt = salt; - if (commitSave) - [context ds_save]; - [self notifyUsernameUpdate:@{ - DSChainManagerNotificationChainKey: self.chain, - DSIdentityKey: self, - DSIdentityUsernameKey: username, - DSIdentityUsernameDomainKey: domain - }]; - } - }]; -} +//- (NSMutableDictionary *)saltedDomainHashesForUsernameFullPaths:(NSArray *)usernameFullPaths +// inContext:(NSManagedObjectContext *)context { +// NSMutableDictionary *mSaltedDomainHashes = [NSMutableDictionary dictionary]; +// for (NSString *unregisteredUsernameFullPath in usernameFullPaths) { +// NSMutableData *saltedDomain = [NSMutableData data]; +// NSData *salt; +// BOOL is_initial = dash_spv_platform_identity_model_IdentityModel_status_of_username_full_path_is_initial(self.model, DChar(unregisteredUsernameFullPath)); +// u256 *maybe_salt = DSaltForUsername(self.model, unregisteredUsernameFullPath); +// if (is_initial || !maybe_salt) { +// salt = uint256_data(uint256_random); +// DAddSaltForUsername(self.model, unregisteredUsernameFullPath, u256_ctor(salt)); +// [self saveUsername:[self usernameOfUsernameFullPath:unregisteredUsernameFullPath] +// inDomain:[self domainOfUsernameFullPath:unregisteredUsernameFullPath] +// status:DUsernameStatusIndex([self statusOfUsernameFullPath:unregisteredUsernameFullPath]) +// salt:salt +// commitSave:YES +// inContext:context]; +// } else { +// salt = NSDataFromPtr(maybe_salt); +// } +// if (maybe_salt) +// u256_dtor(maybe_salt); +// +// [saltedDomain appendData:salt]; +// [saltedDomain appendData:[unregisteredUsernameFullPath dataUsingEncoding:NSUTF8StringEncoding]]; +// mSaltedDomainHashes[unregisteredUsernameFullPath] = uint256_data([saltedDomain SHA256_2]); +// DAddSaltForUsername(self.model, unregisteredUsernameFullPath, u256_ctor(salt)); +// } +// return [mSaltedDomainHashes copy]; +//} +// +//- (void)saveNewUsername:(NSString *)username +// inDomain:(NSString *)domain +// status:(uint16_t)status +// inContext:(NSManagedObjectContext *)context { +// NSAssert([username containsString:@"."] == FALSE, @"This is most likely an error"); +// NSAssert(domain, @"Domain must not be nil"); +// if (self.isTransient || !self.isActive) return; +// [context performBlockAndWait:^{ +// DSBlockchainIdentityEntity *entity = [self identityEntityInContext:context]; +// DSBlockchainIdentityUsernameEntity *usernameEntity = [DSBlockchainIdentityUsernameEntity managedObjectInBlockedContext:context]; +// usernameEntity.status = status; +// usernameEntity.stringValue = username; +// NSString *usernameFullPath = [self fullPathForUsername:username inDomain:domain]; +// BOOL is_initial = dash_spv_platform_identity_model_IdentityModel_status_of_username_full_path_is_initial(self.model, DChar(usernameFullPath)); +// u256 *maybe_salt = DSaltForUsername(self.model, usernameFullPath); +// if (is_initial || !maybe_salt) { +// NSData *salt = uint256_data(uint256_random); +// usernameEntity.salt = salt; +// DAddSaltForUsername(self.model, usernameFullPath, u256_ctor(salt)); +// } else { +// usernameEntity.salt = NSDataFromPtr(maybe_salt); +// } +// if (maybe_salt) +// u256_dtor(maybe_salt); +// usernameEntity.domain = domain; +// [entity addUsernamesObject:usernameEntity]; +// [entity setDashpayUsername:usernameEntity]; +// [context ds_save]; +// [self notifyUsernameUpdate:@{ +// DSChainManagerNotificationChainKey: self.chain, +// DSIdentityKey: self +// }]; +// }]; +//} +// +//- (void)setAndSaveUsernameFullPaths:(NSArray *)usernameFullPaths +// toStatus:(DUsernameStatus *)status +// inContext:(NSManagedObjectContext *)context { +// Vec_String *username_full_paths = [NSArray ffi_to_vec_of_string:usernameFullPaths]; +// dash_spv_platform_identity_model_IdentityModel_set_username_full_paths(self.model, username_full_paths, status); +// if (self.isTransient || !self.isActive) return; +// Vec_Tuple_String_String *result = dash_spv_platform_identity_model_IdentityModel_usernames_and_domains(self.model, username_full_paths); +// [context performBlockAndWait:^{ +// for (int i = 0; i < result->count; i++) { +// Tuple_String_String *pair = result->values[i]; +// NSString *username = NSStringFromPtr(pair->o_0); +// NSString *domain = NSStringFromPtr(pair->o_1); +// [self saveUsername:username +// inDomain:domain +// status:DUsernameStatusIndex(status) +// salt:nil +// commitSave:NO +// inContext:context]; +// } +// [context ds_save]; +// }]; +// Vec_Tuple_String_String_destroy(result); +//} +// +//- (void)saveUsernameFullPath:(NSString *)usernameFullPath +// status:(DUsernameStatus *)status +// salt:(NSData *)salt +// commitSave:(BOOL)commitSave +// inContext:(NSManagedObjectContext *)context { +// if (self.isTransient || !self.isActive) return; +// [context performBlockAndWait:^{ +// DSBlockchainIdentityEntity *entity = [self identityEntityInContext:context]; +// NSSet *usernamesPassingTest = [entity.usernames objectsPassingTest:^BOOL(DSBlockchainIdentityUsernameEntity *_Nonnull obj, BOOL *_Nonnull stop) { +// BOOL isEqual = [[self fullPathForUsername:obj.stringValue inDomain:obj.domain] isEqualToString:usernameFullPath]; +// if (isEqual) *stop = YES; +// return isEqual; +// }]; +// if ([usernamesPassingTest count]) { +// NSAssert([usernamesPassingTest count] == 1, @"There should never be more than 1"); +// DSBlockchainIdentityUsernameEntity *usernameEntity = [usernamesPassingTest anyObject]; +// usernameEntity.status = DUsernameStatusIndex(status); +// if (salt) +// usernameEntity.salt = salt; +// if (commitSave) +// [context ds_save]; +// [self notifyUsernameUpdate:@{ +// DSChainManagerNotificationChainKey: self.chain, +// DSIdentityKey: self, +// DSIdentityUsernameKey: usernameEntity.stringValue, +// DSIdentityUsernameDomainKey: usernameEntity.domain +// }]; +// } +// }]; +//} +// +//- (void)saveUsername:(NSString *)username +// inDomain:(NSString *)domain +// status:(uint16_t)status +// salt:(NSData *)salt +// commitSave:(BOOL)commitSave +// inContext:(NSManagedObjectContext *)context { +// if (self.isTransient || !self.isActive) return; +// [context performBlockAndWait:^{ +// DSBlockchainIdentityEntity *entity = [self identityEntityInContext:context]; +// NSSet *usernamesPassingTest = [entity.usernames objectsPassingTest:^BOOL(DSBlockchainIdentityUsernameEntity *_Nonnull obj, BOOL *_Nonnull stop) { +// BOOL isEqual = [obj.stringValue isEqualToString:username]; +// if (isEqual) *stop = YES; +// return isEqual; +// }]; +// if ([usernamesPassingTest count]) { +// NSAssert([usernamesPassingTest count] == 1, @"There should never be more than 1"); +// DSBlockchainIdentityUsernameEntity *usernameEntity = [usernamesPassingTest anyObject]; +// usernameEntity.status = status; +// if (salt) +// usernameEntity.salt = salt; +// if (commitSave) +// [context ds_save]; +// [self notifyUsernameUpdate:@{ +// DSChainManagerNotificationChainKey: self.chain, +// DSIdentityKey: self, +// DSIdentityUsernameKey: username, +// DSIdentityUsernameDomainKey: domain +// }]; +// } +// }]; +//} @@ -340,6 +362,10 @@ - (void)fetchUsernamesInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(BOOL success, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue { NSMutableString *debugInfo = [NSMutableString stringWithFormat:@"%@ Fetch Usernames", self.logPrefix]; + + + + DSLog(@"%@", debugInfo); DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dpnsContract; if (contract.contractState != DPContractState_Registered) { @@ -348,175 +374,158 @@ - (void)fetchUsernamesInContext:(NSManagedObjectContext *)context return; } - DMaybeDocumentsMap *result = dash_spv_platform_document_manager_DocumentsManager_stream_dpns_documents_for_identity_with_user_id_using_contract(self.chain.sharedRuntime, self.chain.sharedDocumentsObj, u256_ctor_u(self.uniqueID), contract.raw_contract, DRetryLinear(DEFAULT_FETCH_USERNAMES_RETRY_COUNT), DNotFoundAsAnError(), 1000); + Result_ok_bool_err_dash_spv_platform_error_Error *result = dash_spv_platform_document_manager_DocumentsManager_fetch_usernames(self.chain.sharedRuntime, self.chain.sharedDocumentsObj, self.model, contract.raw_contract, ((__bridge void *)(self))); if (result->error) { NSError *error = [NSError ffi_from_platform_error:result->error]; - DMaybeDocumentsMapDtor(result); + Result_ok_bool_err_dash_spv_platform_error_Error_destroy(result); +// DMaybeDocumentsMapDtor(result); DSLog(@"%@: ERROR: %@", debugInfo, error); if (completion) dispatch_async(completionQueue, ^{ completion(NO, error); }); return; } - DDocumentsMap *documents = result->ok; - if (documents->count == 0) { - DMaybeDocumentsMapDtor(result); - DSLog(@"%@: ERROR: No documents", debugInfo); - if (completion) dispatch_async(completionQueue, ^{ completion(YES, nil); }); - return; - } - [debugInfo appendFormat:@"docs: %lu", documents->count]; - for (int i = 0; i < documents->count; i++) { - DDocument *document = documents->values[i]; - switch (document->tag) { - case dpp_document_Document_V0: { - NSString *username = DGetTextDocProperty(document, @"label"); - NSString *lowercaseUsername = DGetTextDocProperty(document, @"normalizedLabel"); - NSString *domain = DGetTextDocProperty(document, @"normalizedParentDomainName"); - [debugInfo appendFormat:@"\t%i: %@ -- %@ -- %@", i, username, lowercaseUsername, domain]; - - if (username && lowercaseUsername && domain) { - BOOL isNew = dash_spv_platform_identity_model_IdentityModel_set_username_status_confirmed2(self.identity_model, DChar(username), DChar(domain), DChar(lowercaseUsername)); - if (isNew) { - [self saveNewUsername:username - inDomain:domain - status:dash_spv_platform_document_usernames_UsernameStatus_Confirmed - inContext:context]; - } else { - [self saveUsername:username - inDomain:domain - status:dash_spv_platform_document_usernames_UsernameStatus_Confirmed - salt:nil - commitSave:YES - inContext:context]; - } - } - - break; - } - default: - break; - } - } - DSLog(@"%@: OK", debugInfo); - DMaybeDocumentsMapDtor(result); if (completion) dispatch_async(completionQueue, ^{ completion(YES, nil); }); + + + + +// DMaybeDocumentsMap *result = dash_spv_platform_document_manager_DocumentsManager_stream_dpns_documents_for_identity_with_user_id_using_contract(self.chain.sharedRuntime, self.chain.sharedDocumentsObj, u256_ctor_u(self.uniqueID), contract.raw_contract, DRetryLinear(DEFAULT_FETCH_USERNAMES_RETRY_COUNT), DNotFoundAsAnError(), 1000); +// if (result->error) { +// NSError *error = [NSError ffi_from_platform_error:result->error]; +// DMaybeDocumentsMapDtor(result); +// DSLog(@"%@: ERROR: %@", debugInfo, error); +// if (completion) dispatch_async(completionQueue, ^{ completion(NO, error); }); +// return; +// } +// DDocumentsMap *documents = result->ok; +// if (documents->count == 0) { +// DMaybeDocumentsMapDtor(result); +// DSLog(@"%@: ERROR: No documents", debugInfo); +// if (completion) dispatch_async(completionQueue, ^{ completion(YES, nil); }); +// return; +// } +// [debugInfo appendFormat:@"docs: %lu", documents->count]; +// for (int i = 0; i < documents->count; i++) { +// DDocument *document = documents->values[i]; +// switch (document->tag) { +// case dpp_document_Document_V0: { +// NSString *username = DGetTextDocProperty(document, @"label"); +// NSString *lowercaseUsername = DGetTextDocProperty(document, @"normalizedLabel"); +// NSString *domain = DGetTextDocProperty(document, @"normalizedParentDomainName"); +// [debugInfo appendFormat:@"\t%i: %@ -- %@ -- %@", i, username, lowercaseUsername, domain]; +// +// if (username && lowercaseUsername && domain) { +// BOOL isNew = dash_spv_platform_identity_model_IdentityModel_set_username_status_confirmed2(self.model, DChar(username), DChar(domain), DChar(lowercaseUsername)); +// if (isNew) { +// [self saveNewUsername:username +// inDomain:domain +// status:dash_spv_platform_document_usernames_UsernameStatus_Confirmed +// inContext:context]; +// } else { +// [self saveUsername:username +// inDomain:domain +// status:dash_spv_platform_document_usernames_UsernameStatus_Confirmed +// salt:nil +// commitSave:YES +// inContext:context]; +// } +// } +// +// break; +// } +// default: +// break; +// } +// } +// DSLog(@"%@: OK", debugInfo); +// DMaybeDocumentsMapDtor(result); +// if (completion) dispatch_async(completionQueue, ^{ completion(YES, nil); }); } - (void)registerUsernamesWithCompletion:(void (^_Nullable)(BOOL success, NSArray *errors))completion { [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_Initial_ctor() - inContext:self.platformContext completion:completion + inContext:self.platformContext + completion:completion onCompletionQueue:dispatch_get_main_queue()]; } -- (NSError *_Nullable)registerUsernameWithSaltedDomainHash:(NSData *)saltedDomainHashData - usingContract:(DDataContract *)contract - andEntropyData:(NSData *)entropyData - withIdentityPublicKey:(DIdentityPublicKey *)identity_public_key - withPrivateKey:(DMaybeOpaqueKey *)maybe_private_key { - NSMutableString *debugInfo = [NSMutableString stringWithFormat:@"[%@] Register Username With SaltedDomainHash [%@]", self.logPrefix, saltedDomainHashData.hexString]; - DDocumentResult *result = dash_spv_platform_PlatformSDK_register_preordered_salted_domain_hash_for_username_full_path(self.chain.sharedRuntime, self.chain.sharedPlatformObj, contract, u256_ctor_u(self.uniqueID), identity_public_key, bytes_ctor(saltedDomainHashData), u256_ctor(entropyData)); - if (result->error) { - NSError *error = [NSError ffi_from_platform_error:result->error]; - DDocumentResultDtor(result); - DSLog(@"%@: ERROR: (%@)", debugInfo, error); - return error; - } - DDocument *document = result->ok; - switch (document->tag) { - case dpp_document_Document_V0: { - DSLog(@"%@: OK: (%@)", debugInfo, u256_hex(document->v0->id->_0->_0)); - DDocumentResultDtor(result); - return nil; - } - default: { - NSError *error = ERROR_UNSUPPORTED_DOCUMENT_VERSION(document->tag); - DSLog(@"%@: ERROR: (%@)", debugInfo, error); - DDocumentResultDtor(result); - return error; - } - } -} +//- (NSError *_Nullable)registerUsernameWithSaltedDomainHash:(NSData *)saltedDomainHashData +// usingContract:(DDataContract *)contract +// andEntropyData:(NSData *)entropyData +// withIdentityPublicKey:(DIdentityPublicKey *)identity_public_key +// withPrivateKey:(DMaybeOpaqueKey *)maybe_private_key { +// NSMutableString *debugInfo = [NSMutableString stringWithFormat:@"[%@] Register Username With SaltedDomainHash [%@]", self.logPrefix, saltedDomainHashData.hexString]; +// DDocumentResult *result = dash_spv_platform_PlatformSDK_register_preordered_salted_domain_hash_for_username_full_path(self.chain.sharedRuntime, self.chain.sharedPlatformObj, contract, u256_ctor_u(self.uniqueID), identity_public_key, bytes_ctor(saltedDomainHashData), u256_ctor(entropyData)); +// if (result->error) { +// NSError *error = [NSError ffi_from_platform_error:result->error]; +// DDocumentResultDtor(result); +// DSLog(@"%@: ERROR: (%@)", debugInfo, error); +// return error; +// } +// DDocument *document = result->ok; +// switch (document->tag) { +// case dpp_document_Document_V0: { +// DSLog(@"%@: OK: (%@)", debugInfo, u256_hex(document->v0->id->_0->_0)); +// DDocumentResultDtor(result); +// return nil; +// } +// default: { +// NSError *error = ERROR_UNSUPPORTED_DOCUMENT_VERSION(document->tag); +// DSLog(@"%@: ERROR: (%@)", debugInfo, error); +// DDocumentResultDtor(result); +// return error; +// } +// } +//} - (void)registerUsernamesAtStage:(DUsernameStatus *)status inContext:(NSManagedObjectContext *)context completion:(void (^_Nullable)(BOOL success, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { NSMutableString *debugInfo = [NSMutableString stringWithFormat:@"%@ Register Usernames At Stage [%hhu]", self.logPrefix, DUsernameStatusIndex(status)]; - DSLog(@"%@", debugInfo); - Vec_String *result = dash_spv_platform_identity_model_IdentityModel_username_full_paths_with_status(self.identity_model, status); - NSArray *usernameFullPaths = [NSArray ffi_from_vec_of_string:result]; - Vec_String_destroy(result); - uint8_t status_index = DUsernameStatusIndex(status); - switch (status_index) { - case dash_spv_platform_document_usernames_UsernameStatus_Initial: { - [debugInfo appendFormat:@" (Initial) usernameFullPaths: %@\n", usernameFullPaths]; - if (usernameFullPaths.count) { - NSData *entropyData = uint256_random_data; - NSDictionary *saltedDomainHashesForUsernameFullPaths = [self saltedDomainHashesForUsernameFullPaths:usernameFullPaths inContext:context]; - [debugInfo appendFormat:@" saltedDomainHashesForUsernameFullPaths: %@\n", saltedDomainHashesForUsernameFullPaths]; - if (![saltedDomainHashesForUsernameFullPaths count]) { - DSLog(@"%@ ERROR: No usernamePreorderDocuments", debugInfo); + Result_ok_bool_err_dash_spv_platform_error_Error *result = dash_spv_platform_PlatformSDK_register_usernames_at_stage(self.chain.sharedRuntime, self.chain.sharedPlatformObj, self.model, status, ((__bridge void *)(context))); + + + if (result->error) { + switch (result->error->tag) { + case dash_spv_platform_error_Error_UsernameRegistrationError: { + DUsernameStatus *next_status = dash_spv_platform_document_usernames_UsernameStatus_next_status(status); + BOOL proceedToNext = result->error->username_registration_error->tag != dash_spv_platform_identity_username_registration_error_UsernameRegistrationError_NotSupported && next_status != nil; + Result_ok_bool_err_dash_spv_platform_error_Error_destroy(result); + if (proceedToNext) { + [self registerUsernamesAtStage:next_status inContext:context completion:completion onCompletionQueue:completionQueue]; + } else { if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil); }); - return; - } - DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dpnsContract; - DMaybeOpaqueKey *private_key = [self privateKeyAtIndex:self.currentMainKeyIndex ofType:self.currentMainKeyType]; - - if (!self.keysCreated) { - uint32_t index; - [self createNewKeyOfType:DKeyKindECDSA() - securityLevel:DSecurityLevelHigh() - purpose:DPurposeAuth() - saveKey:!self.wallet.isTransient - returnIndex:&index]; - } - - DSecurityLevel *level = DSecurityLevelHigh(); - DPurpose *purpose = DPurposeAuth(); - DIdentityPublicKey *maybe_identity_public_key = [self firstIdentityPublicKeyOfSecurityLevel:level andPurpose:purpose]; - if (!maybe_identity_public_key) { - DSecurityLevelDtor(level); - DPurposeDtor(purpose); - NSAssert(NULL, @"Key with security_level: HIGH and purpose: AUTHENTICATION should exist"); - } - - uintptr_t i = 0; - NSMutableArray *errors = [NSMutableArray array]; - for (NSData *saltedDomainHashData in [saltedDomainHashesForUsernameFullPaths allValues]) { - - NSError *error = [self registerUsernameWithSaltedDomainHash:saltedDomainHashData - usingContract:contract.raw_contract - andEntropyData:entropyData - withIdentityPublicKey:maybe_identity_public_key - withPrivateKey:private_key]; - if (error) [errors addObject:error]; - i++; - } - if ([errors count]) { - if (completion) dispatch_async(completionQueue, ^{ completion(NO, [errors copy]); }); - return; } - DSLog(@"%@: OK", debugInfo); - if (completion) - dispatch_async(self.identityQueue, ^{ - [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_PreorderRegistrationPending_ctor() - inContext:context - completion:completion - onCompletionQueue:completionQueue]; - }); - } else { - DSLog(@"%@: Ok (No usernameFullPaths)", debugInfo); - [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_PreorderRegistrationPending_ctor() - inContext:context - completion:completion - onCompletionQueue:completionQueue]; + break; + } + default: { + Result_ok_bool_err_dash_spv_platform_error_Error_destroy(result); + if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil); }); + break; } - break; } -// case DSIdentityUsernameStatus_Initial: { + return; + } + DUsernameStatus *next_status = dash_spv_platform_document_usernames_UsernameStatus_next_status(status); + BOOL ok = result->ok[0]; + Result_ok_bool_err_dash_spv_platform_error_Error_destroy(result); + if (next_status) { + [self registerUsernamesAtStage:next_status inContext:context completion:completion onCompletionQueue:completionQueue]; + } else { + if (completion) dispatch_async(completionQueue, ^{ completion(ok, nil); }); + } + + +// Vec_String *result = dash_spv_platform_identity_model_IdentityModel_username_full_paths_with_status(self.model, status); +// NSArray *usernameFullPaths = [NSArray ffi_from_vec_of_string:result]; +// Vec_String_destroy(result); +// +// uint8_t status_index = DUsernameStatusIndex(status); +// switch (status_index) { +// case dash_spv_platform_document_usernames_UsernameStatus_Initial: { // [debugInfo appendFormat:@" (Initial) usernameFullPaths: %@\n", usernameFullPaths]; // if (usernameFullPaths.count) { -// NSData *entropyData = uint256_random_data; // NSDictionary *saltedDomainHashesForUsernameFullPaths = [self saltedDomainHashesForUsernameFullPaths:usernameFullPaths inContext:context]; // [debugInfo appendFormat:@" saltedDomainHashesForUsernameFullPaths: %@\n", saltedDomainHashesForUsernameFullPaths]; // if (![saltedDomainHashesForUsernameFullPaths count]) { @@ -524,285 +533,352 @@ - (void)registerUsernamesAtStage:(DUsernameStatus *)status // if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil); }); // return; // } +// +// if (!self.keysCreated) { +// uint32_t index; +// [self createNewKeyOfType:DKeyKindECDSA() +// securityLevel:DSecurityLevelHigh() +// purpose:DPurposeAuth() +// saveKey:!self.wallet.isTransient +// returnIndex:&index]; +// } +// +// DSecurityLevel *level = DSecurityLevelHigh(); +// DPurpose *purpose = DPurposeAuth(); +// DIdentityPublicKey *maybe_identity_public_key = [self firstIdentityPublicKeyOfSecurityLevel:level andPurpose:purpose]; +// if (!maybe_identity_public_key) { +// DSecurityLevelDtor(level); +// DPurposeDtor(purpose); +// NSAssert(NULL, @"Key with security_level: HIGH and purpose: AUTHENTICATION should exist"); +// } +// // uintptr_t i = 0; -// Vec_u8 **salted_domain_hashes_values = malloc(sizeof(Vec_u8 *) * saltedDomainHashesForUsernameFullPaths.count); +// NSData *entropyData = uint256_random_data; +// DDataContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dpnsRawContract; +// DMaybeOpaqueKey *private_key = [self privateKeyAtIndex:self.currentMainKeyIndex ofType:self.currentMainKeyType]; +// +// NSMutableArray *errors = [NSMutableArray array]; // for (NSData *saltedDomainHashData in [saltedDomainHashesForUsernameFullPaths allValues]) { -// salted_domain_hashes_values[i] = bytes_ctor(saltedDomainHashData); +// +// NSError *error = [self registerUsernameWithSaltedDomainHash:saltedDomainHashData +// usingContract:contract +// andEntropyData:entropyData +// withIdentityPublicKey:maybe_identity_public_key +// withPrivateKey:private_key]; +// if (error) [errors addObject:error]; +// i++; +// } +// if ([errors count]) { +// if (completion) dispatch_async(completionQueue, ^{ completion(NO, [errors copy]); }); +// return; +// } +// DSLog(@"%@: OK", debugInfo); +// if (completion) +// dispatch_async(self.identityQueue, ^{ +// [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_PreorderRegistrationPending_ctor() +// inContext:context +// completion:completion +// onCompletionQueue:completionQueue]; +// }); +// } else { +// DSLog(@"%@: Ok (No usernameFullPaths)", debugInfo); +// [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_PreorderRegistrationPending_ctor() +// inContext:context +// completion:completion +// onCompletionQueue:completionQueue]; +// } +// break; +// } +//// case DSIdentityUsernameStatus_Initial: { +//// [debugInfo appendFormat:@" (Initial) usernameFullPaths: %@\n", usernameFullPaths]; +//// if (usernameFullPaths.count) { +//// NSData *entropyData = uint256_random_data; +//// NSDictionary *saltedDomainHashesForUsernameFullPaths = [self saltedDomainHashesForUsernameFullPaths:usernameFullPaths inContext:context]; +//// [debugInfo appendFormat:@" saltedDomainHashesForUsernameFullPaths: %@\n", saltedDomainHashesForUsernameFullPaths]; +//// if (![saltedDomainHashesForUsernameFullPaths count]) { +//// DSLog(@"%@ ERROR: No usernamePreorderDocuments", debugInfo); +//// if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil); }); +//// return; +//// } +//// uintptr_t i = 0; +//// Vec_u8 **salted_domain_hashes_values = malloc(sizeof(Vec_u8 *) * saltedDomainHashesForUsernameFullPaths.count); +//// for (NSData *saltedDomainHashData in [saltedDomainHashesForUsernameFullPaths allValues]) { +//// salted_domain_hashes_values[i] = bytes_ctor(saltedDomainHashData); +//// i++; +//// } +//// if (!self.keysCreated) { +//// uint32_t index; +//// [self createNewKeyOfType:DKeyKindECDSA() saveKey:!self.wallet.isTransient returnIndex:&index]; +//// } +//// DMaybeOpaqueKey *private_key = [self privateKeyAtIndex:self.currentMainKeyIndex ofType:self.currentMainKeyType]; +//// DSUsernameFullPathSaveContext *saveContext = [DSUsernameFullPathSaveContext contextWithUsernames:usernameFullPaths forIdentity:self inContext:context]; +//// DUsernameStatusCallback save_callback = { .caller = &usernames_save_context_caller }; +//// DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dpnsContract; +//// DMaybeStateTransitionProofResult *result = dash_spv_platform_PlatformSDK_register_preordered_salted_domain_hashes_for_username_full_paths(self.chain.sharedRuntime, self.chain.sharedPlatformObj, contract.raw_contract, u256_ctor_u(self.uniqueID), Vec_Vec_u8_ctor(i, salted_domain_hashes_values), u256_ctor(entropyData), private_key->ok, ((__bridge void *)(saveContext)), save_callback); +//// if (result->error) { +//// NSError *error = [NSError ffi_from_platform_error:result->error]; +//// DMaybeStateTransitionProofResultDtor(result); +//// DSLog(@"%@: ERROR: %@", debugInfo, error); +//// if (completion) dispatch_async(completionQueue, ^{ completion(NO, error); }); +//// return; +//// } +//// DMaybeStateTransitionProofResultDtor(result); +//// DSLog(@"%@: OK", debugInfo); +//// if (completion) +//// dispatch_async(self.identityQueue, ^{ +//// [self registerUsernamesAtStage:DSIdentityUsernameStatus_PreorderRegistrationPending +//// inContext:context +//// completion:completion +//// onCompletionQueue:completionQueue]; +//// }); +//// } else { +//// DSLog(@"%@: Ok (No usernameFullPaths)", debugInfo); +//// [self registerUsernamesAtStage:DSIdentityUsernameStatus_PreorderRegistrationPending +//// inContext:context +//// completion:completion +//// onCompletionQueue:completionQueue]; +//// } +//// break; +//// } +// case dash_spv_platform_document_usernames_UsernameStatus_PreorderRegistrationPending: { +// [debugInfo appendFormat:@" (PreorderRegistrationPending) usernameFullPaths: %@", usernameFullPaths]; +// NSDictionary *saltedDomainHashes = [self saltedDomainHashesForUsernameFullPaths:usernameFullPaths inContext:context]; +// [debugInfo appendFormat:@", saltedDomainHashes: %@", saltedDomainHashes]; +// if (saltedDomainHashes.count) { +// Vec_u8_32 *vec_hashes = [NSArray ffi_to_vec_u256:[saltedDomainHashes allValues]]; +// DMaybeDocumentsMap *result = dash_spv_platform_document_salted_domain_hashes_SaltedDomainHashesManager_preorder_salted_domain_hashes_stream(self.chain.sharedRuntime, self.chain.sharedSaltedDomainHashesObj, vec_hashes, DRetryLinear(4), dash_spv_platform_document_salted_domain_hashes_SaltedDomainHashValidator_None_ctor(), 100); +// BOOL allFound = NO; +// if (result->error) { +// NSError *error = [NSError ffi_from_platform_error:result->error]; +// DMaybeDocumentsMapDtor(result); +// DSLog(@"%@: ERROR: %@", debugInfo, error); +// if (completion) dispatch_async(self.identityQueue, ^{ completion(allFound, @[error]); }); +// return; +// } +// for (NSString *usernameFullPath in saltedDomainHashes) { +// NSData *saltedDomainHashData = saltedDomainHashes[usernameFullPath]; +// DDocumentsMap *documents = result->ok; +// for (int i = 0; i < documents->count; i++) { +// allFound &= [self processSaltedDomainHashDocument:usernameFullPath +// hash:saltedDomainHashData +// document:documents->values[i] +// inContext:context]; +// } +// } +// DSLog(@"%@: OK: allFound: %u", debugInfo, allFound); +// DMaybeDocumentsMapDtor(result); +// if (completion) +// dispatch_async(self.identityQueue, ^{ +// if (allFound) { +// [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_Preordered_ctor() +// inContext:context +// completion:completion +// onCompletionQueue:completionQueue]; +// } else { +// //todo: This needs to be done per username and not for all usernames +// [self setAndSaveUsernameFullPaths:usernameFullPaths +// toStatus:dash_spv_platform_document_usernames_UsernameStatus_Initial_ctor() +// inContext:context]; +// [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_Initial_ctor() +// inContext:context +// completion:completion +// onCompletionQueue:completionQueue]; +// } +// }); +// } else { +// DSLog(@"%@: OK (No saltedDomainHashes)", debugInfo); +// [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_Preordered_ctor() +// inContext:context +// completion:completion +// onCompletionQueue:completionQueue]; +// } +// break; +// } +// case dash_spv_platform_document_usernames_UsernameStatus_Preordered: { +// [debugInfo appendFormat:@" (Preordered) usernameFullPaths: %@: ", usernameFullPaths]; +// if (usernameFullPaths.count) { +// NSError *error = nil; +// NSData *entropyData = uint256_random_data; +// NSDictionary *saltedDomainHashesForUsernameFullPaths = [self saltedDomainHashesForUsernameFullPaths:usernameFullPaths inContext:context]; +// if (![saltedDomainHashesForUsernameFullPaths count]) { +// DSLog(@"%@ ERROR: No username preorder documents", debugInfo); +// if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[error]); }); +// return; +// } +// +// uintptr_t i = 0; +// DValue **values_values = malloc(sizeof(DValue *) * saltedDomainHashesForUsernameFullPaths.count); +// for (NSString *usernameFullPath in saltedDomainHashesForUsernameFullPaths) { +// NSString *username = [self usernameOfUsernameFullPath:usernameFullPath]; +// NSString *domain = [self domainOfUsernameFullPath:usernameFullPath]; +// DValuePair **values = malloc(sizeof(DValuePair *) * 6); +// DValuePair **records = malloc(sizeof(DValuePair *) * 1); +// DValuePair **subdomain_rules = malloc(sizeof(DValuePair *) * 1); +// records[0] = DValueTextBytesPairCtor(@"identity", uint256_data(self.uniqueID)); +// subdomain_rules[0] = DValueTextBoolPairCtor(@"allowSubdomains", false); +// values[0] = DValueTextTextPairCtor(@"label", username); +// values[1] = DValueTextTextPairCtor(@"normalizedLabel", [username lowercaseString]); +// values[2] = DValueTextTextPairCtor(@"normalizedParentDomainName", domain); +// values[3] = DValueTextPairCtor(@"preorderSalt", platform_value_Value_Bytes32_ctor(DSaltForUsername(self.model, usernameFullPath))); +// values[4] = DValueTextMapPairCtor(@"records", DValueMapCtor(DValuePairVecCtor(1, records))); +// values[5] = DValueTextMapPairCtor(@"subdomainRules", DValueMapCtor(DValuePairVecCtor(1, subdomain_rules))); +// values_values[i] = platform_value_Value_Map_ctor(DValueMapCtor(DValuePairVecCtor(6, values))); // i++; // } // if (!self.keysCreated) { // uint32_t index; -// [self createNewKeyOfType:DKeyKindECDSA() saveKey:!self.wallet.isTransient returnIndex:&index]; +// [self createNewKeyOfType:DKeyKindECDSA() +// securityLevel:DSecurityLevelMaster() +// purpose:DPurposeAuth() +// saveKey:!self.wallet.isTransient +// returnIndex:&index]; // } // DMaybeOpaqueKey *private_key = [self privateKeyAtIndex:self.currentMainKeyIndex ofType:self.currentMainKeyType]; // DSUsernameFullPathSaveContext *saveContext = [DSUsernameFullPathSaveContext contextWithUsernames:usernameFullPaths forIdentity:self inContext:context]; // DUsernameStatusCallback save_callback = { .caller = &usernames_save_context_caller }; // DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dpnsContract; -// DMaybeStateTransitionProofResult *result = dash_spv_platform_PlatformSDK_register_preordered_salted_domain_hashes_for_username_full_paths(self.chain.sharedRuntime, self.chain.sharedPlatformObj, contract.raw_contract, u256_ctor_u(self.uniqueID), Vec_Vec_u8_ctor(i, salted_domain_hashes_values), u256_ctor(entropyData), private_key->ok, ((__bridge void *)(saveContext)), save_callback); +// DMaybeStateTransitionProofResult *result = dash_spv_platform_PlatformSDK_register_username_domains_for_username_full_paths(self.chain.sharedRuntime, self.chain.sharedPlatformObj, contract.raw_contract, u256_ctor_u(self.uniqueID), DValueVecCtor(i, values_values), u256_ctor(entropyData), private_key->ok, ((__bridge void *)(saveContext)), save_callback); // if (result->error) { // NSError *error = [NSError ffi_from_platform_error:result->error]; // DMaybeStateTransitionProofResultDtor(result); // DSLog(@"%@: ERROR: %@", debugInfo, error); -// if (completion) dispatch_async(completionQueue, ^{ completion(NO, error); }); +// if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[error]); }); // return; // } // DMaybeStateTransitionProofResultDtor(result); // DSLog(@"%@: OK", debugInfo); // if (completion) -// dispatch_async(self.identityQueue, ^{ -// [self registerUsernamesAtStage:DSIdentityUsernameStatus_PreorderRegistrationPending +// dispatch_async(completionQueue, ^{ +// [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_RegistrationPending_ctor() // inContext:context // completion:completion // onCompletionQueue:completionQueue]; // }); // } else { -// DSLog(@"%@: Ok (No usernameFullPaths)", debugInfo); -// [self registerUsernamesAtStage:DSIdentityUsernameStatus_PreorderRegistrationPending +// DSLog(@"%@: OK (No usernameFullPaths)", debugInfo); +// [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_RegistrationPending_ctor() // inContext:context // completion:completion // onCompletionQueue:completionQueue]; // } // break; // } - case dash_spv_platform_document_usernames_UsernameStatus_PreorderRegistrationPending: { - [debugInfo appendFormat:@" (PreorderRegistrationPending) usernameFullPaths: %@", usernameFullPaths]; - NSDictionary *saltedDomainHashes = [self saltedDomainHashesForUsernameFullPaths:usernameFullPaths inContext:context]; - [debugInfo appendFormat:@", saltedDomainHashes: %@", saltedDomainHashes]; - if (saltedDomainHashes.count) { - Vec_Vec_u8 *vec_hashes = [NSArray ffi_to_vec_vec_u8:[saltedDomainHashes allValues]]; - DMaybeDocumentsMap *result = dash_spv_platform_document_salted_domain_hashes_SaltedDomainHashesManager_preorder_salted_domain_hashes_stream(self.chain.sharedRuntime, self.chain.sharedSaltedDomainHashesObj, vec_hashes, DRetryLinear(4), dash_spv_platform_document_salted_domain_hashes_SaltedDomainHashValidator_None_ctor(), 100); - BOOL allFound = NO; - if (result->error) { - NSError *error = [NSError ffi_from_platform_error:result->error]; - DMaybeDocumentsMapDtor(result); - DSLog(@"%@: ERROR: %@", debugInfo, error); - if (completion) dispatch_async(self.identityQueue, ^{ completion(allFound, @[error]); }); - return; - } - for (NSString *usernameFullPath in saltedDomainHashes) { - NSData *saltedDomainHashData = saltedDomainHashes[usernameFullPath]; - DDocumentsMap *documents = result->ok; - for (int i = 0; i < documents->count; i++) { - allFound &= [self processSaltedDomainHashDocument:usernameFullPath - hash:saltedDomainHashData - document:documents->values[i] - inContext:context]; - } - } - DSLog(@"%@: OK: allFound: %u", debugInfo, allFound); - DMaybeDocumentsMapDtor(result); - if (completion) - dispatch_async(self.identityQueue, ^{ - if (allFound) { - [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_Preordered_ctor() - inContext:context - completion:completion - onCompletionQueue:completionQueue]; - } else { - //todo: This needs to be done per username and not for all usernames - [self setAndSaveUsernameFullPaths:usernameFullPaths - toStatus:dash_spv_platform_document_usernames_UsernameStatus_Initial_ctor() - inContext:context]; - [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_Initial_ctor() - inContext:context - completion:completion - onCompletionQueue:completionQueue]; - } - }); - } else { - DSLog(@"%@: OK (No saltedDomainHashes)", debugInfo); - [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_Preordered_ctor() - inContext:context - completion:completion - onCompletionQueue:completionQueue]; - } - break; - } - case dash_spv_platform_document_usernames_UsernameStatus_Preordered: { - [debugInfo appendFormat:@" (Preordered) usernameFullPaths: %@: ", usernameFullPaths]; - if (usernameFullPaths.count) { - NSError *error = nil; - NSData *entropyData = uint256_random_data; - NSDictionary *saltedDomainHashesForUsernameFullPaths = [self saltedDomainHashesForUsernameFullPaths:usernameFullPaths inContext:context]; - if (![saltedDomainHashesForUsernameFullPaths count]) { - DSLog(@"%@ ERROR: No username preorder documents", debugInfo); - if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[error]); }); - return; - } - - uintptr_t i = 0; - DValue **values_values = malloc(sizeof(DValue *) * saltedDomainHashesForUsernameFullPaths.count); - for (NSString *usernameFullPath in saltedDomainHashesForUsernameFullPaths) { - NSString *username = [self usernameOfUsernameFullPath:usernameFullPath]; - NSString *domain = [self domainOfUsernameFullPath:usernameFullPath]; - DValuePair **values = malloc(sizeof(DValuePair *) * 6); - DValuePair **records = malloc(sizeof(DValuePair *) * 1); - DValuePair **subdomain_rules = malloc(sizeof(DValuePair *) * 1); - records[0] = DValueTextBytesPairCtor(@"identity", uint256_data(self.uniqueID)); - subdomain_rules[0] = DValueTextBoolPairCtor(@"allowSubdomains", false); - values[0] = DValueTextTextPairCtor(@"label", username); - values[1] = DValueTextTextPairCtor(@"normalizedLabel", [username lowercaseString]); - values[2] = DValueTextTextPairCtor(@"normalizedParentDomainName", domain); - values[3] = DValueTextPairCtor(@"preorderSalt", platform_value_Value_Bytes_ctor(DSaltForUsername(self.identity_model, usernameFullPath))); - values[4] = DValueTextMapPairCtor(@"records", DValueMapCtor(DValuePairVecCtor(1, records))); - values[5] = DValueTextMapPairCtor(@"subdomainRules", DValueMapCtor(DValuePairVecCtor(1, subdomain_rules))); - values_values[i] = platform_value_Value_Map_ctor(DValueMapCtor(DValuePairVecCtor(6, values))); - i++; - } - if (!self.keysCreated) { - uint32_t index; - [self createNewKeyOfType:DKeyKindECDSA() - securityLevel:DSecurityLevelMaster() - purpose:DPurposeAuth() - saveKey:!self.wallet.isTransient - returnIndex:&index]; - } - DMaybeOpaqueKey *private_key = [self privateKeyAtIndex:self.currentMainKeyIndex ofType:self.currentMainKeyType]; - DSUsernameFullPathSaveContext *saveContext = [DSUsernameFullPathSaveContext contextWithUsernames:usernameFullPaths forIdentity:self inContext:context]; - DUsernameStatusCallback save_callback = { .caller = &usernames_save_context_caller }; - DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dpnsContract; - DMaybeStateTransitionProofResult *result = dash_spv_platform_PlatformSDK_register_username_domains_for_username_full_paths(self.chain.sharedRuntime, self.chain.sharedPlatformObj, contract.raw_contract, u256_ctor_u(self.uniqueID), DValueVecCtor(i, values_values), u256_ctor(entropyData), private_key->ok, ((__bridge void *)(saveContext)), save_callback); - if (result->error) { - NSError *error = [NSError ffi_from_platform_error:result->error]; - DMaybeStateTransitionProofResultDtor(result); - DSLog(@"%@: ERROR: %@", debugInfo, error); - if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[error]); }); - return; - } - DMaybeStateTransitionProofResultDtor(result); - DSLog(@"%@: OK", debugInfo); - if (completion) - dispatch_async(completionQueue, ^{ - [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_RegistrationPending_ctor() - inContext:context - completion:completion - onCompletionQueue:completionQueue]; - }); - } else { - DSLog(@"%@: OK (No usernameFullPaths)", debugInfo); - [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_RegistrationPending_ctor() - inContext:context - completion:completion - onCompletionQueue:completionQueue]; - } - break; - } - case dash_spv_platform_document_usernames_UsernameStatus_RegistrationPending: { - [debugInfo appendFormat:@" (RegistrationPending) usernameFullPaths: %@", usernameFullPaths]; - if (usernameFullPaths.count) { - NSMutableDictionary *domains = [NSMutableDictionary dictionary]; - for (NSString *usernameFullPath in usernameFullPaths) { - NSArray *components = [usernameFullPath componentsSeparatedByString:@"."]; - NSString *domain = @""; - NSString *name = components[0]; - if (components.count > 1) - domain = [[components subarrayWithRange:NSMakeRange(1, components.count - 1)] componentsJoinedByString:@"."]; - if (!domains[domain]) - domains[domain] = [NSMutableArray array]; - [domains[domain] addObject:name]; - } - __block BOOL finished = FALSE; - __block NSUInteger countAllFound = 0; - __block NSUInteger countReturned = 0; - for (NSString *domain in domains) { - NSArray *usernames = domains[domain]; - DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dpnsContract; - Vec_String *usernames_vec = [NSArray ffi_to_vec_of_string:usernames]; - DMaybeDocumentsMap *result = dash_spv_platform_document_usernames_UsernamesManager_stream_usernames_with_contract(self.chain.sharedRuntime, self.chain.shareCore.usernames->obj, DChar(domain), usernames_vec, contract.raw_contract, DRetryLinear(5), dash_spv_platform_document_usernames_UsernameValidator_None_ctor(), 5 * NSEC_PER_SEC); - - if (result->error) { - NSError *error = [NSError ffi_from_platform_error:result->error]; - DMaybeDocumentsMapDtor(result); - DSLog(@"%@: ERROR: %@", debugInfo, error); - dispatch_async(completionQueue, ^{ completion(FALSE, @[error]); }); - return; - } - DDocumentsMap *documents = result->ok; - BOOL allDomainFound = NO; - - for (NSString *username in usernames) { - for (int i = 0; i < documents->count; i++) { - DDocument *document = documents->values[i]; - NSString *normalizedLabel = DGetTextDocProperty(document, @"normalizedLabel"); - NSString *label = DGetTextDocProperty(document, @"label"); - NSString *normalizedParentDomainName = DGetTextDocProperty(document, @"normalizedParentDomainName"); - BOOL equal = [normalizedLabel isEqualToString:[username lowercaseString]]; - DSLog(@"%@: %u: %@ == %@", debugInfo, equal, normalizedLabel, [username lowercaseString]); - allDomainFound &= equal; - - if (equal) { - dash_spv_platform_identity_model_IdentityModel_set_username_status_confirmed(self.identity_model, DChar(username), DChar(normalizedParentDomainName), DChar(label)); - [self saveUsername:username - inDomain:normalizedParentDomainName - status:dash_spv_platform_document_usernames_UsernameStatus_Confirmed - salt:nil - commitSave:YES - inContext:context]; - } - } - } - DMaybeDocumentsMapDtor(result); - if (allDomainFound) - countAllFound++; - countReturned++; - if (countReturned == domains.count) { - finished = TRUE; - if (countAllFound == domains.count) { - dispatch_async(completionQueue, ^{ completion(YES, nil); }); - } else { - //todo: This needs to be done per username and not for all usernames - [self setAndSaveUsernameFullPaths:usernameFullPaths - toStatus:dash_spv_platform_document_usernames_UsernameStatus_Preordered_ctor() - inContext:context]; - [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_Preordered_ctor() - inContext:context - completion:completion - onCompletionQueue:completionQueue]; - } - if (completion) - completion(countAllFound == domains.count, nil); - } - } - DSLog(@"%@: OK: all found (%lu) == domains (%lu)", debugInfo, countAllFound, domains.count); - if (completion) - completion(countAllFound == domains.count, nil); - } else if (completion) { - DSLog(@"%@: OK (No usernameFullPaths)", debugInfo); - dispatch_async(completionQueue, ^{ completion(YES, nil); }); - } - break; - } - default: - if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil); }); - break; - } -} - - -- (BOOL)processSaltedDomainHashDocument:(NSString *)usernameFullPath - hash:(NSData *)hash - document:(DDocument *)document - inContext:(NSManagedObjectContext *)context { - switch (document->tag) { - case dpp_document_Document_V0: { - NSData *saltedDomainHash = DGetBytesDocProperty(document, @"saltedDomainHash"); - if ([saltedDomainHash isEqualToData:hash]) { - DUsernameStatus *status = dash_spv_platform_document_usernames_UsernameStatus_Preordered_ctor(); - dash_spv_platform_identity_model_IdentityModel_set_username_status(self.identity_model, DChar(usernameFullPath), status); - [self saveUsernameFullPath:usernameFullPath - status:status - salt:nil - commitSave:YES - inContext:context]; - return YES; - } - break; - } - default: - break; - } - return NO; +// case dash_spv_platform_document_usernames_UsernameStatus_RegistrationPending: { +// [debugInfo appendFormat:@" (RegistrationPending) usernameFullPaths: %@", usernameFullPaths]; +// if (usernameFullPaths.count) { +// NSMutableDictionary *domains = [NSMutableDictionary dictionary]; +// for (NSString *usernameFullPath in usernameFullPaths) { +// NSArray *components = [usernameFullPath componentsSeparatedByString:@"."]; +// NSString *domain = @""; +// NSString *name = components[0]; +// if (components.count > 1) +// domain = [[components subarrayWithRange:NSMakeRange(1, components.count - 1)] componentsJoinedByString:@"."]; +// if (!domains[domain]) +// domains[domain] = [NSMutableArray array]; +// [domains[domain] addObject:name]; +// } +// __block BOOL finished = FALSE; +// __block NSUInteger countAllFound = 0; +// __block NSUInteger countReturned = 0; +// DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dpnsContract; +// for (NSString *domain in domains) { +// NSArray *usernames = domains[domain]; +// Vec_String *usernames_vec = [NSArray ffi_to_vec_of_string:usernames]; +// DMaybeDocumentsMap *result = dash_spv_platform_document_usernames_UsernamesManager_stream_usernames_with_contract(self.chain.sharedRuntime, self.chain.shareCore.usernames->obj, DChar(domain), usernames_vec, contract.raw_contract, DRetryLinear(5), dash_spv_platform_document_usernames_UsernameValidator_None_ctor(), 5 * NSEC_PER_SEC); +// +// if (result->error) { +// NSError *error = [NSError ffi_from_platform_error:result->error]; +// DMaybeDocumentsMapDtor(result); +// DSLog(@"%@: ERROR: %@", debugInfo, error); +// dispatch_async(completionQueue, ^{ completion(FALSE, @[error]); }); +// return; +// } +// DDocumentsMap *documents = result->ok; +// BOOL allDomainFound = NO; +// +// for (NSString *username in usernames) { +// for (int i = 0; i < documents->count; i++) { +// DDocument *document = documents->values[i]; +// NSString *normalizedLabel = DGetTextDocProperty(document, @"normalizedLabel"); +// NSString *label = DGetTextDocProperty(document, @"label"); +// NSString *normalizedParentDomainName = DGetTextDocProperty(document, @"normalizedParentDomainName"); +// BOOL equal = [normalizedLabel isEqualToString:[username lowercaseString]]; +// DSLog(@"%@: %u: %@ == %@", debugInfo, equal, normalizedLabel, [username lowercaseString]); +// allDomainFound &= equal; +// +// if (equal) { +// dash_spv_platform_identity_model_IdentityModel_set_username_status_confirmed(self.model, DChar(username), DChar(normalizedParentDomainName), DChar(label)); +// [self saveUsername:username +// inDomain:normalizedParentDomainName +// status:dash_spv_platform_document_usernames_UsernameStatus_Confirmed +// salt:nil +// commitSave:YES +// inContext:context]; +// } +// } +// } +// DMaybeDocumentsMapDtor(result); +// if (allDomainFound) +// countAllFound++; +// countReturned++; +// if (countReturned == domains.count) { +// finished = TRUE; +// if (countAllFound == domains.count) { +// dispatch_async(completionQueue, ^{ completion(YES, nil); }); +// } else { +// //todo: This needs to be done per username and not for all usernames +// [self setAndSaveUsernameFullPaths:usernameFullPaths +// toStatus:dash_spv_platform_document_usernames_UsernameStatus_Preordered_ctor() +// inContext:context]; +// [self registerUsernamesAtStage:dash_spv_platform_document_usernames_UsernameStatus_Preordered_ctor() +// inContext:context +// completion:completion +// onCompletionQueue:completionQueue]; +// } +// if (completion) +// completion(countAllFound == domains.count, nil); +// } +// } +// DSLog(@"%@: OK: all found (%lu) == domains (%lu)", debugInfo, countAllFound, domains.count); +// if (completion) +// completion(countAllFound == domains.count, nil); +// } else if (completion) { +// DSLog(@"%@: OK (No usernameFullPaths)", debugInfo); +// dispatch_async(completionQueue, ^{ completion(YES, nil); }); +// } +// break; +// } +// default: +// if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil); }); +// break; +// } } +// +// +//- (BOOL)processSaltedDomainHashDocument:(NSString *)usernameFullPath +// hash:(NSData *)hash +// document:(DDocument *)document +// inContext:(NSManagedObjectContext *)context { +// switch (document->tag) { +// case dpp_document_Document_V0: { +// NSData *saltedDomainHash = DGetBytesDocProperty(document, @"saltedDomainHash"); +// if ([saltedDomainHash isEqualToData:hash]) { +// DUsernameStatus *status = dash_spv_platform_document_usernames_UsernameStatus_Preordered_ctor(); +// dash_spv_platform_identity_model_IdentityModel_set_username_status(self.model, DChar(usernameFullPath), status); +// [self saveUsernameFullPath:usernameFullPath +// status:status +// salt:nil +// commitSave:YES +// inContext:context]; +// return YES; +// } +// break; +// } +// default: +// break; +// } +// return NO; +//} - (void)notifyUsernameUpdate:(nullable NSDictionary *)userInfo { dispatch_async(dispatch_get_main_queue(), ^{ diff --git a/DashSync/shared/Models/Identity/DSIdentity.h b/DashSync/shared/Models/Identity/DSIdentity.h index 5bbdddd6..17c4664a 100644 --- a/DashSync/shared/Models/Identity/DSIdentity.h +++ b/DashSync/shared/Models/Identity/DSIdentity.h @@ -32,12 +32,6 @@ typedef NS_ENUM(NSUInteger, DSIdentityRegistrationStep) DSIdentityRegistrationStep_Cancelled = 1 << 30 }; -typedef NS_ENUM(NSUInteger, DSIdentityMonitorOptions) -{ - DSIdentityMonitorOptions_None = 0, - DSIdentityMonitorOptions_AcceptNotFoundAsNotAnError = 1, -}; - typedef NS_ENUM(NSUInteger, DSIdentityQueryStep) { DSIdentityQueryStep_None = DSIdentityRegistrationStep_None, //0 @@ -82,6 +76,12 @@ FOUNDATION_EXPORT NSString *const DSIdentityUpdateEventDashpaySyncronizationBloc NSString * DSRegistrationStepsDescription(DSIdentityRegistrationStep step); NSString * DSIdentityQueryStepsDescription(DSIdentityQueryStep step); +@interface DSDerivationContext : NSObject +@property (nonatomic, readwrite) DSDerivationPath *derivationPath; +@property (nonatomic, readwrite) NSIndexPath *indexPath; ++ (instancetype)withDerivationPath:(DSDerivationPath *)derivationPath indexPath:(NSIndexPath *)indexPath; +@end + @interface DSIdentity : NSObject /*! @brief This is the unique identifier representing the blockchain identity. It is derived from the asset lock transaction credit burn UTXO (as of dpp v10). Returned as a 256 bit number */ @@ -105,7 +105,7 @@ NSString * DSIdentityQueryStepsDescription(DSIdentityQueryStep step); /*! @brief This is TRUE only if the blockchain identity is contained within a wallet. It could be in a cleanup phase where it was removed from the wallet but still being help in memory by callbacks. */ @property (nonatomic, readonly) BOOL isActive; /*! @brief This references transient Dashpay user info if on a transient blockchain identity. */ -@property (nonatomic, readonly) DSTransientDashpayUser *transientDashpayUser; +@property (nonatomic, readonly) DTransientUser *transientDashpayUser; /*! @brief This is the bitwise steps that the identity has already performed in registration. */ @property (nonatomic, readonly) DSIdentityRegistrationStep stepsCompleted; /*! @brief This is the wallet holding the blockchain identity. There should always be a wallet associated to a blockchain identity if the blockchain identity is local, but never if it is not. */ @@ -219,6 +219,8 @@ NSString * DSIdentityQueryStepsDescription(DSIdentityQueryStep step); - (BOOL)hasIdentityExtendedPublicKeys; - (DIdentityKeyStatus *)statusOfKeyAtIndex:(NSUInteger)index; - (DOpaqueKey *_Nullable)keyAtIndex:(NSUInteger)index; +- (BOOL)hasKeyAtIndex:(NSUInteger)index; + + (NSString *)localizedStatusOfKeyForIdentityKeyStatus:(DIdentityKeyStatus *)status; - (NSString *)localizedStatusOfKeyAtIndex:(NSUInteger)index; - (DMaybeOpaqueKey *_Nullable)createNewKeyOfType:(DKeyKind *)type @@ -235,6 +237,9 @@ NSString * DSIdentityQueryStepsDescription(DSIdentityQueryStep step); - (NSString *)logPrefix; +- (BOOL)registrationStatusIsPending; +- (BOOL)registrationStatusIsClaimed; + @end diff --git a/DashSync/shared/Models/Identity/DSIdentity.m b/DashSync/shared/Models/Identity/DSIdentity.m index 8a98eb50..169dbbb5 100644 --- a/DashSync/shared/Models/Identity/DSIdentity.m +++ b/DashSync/shared/Models/Identity/DSIdentity.m @@ -59,14 +59,6 @@ #define ERROR_FUNDING_TX_NOT_MINED [NSError errorWithCode:500 localizedDescriptionKey:@"The registration credit funding transaction has not been mined yet and has no instant send lock"] #define ERROR_NO_IDENTITY [NSError errorWithCode:500 localizedDescriptionKey:@"Platform returned no identity when one was expected"] -typedef NS_ENUM(NSUInteger, DSIdentityKeyDictionary) { - DSIdentityKeyDictionary_Key = 0, - DSIdentityKeyDictionary_KeyType = 1, - DSIdentityKeyDictionary_KeyStatus = 2, - DSIdentityKeyDictionary_KeyLevel = 3, - DSIdentityKeyDictionary_KeyPurpose = 4, -}; - NSString * DSRegistrationStepsDescription(DSIdentityRegistrationStep step) { NSMutableArray *components = [NSMutableArray array]; if (FLAG_IS_SET(step, DSIdentityRegistrationStep_None)) @@ -129,26 +121,339 @@ typedef NS_ENUM(NSUInteger, DSIdentityKeyDictionary) { return [components componentsJoinedByString:@" | "]; } +#define AS_OBJC(context) ((__bridge DSIdentity *)(context)) +#define AS_RUST(context) ((__bridge void *)(context)) + + +@implementation DSDerivationContext ++ (instancetype)withDerivationPath:(DSDerivationPath *)derivationPath indexPath:(NSIndexPath *)indexPath { + DSDerivationContext *context = [[DSDerivationContext alloc] init]; + context.derivationPath = derivationPath; + context.indexPath = indexPath; + return context; +} +@end + @interface DSIdentity () -@property (nonatomic, assign) UInt256 uniqueID; -@property (nonatomic, assign) BOOL isOutgoingInvitation; -@property (nonatomic, assign) BOOL isFromIncomingInvitation; +//@property (nonatomic, assign) UInt256 uniqueID; @property (nonatomic, assign) DSUTXO lockedOutpoint; -@property (nonatomic, assign) uint32_t index; - -@property (nonatomic, assign) uint64_t creditBalance; @property (nonatomic, strong) DSDashpayUserEntity *matchingDashpayUserInViewContext; @property (nonatomic, strong) DSDashpayUserEntity *matchingDashpayUserInPlatformContext; @property (nonatomic, assign) DMaybeOpaqueKey *internalRegistrationFundingPrivateKey; @property (nonatomic, assign) DMaybeOpaqueKey *internalTopupFundingPrivateKey; -@property (nonatomic, assign) UInt256 dashpaySyncronizationBlockHash; @property (nonatomic, strong) DSAssetLockTransaction *registrationAssetLockTransaction; -@property (nonatomic, assign) uint64_t lastCheckedUsernamesTimestamp; -@property (nonatomic, assign) uint64_t lastCheckedProfileTimestamp; @end + +const void *get_derivation_context_caller(const void *context, DKeyKind *key_kind, DOpaqueKey *key, uint32_t identity_index, uint32_t key_id) { + DSIdentity *identity = AS_OBJC(context); + NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:(const NSUInteger[]){identity_index, key_id} length:2]; + DSAuthenticationKeysDerivationPath *derivationPath = [identity derivationPathForType:key_kind]; + + + DMaybeOpaqueKey *key_to_check = [derivationPath publicKeyAtIndexPath:[indexPath hardenAllItems]]; + BOOL isEqual = [DSKeyManager keysPublicKeyDataIsEqual:key_to_check->ok key2:key]; + DKeyKindDtor(key_kind); + DOpaqueKeyDtor(key); + DMaybeOpaqueKeyDtor(key_to_check); + return isEqual ? ((__bridge void *)([DSDerivationContext withDerivationPath:derivationPath indexPath:indexPath])) : nil; +} + +//NSIndexPath *hardenedIndexPath = [self indexPathForIndex:keyIndex]; +//DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:key_kind]; +//DMaybeOpaqueKey *publicKey = [derivationPath publicKeyAtIndexPath:hardenedIndexPath]; +//NSAssert([derivationPath hasExtendedPrivateKey], @"The derivation path should have an extended private key"); +//DMaybeOpaqueKey *privateKey = [derivationPath privateKeyAtIndexPath:hardenedIndexPath]; +//NSAssert(privateKey && privateKey->ok, @"The private key should have been derived"); +//NSAssert([DSKeyManager keysPublicKeyDataIsEqual:publicKey->ok key2:privateKey->ok], @"These should be equal"); + +void get_derivation_context_dtor(const void *derivation_context) {} + +BOOL save_key_caller(const void *context, dash_spv_platform_identity_storage_key_SaveKeyContext *update) { + DSIdentity *identity = AS_OBJC(context); + if (!identity.isActive) { + dash_spv_platform_identity_storage_key_SaveKeyContext_destroy(update); + return NO; + } + NSManagedObjectContext *storageContext = identity.platformContext; + + switch (update->tag) { + case dash_spv_platform_identity_storage_key_SaveKeyContext_Full: { + dash_spv_platform_identity_storage_key_SaveKeyContext_Full_Body body = update->full; + IdentityKeyPlacement *placement = body._0; + DOpaqueKey *key = body._1; + DSecurityLevel *security_level = body._2; + DPurpose *purpose = body._3; + const void** maybe_derivation_context = dash_spv_platform_identity_storage_key_IdentityKeyPlacement_maybe_derivation_context(placement); + uint32_t *maybe_key_id = dash_spv_platform_identity_storage_key_IdentityKeyPlacement_maybe_key_id(placement); + if (maybe_derivation_context != NULL) { + DSDerivationContext *derivationContext = ((__bridge DSDerivationContext *)(maybe_derivation_context[0])); + [identity saveNewKeyForCurrentEntity:key + atPath:derivationContext.indexPath + withStatus:dash_spv_platform_identity_key_status_IdentityKeyStatus_Registered_ctor() + withSecurityLevel:security_level + withPurpose:purpose + fromDerivationPath:derivationContext.derivationPath + inContext:storageContext]; + // TODO: destroy derivation context + } else if (maybe_key_id != NULL) { + uint32_t index = maybe_key_id[0]; + [storageContext performBlockAndWait:^{ + DSBlockchainIdentityEntity *identityEntity = [identity identityEntityInContext:storageContext]; + NSUInteger count = [DSBlockchainIdentityKeyPathEntity countObjectsInContext:storageContext matching:@"blockchainIdentity == %@ && keyID == %@", identityEntity, @(index)]; + if (!count) { + DSBlockchainIdentityKeyPathEntity *keyPathEntity = [DSBlockchainIdentityKeyPathEntity managedObjectInBlockedContext:storageContext]; + // TODO: migrate OpaqueKey/KeyKind to KeyType + keyPathEntity.keyType = DOpaqueKeyToKeyTypeIndex(key); + keyPathEntity.keyStatus = dash_spv_platform_identity_key_status_IdentityKeyStatus_Registered; + keyPathEntity.keyID = index; + keyPathEntity.publicKeyData = [DSKeyManager publicKeyData:key]; + keyPathEntity.securityLevel = DSecurityLevelIndex(security_level); + keyPathEntity.purpose = DPurposeIndex(purpose); + [identityEntity addKeyPathsObject:keyPathEntity]; + [storageContext ds_save]; + } + }]; + // TODO: destroy maybe_key_id + } + + break; + } + case dash_spv_platform_identity_storage_key_SaveKeyContext_Status: { + IdentityKeyPlacement *placement = update->status; + const void** maybe_derivation_context = dash_spv_platform_identity_storage_key_IdentityKeyPlacement_maybe_derivation_context(placement); + uint32_t *maybe_key_id = dash_spv_platform_identity_storage_key_IdentityKeyPlacement_maybe_key_id(placement); + __block DSBlockchainIdentityKeyPathEntity *keyPathEntity = NULL; + if (maybe_derivation_context != NULL) { + DSDerivationContext *derivationContext = ((__bridge DSDerivationContext *)(maybe_derivation_context[0])); + [storageContext performBlockAndWait:^{ + DSBlockchainIdentityEntity *identityEntity = [identity identityEntityInContext:storageContext]; + DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:derivationContext.derivationPath inContext:storageContext]; + keyPathEntity = [[DSBlockchainIdentityKeyPathEntity objectsInContext:storageContext matching:@"blockchainIdentity == %@ && derivationPath == %@ && path == %@ && keyStatus != %u", identityEntity, derivationPathEntity, derivationContext.indexPath, dash_spv_platform_identity_key_status_IdentityKeyStatus_Registered] firstObject]; + }]; + } else if (maybe_key_id != NULL) { + [storageContext performBlockAndWait:^{ + uint32_t index = maybe_key_id[0]; + DSBlockchainIdentityEntity *identityEntity = [identity identityEntityInContext:storageContext]; + keyPathEntity = [[DSBlockchainIdentityKeyPathEntity objectsInContext:storageContext matching:@"blockchainIdentity == %@ && derivationPath == NULL && keyID == %@", identityEntity, @(index)] firstObject]; + }]; + } + if (keyPathEntity) { + keyPathEntity.keyStatus = dash_spv_platform_identity_key_status_IdentityKeyStatus_Registered; + [storageContext ds_save]; + } + break; + } + default: + break; + } + dash_spv_platform_identity_storage_key_SaveKeyContext_destroy(update); + return YES; +} +void save_key_dtor(bool saved) {} + +Vec_u8 *get_private_key_caller(const void *context, uint32_t index, DKeyKind *key_kind) { + DSIdentity *identity = AS_OBJC(context); + const NSUInteger indexes[] = {DIdentityModelIndex(identity.model) | BIP32_HARD, index | BIP32_HARD}; + NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; + DSAuthenticationKeysDerivationPath *derivationPath = [identity derivationPathForType:key_kind]; + DKeyKindDtor(key_kind); + NSError *error = nil; + NSString *identifier = [NSString stringWithFormat:@"%@-%@-%@", identity.uniqueIdString, derivationPath.standaloneExtendedPublicKeyUniqueID, [[indexPath softenAllItems] indexPathString]]; + NSData *keySecret = getKeychainData(identifier, &error); +// NSAssert(keySecret, @"This should be present"); + if (!keySecret || error) return nil; + return bytes_ctor(keySecret); +} +void get_private_key_dtor(Vec_u8 *private_key_data) {} + +void save_username_caller(const void *context, DSaveUsernameContext* save_username_context) { + DSIdentity *identity = AS_OBJC(context); + if (identity.isTransient || !identity.isActive) { + dash_spv_platform_identity_storage_username_SaveUsernameContext_destroy(save_username_context); + return; + } + NSManagedObjectContext *platformContext = identity.platformContext; + __block NSDictionary *maybeUserInfo = NULL; + [platformContext performBlockAndWait:^{ + switch (save_username_context->tag) { + case dash_spv_platform_identity_storage_username_SaveUsernameContext_NewUsername: { + NSString *username = NSStringFromPtr(save_username_context->new_username.username); + NSString *domain = NSStringFromPtr(save_username_context->new_username.domain); + uint16_t status = DUsernameStatusIndex(save_username_context->new_username.status); + u256 *maybe_salt = save_username_context->new_username.salt; + DSBlockchainIdentityEntity *entity = [identity identityEntityInContext:platformContext]; + DSBlockchainIdentityUsernameEntity *usernameEntity = [DSBlockchainIdentityUsernameEntity managedObjectInBlockedContext:platformContext]; + usernameEntity.status = status; + usernameEntity.domain = domain; + usernameEntity.stringValue = username; + if (maybe_salt) + usernameEntity.salt = NSDataFromPtr(maybe_salt); + [entity addUsernamesObject:usernameEntity]; + [entity setDashpayUsername:usernameEntity]; + [platformContext ds_save]; + [identity notifyUsernameUpdate:@{ + DSChainManagerNotificationChainKey: identity.chain, + DSIdentityKey: identity + }]; + break; + } + case dash_spv_platform_identity_storage_username_SaveUsernameContext_Username: { + NSString *username = NSStringFromPtr(save_username_context->username.username); + DSBlockchainIdentityEntity *entity = [identity identityEntityInContext:platformContext]; + NSSet *usernamesPassingTest = [entity.usernames objectsPassingTest:^BOOL(DSBlockchainIdentityUsernameEntity *_Nonnull obj, BOOL *_Nonnull stop) { + BOOL isEqual = [obj.stringValue isEqualToString:username]; + if (isEqual) *stop = YES; + return isEqual; + }]; + if ([usernamesPassingTest count]) { + NSString *domain = NSStringFromPtr(save_username_context->username.domain); + uint16_t status = DUsernameStatusIndex(save_username_context->username.status); + u256 *maybe_salt = save_username_context->username.salt; + // NSAssert([usernamesPassingTest count] == 1, @"There should never be more than 1"); + DSBlockchainIdentityUsernameEntity *usernameEntity = [usernamesPassingTest anyObject]; + usernameEntity.status = status; + if (maybe_salt) + usernameEntity.salt = NSDataFromPtr(maybe_salt); + if (save_username_context->username.commit_save) + [platformContext ds_save]; + [identity notifyUsernameUpdate:@{ + DSChainManagerNotificationChainKey: identity.chain, + DSIdentityKey: identity, + DSIdentityUsernameKey: username, + DSIdentityUsernameDomainKey: domain + }]; + } + break; + } + case dash_spv_platform_identity_storage_username_SaveUsernameContext_UsernameFullPath: { + NSString *usernameFullPath = NSStringFromPtr(save_username_context->username_full_path.username_full_path); + + DSBlockchainIdentityEntity *entity = [identity identityEntityInContext:platformContext]; + NSSet *usernamesPassingTest = [entity.usernames objectsPassingTest:^BOOL(DSBlockchainIdentityUsernameEntity *_Nonnull obj, BOOL *_Nonnull stop) { + BOOL isEqual = [[[obj.stringValue lowercaseString] stringByAppendingFormat:@".%@", [obj.domain lowercaseString]] isEqualToString:usernameFullPath]; + if (isEqual) *stop = YES; + return isEqual; + }]; + if ([usernamesPassingTest count]) { + // NSAssert([usernamesPassingTest count] == 1, @"There should never be more than 1"); + DSBlockchainIdentityUsernameEntity *usernameEntity = [usernamesPassingTest anyObject]; + uint16_t status = DUsernameStatusIndex(save_username_context->username_full_path.status); + u256 *maybe_salt = save_username_context->username_full_path.salt; + usernameEntity.status = status; + if (maybe_salt) + usernameEntity.salt = NSDataFromPtr(maybe_salt); + if (save_username_context->username_full_path.commit_save) + [platformContext ds_save]; + [identity notifyUsernameUpdate:@{ + DSChainManagerNotificationChainKey: identity.chain, + DSIdentityKey: identity, + DSIdentityUsernameKey: usernameEntity.stringValue, + DSIdentityUsernameDomainKey: usernameEntity.domain + }]; + } + break; + } + case dash_spv_platform_identity_storage_username_SaveUsernameContext_UsernameFullPaths: { + Vec_String *username_full_paths = save_username_context->username_full_paths.username_full_paths; + DUsernameStatus *status = save_username_context->username_full_paths.status; + Vec_Tuple_String_String *result = dash_spv_platform_identity_model_IdentityModel_usernames_and_domains(identity.model, save_username_context->username_full_paths.username_full_paths); + + for (int i = 0; i < result->count; i++) { + Tuple_String_String *pair = result->values[i]; + NSString *username = NSStringFromPtr(pair->o_0); + NSString *domain = NSStringFromPtr(pair->o_1); + DSBlockchainIdentityEntity *entity = [identity identityEntityInContext:platformContext]; + NSSet *usernamesPassingTest = [entity.usernames objectsPassingTest:^BOOL(DSBlockchainIdentityUsernameEntity *_Nonnull obj, BOOL *_Nonnull stop) { + BOOL isEqual = [obj.stringValue isEqualToString:username]; + if (isEqual) *stop = YES; + return isEqual; + }]; + if ([usernamesPassingTest count]) { + // NSAssert([usernamesPassingTest count] == 1, @"There should never be more than 1"); + DSBlockchainIdentityUsernameEntity *usernameEntity = [usernamesPassingTest anyObject]; + usernameEntity.status = DUsernameStatusIndex(status); + [identity notifyUsernameUpdate:@{ + DSChainManagerNotificationChainKey: identity.chain, + DSIdentityKey: identity, + DSIdentityUsernameKey: username, + DSIdentityUsernameDomainKey: domain + }]; + } + } + Vec_Tuple_String_String_destroy(result); + [platformContext ds_save]; + break; + } + } + }]; + dash_spv_platform_identity_storage_username_SaveUsernameContext_destroy(save_username_context); +} + +Result_ok_u32_err_dash_spv_platform_error_Error *create_new_key_caller(const void *context, DKeyKind *key_kind, DSecurityLevel *security_level, DPurpose *purpose) { + DSIdentity *identity = AS_OBJC(context); + uint32_t rIndex; + [identity createNewKeyOfType:key_kind + securityLevel:security_level + purpose:purpose + saveKey:YES + returnIndex:&rIndex]; + return Result_ok_u32_err_dash_spv_platform_error_Error_ctor(&rIndex, NULL); +} +void create_new_key_dtor(Result_ok_u32_err_dash_spv_platform_error_Error *result) {} + +Result_ok_bool_err_dash_spv_platform_error_Error *active_private_keys_are_loaded_caller(const void *context, BOOL is_local, DKeyInfoDictionaries *key_infos) { + DSIdentity *identity = AS_OBJC(context); + NSError *error = nil; + BOOL loaded = YES; + for (uint32_t i = 0; i < key_infos->count; i++) { + DKeyInfo *key_info = key_infos->values[i]; + uint32_t index = key_infos->keys[i]; + const NSUInteger indexes[] = {DIdentityModelIndex(identity.model) | BIP32_HARD, index | BIP32_HARD}; + NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; + DSDerivationPath *derivationPath = [identity derivationPathForType:key_info->key_type]; + NSString *identifier = [NSString stringWithFormat:@"%@-%@-%@", identity.uniqueIdString, derivationPath.standaloneExtendedPublicKeyUniqueID, [[indexPath softenAllItems] indexPathString]]; + loaded &= !identity.isLocal ? NO : hasKeychainData(identifier, &error); + if (error) { + DKeyInfoDictionariesDtor(key_infos); + return Result_ok_bool_err_dash_spv_platform_error_Error_ctor(NULL, dash_spv_platform_error_Error_Any_ctor(0, DChar([error description]))); + } + } + DKeyInfoDictionariesDtor(key_infos); + return Result_ok_bool_err_dash_spv_platform_error_Error_ctor(&loaded, NULL); +} +void active_private_keys_are_loaded_dtor(Result_ok_bool_err_dash_spv_platform_error_Error *result) {} + +Fn_ARGS_std_os_raw_c_void_dash_spv_crypto_keys_key_KeyKind_dash_spv_crypto_keys_key_OpaqueKey_u32_u32_RTRN_std_os_raw_c_void get_derivation_context = { + .caller = &get_derivation_context_caller, + .destructor = &get_derivation_context_dtor +}; +Fn_ARGS_std_os_raw_c_void_dash_spv_platform_identity_storage_key_SaveKeyContext_RTRN_bool save_key = { + .caller = &save_key_caller, + .destructor = &save_key_dtor +}; + +Fn_ARGS_std_os_raw_c_void_u32_dash_spv_crypto_keys_key_KeyKind_RTRN_Option_Vec_u8 get_private_key = { + .caller = &get_private_key_caller, + .destructor = &get_private_key_dtor, +}; +Fn_ARGS_std_os_raw_c_void_dash_spv_platform_identity_storage_username_SaveUsernameContext_RTRN_ save_username = { + .caller = &save_username_caller, +}; +Fn_ARGS_std_os_raw_c_void_dash_spv_crypto_keys_key_KeyKind_dpp_identity_identity_public_key_security_level_SecurityLevel_dpp_identity_identity_public_key_purpose_Purpose_RTRN_Result_ok_u32_err_dash_spv_platform_error_Error create_new_key = { + .caller = &create_new_key_caller, + .destructor = &create_new_key_dtor +}; +Fn_ARGS_std_os_raw_c_void_bool_std_collections_Map_keys_u32_values_dash_spv_platform_identity_key_info_KeyInfo_RTRN_Result_ok_bool_err_dash_spv_platform_error_Error active_private_keys_are_loaded = { + .caller = &active_private_keys_are_loaded_caller, + .destructor = &active_private_keys_are_loaded_dtor +}; + +#define DIdentityModelNew(unique_id, registration_status, is_local, is_transient, main_index, main_key_type) dash_spv_platform_identity_model_IdentityModel_new(unique_id, registration_status, is_local, is_transient, main_index, main_key_type, get_derivation_context, save_key, save_username, get_private_key, create_new_key, active_private_keys_are_loaded) + + @implementation DSIdentity - (void)dealloc { @@ -156,29 +461,54 @@ - (void)dealloc { DMaybeOpaqueKeyDtor(_internalRegistrationFundingPrivateKey); if (_internalTopupFundingPrivateKey != NULL) DMaybeOpaqueKeyDtor(_internalTopupFundingPrivateKey); - // TODO: identity_model dtor -// if [(_identity_model != NULL) -// TODO::// NO + if (_model != NULL) + dash_spv_platform_identity_model_IdentityModel_destroy(_model); } // MARK: - Initialization +- (instancetype)initWithModel:(IdentityModel *)model onChain:(DSChain *)chain { + if (!(self = [super init])) return nil; + self.model = model; + self.chain = chain; + return self; +} - (instancetype)initWithUniqueId:(UInt256)uniqueId isTransient:(BOOL)isTransient onChain:(DSChain *)chain { //this is the initialization of a non local identity if (!(self = [super init])) return nil; NSAssert(uint256_is_not_zero(uniqueId), @"uniqueId must not be null"); - _uniqueID = uniqueId; - _isLocal = FALSE; - _isTransient = isTransient; - _keysCreated = 0; - _currentMainKeyIndex = 0; - _currentMainKeyType = DKeyKindECDSA(); - self.identity_model = dash_spv_platform_identity_model_IdentityModel_new(DIdentityRegistrationStatusRegistered()); + + self.model = DIdentityModelNew(u256_ctor_u(uniqueId), DIdentityRegistrationStatusRegistered(), NO, isTransient, 0, DKeyKindECDSA()); self.chain = chain; return self; } +- (instancetype)initAtIndex:(uint32_t)index + inWallet:(DSWallet *)wallet { + //this is the creation of a new blockchain identity + NSParameterAssert(wallet); + if (!(self = [super init])) return nil; + self.wallet = wallet; + + self.model = DIdentityModelNew(u256_ctor_u(UINT256_ZERO), DIdentityRegistrationStatusUnknown(), YES, NO, 0, DKeyKindECDSA()); + dash_spv_platform_identity_model_IdentityModel_set_index(self.model, index); + self.chain = wallet.chain; + return self; +} + +- (instancetype)initAtIndex:(uint32_t)index + uniqueId:(UInt256)uniqueId + inWallet:(DSWallet *)wallet { + NSParameterAssert(wallet); + if (!(self = [super init])) return nil; + self.wallet = wallet; + self.model = DIdentityModelNew(u256_ctor_u(uniqueId), DIdentityRegistrationStatusRegistered(), YES, NO, 0, DKeyKindECDSA()); + dash_spv_platform_identity_model_IdentityModel_set_index(self.model, index); + self.chain = wallet.chain; + return self; +} + - (instancetype)initWithIdentityEntity:(DSBlockchainIdentityEntity *)entity { if (!(self = [self initWithUniqueId:entity.uniqueID.UInt256 isTransient:FALSE @@ -203,7 +533,7 @@ - (instancetype)initAtIndex:(uint32_t)index inWallet:(DSWallet *)wallet withIdentityEntity:(DSBlockchainIdentityEntity *)entity { if (!(self = [self initAtIndex:index - withUniqueId:uniqueId + uniqueId:uniqueId inWallet:wallet])) return nil; [self applyIdentityEntity:entity]; return self; @@ -223,26 +553,33 @@ - (instancetype)initAtIndex:(uint32_t)index } - (instancetype)initAtIndex:(uint32_t)index + withLockedOutpoint:(DSUTXO)lockedOutpoint + inWallet:(DSWallet *)wallet { + + NSAssert(dsutxo_hash_is_not_zero(lockedOutpoint), @"utxo must not be nil"); + if (!(self = [self initAtIndex:index uniqueId:[dsutxo_data(lockedOutpoint) SHA256_2] inWallet:wallet])) + return nil; + self.lockedOutpoint = lockedOutpoint; + DSLog(@"%@ initAtIndex: %u lockedOutpoint: %@: %lu", self.logPrefix, index, uint256_hex(lockedOutpoint.hash), lockedOutpoint.n); + return self; +} + +- (instancetype)initAtIndex:(uint32_t)index + withAssetLockTransaction:(DSAssetLockTransaction *)transaction inWallet:(DSWallet *)wallet { - //this is the creation of a new blockchain identity NSParameterAssert(wallet); - if (!(self = [super init])) return nil; - self.wallet = wallet; - self.isLocal = YES; - self.isOutgoingInvitation = NO; - self.isTransient = FALSE; - _keysCreated = 0; - self.currentMainKeyIndex = 0; - self.currentMainKeyType = DKeyKindECDSA(); - self.index = index; - self.identity_model = dash_spv_platform_identity_model_IdentityModel_new(dash_spv_platform_identity_model_IdentityRegistrationStatus_Unknown_ctor()); - self.chain = wallet.chain; + NSAssert(index != UINT32_MAX, @"index must be found"); + if (!(self = [self initAtIndex:index withLockedOutpoint:transaction.lockedOutpoint inWallet:wallet])) + return nil; + self.registrationAssetLockTransaction = transaction; return self; } + + - (void)saveProfileTimestamp { [self.platformContext performBlockAndWait:^{ - self.lastCheckedProfileTimestamp = [NSDate timeIntervalSince1970]; + DIdentityModelSetLastCheckedProfileTimestamp(self.model, [NSDate timeIntervalSince1970]); //[self saveInContext:self.platformContext]; }]; } @@ -262,14 +599,15 @@ - (void)registerKeyFromKeyPathEntity:(DSBlockchainIdentityKeyPathEntity *)entity } - (void)applyIdentityEntity:(DSBlockchainIdentityEntity *)identityEntity { [self applyUsernameEntitiesFromIdentityEntity:identityEntity]; - _creditBalance = identityEntity.creditBalance; - DIdentityModelSetStatus(self.identity_model, DIdentityRegistrationStatusFromIndex(identityEntity.registrationStatus)); - _lastCheckedProfileTimestamp = identityEntity.lastCheckedProfileTimestamp; - _lastCheckedUsernamesTimestamp = identityEntity.lastCheckedUsernamesTimestamp; - _lastCheckedIncomingContactsTimestamp = identityEntity.lastCheckedIncomingContactsTimestamp; - _lastCheckedOutgoingContactsTimestamp = identityEntity.lastCheckedOutgoingContactsTimestamp; - - self.dashpaySyncronizationBlockHash = identityEntity.dashpaySyncronizationBlockHash.UInt256; + DIdentityModelSetBalance(self.model, identityEntity.creditBalance); + DIdentityModelSetStatus(self.model, DIdentityRegistrationStatusFromIndex(identityEntity.registrationStatus)); + DIdentityModelSetLastCheckedProfileTimestamp(self.model, identityEntity.lastCheckedProfileTimestamp); + DIdentityModelSetLastCheckedUsernamesTimestamp(self.model, identityEntity.lastCheckedUsernamesTimestamp); + DIdentityModelSetLastCheckedIncomingContactsTimestamp(self.model, identityEntity.lastCheckedIncomingContactsTimestamp); + DIdentityModelSetLastCheckedOutgoingContactsTimestamp(self.model, identityEntity.lastCheckedOutgoingContactsTimestamp); + NSData *dashpaySyncronizationBlockHash = identityEntity.dashpaySyncronizationBlockHash; + if (dashpaySyncronizationBlockHash) + DIdentityModelSetSyncBlockHash(self.model, u256_ctor(dashpaySyncronizationBlockHash)); for (DSBlockchainIdentityKeyPathEntity *keyPathEntity in identityEntity.keyPaths) { NSIndexPath *keyIndexPath = (NSIndexPath *)[keyPathEntity path]; DKeyKind *keyType = DKeyKindFromIndex(keyPathEntity.keyType); @@ -284,8 +622,8 @@ - (void)applyIdentityEntity:(DSBlockchainIdentityEntity *)identityEntity { if (key->ok) { uint32_t index = (uint32_t)[nonhardenedPath indexAtPosition:[nonhardenedPath length] - 1]; _keysCreated = MAX(self.keysCreated, index + 1); - DKeyInfo *key_info = dash_spv_platform_identity_model_KeyInfo_ctor(key->ok, keyType, DIdentityKeyStatusFromIndex(keyPathEntity.keyStatus), level, purpose); - dash_spv_platform_identity_model_IdentityModel_add_key_info(self.identity_model, index, key_info); + DKeyInfo *key_info = DKeyInfoCtor(key->ok, keyType, DIdentityKeyStatusFromIndex(keyPathEntity.keyStatus), level, purpose); + DIdentityModelAddKeyInfo(self.model, index, key_info); added = YES; } } @@ -316,69 +654,50 @@ - (void)applyIdentityEntity:(DSBlockchainIdentityEntity *)identityEntity { } } +- (BOOL)isLocal { + return dash_spv_platform_identity_model_IdentityModel_is_local(self.model); +} +- (BOOL)isTransient { + return dash_spv_platform_identity_model_IdentityModel_is_transient(self.model); +} -- (void)setAssociatedInvitation:(DSInvitation *)associatedInvitation { - _associatedInvitation = associatedInvitation; - // It was created locally, we are sending the invite - if (associatedInvitation.createdLocally) { - self.isOutgoingInvitation = TRUE; - self.isFromIncomingInvitation = FALSE; - self.isLocal = FALSE; - } else { - // It was created on another device, we are receiving the invite - self.isOutgoingInvitation = FALSE; - self.isFromIncomingInvitation = TRUE; - self.isLocal = TRUE; - } +- (uint64_t)creditBalance { + return dash_spv_platform_identity_model_IdentityModel_credit_balance(self.model); } -- (instancetype)initAtIndex:(uint32_t)index - withUniqueId:(UInt256)uniqueId - inWallet:(DSWallet *)wallet { - if (!(self = [self initAtIndex:index inWallet:wallet])) return nil; - self.uniqueID = uniqueId; - return self; +- (BOOL)isOutgoingInvitation { + return dash_spv_platform_identity_model_IdentityModel_is_outgoing_invitation(self.model); } -- (instancetype)initAtIndex:(uint32_t)index - withLockedOutpoint:(DSUTXO)lockedOutpoint - inWallet:(DSWallet *)wallet { - if (!(self = [self initAtIndex:index inWallet:wallet])) return nil; - NSAssert(dsutxo_hash_is_not_zero(lockedOutpoint), @"utxo must not be nil"); +- (BOOL)isFromIncomingInvitation { + return dash_spv_platform_identity_model_IdentityModel_is_from_incoming_invitation(self.model); +} - self.lockedOutpoint = lockedOutpoint; - self.uniqueID = [dsutxo_data(lockedOutpoint) SHA256_2]; - DSLog(@"%@ initAtIndex: %u lockedOutpoint: %@: %lu", self.logPrefix, index, uint256_hex(lockedOutpoint.hash), lockedOutpoint.n); - return self; +- (uint32_t)index { + return DIdentityModelIndex(self.model); } -- (instancetype)initAtIndex:(uint32_t)index - withAssetLockTransaction:(DSAssetLockTransaction *)transaction - inWallet:(DSWallet *)wallet { - NSParameterAssert(wallet); - NSAssert(index != UINT32_MAX, @"index must be found"); - if (!(self = [self initAtIndex:index withLockedOutpoint:transaction.lockedOutpoint inWallet:wallet])) return nil; - self.registrationAssetLockTransaction = transaction; - return self; +- (uint32_t)currentMainKeyIndex { + return dash_spv_platform_identity_model_IdentityModel_current_main_index(self.model); } -- (instancetype)initAtIndex:(uint32_t)index - uniqueId:(UInt256)uniqueId - inWallet:(DSWallet *)wallet { - NSParameterAssert(wallet); - if (!(self = [super init])) return nil; - self.wallet = wallet; - self.isLocal = YES; - self.isOutgoingInvitation = NO; - self.isTransient = FALSE; - _keysCreated = 0; - self.currentMainKeyIndex = 0; - self.currentMainKeyType = DKeyKindECDSA(); - self.uniqueID = uniqueId; - self.identity_model = dash_spv_platform_identity_model_IdentityModel_new(DIdentityRegistrationStatusRegistered()); - self.chain = wallet.chain; - self.index = index; - return self; +- (DKeyKind *)currentMainKeyType { + return dash_spv_platform_identity_model_IdentityModel_current_main_key_type(self.model); +} + +- (void)setAssociatedInvitation:(DSInvitation *)associatedInvitation { + _associatedInvitation = associatedInvitation; + // It was created locally, we are sending the invite + if (associatedInvitation.createdLocally) { + dash_spv_platform_identity_model_IdentityModel_set_is_outgoing_invitation(self.model, YES); + dash_spv_platform_identity_model_IdentityModel_set_is_from_incoming_invitation(self.model, NO); + dash_spv_platform_identity_model_IdentityModel_set_is_local(self.model, NO); + } else { + // It was created on another device, we are receiving the invite + dash_spv_platform_identity_model_IdentityModel_set_is_outgoing_invitation(self.model, NO); + dash_spv_platform_identity_model_IdentityModel_set_is_from_incoming_invitation(self.model, YES); + dash_spv_platform_identity_model_IdentityModel_set_is_local(self.model, YES); + } } - (dispatch_queue_t)identityQueue { @@ -393,7 +712,7 @@ - (DSIdentityRegistrationStep)stepsCompleted { DSIdentityRegistrationStep stepsCompleted = DSIdentityRegistrationStep_None; if (self.isRegistered) { stepsCompleted = DSIdentityRegistrationStep_RegistrationSteps; - if (dash_spv_platform_identity_model_IdentityModel_confirmed_username_full_paths_count(self.identity_model)) + if (dash_spv_platform_identity_model_IdentityModel_confirmed_username_full_paths_count(self.model)) stepsCompleted |= DSIdentityRegistrationStep_Username; } else if (self.registrationAssetLockTransaction) { stepsCompleted |= DSIdentityRegistrationStep_FundingTransactionCreation; @@ -496,12 +815,12 @@ - (void)continueRegisteringOnNetwork:(DSIdentityRegistrationStep)steps pinPrompt:prompt stepCompletion:stepCompletion completion:completion]; - } else if (dash_spv_platform_identity_model_IdentityModel_is_registered(self.identity_model)) { + } else if (dash_spv_platform_identity_model_IdentityModel_is_registered(self.model)) { [self continueRegisteringIdentityOnNetwork:steps stepsCompleted:DSIdentityRegistrationStep_L1Steps stepCompletion:stepCompletion completion:completion]; - } else if (dash_spv_platform_identity_model_IdentityModel_unregistered_username_full_paths_count(self.identity_model)) { + } else if (dash_spv_platform_identity_model_IdentityModel_unregistered_username_full_paths_count(self.model)) { [self continueRegisteringUsernamesOnNetwork:steps stepsCompleted:DSIdentityRegistrationStep_L1Steps | DSIdentityRegistrationStep_Identity stepCompletion:stepCompletion @@ -568,7 +887,7 @@ - (void)publishTransactionAndWait:(DSAssetLockTransaction *)transaction completion(NO, nil, error); return; } - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + dispatch_async(self.identityQueue, ^{ dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, 50 * NSEC_PER_SEC)); [[NSNotificationCenter defaultCenter] removeObserver:observer]; completion(transactionSuccessfullyPublished, instantSendLock, nil); @@ -661,9 +980,9 @@ - (void)registerOnNetwork:(DSIdentityRegistrationStep)steps // MARK: - Local Registration and Generation - (BOOL)hasIdentityExtendedPublicKeys { - NSAssert(_isLocal || _isOutgoingInvitation, @"This should not be performed on a non local identity (but can be done for an invitation)"); - if (!_isLocal && !_isOutgoingInvitation) return FALSE; - if (_isLocal) { + NSAssert(self.isLocal || self.isOutgoingInvitation, @"This should not be performed on a non local identity (but can be done for an invitation)"); + if (!self.isLocal && !self.isOutgoingInvitation) return FALSE; + if (self.isLocal) { DSAuthenticationKeysDerivationPath *derivationPathBLS = [[DSDerivationPathFactory sharedInstance] identityBLSKeysDerivationPathForWallet:self.wallet]; DSAuthenticationKeysDerivationPath *derivationPathECDSA = [[DSDerivationPathFactory sharedInstance] identityECDSAKeysDerivationPathForWallet:self.wallet]; DSAssetLockDerivationPath *derivationPathRegistrationFunding = [[DSDerivationPathFactory sharedInstance] identityRegistrationFundingDerivationPathForWallet:self.wallet]; @@ -673,7 +992,7 @@ - (BOOL)hasIdentityExtendedPublicKeys { && [derivationPathRegistrationFunding hasExtendedPublicKey] && [derivationPathTopupFunding hasExtendedPublicKey]; } - if (_isOutgoingInvitation) { + if (self.isOutgoingInvitation) { DSAssetLockDerivationPath *derivationPathInvitationFunding = [[DSDerivationPathFactory sharedInstance] identityInvitationFundingDerivationPathForWallet:self.wallet]; return [derivationPathInvitationFunding hasExtendedPublicKey]; } @@ -682,8 +1001,9 @@ - (BOOL)hasIdentityExtendedPublicKeys { - (void)generateIdentityExtendedPublicKeysWithPrompt:(NSString *)prompt completion:(void (^_Nullable)(BOOL registered))completion { - NSAssert(_isLocal || _isOutgoingInvitation, @"This should not be performed on a non local identity (but can be done for an invitation)"); - if (!_isLocal && !_isOutgoingInvitation) return; + BOOL isLocal = self.isLocal; + NSAssert(isLocal || self.isOutgoingInvitation, @"This should not be performed on a non local identity (but can be done for an invitation)"); + if (!isLocal && !self.isOutgoingInvitation) return; if ([self hasIdentityExtendedPublicKeys]) { if (completion) completion(YES); return; @@ -697,20 +1017,20 @@ - (void)generateIdentityExtendedPublicKeysWithPrompt:(NSString *)prompt if (completion) completion(NO); return; } - if (self->_isLocal) { + if (self.isLocal) { DSAuthenticationKeysDerivationPath *derivationPathBLS = [[DSDerivationPathFactory sharedInstance] identityBLSKeysDerivationPathForWallet:self.wallet]; DSAuthenticationKeysDerivationPath *derivationPathECDSA = [[DSDerivationPathFactory sharedInstance] identityECDSAKeysDerivationPathForWallet:self.wallet]; [derivationPathBLS generateExtendedPublicKeyFromSeed:seed storeUnderWalletUniqueId:self.wallet.uniqueIDString]; [derivationPathECDSA generateExtendedPublicKeyFromSeed:seed storeUnderWalletUniqueId:self.wallet.uniqueIDString]; - if (!self->_isFromIncomingInvitation) { + if (!self.isOutgoingInvitation) { DSAssetLockDerivationPath *derivationPathRegistrationFunding = [[DSDerivationPathFactory sharedInstance] identityRegistrationFundingDerivationPathForWallet:self.wallet]; DSAssetLockDerivationPath *derivationPathTopupFunding = [[DSDerivationPathFactory sharedInstance] identityTopupFundingDerivationPathForWallet:self.wallet]; [derivationPathRegistrationFunding generateExtendedPublicKeyFromSeed:seed storeUnderWalletUniqueId:self.wallet.uniqueIDString]; [derivationPathTopupFunding generateExtendedPublicKeyFromSeed:seed storeUnderWalletUniqueId:self.wallet.uniqueIDString]; } } - if (self->_isOutgoingInvitation) { + if (self.isOutgoingInvitation) { DSAssetLockDerivationPath *derivationPathInvitationFunding = [[DSDerivationPathFactory sharedInstance] identityInvitationFundingDerivationPathForWallet:self.wallet]; [derivationPathInvitationFunding generateExtendedPublicKeyFromSeed:seed storeUnderWalletUniqueId:self.wallet.uniqueIDString]; } @@ -719,8 +1039,8 @@ - (void)generateIdentityExtendedPublicKeysWithPrompt:(NSString *)prompt } - (void)registerInWalletForAssetLockTransaction:(DSAssetLockTransaction *)transaction { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) return; + NSAssert(self.isLocal, @"This should not be performed on a non local blockchain identity"); + if (!self.isLocal) return; self.registrationAssetLockTransactionHash = transaction.txHash; DSUTXO lockedOutpoint = transaction.lockedOutpoint; UInt256 creditBurnIdentityIdentifier = transaction.creditBurnIdentityIdentifier; @@ -732,8 +1052,8 @@ - (void)registerInWalletForAssetLockTransaction:(DSAssetLockTransaction *)transa } - (void)registerInWalletForAssetLockTopupTransaction:(DSAssetLockTransaction *)transaction { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) return; + NSAssert(self.isLocal, @"This should not be performed on a non local blockchain identity"); + if (!self.isLocal) return; [self.topupAssetLockTransactionHashes addObject:uint256_data(transaction.txHash)]; DSUTXO lockedOutpoint = transaction.lockedOutpoint; @@ -746,29 +1066,30 @@ - (void)registerInWalletForAssetLockTopupTransaction:(DSAssetLockTransaction *)t } - (void)registerInWalletForIdentityUniqueId:(UInt256)identityUniqueId { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) return; - self.uniqueID = identityUniqueId; + NSAssert(self.isLocal, @"This should not be performed on a non local blockchain identity"); + if (!self.isLocal) return; + dash_spv_platform_identity_model_IdentityModel_set_unique_id(self.model, u256_ctor_u(identityUniqueId)); +// self.uniqueID = identityUniqueId; [self registerInWallet]; } - (BOOL)isRegisteredInWallet { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) return FALSE; + NSAssert(self.isLocal, @"This should not be performed on a non local blockchain identity"); + if (!self.isLocal) return FALSE; if (!self.wallet) return FALSE; return [self.wallet containsIdentity:self]; } - (void)registerInWallet { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) return; + NSAssert(self.isLocal, @"This should not be performed on a non local blockchain identity"); + if (!self.isLocal) return; [self.wallet registerIdentity:self]; [self saveInitial]; } - (BOOL)unregisterLocally { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) return FALSE; + NSAssert(self.isLocal, @"This should not be performed on a non local blockchain identity"); + if (!self.isLocal) return FALSE; if (self.isRegistered) return FALSE; //if it is already registered we can not unregister it from the wallet [self.wallet unregisterIdentity:self]; [self deletePersistentObjectAndSave:YES inContext:self.platformContext]; @@ -776,15 +1097,16 @@ - (BOOL)unregisterLocally { } - (void)setInvitationUniqueId:(UInt256)uniqueId { - NSAssert(_isOutgoingInvitation, @"This can only be done on an invitation"); - if (!_isOutgoingInvitation) return; - self.uniqueID = uniqueId; + NSAssert(self.isOutgoingInvitation, @"This can only be done on an invitation"); + if (!self.isOutgoingInvitation) return; +// self.uniqueID = uniqueId; + dash_spv_platform_identity_model_IdentityModel_set_unique_id(self.model, u256_ctor_u(uniqueId)); } - (void)setInvitationAssetLockTransaction:(DSAssetLockTransaction *)transaction { NSParameterAssert(transaction); - NSAssert(_isOutgoingInvitation, @"This can only be done on an invitation"); - if (!_isOutgoingInvitation) return; + NSAssert(self.isOutgoingInvitation, @"This can only be done on an invitation"); + if (!self.isOutgoingInvitation) return; self.registrationAssetLockTransaction = transaction; self.lockedOutpoint = transaction.lockedOutpoint; @@ -808,8 +1130,18 @@ - (DSAssetLockTransaction *)registrationAssetLockTransaction { return _registrationAssetLockTransaction; } +- (UInt256)uniqueID { + u256 *unique_id = dash_spv_platform_identity_model_IdentityModel_unique_id(self.model); + UInt256 result = u256_cast(unique_id); + u256_dtor(unique_id); + return result; +} + - (NSData *)uniqueIDData { - return uint256_data(self.uniqueID); + u256 *unique_id = dash_spv_platform_identity_model_IdentityModel_unique_id(self.model); + NSData *result = NSDataFromPtr(unique_id); + u256_dtor(unique_id); + return result; } - (NSData *)lockedOutpointData { @@ -821,7 +1153,7 @@ - (NSString *)currentDashpayUsername { } - (NSArray *)derivationPaths { - if (!_isLocal) return nil; + if (!self.isLocal) return nil; return [[DSDerivationPathFactory sharedInstance] unloadedSpecializedDerivationPathsForWallet:self.wallet]; } @@ -850,15 +1182,21 @@ - (DMaybeOpaqueKey *)topupFundingPrivateKey { return self.internalTopupFundingPrivateKey; } +- (UInt256)dashpaySyncronizationBlockHash { + u256 *block_hash = dash_spv_platform_identity_model_IdentityModel_sync_block_hash(self.model); + UInt256 blockHash = u256_cast(block_hash); + u256_dtor(block_hash); + return blockHash; +} + - (void)setDashpaySyncronizationBlockHash:(UInt256)dashpaySyncronizationBlockHash { - _dashpaySyncronizationBlockHash = dashpaySyncronizationBlockHash; - if (uint256_is_zero(_dashpaySyncronizationBlockHash)) { + dash_spv_platform_identity_model_IdentityModel_set_sync_block_hash(self.model, u256_ctor_u(dashpaySyncronizationBlockHash)); + if (uint256_is_zero(dashpaySyncronizationBlockHash)) { _dashpaySyncronizationBlockHeight = 0; } else { - _dashpaySyncronizationBlockHeight = [self.chain heightForBlockHash:_dashpaySyncronizationBlockHash]; - if (_dashpaySyncronizationBlockHeight == UINT32_MAX) { + _dashpaySyncronizationBlockHeight = [self.chain heightForBlockHash:dashpaySyncronizationBlockHash]; + if (_dashpaySyncronizationBlockHeight == UINT32_MAX) _dashpaySyncronizationBlockHeight = 0; - } } } @@ -924,10 +1262,10 @@ - (void)createFundingPrivateKeyWithPrompt:(NSString *)prompt - (BOOL)activePrivateKeysAreLoadedWithFetchingError:(NSError **)error { BOOL loaded = YES; - DKeyInfoDictionaries *key_infos = DGetRegisteredKeyInfoDictionaries(self.identity_model); + DKeyInfoDictionaries *key_infos = DGetRegisteredKeyInfoDictionaries(self.model); for (uint32_t index = 0; index < key_infos->count; index++) { DKeyInfo *key_info = key_infos->values[index]; - loaded &= !_isLocal ? NO : hasKeychainData([self identifierForKeyAtPath:[self indexPathForIndex:key_infos->keys[index]] fromDerivationPath:[self derivationPathForType:key_info->key_type]], error); + loaded &= !self.isLocal ? NO : hasKeychainData([self identifierForKeyAtPath:[self indexPathForIndex:key_infos->keys[index]] fromDerivationPath:[self derivationPathForType:key_info->key_type]], error); if (*error) { DKeyInfoDictionariesDtor(key_infos); return NO; @@ -938,17 +1276,17 @@ - (BOOL)activePrivateKeysAreLoadedWithFetchingError:(NSError **)error { } - (uintptr_t)activeKeyCount { - return dash_spv_platform_identity_model_IdentityModel_active_key_count(self.identity_model); + return dash_spv_platform_identity_model_IdentityModel_active_key_count(self.model); } - (uintptr_t)totalKeyCount { - return dash_spv_platform_identity_model_IdentityModel_total_key_count(self.identity_model); + return dash_spv_platform_identity_model_IdentityModel_total_key_count(self.model); } - (BOOL)verifyKeysForWallet:(DSWallet *)wallet { DSWallet *originalWallet = self.wallet; self.wallet = wallet; - DKeyInfoDictionaries *key_info_dictionaries = DGetKeyInfoDictionaries(self.identity_model); + DKeyInfoDictionaries *key_info_dictionaries = DGetKeyInfoDictionaries(self.model); for (uint32_t index = 0; index < key_info_dictionaries->count; index++) { DKeyInfo *key_info = key_info_dictionaries->values[index]; if (!key_info->key) { @@ -982,11 +1320,14 @@ - (BOOL)verifyKeysForWallet:(DSWallet *)wallet { } - (DIdentityKeyStatus *)statusOfKeyAtIndex:(NSUInteger)index { - return dash_spv_platform_identity_model_IdentityModel_status_of_key_at_index(self.identity_model, (uint32_t) index); + return dash_spv_platform_identity_model_IdentityModel_status_of_key_at_index(self.model, (uint32_t) index); } - (DOpaqueKey *_Nullable)keyAtIndex:(NSUInteger)index { - return dash_spv_platform_identity_model_IdentityModel_key_at_index(self.identity_model, (uint32_t) index); + return dash_spv_platform_identity_model_IdentityModel_key_at_index(self.model, (uint32_t) index); +} +- (BOOL)hasKeyAtIndex:(NSUInteger)index { + return dash_spv_platform_identity_model_IdentityModel_has_key_at_index(self.model, (uint32_t) index); } - (NSString *)localizedStatusOfKeyAtIndex:(NSUInteger)index { @@ -994,8 +1335,8 @@ - (NSString *)localizedStatusOfKeyAtIndex:(NSUInteger)index { } + (NSString *)localizedStatusOfKeyForIdentityKeyStatus:(DIdentityKeyStatus *)status { - char *str = dash_spv_platform_identity_model_IdentityKeyStatus_string(status); - char *desc = dash_spv_platform_identity_model_IdentityKeyStatus_string_description(status); + char *str = dash_spv_platform_identity_key_status_IdentityKeyStatus_string(status); + char *desc = dash_spv_platform_identity_key_status_IdentityKeyStatus_string_description(status); NSString *localizedStatus = DSLocalizedString(NSStringFromPtr(str), NSStringFromPtr(desc)); DCharDtor(str); DCharDtor(desc); @@ -1003,7 +1344,7 @@ + (NSString *)localizedStatusOfKeyForIdentityKeyStatus:(DIdentityKeyStatus *)sta } - (DSAuthenticationKeysDerivationPath *)derivationPathForType:(DKeyKind *)type { - if (!_isLocal) return nil; + if (!self.isLocal) return nil; // TODO: ed25519 + bls basic int16_t index = DKeyKindIndex(type); switch (index) { @@ -1018,13 +1359,13 @@ - (DSAuthenticationKeysDerivationPath *)derivationPathForType:(DKeyKind *)type { } - (NSIndexPath *)indexPathForIndex:(uint32_t)index { - const NSUInteger indexes[] = {_index | BIP32_HARD, index | BIP32_HARD}; + const NSUInteger indexes[] = {DIdentityModelIndex(self.model) | BIP32_HARD, index | BIP32_HARD}; NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; return indexPath; } - (DMaybeOpaqueKey *)privateKeyAtIndex:(uint32_t)index ofType:(DKeyKind *)type { - if (!_isLocal) return nil; + if (!self.isLocal) return nil; NSIndexPath *indexPath = [self indexPathForIndex:index]; DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; NSError *error = nil; @@ -1035,13 +1376,13 @@ - (DMaybeOpaqueKey *)privateKeyAtIndex:(uint32_t)index ofType:(DKeyKind *)type { } - (DMaybeOpaqueKey *)derivePrivateKeyAtIndexPath:(NSIndexPath *)indexPath ofType:(DKeyKind *)type { - if (!_isLocal) return nil; + if (!self.isLocal) return nil; DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; return [derivationPath privateKeyAtIndexPath:[indexPath hardenAllItems]]; } - (DMaybeOpaqueKey *_Nullable)publicKeyAtIndex:(uint32_t)index ofType:(DKeyKind *)type { - if (!_isLocal) return nil; + if (!self.isLocal) return nil; NSIndexPath *hardenedIndexPath = [self indexPathForIndex:index]; DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; return [derivationPath publicKeyAtIndexPath:hardenedIndexPath]; @@ -1052,40 +1393,50 @@ - (DMaybeOpaqueKey *)createNewKeyOfType:(DKeyKind *)type purpose:(DPurpose *)purpose saveKey:(BOOL)saveKey returnIndex:(uint32_t *)rIndex { - if (!_isLocal) return nil; + if (!self.isLocal) return nil; uint32_t keyIndex = self.keysCreated; NSIndexPath *hardenedIndexPath = [self indexPathForIndex:keyIndex]; DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; - DMaybeOpaqueKey *publicKey = [derivationPath publicKeyAtIndexPath:hardenedIndexPath]; + DMaybeOpaqueKey *maybe_public_key = [derivationPath publicKeyAtIndexPath:hardenedIndexPath]; + if (maybe_public_key->error) { + DSLog(@"%@ Error creating public key of type %u with security level %u for purpose %u at %@", self.logPrefix, DKeyKindIndex(type), DSecurityLevelIndex(security_level), DPurposeIndex(purpose), hardenedIndexPath); + return nil; + } + DOpaqueKey *public_key = maybe_public_key->ok; NSAssert([derivationPath hasExtendedPrivateKey], @"The derivation path should have an extended private key"); DMaybeOpaqueKey *privateKey = [derivationPath privateKeyAtIndexPath:hardenedIndexPath]; NSAssert(privateKey && privateKey->ok, @"The private key should have been derived"); - NSAssert([DSKeyManager keysPublicKeyDataIsEqual:publicKey->ok key2:privateKey->ok], @"These should be equal"); + NSAssert([DSKeyManager keysPublicKeyDataIsEqual:public_key key2:privateKey->ok], @"These should be equal"); _keysCreated++; if (rIndex) *rIndex = keyIndex; - [self addKeyInfo:publicKey->ok + [self addKeyInfo:public_key type:type securityLevel:security_level purpose:purpose - status:dash_spv_platform_identity_model_IdentityKeyStatus_Registering_ctor() + status:dash_spv_platform_identity_key_status_IdentityKeyStatus_Registering_ctor() index:keyIndex]; - if (saveKey) - [self saveNewKey:publicKey->ok - atPath:hardenedIndexPath - withStatus:dash_spv_platform_identity_model_IdentityKeyStatus_Registering_ctor() - withSecurityLevel:security_level - withPurpose:purpose - fromDerivationPath:derivationPath - inContext:[NSManagedObjectContext viewContext]]; - return publicKey; + if (saveKey && !self.isTransient && self.isActive) { + [self saveNewKeyForCurrentEntity:public_key + atPath:hardenedIndexPath + withStatus:dash_spv_platform_identity_key_status_IdentityKeyStatus_Registering_ctor() + withSecurityLevel:security_level + withPurpose:purpose + fromDerivationPath:derivationPath + inContext:[NSManagedObjectContext viewContext]]; + [self notifyUpdate:@{ + DSChainManagerNotificationChainKey: self.chain, + DSIdentityKey: self, + DSIdentityUpdateEvents: @[DSIdentityUpdateEventKeyUpdate] + }]; + } + return maybe_public_key; } - - (uint32_t)firstIndexOfKeyOfType:(DKeyKind *)type createIfNotPresent:(BOOL)createIfNotPresent saveKey:(BOOL)saveKey { - DKeyInfoDictionaries *key_info_dictionaries = DGetKeyInfoDictionaries(self.identity_model); + DKeyInfoDictionaries *key_info_dictionaries = DGetKeyInfoDictionaries(self.model); for (uint32_t index = 0; index < key_info_dictionaries->count; index++) { uint32_t key_info_index = key_info_dictionaries->keys[index]; DKeyInfo *key_info = key_info_dictionaries->values[index]; @@ -1096,7 +1447,7 @@ - (uint32_t)firstIndexOfKeyOfType:(DKeyKind *)type } } DKeyInfoDictionariesDtor(key_info_dictionaries); - if (_isLocal && createIfNotPresent) { + if (self.isLocal && createIfNotPresent) { uint32_t rIndex; [self createNewKeyOfType:type securityLevel:DSecurityLevelMaster() @@ -1111,125 +1462,136 @@ - (uint32_t)firstIndexOfKeyOfType:(DKeyKind *)type - (DIdentityPublicKey *_Nullable)firstIdentityPublicKeyOfSecurityLevel:(DSecurityLevel *)security_level andPurpose:(DPurpose *)purpose { - return dash_spv_platform_identity_model_IdentityModel_first_identity_public_key(self.identity_model, security_level, purpose); + return dash_spv_platform_identity_model_IdentityModel_first_identity_public_key(self.model, security_level, purpose); } -- (void)addKey:(DOpaqueKey *)key - securityLevel:(DSecurityLevel *)security_level - purpose:(DPurpose *)purpose - atIndex:(uint32_t)index - withStatus:(DIdentityKeyStatus *)status - save:(BOOL)save - inContext:(NSManagedObjectContext *)context { +- (void)addRegisteredKey:(DIdentityPublicKey *)public_key + atIndex:(DKeyID *)key_id + save:(BOOL)save + inContext:(NSManagedObjectContext *)context { + DIdentityKeyStatus *status = DIdentityKeyStatusRegistered(); + DMaybeOpaqueKey *maybe_key = DOpaqueKeyFromIdentityPubKey(public_key); + DOpaqueKey *key = maybe_key->ok; DKeyKind *type = DOpaqueKeyKind(key); - DSLogPrivate(@"Identity (local: %u) add key: %p at %u of %u with %lu", self.isLocal, key, index, DKeyKindIndex(type), (unsigned long)status); - if (self.isLocal) { - [self addKey:key - securityLevel:security_level - purpose:purpose - atIndexPath:[NSIndexPath indexPathWithIndexes:(const NSUInteger[]){_index, index} length:2] - ofType:type - withStatus:status - save:save - inContext:context]; - } else { - DKeyInfo *key_info = DKeyInfoAtIndex(self.identity_model, index); - if (key_info) { - DOpaqueKey *maybe_opaque_key = key_info->key; - DIdentityKeyStatus *keyToCheckInDictionaryStatus = key_info->key_status; - if (maybe_opaque_key && [DSKeyManager keysPublicKeyDataIsEqual:maybe_opaque_key key2:key]) { - if (save && status != keyToCheckInDictionaryStatus) - [self updateStatus:status forKeyWithIndexID:index inContext:context]; + uint32_t index = key_id->_0; + DSLogPrivate(@"Identity (local: %u) add registered key: %p at %u of %u", self.isLocal, key, index, DKeyKindIndex(type)); + __block BOOL updated = NO; + switch (public_key->tag) { + case dpp_identity_identity_public_key_IdentityPublicKey_V0: { + DSecurityLevel *security_level = public_key->v0->security_level; + DPurpose *purpose = public_key->v0->purpose; + DKeyInfo *key_info = DKeyInfoAtIndex(self.model, index); + __block BOOL addKeyInfo = NO; + if (self.isLocal) { + NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:(const NSUInteger[]){DIdentityModelIndex(self.model), index} length:2]; + DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; + DMaybeOpaqueKey *keyToCheck = [derivationPath publicKeyAtIndexPath:[indexPath hardenAllItems]]; + NSAssert(keyToCheck != nil && keyToCheck->ok, @"This key should be found"); + NSAssert([DSKeyManager keysPublicKeyDataIsEqual:keyToCheck->ok key2:key], @"these should really match up"); + + if (key_info) { + addKeyInfo = key_info->key && [DSKeyManager keysPublicKeyDataIsEqual:key_info->key key2:key]; + if (addKeyInfo) { + if (save && !self.isTransient && self.isActive) { + [context performBlockAndWait:^{ + DSBlockchainIdentityEntity *identityEntity = [self identityEntityInContext:context]; + DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:derivationPath inContext:context]; + DSBlockchainIdentityKeyPathEntity *keyPathEntity = [[DSBlockchainIdentityKeyPathEntity objectsInContext:context matching:@"blockchainIdentity == %@ && derivationPath == %@ && path == %@ && keyStatus != %u", identityEntity, derivationPathEntity, indexPath, dash_spv_platform_identity_key_status_IdentityKeyStatus_Registered] firstObject]; + if (keyPathEntity) { + keyPathEntity.keyStatus = dash_spv_platform_identity_key_status_IdentityKeyStatus_Registered; + [context ds_save]; + updated = YES; + } + }]; + } + } else { + NSAssert(FALSE, @"these should really match up"); + DSLog(@"these should really match up"); + } + DKeyInfoDtor(key_info); + } else { + _keysCreated = MAX(self.keysCreated, index + 1); + if (save && !self.isTransient && self.isActive) { + updated = [self saveNewKeyForCurrentEntity:key + atPath:indexPath + withStatus:dash_spv_platform_identity_key_status_IdentityKeyStatus_Registered_ctor() + withSecurityLevel:security_level + withPurpose:purpose + fromDerivationPath:derivationPath + inContext:context]; + } + } } else { - NSAssert(FALSE, @"these should really match up"); - DSLog(@"these should really match up"); - DKeyInfoDtor(key_info); - return; + if (key_info) { + addKeyInfo = key_info->key && [DSKeyManager keysPublicKeyDataIsEqual:key_info->key key2:key]; + if (addKeyInfo) { + if (save && !self.isTransient && self.isActive && status != key_info->key_status) { + [context performBlockAndWait:^{ + DSBlockchainIdentityEntity *identityEntity = [self identityEntityInContext:context]; + DSBlockchainIdentityKeyPathEntity *keyPathEntity = [[DSBlockchainIdentityKeyPathEntity objectsInContext:context matching:@"blockchainIdentity == %@ && derivationPath == NULL && keyID == %@", identityEntity, @(index)] firstObject]; + if (keyPathEntity) { + keyPathEntity.keyStatus = dash_spv_platform_identity_key_status_IdentityKeyStatus_Registered; + [context ds_save]; + updated = YES; + } + }]; + } + } else { + NSAssert(FALSE, @"these should really match up"); + DSLog(@"these should really match up"); + } + DKeyInfoDtor(key_info); + } else { + _keysCreated = MAX(self.keysCreated, index + 1); + if (save && !self.isTransient && self.isActive) { + [context performBlockAndWait:^{ + DSBlockchainIdentityEntity *identityEntity = [self identityEntityInContext:context]; + NSUInteger count = [DSBlockchainIdentityKeyPathEntity countObjectsInContext:context matching:@"blockchainIdentity == %@ && keyID == %@", identityEntity, @(index)]; + if (!count) { + DSBlockchainIdentityKeyPathEntity *keyPathEntity = [DSBlockchainIdentityKeyPathEntity managedObjectInBlockedContext:context]; + // TODO: migrate OpaqueKey/KeyKind to KeyType + keyPathEntity.keyType = DOpaqueKeyToKeyTypeIndex(key); + keyPathEntity.keyStatus = DIdentityKeyStatusToIndex(status); + keyPathEntity.keyID = index; + keyPathEntity.publicKeyData = [DSKeyManager publicKeyData:key]; + keyPathEntity.securityLevel = DSecurityLevelIndex(security_level); + keyPathEntity.purpose = DPurposeIndex(purpose); + [identityEntity addKeyPathsObject:keyPathEntity]; + [context ds_save]; + } + updated = YES; + }]; + } + } } - } else { - _keysCreated = MAX(self.keysCreated, index + 1); - if (save) - [self saveNewRemoteIdentityKey:key - forKeyWithIndexID:index - withStatus:status - withSecurityLevel:security_level - withPurpose:purpose - inContext:context]; + if (addKeyInfo) + [self addKeyInfo:key type:type securityLevel:security_level purpose:purpose status:status index:index]; + break; } - [self addKeyInfo:key type:type securityLevel:security_level purpose:purpose status:status index:index]; - if (key_info) - DKeyInfoDtor(key_info); - } -} - -- (void)addKey:(DOpaqueKey *)key - securityLevel:(DSecurityLevel *)security_level - purpose:(DPurpose *)purpose - atIndexPath:(NSIndexPath *)indexPath - ofType:(DKeyKind *)type - withStatus:(DIdentityKeyStatus *)status - save:(BOOL)save - inContext:(NSManagedObjectContext *_Nullable)context { - NSAssert(self.isLocal, @"This should only be called on local blockchain identities"); - if (!self.isLocal) return; - DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; - //derivationPath will be nil if not local - - DMaybeOpaqueKey *keyToCheck = [derivationPath publicKeyAtIndexPath:[indexPath hardenAllItems]]; - NSAssert(keyToCheck != nil && keyToCheck->ok, @"This key should be found"); - if ([DSKeyManager keysPublicKeyDataIsEqual:keyToCheck->ok key2:key]) { //if it isn't local we shouldn't verify - uint32_t index = (uint32_t)[indexPath indexAtPosition:[indexPath length] - 1]; - DKeyInfo *key_info = DKeyInfoAtIndex(self.identity_model, index); - - if (key_info) { - if (key_info->key && [DSKeyManager keysPublicKeyDataIsEqual:key_info->key key2:key]) { - if (save) - [self updateStatus:status - forKeyAtPath:indexPath - fromDerivationPath:derivationPath - inContext:context]; - } else { - NSAssert(FALSE, @"these should really match up"); - DSLog(@"these should really match up"); - DKeyInfoDtor(key_info); - return; - } - } else { - _keysCreated = MAX(self.keysCreated, index + 1); - if (save) - [self saveNewKey:key - atPath:indexPath - withStatus:status - withSecurityLevel:security_level - withPurpose:purpose - fromDerivationPath:derivationPath - inContext:context]; + default: { + DSLog(@"WARN: Unsupported Identity Public Key Version %u", public_key->tag); + break; } - [self addKeyInfo:key - type:type - securityLevel:security_level - purpose:purpose - status:status - index:index]; - if (key_info) - DKeyInfoDtor(key_info); - } else { - DSLog(@"these should really match up"); } + if (updated) + [self notifyUpdate:@{ + DSChainManagerNotificationChainKey: self.chain, + DSIdentityKey: self, + DSIdentityUpdateEvents: @[DSIdentityUpdateEventKeyUpdate] + }]; + } + - (void)addKeyInfo:(DOpaqueKey *)key type:(DKeyKind *)type securityLevel:(DSecurityLevel *)security_level purpose:(DPurpose *)purpose status:(DIdentityKeyStatus *)status index:(uint32_t)index { -// DSLogPrivate(@"%@: addKeyInfo: %p %u %hhu %u", self.logPrefix, key, DKeyKindIndex(type), DSecurityLevelIndex(security_level), index); - DKeyInfo *key_info = dash_spv_platform_identity_model_KeyInfo_ctor(key, type, status, security_level, purpose); - dash_spv_platform_identity_model_IdentityModel_add_key_info(self.identity_model, index, key_info); + DIdentityModelAddKeyInfo(self.model, index, DKeyInfoCtor(key, type, status, security_level, purpose)); } - // MARK: - Funding - (NSString *)registrationFundingAddress { @@ -1247,11 +1609,11 @@ - (NSString *)registrationFundingAddress { // MARK: Helpers - (BOOL)isRegistered { - return dash_spv_platform_identity_model_IdentityModel_is_registered(self.identity_model); + return dash_spv_platform_identity_model_IdentityModel_is_registered(self.model); } - (NSString *)localizedRegistrationStatusString { - char *status_string = dash_spv_platform_identity_model_IdentityRegistrationStatus_string(self.registrationStatus); + char *status_string = dash_spv_platform_identity_registration_status_IdentityRegistrationStatus_string(self.registrationStatus); NSString *status = NSStringFromPtr(status_string); DCharDtor(status_string); return status; @@ -1262,27 +1624,14 @@ - (void)applyIdentity:(DIdentity *)identity inContext:(NSManagedObjectContext *_Nullable)context { switch (identity->tag) { case dpp_identity_identity_Identity_V0: { - dpp_identity_v0_IdentityV0 *versioned = identity->v0; - _creditBalance = versioned->balance; - DIdentityPublicKeysMap *public_keys = versioned->public_keys; - for (int k = 0; k < public_keys->count; k++) { - DIdentityPublicKey *public_key = public_keys->values[k]; - switch (public_key->tag) { - case dpp_identity_identity_public_key_IdentityPublicKey_V0: { - DKeyID *key_id = public_keys->keys[k]; - DMaybeOpaqueKey *opaque = DOpaqueKeyFromIdentityPubKey(public_key); - [self addKey:opaque->ok - securityLevel:public_key->v0->security_level - purpose:public_key->v0->purpose - atIndex:key_id->_0 - withStatus:DIdentityKeyStatusRegistered() - save:save - inContext:context]; - break; - } - default: - break; - } + dpp_identity_v0_IdentityV0 *v0 = identity->v0; + DIdentityModelSetBalance(self.model, v0->balance); + DIdentityPublicKeysMap *public_keys = v0->public_keys; + for (int i = 0; i < public_keys->count; i++) { + [self addRegisteredKey:public_keys->values[i] + atIndex:public_keys->keys[i] + save:save + inContext:context]; } break; } @@ -1394,7 +1743,7 @@ - (void)registerIdentityWithProof:(DAssetLockProof *)proof } - (BOOL)containsPublicKey:(DIdentityPublicKey *)identity_public_key { - return dash_spv_platform_identity_model_IdentityModel_has_identity_public_key(self.identity_model, identity_public_key); + return dash_spv_platform_identity_model_IdentityModel_has_identity_public_key(self.model, identity_public_key); } - (BOOL)containsTopupTransaction:(DSAssetLockTransaction *)transaction { @@ -1579,57 +1928,22 @@ - (void)fetchIdentityNetworkStateInformationWithCompletion:(void (^)(BOOL succes NSMutableString *debugString = [NSMutableString stringWithFormat:@"%@ Fetch Identity State", self.logPrefix]; DSLog(@"%@", debugString); dispatch_async(self.identityQueue, ^{ - DMaybeIdentity *result = dash_spv_platform_identity_manager_IdentitiesManager_monitor_for_id_bytes(self.chain.sharedRuntime, self.chain.sharedIdentitiesObj, u256_ctor(self.uniqueIDData), DRetryDown50(DEFAULT_FETCH_IDENTITY_RETRY_COUNT), self.isLocal ? DAcceptIdentityNotFound() : DRaiseIdentityNotFound()); + Result_Tuple_bool_bool_err_dash_spv_platform_error_Error *result = dash_spv_platform_identity_manager_IdentitiesManager_fetch_identity_network_state_information(self.chain.sharedRuntime, self.chain.sharedIdentitiesObj, self.model, AS_RUST(self)); if (result->error) { NSError *error = [NSError ffi_from_platform_error:result->error]; DSLog(@"%@: ERROR: %@", debugString, error); - DMaybeIdentityDtor(result); + Result_Tuple_bool_bool_err_dash_spv_platform_error_Error_destroy(result); completion(NO, NO, error); return; } - DIdentity *identity = result->ok; - if (!identity) { - DSLog(@"%@ ERROR: None", debugString); - DMaybeIdentityDtor(result); - completion(YES, NO, nil); - return; - } - switch (identity->tag) { - case dpp_identity_identity_Identity_V0: { - dpp_identity_v0_IdentityV0 *identity_v0 = identity->v0; - self->_creditBalance = identity_v0->balance; - DIdentityPublicKeysMap *public_keys = identity_v0->public_keys; - for (int i = 0; i < public_keys->count; i++) { - DIdentityPublicKey *key = public_keys->values[i]; - switch (key->tag) { - case dpp_identity_identity_public_key_IdentityPublicKey_V0: { - DMaybeOpaqueKey *maybe_key = DOpaqueKeyFromIdentityPubKey(key); - [self addKey:maybe_key->ok - securityLevel:key->v0->security_level - purpose:key->v0->purpose - atIndex:i - withStatus:DIdentityKeyStatusRegistered() - save:!self.isTransient - inContext:self.platformContext]; - break; - } - default: - DSLog(@"%@ WARN: Unsupported Identity Public Key Version %u", debugString, key->tag); - break; - } - } - DIdentityModelSetStatus(self.identity_model, DIdentityRegistrationStatusRegistered()); - break; - } - default: - break; - } - DMaybeIdentityDtor(result); - DSLog(@"%@: OK", debugString); - completion(YES, YES, nil); + BOOL success = result->ok->o_0; + BOOL found = result->ok->o_1; + Result_Tuple_bool_bool_err_dash_spv_platform_error_Error_destroy(result); + completion(success, found, nil); }); } + - (void)fetchAllNetworkStateInformationWithCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion { dispatch_async(self.identityQueue, ^{ [self fetchAllNetworkStateInformationInContext:self.platformContext @@ -1671,8 +1985,9 @@ - (void)fetchL3NetworkStateInformation:(DSIdentityQueryStep)queryStep inContext:(NSManagedObjectContext *)context withCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - DSLog(@"%@ Fetch L3 State (%@)", self.logPrefix, DSIdentityQueryStepsDescription(queryStep)); - if (!(queryStep & DSIdentityQueryStep_Identity) && (!self.activeKeyCount)) { + NSMutableString *debugString = [NSMutableString stringWithFormat:@"%@ Fetch L3 State (%@)", self.logPrefix, DSIdentityQueryStepsDescription(queryStep)]; + DSLog(@"%@", debugString); + if (!(queryStep & DSIdentityQueryStep_Identity) && !self.activeKeyCount) { // We need to fetch keys if we want to query other information if (completion) completion(DSIdentityQueryStep_BadQuery, @[ERROR_ATTEMPT_QUERY_WITHOUT_KEYS]); return; @@ -1742,11 +2057,11 @@ - (void)fetchL3NetworkStateInformation:(DSIdentityQueryStep)queryStep if (completion) { dispatch_group_notify(dispatchGroup, self.identityQueue, ^{ #if DEBUG - DSLogPrivate(@"%@: Completed fetching of identity information for user %@ (query %@ - failures %@)", self.logPrefix, + DSLog(@"%@: Finished for user %@ (query %@ - failures %@)", debugString, self.currentDashpayUsername ? self.currentDashpayUsername : self.uniqueIdString, DSIdentityQueryStepsDescription(queryStep), DSIdentityQueryStepsDescription(failureStep)); #else - DSLog(@"%@: Completed fetching of identity information for user %@ (query %@ - failures %@)", - @"", self.logPrefix, DSIdentityQueryStepsDescription(queryStep), DSIdentityQueryStepsDescription(failureStep)); + DSLog(@"%@: Finished for user %@ (query %@ - failures %@)", + @"", debugString, DSIdentityQueryStepsDescription(queryStep), DSIdentityQueryStepsDescription(failureStep)); #endif /* DEBUG */ if (!(failureStep & DSIdentityQueryStep_ContactRequests)) { __strong typeof(weakSelf) strongSelf = weakSelf; @@ -1814,9 +2129,9 @@ - (void)fetchIfNeededNetworkStateInformation:(DSIdentityQueryStep)querySteps DSIdentityQueryStep stepsNeeded = DSIdentityQueryStep_None; if ([DSOptionsManager sharedInstance].syncType & DSSyncType_Identities) stepsNeeded |= DSIdentityQueryStep_Identity; - if (!self.dashpayUsernameCount && self.lastCheckedUsernamesTimestamp == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) + if (!self.dashpayUsernameCount && DIdentityModelLastCheckedUsernamesTimestamp(self.model) == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) stepsNeeded |= DSIdentityQueryStep_Username; - if ((self.lastCheckedProfileTimestamp < [NSDate timeIntervalSince1970MinusHour]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + if ((DIdentityModelLastCheckedProfileTimestamp(self.model) < [NSDate timeIntervalSince1970MinusHour]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) stepsNeeded |= DSIdentityQueryStep_Profile; if (stepsNeeded != DSIdentityQueryStep_None) { [self fetchNetworkStateInformation:stepsNeeded & querySteps inContext:context withCompletion:completion onCompletionQueue:completionQueue]; @@ -1826,18 +2141,18 @@ - (void)fetchIfNeededNetworkStateInformation:(DSIdentityQueryStep)querySteps } } else { DSIdentityQueryStep stepsNeeded = DSIdentityQueryStep_None; - if (!self.dashpayUsernameCount && self.lastCheckedUsernamesTimestamp == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) { + if (!self.dashpayUsernameCount && DIdentityModelLastCheckedUsernamesTimestamp(self.model) == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) { stepsNeeded |= DSIdentityQueryStep_Username; } __block uint64_t createdAt; [context performBlockAndWait:^{ createdAt = [[self matchingDashpayUserInContext:context] createdAt]; }]; - if (!createdAt && (self.lastCheckedProfileTimestamp < [NSDate timeIntervalSince1970MinusHour]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + if (!createdAt && (DIdentityModelLastCheckedProfileTimestamp(self.model) < [NSDate timeIntervalSince1970MinusHour]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) stepsNeeded |= DSIdentityQueryStep_Profile; - if (self.isLocal && (self.lastCheckedIncomingContactsTimestamp < [NSDate timeIntervalSince1970MinusHour]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + if (self.isLocal && (DIdentityModelLastCheckedIncomingContactsTimestamp(self.model) < [NSDate timeIntervalSince1970MinusHour]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) stepsNeeded |= DSIdentityQueryStep_IncomingContactRequests; - if (self.isLocal && (self.lastCheckedOutgoingContactsTimestamp < [NSDate timeIntervalSince1970MinusHour]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + if (self.isLocal && (DIdentityModelLastCheckedOutgoingContactsTimestamp(self.model) < [NSDate timeIntervalSince1970MinusHour]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) stepsNeeded |= DSIdentityQueryStep_OutgoingContactRequests; if (stepsNeeded != DSIdentityQueryStep_None) { [self fetchNetworkStateInformation:stepsNeeded & querySteps inContext:context withCompletion:completion onCompletionQueue:completionQueue]; @@ -1861,9 +2176,9 @@ - (void)fetchNeededNetworkStateInformationInContext:(NSManagedObjectContext *)co DSIdentityQueryStep stepsNeeded = DSIdentityQueryStep_None; if ([DSOptionsManager sharedInstance].syncType & DSSyncType_Identities) stepsNeeded |= DSIdentityQueryStep_Identity; - if (!self.dashpayUsernameCount && self.lastCheckedUsernamesTimestamp == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) + if (!self.dashpayUsernameCount && DIdentityModelLastCheckedUsernamesTimestamp(self.model) == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) stepsNeeded |= DSIdentityQueryStep_Username; - if ((self.lastCheckedProfileTimestamp < [NSDate timeIntervalSince1970Minus:HOUR_TIME_INTERVAL]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + if ((DIdentityModelLastCheckedProfileTimestamp(self.model) < [NSDate timeIntervalSince1970Minus:HOUR_TIME_INTERVAL]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) stepsNeeded |= DSIdentityQueryStep_Profile; if (stepsNeeded != DSIdentityQueryStep_None) { [self fetchNetworkStateInformation:stepsNeeded @@ -1876,13 +2191,13 @@ - (void)fetchNeededNetworkStateInformationInContext:(NSManagedObjectContext *)co } } else { DSIdentityQueryStep stepsNeeded = DSIdentityQueryStep_None; - if (!self.dashpayUsernameCount && self.lastCheckedUsernamesTimestamp == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) + if (!self.dashpayUsernameCount && DIdentityModelLastCheckedUsernamesTimestamp(self.model) == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) stepsNeeded |= DSIdentityQueryStep_Username; - if (![[self matchingDashpayUserInContext:context] createdAt] && (self.lastCheckedProfileTimestamp < [NSDate timeIntervalSince1970MinusHour]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + if (![[self matchingDashpayUserInContext:context] createdAt] && (DIdentityModelLastCheckedProfileTimestamp(self.model) < [NSDate timeIntervalSince1970MinusHour]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) stepsNeeded |= DSIdentityQueryStep_Profile; - if (self.isLocal && (self.lastCheckedIncomingContactsTimestamp < [NSDate timeIntervalSince1970MinusHour]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + if (self.isLocal && (DIdentityModelLastCheckedIncomingContactsTimestamp(self.model) < [NSDate timeIntervalSince1970MinusHour]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) stepsNeeded |= DSIdentityQueryStep_IncomingContactRequests; - if (self.isLocal && (self.lastCheckedOutgoingContactsTimestamp < [NSDate timeIntervalSince1970MinusHour]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + if (self.isLocal && (DIdentityModelLastCheckedOutgoingContactsTimestamp(self.model) < [NSDate timeIntervalSince1970MinusHour]) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) stepsNeeded |= DSIdentityQueryStep_OutgoingContactRequests; if (stepsNeeded != DSIdentityQueryStep_None) { [self fetchNetworkStateInformation:stepsNeeded @@ -2042,7 +2357,7 @@ - (void)updateCreditBalance { DMaybeIdentityBalanceDtor(result); DSLog(@"%@ Update Credit Balance: OK: %llu", self.logPrefix, balance); dispatch_async(self.identityQueue, ^{ - strongSelf.creditBalance = balance; + DIdentityModelSetBalance(strongSelf.model, balance); }); }); } @@ -2069,9 +2384,24 @@ - (UInt256)contractIdIfRegistered:(DDataContract *)contract { } - (DIdentityRegistrationStatus *)registrationStatus { - return dash_spv_platform_identity_model_IdentityModel_registration_status(self.identity_model); + return dash_spv_platform_identity_model_IdentityModel_registration_status(self.model); } +- (BOOL)registrationStatusIsPending { + DIdentityRegistrationStatus *status = self.registrationStatus; + BOOL is_pending = dash_spv_platform_identity_registration_status_IdentityRegistrationStatus_is_unknown(status) || dash_spv_platform_identity_registration_status_IdentityRegistrationStatus_is_not_registered(status); + DIdentityRegistrationStatusDtor(status); + return is_pending; +} +- (BOOL)registrationStatusIsClaimed { + DIdentityRegistrationStatus *status = self.registrationStatus; + BOOL is_claimed = dash_spv_platform_identity_registration_status_IdentityRegistrationStatus_is_registering(status) || dash_spv_platform_identity_registration_status_IdentityRegistrationStatus_is_registered(status); +// is_claimed = dash_spv_platform_identity_registration_status_IdentityRegistrationStatus_is_unknown(status) || dash_spv_platform_identity_registration_status_IdentityRegistrationStatus_is_not_registered(status); + DIdentityRegistrationStatusDtor(status); + return is_claimed; +} + + // MARK: - Persistence // MARK: Saving @@ -2085,12 +2415,12 @@ - (DSBlockchainIdentityEntity *)initialEntityInContext:(NSManagedObjectContext * DSBlockchainIdentityEntity *entity = [DSBlockchainIdentityEntity managedObjectInBlockedContext:context]; entity.uniqueID = uint256_data(self.uniqueID); entity.isLocal = self.isLocal; - entity.registrationStatus = DIdentityRegistrationStatusIndex(self.identity_model); + entity.registrationStatus = DIdentityRegistrationStatusIndex(self.model); if (self.isLocal) entity.registrationFundingTransaction = [DSAssetLockTransactionEntity anyObjectInContext:context matching:@"transactionHash.txHash == %@", uint256_data(self.registrationAssetLockTransaction.txHash)]; entity.chain = chainEntity; [self collectUsernameEntitiesIntoIdentityEntityInContext:entity context:context]; - DKeyInfoDictionaries *key_info_dictionaries = DGetKeyInfoDictionaries(self.identity_model); + DKeyInfoDictionaries *key_info_dictionaries = DGetKeyInfoDictionaries(self.model); for (uint32_t index = 0; index < key_info_dictionaries->count; index++) { uint32_t key_info_index = key_info_dictionaries->keys[index]; @@ -2101,7 +2431,7 @@ - (DSBlockchainIdentityEntity *)initialEntityInContext:(NSManagedObjectContext * DSecurityLevel *level = key_info->security_level; DPurpose *purpose = key_info->purpose; DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:key_type]; - const NSUInteger indexes[] = {_index, key_info_index}; + const NSUInteger indexes[] = {DIdentityModelIndex(self.model), key_info_index}; [self createNewKey:key forIdentityEntity:entity atPath:[NSIndexPath indexPathWithIndexes:indexes length:2] @@ -2157,7 +2487,7 @@ - (void)saveInContext:(NSManagedObjectContext *)context { [updateEvents addObject:DSIdentityUpdateEventCreditBalance]; } - uint16_t registrationStatus = DIdentityRegistrationStatusIndex(self.identity_model); + uint16_t registrationStatus = DIdentityRegistrationStatusIndex(self.model); if (entity.registrationStatus != registrationStatus) { entity.registrationStatus = registrationStatus; changeOccured = YES; @@ -2169,24 +2499,24 @@ - (void)saveInContext:(NSManagedObjectContext *)context { changeOccured = YES; [updateEvents addObject:DSIdentityUpdateEventDashpaySyncronizationBlockHash]; } - - if (entity.lastCheckedUsernamesTimestamp != self.lastCheckedUsernamesTimestamp) { - entity.lastCheckedUsernamesTimestamp = self.lastCheckedUsernamesTimestamp; + uint64_t lastCheckedUsernamesTimestamp = DIdentityModelLastCheckedUsernamesTimestamp(self.model); + if (entity.lastCheckedUsernamesTimestamp != lastCheckedUsernamesTimestamp) { + entity.lastCheckedUsernamesTimestamp = lastCheckedUsernamesTimestamp; changeOccured = YES; } - - if (entity.lastCheckedProfileTimestamp != self.lastCheckedProfileTimestamp) { - entity.lastCheckedProfileTimestamp = self.lastCheckedProfileTimestamp; + uint64_t lastCheckedProfileTimestamp = DIdentityModelLastCheckedProfileTimestamp(self.model); + if (entity.lastCheckedProfileTimestamp != lastCheckedProfileTimestamp) { + entity.lastCheckedProfileTimestamp = lastCheckedProfileTimestamp; changeOccured = YES; } - - if (entity.lastCheckedIncomingContactsTimestamp != self.lastCheckedIncomingContactsTimestamp) { - entity.lastCheckedIncomingContactsTimestamp = self.lastCheckedIncomingContactsTimestamp; + uint64_t lastCheckedIncomingContactsTimestamp = DIdentityModelLastCheckedIncomingContactsTimestamp(self.model); + if (entity.lastCheckedIncomingContactsTimestamp != lastCheckedIncomingContactsTimestamp) { + entity.lastCheckedIncomingContactsTimestamp = lastCheckedIncomingContactsTimestamp; changeOccured = YES; } - - if (entity.lastCheckedOutgoingContactsTimestamp != self.lastCheckedOutgoingContactsTimestamp) { - entity.lastCheckedOutgoingContactsTimestamp = self.lastCheckedOutgoingContactsTimestamp; + uint64_t lastCheckedOutgoingContactsTimestamp = DIdentityModelLastCheckedOutgoingContactsTimestamp(self.model); + if (entity.lastCheckedOutgoingContactsTimestamp != lastCheckedOutgoingContactsTimestamp) { + entity.lastCheckedOutgoingContactsTimestamp = lastCheckedOutgoingContactsTimestamp; changeOccured = YES; } @@ -2207,6 +2537,28 @@ - (NSString *)identifierForKeyAtPath:(NSIndexPath *)path return [NSString stringWithFormat:@"%@-%@-%@", self.uniqueIdString, derivationPath.standaloneExtendedPublicKeyUniqueID, [[path softenAllItems] indexPathString]]; } +- (BOOL)saveNewKeyForCurrentEntity:(DOpaqueKey *)key + atPath:(NSIndexPath *)path + withStatus:(DIdentityKeyStatus *)status + withSecurityLevel:(DSecurityLevel *)security_level + withPurpose:(DPurpose *)purpose + fromDerivationPath:(DSDerivationPath *)derivationPath + inContext:(NSManagedObjectContext *)context { + __block BOOL save = NO; + [context performBlockAndWait:^{ + save = [self createNewKey:key + forIdentityEntity:[self identityEntityInContext:context] + atPath:path + withStatus:status + withSecurityLevel:security_level + withPurpose:purpose + fromDerivationPath:derivationPath + inContext:context]; + if (save) + [context ds_save]; + }]; + return save; +} - (BOOL)createNewKey:(DOpaqueKey *)key forIdentityEntity:(DSBlockchainIdentityEntity *)identityEntity atPath:(NSIndexPath *)path @@ -2216,15 +2568,9 @@ - (BOOL)createNewKey:(DOpaqueKey *)key fromDerivationPath:(DSDerivationPath *)derivationPath inContext:(NSManagedObjectContext *)context { NSAssert(identityEntity, @"Entity should be present"); - DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:derivationPath inContext:context]; NSUInteger count = [DSBlockchainIdentityKeyPathEntity countObjectsInContext:context matching:@"blockchainIdentity == %@ && derivationPath == %@ && path == %@", identityEntity, derivationPathEntity, path]; if (!count) { - DSBlockchainIdentityKeyPathEntity *keyPathEntity = [DSBlockchainIdentityKeyPathEntity managedObjectInBlockedContext:context]; - keyPathEntity.derivationPath = derivationPathEntity; - // TODO: that's wrong should convert KeyType <-> KeyKind - keyPathEntity.keyType = DOpaqueKeyToKeyTypeIndex(key); - keyPathEntity.keyStatus = DIdentityKeyStatusToIndex(status); NSData *privateKeyData = [DSKeyManager privateKeyData:key]; if (!privateKeyData) { DKeyKind *kind = DOpaqueKeyKind(key); @@ -2233,6 +2579,11 @@ - (BOOL)createNewKey:(DOpaqueKey *)key privateKeyData = [DSKeyManager privateKeyData:privateKey->ok]; NSAssert(privateKeyData, @"Private key data should exist"); } + DSBlockchainIdentityKeyPathEntity *keyPathEntity = [DSBlockchainIdentityKeyPathEntity managedObjectInBlockedContext:context]; + keyPathEntity.derivationPath = derivationPathEntity; + // TODO: that's wrong should convert KeyType <-> KeyKind + keyPathEntity.keyType = DOpaqueKeyToKeyTypeIndex(key); + keyPathEntity.keyStatus = DIdentityKeyStatusToIndex(status); NSString *identifier = [self identifierForKeyAtPath:path fromDerivationPath:derivationPath]; setKeychainData(privateKeyData, identifier, YES); @@ -2248,110 +2599,6 @@ - (BOOL)createNewKey:(DOpaqueKey *)key } } -- (void)saveNewKey:(DOpaqueKey *)key - atPath:(NSIndexPath *)path - withStatus:(DIdentityKeyStatus *)status - withSecurityLevel:(DSecurityLevel *)security_level - withPurpose:(DPurpose *)purpose -fromDerivationPath:(DSDerivationPath *)derivationPath - inContext:(NSManagedObjectContext *)context { - NSAssert(self.isLocal, @"This should only be called on local blockchain identities"); - if (!self.isLocal || self.isTransient || !self.isActive) return; - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *identityEntity = [self identityEntityInContext:context]; - if ([self createNewKey:key - forIdentityEntity:identityEntity - atPath:path - withStatus:status - withSecurityLevel:security_level - withPurpose:purpose - fromDerivationPath:derivationPath - inContext:context]) - [context ds_save]; - [self notifyUpdate:@{ - DSChainManagerNotificationChainKey: self.chain, - DSIdentityKey: self, - DSIdentityUpdateEvents: @[DSIdentityUpdateEventKeyUpdate] - }]; - }]; -} - -- (void)saveNewRemoteIdentityKey:(DOpaqueKey *)key - forKeyWithIndexID:(uint32_t)keyID - withStatus:(DIdentityKeyStatus *)status - withSecurityLevel:(DSecurityLevel *)security_level - withPurpose:(DPurpose *)purpose - inContext:(NSManagedObjectContext *)context { - NSAssert(self.isLocal == FALSE, @"This should only be called on non local identities"); - if (self.isLocal || self.isTransient || !self.isActive) return; - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *identityEntity = [self identityEntityInContext:context]; - NSUInteger count = [DSBlockchainIdentityKeyPathEntity countObjectsInContext:context matching:@"blockchainIdentity == %@ && keyID == %@", identityEntity, @(keyID)]; - if (!count) { - DSBlockchainIdentityKeyPathEntity *keyPathEntity = [DSBlockchainIdentityKeyPathEntity managedObjectInBlockedContext:context]; - // TODO: migrate OpaqueKey/KeyKind to KeyType - keyPathEntity.keyType = DOpaqueKeyToKeyTypeIndex(key); - keyPathEntity.keyStatus = DIdentityKeyStatusToIndex(status); - keyPathEntity.keyID = keyID; - keyPathEntity.publicKeyData = [DSKeyManager publicKeyData:key]; - keyPathEntity.securityLevel = DSecurityLevelIndex(security_level); - keyPathEntity.purpose = DPurposeIndex(purpose); - [identityEntity addKeyPathsObject:keyPathEntity]; - [context ds_save]; - } - [self notifyUpdate:@{ - DSChainManagerNotificationChainKey: self.chain, - DSIdentityKey: self, - DSIdentityUpdateEvents: @[DSIdentityUpdateEventKeyUpdate] - }]; - }]; -} - - -- (void)updateStatus:(DIdentityKeyStatus *)status - forKeyAtPath:(NSIndexPath *)path - fromDerivationPath:(DSDerivationPath *)derivationPath - inContext:(NSManagedObjectContext *)context { - NSAssert(self.isLocal, @"This should only be called on local identities"); - if (!self.isLocal || self.isTransient || !self.isActive) return; - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *identityEntity = [self identityEntityInContext:context]; - DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:derivationPath inContext:context]; - DSBlockchainIdentityKeyPathEntity *keyPathEntity = [[DSBlockchainIdentityKeyPathEntity objectsInContext:context matching:@"blockchainIdentity == %@ && derivationPath == %@ && path == %@", identityEntity, derivationPathEntity, path] firstObject]; - uint16_t keyStatus = DIdentityKeyStatusToIndex(status); - if (keyPathEntity && (keyPathEntity.keyStatus != keyStatus)) { - keyPathEntity.keyStatus = keyStatus; - [context ds_save]; - } - [self notifyUpdate:@{ - DSChainManagerNotificationChainKey: self.chain, - DSIdentityKey: self, - DSIdentityUpdateEvents: @[DSIdentityUpdateEventKeyUpdate] - }]; - }]; -} - -- (void)updateStatus:(DIdentityKeyStatus *)status - forKeyWithIndexID:(uint32_t)keyID - inContext:(NSManagedObjectContext *)context { - NSAssert(self.isLocal == FALSE, @"This should only be called on non local identities"); - if (self.isLocal || self.isTransient || !self.isActive) return; - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *identityEntity = [self identityEntityInContext:context]; - DSBlockchainIdentityKeyPathEntity *keyPathEntity = [[DSBlockchainIdentityKeyPathEntity objectsInContext:context matching:@"blockchainIdentity == %@ && derivationPath == NULL && keyID == %@", identityEntity, @(keyID)] firstObject]; - if (keyPathEntity) { - DSBlockchainIdentityKeyPathEntity *keyPathEntity = [DSBlockchainIdentityKeyPathEntity managedObjectInBlockedContext:context]; - keyPathEntity.keyStatus = DIdentityKeyStatusToIndex(status); - [context ds_save]; - } - [self notifyUpdate:@{ - DSChainManagerNotificationChainKey: self.chain, - DSIdentityKey: self, - DSIdentityUpdateEvents: @[DSIdentityUpdateEventKeyUpdate] - }]; - }]; -} - // MARK: Deletion - (void)deletePersistentObjectAndSave:(BOOL)save diff --git a/DashSync/shared/Models/Identity/DSUsernameFullPathSaveContext.h b/DashSync/shared/Models/Identity/DSUsernameFullPathSaveContext.h index 2bd7f86d..882159a0 100644 --- a/DashSync/shared/Models/Identity/DSUsernameFullPathSaveContext.h +++ b/DashSync/shared/Models/Identity/DSUsernameFullPathSaveContext.h @@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) DSIdentity *identity; @property (nonatomic, assign) NSManagedObjectContext *context; + (instancetype)contextWithUsernames:(NSArray *)usernames forIdentity:(DSIdentity *)identity inContext:(NSManagedObjectContext *)context; -- (void)setAndSaveUsernameFullPaths:(DUsernameStatus *)status; +//- (void)setAndSaveUsernameFullPaths:(DUsernameStatus *)status; @end diff --git a/DashSync/shared/Models/Identity/DSUsernameFullPathSaveContext.m b/DashSync/shared/Models/Identity/DSUsernameFullPathSaveContext.m index 8068686c..271b61f1 100644 --- a/DashSync/shared/Models/Identity/DSUsernameFullPathSaveContext.m +++ b/DashSync/shared/Models/Identity/DSUsernameFullPathSaveContext.m @@ -27,10 +27,10 @@ + (instancetype)contextWithUsernames:(NSArray *)usernames forIdentit ctx.context = context; return ctx; } -- (void)setAndSaveUsernameFullPaths:(DUsernameStatus *)status { - [self.identity setAndSaveUsernameFullPaths:self.usernames - toStatus:status - inContext:self.context]; -} +//- (void)setAndSaveUsernameFullPaths:(DUsernameStatus *)status { +// [self.identity setAndSaveUsernameFullPaths:self.usernames +// toStatus:status +// inContext:self.context]; +//} @end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m index 930d0bab..e7a13f58 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m @@ -321,11 +321,20 @@ - (void)syncBlockchain { - (void)chainFinishedSyncingMasternodeListsAndQuorums:(DSChain *)chain { if (chain.isEvolutionEnabled && ![self.syncState.platformSyncInfo hasRecentIdentitiesSync]) { DSLog(@"%@ Sync Status: masternode list and quorums: OK -> sync identities", self.logPrefix); - [self.identitiesManager syncIdentitiesWithCompletion:^(NSArray *_Nullable identities) { - // always from chain.networkingQueue - DSLog(@"%@ Sync Status: identities: OK -> sync chain", self.logPrefix); - [self syncBlockchain]; - }]; + dispatch_async(dispatch_get_main_queue(), ^{ + // we can't access derivation keychain items from non-background state + if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { + // TODO: implement proper bg management (UIApplicationWillEnterForegroundNotification) + DSLog(@"%@ Sync Status: identities: skipped (background) -> sync chain", self.logPrefix); + dispatch_async(self.chain.networkingQueue, ^{ [self syncBlockchain]; }); + } else { + [self.identitiesManager syncIdentitiesWithCompletion:^(NSArray *_Nullable identities) { + // always from chain.networkingQueue + DSLog(@"%@ Sync Status: identities: OK -> sync chain", self.logPrefix); + [self syncBlockchain]; + }]; + } + }); } else { DSLog(@"%@ Sync Status: masternode list and quorums: OK -> sync chain", self.logPrefix); [self syncBlockchain]; diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.h index 54138d09..45be2c1e 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.h @@ -62,24 +62,24 @@ typedef void (^DashpayUserInfoCompletionBlock)(BOOL success, DSTransientDashpayU inDomain:(NSString *)domain withCompletion:(IdentityCompletionBlock)completion; -- (void)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix - queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo - withCompletion:(IdentitiesCompletionBlock)completion; - -- (void)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix - startAfter:(NSData* _Nullable)startAfter - limit:(uint32_t)limit - queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo - withCompletion:(IdentitiesCompletionBlock)completion; +//- (void)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix +// queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo +// withCompletion:(IdentitiesCompletionBlock)completion; +// +//- (void)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix +// startAfter:(NSData* _Nullable)startAfter +// limit:(uint32_t)limit +// queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo +// withCompletion:(IdentitiesCompletionBlock)completion; - (void)searchIdentitiesByNamePrefix:(NSString *)namePrefix startAfter:(NSData* _Nullable)startAfter limit:(uint32_t)limit withCompletion:(IdentitiesCompletionBlock)completion; -- (void)fetchProfileForIdentity:(DSIdentity *)identity - withCompletion:(DashpayUserInfoCompletionBlock)completion - onCompletionQueue:(dispatch_queue_t)completionQueue; +//- (void)fetchProfileForIdentity:(DSIdentity *)identity +// withCompletion:(DashpayUserInfoCompletionBlock)completion +// onCompletionQueue:(dispatch_queue_t)completionQueue; - (void)searchIdentitiesByDPNSRegisteredIdentityUniqueID:(NSData *)userID withCompletion:(IdentitiesCompletionBlock)completion; diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.m index 7424fcac..5602038e 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.m @@ -147,12 +147,13 @@ - (void)syncIdentitiesWithCompletion:(IdentitiesSuccessCompletionBlock)completio } DSLog(@"%@ Sync Identities", self.logPrefix); dispatch_async(self.identityQueue, ^{ + DSLog(@"%@ Sync Identities Enter Identity Queue", self.logPrefix); NSArray *wallets = self.chain.wallets; __block dispatch_group_t keyHashesDispatchGroup = dispatch_group_create(); __block NSMutableArray *errors = [NSMutableArray array]; __block NSMutableArray *allIdentities = [NSMutableArray array]; - const int keysToCheck = 5; + const int keysToCheck = 1; dispatch_async(self.chain.networkingQueue, ^{ [self.chain.chainManager.syncState addSyncKind:DSSyncStateExtKind_Platform]; [self.chain.chainManager.syncState.platformSyncInfo addSyncKind:DSPlatformSyncStateKind_KeyHashes]; @@ -173,11 +174,12 @@ - (void)syncIdentitiesWithCompletion:(IdentitiesSuccessCompletionBlock)completio key_hashes[i] = u160_ctor_u(publicKeyData.hash160); [keyIndexes setObject:@(unusedIndex + i) forKey:publicKeyData]; } - dispatch_group_enter(keyHashesDispatchGroup); - DRetry *stragegy = DRetryLinear(5); - dash_spv_platform_identity_manager_IdentityValidator *options = DAcceptIdentityNotFound(); - dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{ + dispatch_sync(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{ + DRetry *stragegy = DRetryLinear(5); + dash_spv_platform_identity_manager_IdentityValidator *options = DAcceptIdentityNotFound(); + dispatch_group_enter(keyHashesDispatchGroup); + DSLog(@"%@ Sync Identities Enter QOS Queue", self.logPrefix); Result_ok_std_collections_Map_keys_u8_arr_20_values_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error *result = dash_spv_platform_identity_manager_IdentitiesManager_monitor_for_key_hashes(self.chain.sharedRuntime, self.chain.sharedIdentitiesObj, Vec_u8_20_ctor(keysToCheck, key_hashes), stragegy, options); if (result->error) { @@ -196,9 +198,10 @@ - (void)syncIdentitiesWithCompletion:(IdentitiesSuccessCompletionBlock)completio switch (identity->tag) { case dpp_identity_identity_Identity_V0: { dpp_identity_v0_IdentityV0 *identity_v0 = identity->v0; - DMaybeOpaqueKey *maybe_opaque_key = DOpaqueKeyFromIdentityPubKey(identity_v0->public_keys->values[0]); - NSData *publicKeyData = [DSKeyManager publicKeyData:maybe_opaque_key->ok]; - NSNumber *index = [keyIndexes objectForKey:publicKeyData]; + DMaybeKeyData *maybe_public_key_data = DIdentityPubKeyData(identity_v0->public_keys->values[0]); +// NSData *publicKeyData = [DSKeyManager publicKeyData:maybe_opaque_key->ok]; + NSNumber *index = [keyIndexes objectForKey:NSDataFromPtr(maybe_public_key_data->ok)]; + DMaybeKeyDataDtor(maybe_public_key_data); DSIdentity *identityModel = [[DSIdentity alloc] initAtIndex:index.intValue uniqueId:u256_cast(identity_v0->id->_0->_0) inWallet:wallet]; [identityModel applyIdentity:identity save:NO inContext:nil]; [identities addObject:identityModel]; @@ -214,6 +217,7 @@ - (void)syncIdentitiesWithCompletion:(IdentitiesSuccessCompletionBlock)completio BOOL success = [wallet registerIdentities:identities verify:YES]; dispatch_async(self.chain.networkingQueue, ^{ self.chain.chainManager.syncState.platformSyncInfo.queueCount = keysToCheck; + [self.chain.chainManager notifySyncStateChanged]; }); DSLog(@"%@: Sync Identities: %@", self.logPrefix, DSLocalizedFormat(success ? @"OK (%lu)" : @"Retrieved (%lu) but can't register in wallet", nil, identities.count)); @@ -250,6 +254,7 @@ - (void)syncIdentitiesWithCompletion:(IdentitiesSuccessCompletionBlock)completio } [self.chain.chainManager notifySyncStateChanged]; + dispatch_group_t dispatchGroup = dispatch_group_create(); __block NSMutableArray *errors = [NSMutableArray array]; for (DSIdentity *identity in identities) { @@ -258,16 +263,15 @@ - (void)syncIdentitiesWithCompletion:(IdentitiesSuccessCompletionBlock)completio dispatch_async(self.chain.networkingQueue, ^{ self.chain.chainManager.syncState.platformSyncInfo.queueCount++; [self.chain.chainManager notifySyncStateChanged]; - }); - if (success && identity != nil) { - dispatch_group_leave(dispatchGroup); - } else { + }); + if (!success || !identity) [errors addObject:error]; - } + dispatch_group_leave(dispatchGroup); } completionQueue:self.identityQueue]; } dispatch_group_notify(dispatchGroup, self.chain.networkingQueue, ^{ + DSLog(@""); self.lastSyncedIndentitiesTimestamp = [[NSDate date] timeIntervalSince1970]; self.chain.chainManager.syncState.platformSyncInfo.queueCount = 0; self.chain.chainManager.syncState.platformSyncInfo.queueMaxAmount = 0; @@ -275,6 +279,7 @@ - (void)syncIdentitiesWithCompletion:(IdentitiesSuccessCompletionBlock)completio [self.chain.chainManager.syncState.platformSyncInfo resetSyncKind]; [self.chain.chainManager.syncState removeSyncKind:DSSyncStateExtKind_Platform]; [self.chain.chainManager notifySyncStateChanged]; + if (!errors.count && completion) completion(identities); }); @@ -318,7 +323,7 @@ - (void)searchIdentityByName:(NSString *)name DIdentifier *owner_id = document->v0->owner_id; DSIdentity *identity = [[DSIdentity alloc] initWithUniqueId:u256_cast(owner_id->_0->_0) isTransient:TRUE onChain:self.chain]; - [identity addUsername:normalizedLabel inDomain:domain status:dash_spv_platform_document_usernames_UsernameStatus_Confirmed_ctor() save:NO registerOnNetwork:NO]; + [identity addConfirmedUsername:normalizedLabel inDomain:domain]; [rIdentities addObject:identity]; } @@ -329,122 +334,119 @@ - (void)searchIdentityByName:(NSString *)name }); } -- (void)fetchProfileForIdentity:(DSIdentity *)identity - withCompletion:(DashpayUserInfoCompletionBlock)completion - onCompletionQueue:(dispatch_queue_t)completionQueue { - NSMutableString *debugString = [NSMutableString stringWithFormat:@"%@ Fetch Profile for: %@", self.logPrefix, identity]; - DSLog(@"%@", debugString); - DPContract *dashpayContract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; - if ([dashpayContract contractState] != DPContractState_Registered) { - DSLog(@"%@: ERROR: DashPay Contract Not Registered", debugString); - if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, nil); }); - return; - } - DMaybeDocument *result = dash_spv_platform_document_manager_DocumentsManager_stream_dashpay_profile_for_user_id_using_contract(self.chain.sharedRuntime, self.chain.sharedDocumentsObj, u256_ctor_u(identity.uniqueID), dashpayContract.raw_contract, DRetryDown20(5), DNotFoundAsAnError(), 2000); - if (result->error) { - NSError *error = [NSError ffi_from_platform_error:result->error]; - DSLog(@"%@: ERROR: %@", debugString, error); - dispatch_async(completionQueue, ^{ completion(NO, nil, error); }); - DMaybeDocumentDtor(result); - return; - } - if (!result->ok) { - DSLog(@"%@: ERROR: Profile is None", debugString); - if (completion) dispatch_async(completionQueue, ^{ completion(YES, nil, nil); }); - DMaybeDocumentDtor(result); - return; - } - DSTransientDashpayUser *transientDashpayUser = [[DSTransientDashpayUser alloc] initWithDocument:result->ok]; - DSLog(@"%@: OK: %@", debugString, transientDashpayUser); - dispatch_async(completionQueue, ^{ if (completion) completion(YES, transientDashpayUser, nil); }); -} - -- (void)fetchProfilesForIdentities:(NSArray *)identityUserIds - withCompletion:(DashpayUserInfosCompletionBlock)completion - onCompletionQueue:(dispatch_queue_t)completionQueue { - NSMutableString *debugString = [NSMutableString stringWithFormat:@"%@ Fetch Profiles for: %@", self.logPrefix, identityUserIds]; - DSLog(@"%@", debugString); - DPContract *dashpayContract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; - if ([dashpayContract contractState] != DPContractState_Registered) { - DSLog(@"%@: ERROR: DashPay Contract Not Registered", debugString); - if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, ERROR_CONTRACT_SETUP); }); - return; - } - NSUInteger user_ids_count = identityUserIds.count; - u256 **user_ids_values = malloc(sizeof(u256 *) * user_ids_count); - - for (int i = 0; i < user_ids_count; i++) { - NSData *userID = identityUserIds[i]; - user_ids_values[i] = u256_ctor(userID); - } - Vec_u8_32 *user_ids = Vec_u8_32_ctor(user_ids_count, user_ids_values); - DMaybeDocumentsMap *result = dash_spv_platform_document_manager_DocumentsManager_stream_dashpay_profiles_for_user_ids_using_contract(self.chain.sharedRuntime, self.chain.sharedDocumentsObj, user_ids, dashpayContract.raw_contract, DRetryDown20(5), DNotFoundAsAnError(), 2000); - if (result->error) { - NSError *error = [NSError ffi_from_platform_error:result->error]; - DSLog(@"%@: ERROR: %@", debugString, error); - if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, error); }); - DMaybeDocumentsMapDtor(result); - return; - } - DDocumentsMap *documents = result->ok; - NSMutableDictionary *dashpayUserDictionary = [NSMutableDictionary dictionary]; - for (int i = 0; i < documents->count; i++) { - DDocument *document = documents->values[i]; - switch (document->tag) { - case dpp_document_Document_V0: { - DSTransientDashpayUser *transientDashpayUser = [[DSTransientDashpayUser alloc] initWithDocument:document]; - [dashpayUserDictionary setObject:transientDashpayUser forKey:NSDataFromPtr(document->v0->owner_id->_0->_0)]; - break; - } - default: - break; - } - } - DMaybeDocumentsMapDtor(result); - DSLog(@"%@: OK: %@", debugString, dashpayUserDictionary); - if (completion) dispatch_async(completionQueue, ^{ completion(YES, dashpayUserDictionary, nil); }); -} - -- (void)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix - queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo - withCompletion:(IdentitiesCompletionBlock)completion { - [self searchIdentitiesByDashpayUsernamePrefix:namePrefix - startAfter:nil - limit:100 - queryDashpayProfileInfo:queryDashpayProfileInfo - withCompletion:completion]; -} +//- (void)fetchProfileForIdentity:(DSIdentity *)identity +// withCompletion:(DashpayUserInfoCompletionBlock)completion +// onCompletionQueue:(dispatch_queue_t)completionQueue { +// NSMutableString *debugString = [NSMutableString stringWithFormat:@"%@ Fetch Profile for: %@", self.logPrefix, identity]; +// DSLog(@"%@", debugString); +// DPContract *dashpayContract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; +// if ([dashpayContract contractState] != DPContractState_Registered) { +// DSLog(@"%@: ERROR: DashPay Contract Not Registered", debugString); +// if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, nil); }); +// return; +// } +// Result_ok_dash_spv_platform_models_transient_dashpay_user_TransientDashPayUser_err_dash_spv_platform_error_Error *result = dash_spv_platform_document_manager_DocumentsManager_fetch_profile(self.chain.sharedRuntime, self.chain.sharedDocumentsObj, identity.model, dashpayContract.raw_contract); +// +//// DMaybeDocument *result = dash_spv_platform_document_manager_DocumentsManager_stream_dashpay_profile_for_user_id_using_contract(self.chain.sharedRuntime, self.chain.sharedDocumentsObj, u256_ctor_u(identity.uniqueID), dashpayContract.raw_contract, DRetryDown20(5), DNotFoundAsAnError(), 2000); +// if (result->error) { +// NSError *error = [NSError ffi_from_platform_error:result->error]; +// DSLog(@"%@: ERROR: %@", debugString, error); +// dispatch_async(completionQueue, ^{ completion(NO, nil, error); }); +// DMaybeDocumentDtor(result); +// return; +// } +// +// DSTransientDashpayUser *transientDashpayUser = [[DSTransientDashpayUser alloc] initWithDocument:result->ok]; +// DSLog(@"%@: OK: %@", debugString, transientDashpayUser); +// dispatch_async(completionQueue, ^{ if (completion) completion(YES, transientDashpayUser, nil); }); +//} +// +//- (void)fetchProfilesForIdentities:(NSArray *)identityUserIds +// withCompletion:(DashpayUserInfosCompletionBlock)completion +// onCompletionQueue:(dispatch_queue_t)completionQueue { +// NSMutableString *debugString = [NSMutableString stringWithFormat:@"%@ Fetch Profiles for: %@", self.logPrefix, identityUserIds]; +// DSLog(@"%@", debugString); +// DPContract *dashpayContract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; +// if ([dashpayContract contractState] != DPContractState_Registered) { +// DSLog(@"%@: ERROR: DashPay Contract Not Registered", debugString); +// if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, ERROR_CONTRACT_SETUP); }); +// return; +// } +// NSUInteger user_ids_count = identityUserIds.count; +// u256 **user_ids_values = malloc(sizeof(u256 *) * user_ids_count); +// +// for (int i = 0; i < user_ids_count; i++) { +// NSData *userID = identityUserIds[i]; +// user_ids_values[i] = u256_ctor(userID); +// } +// Vec_u8_32 *user_ids = Vec_u8_32_ctor(user_ids_count, user_ids_values); +// DMaybeDocumentsMap *result = dash_spv_platform_document_manager_DocumentsManager_stream_dashpay_profiles_for_user_ids_using_contract(self.chain.sharedRuntime, self.chain.sharedDocumentsObj, user_ids, dashpayContract.raw_contract, DRetryDown20(5), DNotFoundAsAnError(), 2000); +// if (result->error) { +// NSError *error = [NSError ffi_from_platform_error:result->error]; +// DSLog(@"%@: ERROR: %@", debugString, error); +// if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, error); }); +// DMaybeDocumentsMapDtor(result); +// return; +// } +// DDocumentsMap *documents = result->ok; +// NSMutableDictionary *dashpayUserDictionary = [NSMutableDictionary dictionary]; +// for (int i = 0; i < documents->count; i++) { +// DDocument *document = documents->values[i]; +// switch (document->tag) { +// case dpp_document_Document_V0: { +// DSTransientDashpayUser *transientDashpayUser = [[DSTransientDashpayUser alloc] initWithDocument:document]; +// [dashpayUserDictionary setObject:transientDashpayUser forKey:NSDataFromPtr(document->v0->owner_id->_0->_0)]; +// break; +// } +// default: +// break; +// } +// } +// DMaybeDocumentsMapDtor(result); +// DSLog(@"%@: OK: %@", debugString, dashpayUserDictionary); +// if (completion) dispatch_async(completionQueue, ^{ completion(YES, dashpayUserDictionary, nil); }); +//} -- (void)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix - startAfter:(NSData* _Nullable)startAfter - limit:(uint32_t)limit - queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo - withCompletion:(IdentitiesCompletionBlock)completion { - [self searchIdentitiesByNamePrefix:namePrefix - startAfter:startAfter - limit:limit - withCompletion:^(BOOL success, NSArray *_Nullable identities, NSArray *_Nonnull errors) { - if (errors.count) { - if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(success, identities, errors); }); - } else if (queryDashpayProfileInfo && identities.count) { - __block NSMutableDictionary *identityDictionary = [NSMutableDictionary dictionary]; - for (DSIdentity *identity in identities) { - [identityDictionary setObject:identity forKey:identity.uniqueIDData]; - } - [self fetchProfilesForIdentities:identityDictionary.allKeys - withCompletion:^(BOOL success, NSDictionary *_Nullable dashpayUserInfosByIdentityUniqueId, NSError *_Nullable error) { - for (NSData *identityUniqueIdData in dashpayUserInfosByIdentityUniqueId) { - DSIdentity *identity = identityDictionary[identityUniqueIdData]; - identity.transientDashpayUser = dashpayUserInfosByIdentityUniqueId[identityUniqueIdData]; - } - if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(success, identities, errors); }); - } - onCompletionQueue:self.identityQueue]; - } else if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ completion(success, identities, errors); }); - } - }]; -} +//- (void)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix +// queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo +// withCompletion:(IdentitiesCompletionBlock)completion { +// [self searchIdentitiesByDashpayUsernamePrefix:namePrefix +// startAfter:nil +// limit:100 +// queryDashpayProfileInfo:queryDashpayProfileInfo +// withCompletion:completion]; +//} +// +//- (void)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix +// startAfter:(NSData* _Nullable)startAfter +// limit:(uint32_t)limit +// queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo +// withCompletion:(IdentitiesCompletionBlock)completion { +// [self searchIdentitiesByNamePrefix:namePrefix +// startAfter:startAfter +// limit:limit +// withCompletion:^(BOOL success, NSArray *_Nullable identities, NSArray *_Nonnull errors) { +// if (errors.count) { +// if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(success, identities, errors); }); +// } else if (queryDashpayProfileInfo && identities.count) { +// __block NSMutableDictionary *identityDictionary = [NSMutableDictionary dictionary]; +// for (DSIdentity *identity in identities) { +// [identityDictionary setObject:identity forKey:identity.uniqueIDData]; +// } +// [self fetchProfilesForIdentities:identityDictionary.allKeys +// withCompletion:^(BOOL success, NSDictionary *_Nullable dashpayUserInfosByIdentityUniqueId, NSError *_Nullable error) { +// for (NSData *identityUniqueIdData in dashpayUserInfosByIdentityUniqueId) { +// DSIdentity *identity = identityDictionary[identityUniqueIdData]; +// identity.transientDashpayUser = dashpayUserInfosByIdentityUniqueId[identityUniqueIdData]; +// } +// if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(success, identities, errors); }); +// } +// onCompletionQueue:self.identityQueue]; +// } else if (completion) { +// dispatch_async(dispatch_get_main_queue(), ^{ completion(success, identities, errors); }); +// } +// }]; +//} - (void)searchIdentitiesByNamePrefix:(NSString *)namePrefix startAfter:(NSData* _Nullable)startAfter @@ -480,9 +482,9 @@ - (void)searchIdentitiesByNamePrefix:(NSString *)namePrefix NSString *domain = DGetTextDocProperty(document, @"normalizedParentDomainName"); if (!identity) { identity = [[DSIdentity alloc] initWithUniqueId:uniqueId isTransient:TRUE onChain:self.chain]; - [identity addUsername:label inDomain:domain status:dash_spv_platform_document_usernames_UsernameStatus_Confirmed_ctor() save:NO registerOnNetwork:NO]; + [identity addConfirmedUsername:label inDomain:domain]; } else if (![identity hasDashpayUsername:label]) { - [identity addUsername:label inDomain:domain status:dash_spv_platform_document_usernames_UsernameStatus_Confirmed_ctor() save:YES registerOnNetwork:NO]; + [identity addUsername:label inDomain:domain status:dash_spv_platform_document_usernames_UsernameStatus_Confirmed save:YES registerOnNetwork:NO]; } [rIdentities setObject:identity forKey:userIdData]; break; @@ -521,7 +523,7 @@ - (void)searchIdentitiesByDPNSRegisteredIdentityUniqueID:(NSData *)userID NSString *normalizedLabel = DGetTextDocProperty(document, @"normalizedLabel"); NSString *domain = DGetTextDocProperty(document, @"normalizedParentDomainName"); DSIdentity *identity = [[DSIdentity alloc] initWithUniqueId:u256_cast(document->v0->owner_id->_0->_0) isTransient:TRUE onChain:self.chain]; - [identity addUsername:normalizedLabel inDomain:domain status:dash_spv_platform_document_usernames_UsernameStatus_Confirmed_ctor() save:NO registerOnNetwork:NO]; + [identity addConfirmedUsername:normalizedLabel inDomain:domain]; [identity fetchIdentityNetworkStateInformationWithCompletion:^(BOOL success, BOOL found, NSError *error) {}]; [rIdentities addObject:identity]; break; @@ -571,6 +573,7 @@ - (void)fetchNeededNetworkStateInformationForIdentity:(DSIdentity *)identity onCompletionQueue:dispatch_get_main_queue()]; } + // MARK: - DSChainIdentitiesDelegate // always from chain.networkingQueue diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.h index 0b192533..9527b064 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.h @@ -323,6 +323,7 @@ #define DMaybeKeyStringDtor(ptr) Result_ok_String_err_dash_spv_crypto_keys_KeyError_destroy(ptr) #define DIdentityPublicKey dpp_identity_identity_public_key_IdentityPublicKey +#define DIdentityPublicKeyDtor(ptr) dpp_identity_identity_public_key_IdentityPublicKey_destroy(ptr) #define DIdentifier platform_value_types_identifier_Identifier #define DRetry dash_spv_platform_util_RetryStrategy #define DRetryLinear(max_retry) dash_spv_platform_util_RetryStrategy_Linear_ctor(max_retry) @@ -387,11 +388,17 @@ #define DMaybeDocumentsMap Result_ok_indexmap_IndexMap_platform_value_types_identifier_Identifier_Option_dpp_document_Document_err_dash_spv_platform_error_Error #define DMaybeDocumentsMapDtor(ptr) Result_ok_indexmap_IndexMap_platform_value_types_identifier_Identifier_Option_dpp_document_Document_err_dash_spv_platform_error_Error_destroy(ptr) #define DContactRequest dash_spv_platform_models_contact_request_ContactRequest +#define DContactRequests Vec_dash_spv_platform_models_contact_request_ContactRequest +#define DMaybeContactRequests Result_ok_Vec_dash_spv_platform_models_contact_request_ContactRequest_err_dash_spv_platform_error_Error +#define DMaybeContactRequestsDtor(ptr) Result_ok_Vec_dash_spv_platform_models_contact_request_ContactRequest_err_dash_spv_platform_error_Error_destroy(ptr) #define DContactRequestDtor(ptr) dash_spv_platform_models_contact_request_ContactRequest_destroy(ptr) #define DContactRequestKind dash_spv_platform_models_contact_request_ContactRequestKind -#define DContactRequests Vec_dash_spv_platform_models_contact_request_ContactRequestKind -#define DMaybeContactRequests Result_ok_Vec_dash_spv_platform_models_contact_request_ContactRequestKind_err_dash_spv_platform_error_Error -#define DMaybeContactRequestsDtor(ptr) Result_ok_Vec_dash_spv_platform_models_contact_request_ContactRequestKind_err_dash_spv_platform_error_Error_destroy(ptr) +#define DContactRequestKinds Vec_dash_spv_platform_models_contact_request_ContactRequestKind +#define DMaybeContactRequestKinds Result_ok_Vec_dash_spv_platform_models_contact_request_ContactRequestKind_err_dash_spv_platform_error_Error +#define DMaybeContactRequestKindsDtor(ptr) Result_ok_Vec_dash_spv_platform_models_contact_request_ContactRequestKind_err_dash_spv_platform_error_Error_destroy(ptr) + +#define DFetchOutgoingContactRequests(identity, contract, since, start_after, storage_context, has_contact_request_with_id) dash_spv_platform_document_contact_request_ContactRequestManager_fetch_outgoing_contact_requests_in_context(identity.chain.sharedRuntime, identity.chain.sharedContactsObj, identity.model, contract, since, start_after, storage_context, ((__bridge void *)(identity)), has_contact_request_with_id) +#define DFetchIncomingContactRequests(identity, contract, since, start_after, storage_context, has_contact_request_with_id) dash_spv_platform_document_contact_request_ContactRequestManager_fetch_incoming_contact_requests_in_context(identity.chain.sharedRuntime, identity.chain.sharedContactsObj, identity.model, contract, since, start_after, storage_context, ((__bridge void *)(identity)), has_contact_request_with_id) #define DMaybeContract Result_ok_Option_dpp_data_contract_DataContract_err_dash_spv_platform_error_Error #define DMaybeContractDtor(ptr) Result_ok_Option_dpp_data_contract_DataContract_err_dash_spv_platform_error_Error_destroy(ptr) @@ -399,8 +406,10 @@ #define DMaybeIdentity Result_ok_Option_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error #define DMaybeIdentityDtor(ptr) Result_ok_Option_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error_destroy(ptr) -#define DMaybeTransientUser Result_ok_Option_dash_spv_platform_models_transient_dashpay_user_TransientDashPayUser_err_dash_spv_platform_error_Error -#define DMaybeTransientUserDtor(ptr) Result_ok_Option_dash_spv_platform_models_transient_dashpay_user_TransientDashPayUser_err_dash_spv_platform_error_Error_destroy(ptr) +#define DTransientUser dash_spv_platform_models_transient_dashpay_user_TransientDashPayUser +#define DTransientUserDtor(ptr) dash_spv_platform_models_transient_dashpay_user_TransientDashPayUser_destroy(ptr) +#define DMaybeTransientUser Result_ok_dash_spv_platform_models_transient_dashpay_user_TransientDashPayUser_err_dash_spv_platform_error_Error +#define DMaybeTransientUserDtor(ptr) Result_ok_dash_spv_platform_models_transient_dashpay_user_TransientDashPayUser_err_dash_spv_platform_error_Error_destroy(ptr) #define DMaybeStateTransition Result_ok_dpp_state_transition_StateTransition_err_dash_spv_platform_error_Error #define DMaybeStateTransitionProofResult Result_ok_dpp_state_transition_proof_result_StateTransitionProofResult_err_dash_spv_platform_error_Error @@ -487,6 +496,7 @@ #define DRaiseIdentityNotFound() dash_spv_platform_identity_manager_IdentityValidator_None_ctor() #define DOpaqueKeyFromIdentityPubKey(key) dash_spv_platform_identity_manager_opaque_key_from_identity_public_key(key) +#define DIdentityPubKeyData(key) dash_spv_platform_identity_manager_identity_public_key_data(key) #define DOpaqueKeyToKeyTypeIndex(key) dash_spv_platform_identity_manager_opaque_key_to_key_type_index(key) #define DOpaqueKeyKind(key) dash_spv_crypto_keys_key_OpaqueKey_kind(key) @@ -515,29 +525,50 @@ #define DUsernameAdd(model, username, domain, status) dash_spv_platform_identity_model_IdentityModel_add_username(model, DChar(username), DChar(domain), status); #define DMaybeIdentityBalance Result_ok_Option_u64_err_dash_spv_platform_error_Error #define DMaybeIdentityBalanceDtor(ptr) Result_ok_Option_u64_err_dash_spv_platform_error_Error_destroy(ptr) -#define DIdentityKeyStatus dash_spv_platform_identity_model_IdentityKeyStatus -#define DIdentityKeyStatusDtor(ptr) dash_spv_platform_identity_model_IdentityKeyStatus_destroy(ptr) -#define DIdentityRegistrationStatus dash_spv_platform_identity_model_IdentityRegistrationStatus -#define DIdentityRegistrationStatusDtor(ptr) dash_spv_platform_identity_model_IdentityRegistrationStatus_destroy(ptr) +#define DIdentityKeyStatus dash_spv_platform_identity_key_status_IdentityKeyStatus +#define DIdentityKeyStatusDtor(ptr) dash_spv_platform_identity_key_status_IdentityKeyStatus_destroy(ptr) +#define DIdentityRegistrationStatus dash_spv_platform_identity_registration_status_IdentityRegistrationStatus +#define DIdentityRegistrationStatusDtor(ptr) dash_spv_platform_identity_registration_status_IdentityRegistrationStatus_destroy(ptr) #define DIdentityRegistrationStatusIndex(ptr) dash_spv_platform_identity_model_IdentityModel_registration_status_index(ptr) -#define DIdentityRegistrationStatusFromIndex(index) dash_spv_platform_identity_model_IdentityRegistrationStatus_from_index(index) -#define DIdentityRegistrationStatusRegistered() dash_spv_platform_identity_model_IdentityRegistrationStatus_Registered_ctor() - -#define DKeyInfo dash_spv_platform_identity_model_KeyInfo -#define DKeyInfoDtor(ptr) dash_spv_platform_identity_model_KeyInfo_destroy(ptr) -#define DKeyInfoDictionaries std_collections_Map_keys_u32_values_dash_spv_platform_identity_model_KeyInfo -#define DKeyInfoDictionariesDtor(ptr) std_collections_Map_keys_u32_values_dash_spv_platform_identity_model_KeyInfo_destroy(ptr) +#define DIdentityRegistrationStatusFromIndex(index) dash_spv_platform_identity_registration_status_IdentityRegistrationStatus_from_index(index) +#define DIdentityRegistrationStatusUnknown() dash_spv_platform_identity_registration_status_IdentityRegistrationStatus_Unknown_ctor() +#define DIdentityRegistrationStatusRegistered() dash_spv_platform_identity_registration_status_IdentityRegistrationStatus_Registered_ctor() + +#define DKeyInfo dash_spv_platform_identity_key_info_KeyInfo +#define DKeyInfoCtor(key, type, status, security_level, purpose) dash_spv_platform_identity_key_info_KeyInfo_ctor(key, type, status, security_level, purpose) +#define DKeyInfoDtor(ptr) dash_spv_platform_identity_key_info_KeyInfo_destroy(ptr) +#define DKeyInfoDictionaries std_collections_Map_keys_u32_values_dash_spv_platform_identity_key_info_KeyInfo +#define DKeyInfoDictionariesDtor(ptr) std_collections_Map_keys_u32_values_dash_spv_platform_identity_key_info_KeyInfo_destroy(ptr) #define DKeyInfoAtIndex(model, index) dash_spv_platform_identity_model_IdentityModel_key_info_at_index(model, index) #define DIdentityModelSetStatus(model, status) dash_spv_platform_identity_model_IdentityModel_set_registration_status(model, status) +#define DIdentityModelSetBalance(model, balance) dash_spv_platform_identity_model_IdentityModel_set_credit_balance(model, balance) + +#define DSaveUsernameContext dash_spv_platform_identity_storage_username_SaveUsernameContext + +#define DIdentityModelSetSyncBlockHash(model, block_hash) dash_spv_platform_identity_model_IdentityModel_set_sync_block_hash(model, block_hash) +#define DIdentityModelSetLastCheckedProfileTimestamp(model, timestamp) dash_spv_platform_identity_model_IdentityModel_set_last_checked_profile_timestamp(model, timestamp) +#define DIdentityModelSetLastCheckedUsernamesTimestamp(model, timestamp) dash_spv_platform_identity_model_IdentityModel_set_last_checked_usernames_timestamp(model, timestamp) +#define DIdentityModelSetLastCheckedIncomingContactsTimestamp(model, timestamp) dash_spv_platform_identity_model_IdentityModel_set_last_checked_incoming_contacts_timestamp(model, timestamp) +#define DIdentityModelSetLastCheckedOutgoingContactsTimestamp(model, timestamp) dash_spv_platform_identity_model_IdentityModel_set_last_checked_outgoing_contacts_timestamp(model, timestamp) + +#define DIdentityModelAddKeyInfo(model, index, key_info) dash_spv_platform_identity_model_IdentityModel_add_key_info(model, index, key_info) +#define DIdentityModelSyncBlockHash(model) dash_spv_platform_identity_model_IdentityModel_sync_block_hash(model) +#define DIdentityModelLastCheckedProfileTimestamp(model) dash_spv_platform_identity_model_IdentityModel_last_checked_profile_timestamp(model) +#define DIdentityModelLastCheckedUsernamesTimestamp(model) dash_spv_platform_identity_model_IdentityModel_last_checked_usernames_timestamp(model) +#define DIdentityModelLastCheckedIncomingContactsTimestamp(model) dash_spv_platform_identity_model_IdentityModel_last_checked_incoming_contacts_timestamp(model) +#define DIdentityModelLastCheckedOutgoingContactsTimestamp(model) dash_spv_platform_identity_model_IdentityModel_last_checked_outgoing_contacts_timestamp(model) + +#define DIdentityModelIndex(model) dash_spv_platform_identity_model_IdentityModel_index(model) #define DGetKeyInfoDictionaries(model) dash_spv_platform_identity_model_IdentityModel_key_info_dictionaries(model) #define DGetRegisteredKeyInfoDictionaries(model) dash_spv_platform_identity_model_IdentityModel_registered_key_info_dictionaries(model) -#define DIdentityKeyStatusFromIndex(index) dash_spv_platform_identity_model_IdentityKeyStatus_from_index(index) -#define DIdentityKeyStatusToIndex(status) dash_spv_platform_identity_model_IdentityKeyStatus_to_index(status) +#define DIdentityKeyStatusFromIndex(index) dash_spv_platform_identity_key_status_IdentityKeyStatus_from_index(index) +#define DIdentityKeyStatusToIndex(status) dash_spv_platform_identity_key_status_IdentityKeyStatus_to_index(status) -#define DIdentityKeyStatusRegistered() dash_spv_platform_identity_model_IdentityKeyStatus_Registered_ctor() +#define DIdentityKeyStatusRegistered() dash_spv_platform_identity_key_status_IdentityKeyStatus_Registered_ctor() -#define DUsernameStatuses std_collections_Map_keys_String_values_dash_spv_platform_identity_model_UsernameStatusInfo -#define DUsernameStatusesDtor(ptr) std_collections_Map_keys_String_values_dash_spv_platform_identity_model_UsernameStatusInfo_destroy(ptr) +#define DUsernameStatusInfo dash_spv_platform_identity_username_status_info_UsernameStatusInfo +#define DUsernameStatuses std_collections_Map_keys_String_values_dash_spv_platform_identity_username_status_info_UsernameStatusInfo +#define DUsernameStatusesDtor(ptr) std_collections_Map_keys_String_values_dash_spv_platform_identity_username_status_info_UsernameStatusInfo_destroy(ptr) #define DBLSSignature dashcore_bls_sig_utils_BLSSignature #define DBLSSignatureCtor(sig) dashcore_bls_sig_utils_BLSSignature_ctor(sig) diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m index e1204246..ccac7261 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m @@ -565,6 +565,7 @@ - (void)peer:(DSPeer *)peer relayedMasternodeDiffMessage:(NSData *)message { if (quorumValidationError) { dispatch_async(self.chain.networkingQueue, ^{ [self.chain.chainManager.syncState.masternodeListSyncInfo removeSyncKind:DSMasternodeListSyncStateKind_Quorums]; + [self.chain.chainManager notifySyncStateChanged]; }); } else { [self finishIntitialQrInfoPipeline]; @@ -616,6 +617,7 @@ - (void)tryToProcessQrInfo:(DSPeer *)peer message:(NSData *)message attempt:(uin numOfAttempt++; [self tryToProcessQrInfo:peer message:message attempt:attempt]; }); + return; } break; } @@ -668,11 +670,13 @@ - (void)tryToProcessQrInfo:(DSPeer *)peer message:(NSData *)message attempt:(uin } else { dispatch_async(self.chain.networkingQueue, ^{ [self.chain.chainManager.syncState.masternodeListSyncInfo addSyncKind:DSMasternodeListSyncStateKind_Quorums]; + [self.chain.chainManager notifySyncStateChanged]; }); NSError *quorumValidationError = [self verifyQuorums]; if (quorumValidationError) { dispatch_async(self.chain.networkingQueue, ^{ [self.chain.chainManager.syncState.masternodeListSyncInfo removeSyncKind:DSMasternodeListSyncStateKind_Quorums]; + [self.chain.chainManager notifySyncStateChanged]; }); } else { [self finishIntitialQrInfoPipeline]; diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m index d54e94b4..cda5f116 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m @@ -579,9 +579,9 @@ - (void)resumeBlockchainSynchronizationOnPeers { } - (void)updateFilterOnPeers { + DSLog(@"[%@] [DSPeerManager] filter update needed? (%u), waiting for pong", self.downloadPeer.needsFilterUpdate, self.chain.name); if (self.downloadPeer.needsFilterUpdate) return; - self.downloadPeer.needsFilterUpdate = YES; - DSLog(@"[%@] [DSPeerManager] filter update needed, waiting for pong", self.chain.name); + [self pauseBlockchainSynchronizationOnPeers]; [self.downloadPeer sendPingMessageWithPongHandler:^(BOOL success) { // wait for pong so we include already sent tx if (!success) return; @@ -728,6 +728,7 @@ - (void)connect { dispatch_async(self.networkingQueue, ^{ [self.chain.chainManager.syncState.peersSyncInfo addSyncKind:DSPeersSyncStateKind_Connecting]; + [self.chain.chainManager notifySyncStateChanged]; if ([self.chain syncsBlockchain] && ![self.chain canConstructAFilter]) { DSLog(@"[%@] [DSPeerManager] failed to connect: check that wallet is created", self.chain.name); return; // check to make sure the wallet has been created if only are a basic wallet with no dash features @@ -802,6 +803,7 @@ - (void)connect { [self chainSyncStopped]; DSLog(@"[%@] [DSPeerManager] No peers found -> SyncFailed", self.chain.name); [self.chain.chainManager.syncState.peersSyncInfo removeSyncKind:DSPeersSyncStateKind_Connecting]; + [self.chain.chainManager notifySyncStateChanged]; dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:DSChainManagerSyncFailedNotification object:nil diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m index 0113c2d8..20d03e2a 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m @@ -1280,13 +1280,13 @@ - (void)peer:(DSPeer *)peer relayedTransaction:(DSTransaction *)transaction inBl if (peer) { #if DEBUG - DSLogPrivate(@"[%@: %@:%d] relayed transaction %@", self.chain.name, peer.host, peer.port, uint256_hex(txHash)); + DSLogPrivate(@"[%@: %@:%d] relayed transaction %@ (%@)", self.chain.name, peer.host, peer.port, uint256_hex(txHash), uint256_reverse_hex(txHash)); #else DSLog(@"[%@: %@:%d] relayed transaction %@", self.chain.name, peer.host, peer.port, @""); #endif } else { #if DEBUG - DSLogPrivate(@"[%@: %@:%d] accepting local transaction %@", self.chain.name, peer.host, peer.port, uint256_hex(txHash)); + DSLogPrivate(@"[%@: %@:%d] accepting local transaction %@ (%@)", self.chain.name, peer.host, peer.port, uint256_hex(txHash), uint256_reverse_hex(txHash)); #else DSLog(@"[%@: %@:%d] accepting local transaction %@", self.chain.name, peer.host, peer.port, @""); #endif @@ -1301,13 +1301,13 @@ - (void)peer:(DSPeer *)peer relayedTransaction:(DSTransaction *)transaction inBl if (![self.chain transactionHasLocalReferences:transaction]) { if (peer) { #if DEBUG - DSLogPrivate(@"[%@: %@:%d] no account or local references for transaction %@", self.chain.name, peer.host, peer.port, uint256_hex(txHash)); + DSLogPrivate(@"[%@: %@:%d] no account or local references for transaction %@ (%@)", self.chain.name, peer.host, peer.port, uint256_hex(txHash), uint256_reverse_hex(txHash)); #else DSLog(@"[%@: %@:%d] no account or local references for transaction %@", self.chain.name, peer.host, peer.port, @""); #endif } else { #if DEBUG - DSLogPrivate(@"[%@: %@:%d] no account or local references for transaction %@", self.chain.name, peer.host, peer.port, uint256_hex(txHash)); + DSLogPrivate(@"[%@: %@:%d] no account or local references for transaction %@ (%@)", self.chain.name, peer.host, peer.port, uint256_hex(txHash), uint256_reverse_hex(txHash)); #else DSLog(@"[%@: %@:%d] no account or local references for transaction %@", self.chain.name, peer.host, peer.port, @""); #endif @@ -1316,13 +1316,13 @@ - (void)peer:(DSPeer *)peer relayedTransaction:(DSTransaction *)transaction inBl } else { if (peer) { #if DEBUG - DSLogPrivate(@"[%@: %@:%d] no account for transaction with local references %@", self.chain.name, peer.host, peer.port, uint256_hex(txHash)); + DSLogPrivate(@"[%@: %@:%d] no account for transaction with local references %@ (%@)", self.chain.name, peer.host, peer.port, uint256_hex(txHash), uint256_reverse_hex(txHash)); #else DSLog(@"[%@: %@:%d] no account for transaction with local references %@", self.chain.name, peer.host, peer.port, @""); #endif } else { #if DEBUG - DSLogPrivate(@"[%@: %@:%d] no account for transaction with local references %@", self.chain.name, peer.host, peer.port, uint256_hex(txHash)); + DSLogPrivate(@"[%@: %@:%d] no account for transaction with local references %@ (%@)", self.chain.name, peer.host, peer.port, uint256_hex(txHash), uint256_reverse_hex(txHash)); #else DSLog(@"[%@: %@:%d] no account for transaction with local references %@", self.chain.name, peer.host, peer.port, @""); #endif @@ -1341,13 +1341,13 @@ - (void)peer:(DSPeer *)peer relayedTransaction:(DSTransaction *)transaction inBl } else { if (peer) { #if DEBUG - DSLogPrivate(@"[%@: %@:%d] could not register transaction %@", self.chain.name, peer.host, peer.port, uint256_hex(txHash)); + DSLogPrivate(@"[%@: %@:%d] could not register transaction %@ (%@)", self.chain.name, peer.host, peer.port, uint256_hex(txHash), uint256_reverse_hex(txHash)); #else DSLog(@"[%@: %@:%d] could not register transaction %@", self.chain.name, peer.host, peer.port, @""); #endif } else { #if DEBUG - DSLogPrivate(@"[%@: %@:%d] could not register transaction %@", self.chain.name, peer.host, peer.port, uint256_hex(txHash)); + DSLogPrivate(@"[%@: %@:%d] could not register transaction %@ (%@)", self.chain.name, peer.host, peer.port, uint256_hex(txHash), uint256_reverse_hex(txHash)); #else DSLog(@"[%@: %@:%d] could not register transaction %@", self.chain.name, peer.host, peer.port, @""); #endif diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index 631c549e..6ca4242a 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -666,7 +666,11 @@ - (void)sendGetdataMessageForTxHash:(UInt256)txHash { [self sendRequest:request]; } -- (void)sendGetdataMessageWithTxHashes:(NSArray *)txHashes instantSendLockHashes:(NSArray *)instantSendLockHashes instantSendLockDHashes:(NSArray *)instantSendLockDHashes blockHashes:(NSArray *)blockHashes chainLockHashes:(NSArray *)chainLockHashes { +- (void)sendGetdataMessageWithTxHashes:(NSArray *)txHashes + instantSendLockHashes:(NSArray *)instantSendLockHashes + instantSendLockDHashes:(NSArray *)instantSendLockDHashes + blockHashes:(NSArray *)blockHashes + chainLockHashes:(NSArray *)chainLockHashes { if (!([[DSOptionsManager sharedInstance] syncType] & DSSyncType_GetsNewBlocks)) return; NSUInteger totalCount = txHashes.count + instantSendLockHashes.count + instantSendLockDHashes.count + blockHashes.count + chainLockHashes.count; if (totalCount > MAX_GETDATA_HASHES) { // limit total hash count to MAX_GETDATA_HASHES @@ -682,7 +686,7 @@ - (void)sendGetdataMessageWithTxHashes:(NSArray *)txHashes instantSendLockHashes chainLockHashes:chainLockHashes]; self.sentGetdataTxBlocks = YES; #if MESSAGE_LOGGING - DSLogWithLocation(self, @"sending getdata (transactions and blocks)"); + DSLogWithLocation(self, @"sending getdata (transactions and blocks) %lu", totalCount); #endif [self sendRequest:request]; } @@ -1048,7 +1052,8 @@ - (void)acceptInvMessage:(NSData *)message { DSLogWithLocation(self, @"Got empty Inv message"); } if (count > 0 && ([message UInt32AtOffset:l.unsignedIntegerValue] != DSInvType_MasternodePing) && ([message UInt32AtOffset:l.unsignedIntegerValue] != DSInvType_MasternodePaymentVote) && ([message UInt32AtOffset:l.unsignedIntegerValue] != DSInvType_MasternodeVerify) && ([message UInt32AtOffset:l.unsignedIntegerValue] != DSInvType_GovernanceObjectVote)) { - DSLogWithLocation(self, @"got inv with %u item%@ (first item %@ with hash %@/%@)", (int)count, count == 1 ? @"" : @"s", [self nameOfInvMessage:[message UInt32AtOffset:l.unsignedIntegerValue]], [NSData dataWithUInt256:[message UInt256AtOffset:l.unsignedIntegerValue + sizeof(uint32_t)]].hexString, [NSData dataWithUInt256:[message UInt256AtOffset:l.unsignedIntegerValue + sizeof(uint32_t)]].reverse.hexString); + NSData *d = [NSData dataWithUInt256:[message UInt256AtOffset:l.unsignedIntegerValue + sizeof(uint32_t)]]; + DSLogWithLocation(self, @"got inv with %u item%@ (first item %@ with hash %@ (%@))", (int)count, count == 1 ? @"" : @"s", [self nameOfInvMessage:[message UInt32AtOffset:l.unsignedIntegerValue]], d.hexString, d.reverse.hexString); } #endif BOOL onlyPrivateSendTransactions = NO; @@ -1192,7 +1197,11 @@ - (void)acceptInvMessage:(NSData *)message { } if (txHashes.count + instantSendLockHashes.count + instantSendLockDHashes.count > 0 || (!self.needsFilterUpdate && ((blockHashes.count + chainLockHashes.count) > 0))) { - [self sendGetdataMessageWithTxHashes:txHashes.array instantSendLockHashes:instantSendLockHashes.array instantSendLockDHashes:instantSendLockDHashes.array blockHashes:(self.needsFilterUpdate) ? nil : blockHashes.array chainLockHashes:chainLockHashes.array]; + [self sendGetdataMessageWithTxHashes:txHashes.array + instantSendLockHashes:instantSendLockHashes.array + instantSendLockDHashes:instantSendLockDHashes.array + blockHashes:self.needsFilterUpdate ? nil : blockHashes.array + chainLockHashes:chainLockHashes.array]; } // to improve chain download performance, if we received 500 block hashes, we request the next 500 block hashes diff --git a/DashSync/shared/Models/Notifications/DSSyncState.h b/DashSync/shared/Models/Notifications/DSSyncState.h index ee34a17e..a8608b3f 100644 --- a/DashSync/shared/Models/Notifications/DSSyncState.h +++ b/DashSync/shared/Models/Notifications/DSSyncState.h @@ -139,6 +139,7 @@ NSString * DSMasternodeListSyncStateKindDescription(DSMasternodeListSyncStateKin - (NSString *)masternodesDescription; - (NSString *)platformDescription; +- (BOOL)hasSyncKind:(DSSyncStateExtKind)kind; - (void)addSyncKind:(DSSyncStateExtKind)kind; - (void)removeSyncKind:(DSSyncStateExtKind)kind; - (void)resetSyncKind; diff --git a/DashSync/shared/Models/Notifications/DSSyncState.m b/DashSync/shared/Models/Notifications/DSSyncState.m index 0e635c4b..ad036723 100644 --- a/DashSync/shared/Models/Notifications/DSSyncState.m +++ b/DashSync/shared/Models/Notifications/DSSyncState.m @@ -190,13 +190,14 @@ - (void)resetSyncKind { } - (BOOL)hasRecentIdentitiesSync { - return [[NSDate date] timeIntervalSince1970] - self.lastSyncedIndentitiesTimestamp < 30; + return [[NSDate date] timeIntervalSince1970] - self.lastSyncedIndentitiesTimestamp < 180; } - (double)progress { uint32_t amountLeft = self.queueCount; uint32_t maxAmount = self.queueMaxAmount; - return amountLeft && maxAmount ? MAX(MIN((maxAmount - amountLeft) / maxAmount, 1), 0) : [self hasRecentIdentitiesSync]; + BOOL uptodate = [self hasRecentIdentitiesSync]; + return amountLeft && maxAmount ? MAX(MIN((maxAmount - amountLeft) / maxAmount, 1), 0) : uptodate; } - (double)weight { @@ -206,7 +207,7 @@ - (double)weight { } - (NSString *)description { - return [NSString stringWithFormat:@"[%@: %f %u/%u = %.2f/%.2f]", DSPlatformSyncStateKindDescription(self.kind), self.lastSyncedIndentitiesTimestamp, self.queueCount, self.queueMaxAmount, [self progress], [self weight]]; + return [NSString stringWithFormat:@"[%@: %.3f %u/%u = %.2f/%.2f]", DSPlatformSyncStateKindDescription(self.kind), self.lastSyncedIndentitiesTimestamp, self.queueCount, self.queueMaxAmount, [self progress], [self weight]]; } @end @@ -279,6 +280,10 @@ - (id)copyWithZone:(NSZone *)zone { return copy; } +- (BOOL)hasSyncKind:(DSSyncStateExtKind)kind { + return FLAG_IS_SET(self.extKind, kind); +} + - (void)addSyncKind:(DSSyncStateExtKind)kind { if (!FLAG_IS_SET(self.extKind, kind)) _extKind |= kind; diff --git a/DashSync/shared/Models/Persistence/Migration/DSCoreDataMigrator.m b/DashSync/shared/Models/Persistence/Migration/DSCoreDataMigrator.m index 02bf3d5c..df59304c 100644 --- a/DashSync/shared/Models/Persistence/Migration/DSCoreDataMigrator.m +++ b/DashSync/shared/Models/Persistence/Migration/DSCoreDataMigrator.m @@ -172,7 +172,10 @@ + (void)migrateStoreAtURL:(NSURL *)storeURL toURL:(NSURL *)finalStoreURL toVersi NSURL *currentURL = storeURL; NSArray *migrationSteps = [self migrationStepsForStoreAtURL:storeURL toVersion:version]; - + NSDictionary *options = @{ + NSMigratePersistentStoresAutomaticallyOption: @YES, + NSInferMappingModelAutomaticallyOption: @YES + }; for (DSCoreDataMigrationStep *step in migrationSteps) { NSMigrationManager *manager = [[NSMigrationManager alloc] initWithSourceModel:step.sourceModel destinationModel:step.destinationModel]; @@ -181,11 +184,11 @@ + (void)migrateStoreAtURL:(NSURL *)storeURL toURL:(NSURL *)finalStoreURL toVersi NSError *error = nil; [manager migrateStoreFromURL:currentURL type:NSSQLiteStoreType - options:nil + options:options withMappingModel:step.mappingModel toDestinationURL:destinationURL destinationType:NSSQLiteStoreType - destinationOptions:nil + destinationOptions:options error:&error]; NSAssert(error == nil, @"failed attempting to migrate from %@ to %@, error %@", step.sourceModel, step.destinationModel, error); diff --git a/DashSync/shared/Models/Platform/Contract/DPContract.m b/DashSync/shared/Models/Platform/Contract/DPContract.m index ae0e3f8d..fb3bdb5c 100644 --- a/DashSync/shared/Models/Platform/Contract/DPContract.m +++ b/DashSync/shared/Models/Platform/Contract/DPContract.m @@ -55,18 +55,13 @@ - (void)dealloc { - (instancetype)initWithLocalContractIdentifier:(NSString *)localContractIdentifier raw_contract:(DDataContract *)raw_contract -// documents:(NSDictionary *)documents onChain:(DSChain *)chain { NSParameterAssert(localContractIdentifier); NSParameterAssert(raw_contract); if (!(self = [super init])) return nil; -// _version = DEFAULT_VERSION; _localContractIdentifier = localContractIdentifier; -// _jsonMetaSchema = DEFAULT_SCHEMA; _raw_contract = raw_contract; -// _mutableDocuments = [documents mutableCopy]; -// _definitions = @{}; _chain = chain; // [self.chain.chainManagedObjectContext performBlockAndWait:^{ @@ -142,12 +137,14 @@ - (void)registerCreator:(DSIdentity *)identity { self.registeredIdentityUniqueID = identity ? identity.uniqueID : UINT256_ZERO; self.contractId = UINT256_ZERO; //will be lazy loaded DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath identitiesECDSAKeysDerivationPathForWallet:identity.wallet]; + // use the last key in 32 bit space (it won't probably ever be used anyways) + NSData *lastPublicKeyData = [derivationPath publicKeyDataAtIndexPath:[NSIndexPath indexPathWithIndex:UINT32_MAX - 1]]; Result_ok_Vec_u8_err_dash_spv_platform_error_Error *result = dash_spv_platform_contract_manager_ContractsManager_contract_serialized_hash(self.chain.sharedContractsObj, self.raw_contract); NSData *serializedHash = NSDataFromPtr(result->ok); Result_ok_Vec_u8_err_dash_spv_platform_error_Error_destroy(result); NSMutableData *entropyData = [serializedHash mutableCopy]; [entropyData appendUInt256:identity.uniqueID]; - [entropyData appendData:[derivationPath publicKeyDataAtIndexPath:[NSIndexPath indexPathWithIndex:UINT32_MAX - 1]]]; //use the last key in 32 bit space (it won't probably ever be used anyways) + [entropyData appendData:lastPublicKeyData]; self.entropy = [entropyData SHA256]; } diff --git a/DashSync/shared/Models/Transactions/Base/DSInstantSendTransactionLock.m b/DashSync/shared/Models/Transactions/Base/DSInstantSendTransactionLock.m index 0e8c11e5..8922ccbe 100644 --- a/DashSync/shared/Models/Transactions/Base/DSInstantSendTransactionLock.m +++ b/DashSync/shared/Models/Transactions/Base/DSInstantSendTransactionLock.m @@ -127,7 +127,7 @@ - (instancetype)initWithTransactionHash:(NSData *)transactionHash DOutPoints *inputs = DOutPointsCtor(inputsCount, values); DTxid *txid = DTxidCtor(u256_ctor(transactionHash)); - DCycleHash *chash = dashcore_hash_types_CycleHash_ctor(u256_ctor(cycleHash)); + DCycleHash *chash = dashcore_hash_types_CycleHash_ctor(cycleHash ? u256_ctor(cycleHash) : u256_ctor_u(UINT256_ZERO)); self.lock = DInstantLockCtor(version, inputs, txid, chash, DBLSSignatureCtor(u768_ctor(signature))); self.signatureVerified = signatureVerified; self.quorumVerified = quorumVerified; diff --git a/DashSync/shared/Models/Transactions/Base/DSTransaction+FFI.m b/DashSync/shared/Models/Transactions/Base/DSTransaction+FFI.m index e5081690..ce2f199b 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransaction+FFI.m +++ b/DashSync/shared/Models/Transactions/Base/DSTransaction+FFI.m @@ -44,6 +44,7 @@ + (nonnull instancetype)ffi_from:(nonnull DTransaction *)transaction onChain:(no NSData *script = NSDataFromPtr(script_sig); if (!script.length) { DSTransaction *inputTx = [chain transactionForHash:hashValue]; + DSLog(@"[DSTransaction] ffi_from: %@ == %@ (%@)", uint256_hex(hashValue), inputTx, [chain transactionForHash:uint256_reverse(hashValue)]); if (inputTx) script = inputTx.outputs[index].outScript; } diff --git a/DashSync/shared/Models/Transactions/Base/DSTransaction.m b/DashSync/shared/Models/Transactions/Base/DSTransaction.m index 5cb8dcd5..c3c56b52 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSTransaction.m @@ -690,8 +690,8 @@ - (BOOL)signWithPrivateKeys:(NSArray *)keys anyoneCanPay:(BOOL)anyoneCanPay { } if (!self.isSigned) return NO; // TODO: check if this is wrong -// _txHash = self.data.SHA256_2; - _txHash = [self toData:anyoneCanPay].SHA256_2; + _txHash = self.data.SHA256_2; +// _txHash = [self toData:anyoneCanPay].SHA256_2; return YES; } } diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index 622a97a5..14e58377 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -303,21 +303,16 @@ - (void)loadDerivationPaths { [derivationPath loadAddresses]; } } - if (self.coinJoinDerivationPath && [self.coinJoinDerivationPath hasExtendedPublicKey]) - [self.coinJoinDerivationPath loadAddresses]; } else { for (DSFundsDerivationPath *derivationPath in self.fundDerivationPaths) { if ([derivationPath isKindOfClass:[DSIncomingFundsDerivationPath class]]) { [derivationPath registerAddressesWithSettings:[DSGapLimit withLimit:SEQUENCE_DASHPAY_GAP_LIMIT_INITIAL]]; } else { - [derivationPath registerAddressesWithSettings:[DSGapLimitFunds internal:SEQUENCE_GAP_LIMIT_INITIAL]]; - [derivationPath registerAddressesWithSettings:[DSGapLimitFunds external:SEQUENCE_GAP_LIMIT_INITIAL]]; + uintptr_t limit = derivationPath.type == DSDerivationPathType_AnonymousFunds ? SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN : SEQUENCE_GAP_LIMIT_INITIAL; + [derivationPath registerAddressesWithSettings:[DSGapLimitFunds internal:limit]]; + [derivationPath registerAddressesWithSettings:[DSGapLimitFunds external:limit]]; } } - if (self.coinJoinDerivationPath) { - [self.coinJoinDerivationPath registerAddressesWithSettings:[DSGapLimitFunds internal:SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN]]; - [self.coinJoinDerivationPath registerAddressesWithSettings:[DSGapLimitFunds external:SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN]]; - } } if (!self.isViewOnlyAccount) { if (self.bip44DerivationPath && [self.bip44DerivationPath hasExtendedPublicKey]) { @@ -334,7 +329,6 @@ - (void)loadDerivationPaths { - (void)wipeBlockchainInfo { [self.mFundDerivationPaths removeObjectsInArray:[self.mContactIncomingFundDerivationPathsDictionary allValues]]; - self.coinJoinDerivationPath = nil; [self.mContactIncomingFundDerivationPathsDictionary removeAllObjects]; [self.mContactOutgoingFundDerivationPathsDictionary removeAllObjects]; [self.transactions removeAllObjects]; @@ -471,8 +465,6 @@ - (DSDerivationPath *)derivationPathContainingAddress:(NSString *)address { for (DSDerivationPath *derivationPath in self.fundDerivationPaths) { if ([derivationPath containsAddress:address]) return derivationPath; } - if (self.coinJoinDerivationPath && [self.coinJoinDerivationPath containsAddress:address]) - return self.coinJoinDerivationPath; return nil; } @@ -482,39 +474,43 @@ - (BOOL)hasAnExtendedPublicKeyMissing { for (DSDerivationPath *derivationPath in self.fundDerivationPaths) { if (![derivationPath hasExtendedPublicKey]) return YES; } - if (self.coinJoinDerivationPath && ![self.coinJoinDerivationPath hasExtendedPublicKey]) return YES; return NO; } - (NSArray *)registerAddressesAtStage:(DSGapLimitStage)stage { NSMutableArray *mArray = [NSMutableArray array]; + BOOL isInitial = stage == DSGapLimitStage_Initial; for (DSDerivationPath *derivationPath in self.fundDerivationPaths) { if ([derivationPath isKindOfClass:[DSFundsDerivationPath class]]) { DSFundsDerivationPath *path = (DSFundsDerivationPath *)derivationPath; BOOL useReduced = [path shouldUseReducedGapLimit]; BOOL isAnonymous = path.type == DSDerivationPathType_AnonymousFunds; - BOOL isInitial = stage == DSGapLimitStage_Initial; - NSUInteger externalLimit = useReduced ? (isInitial ? SEQUENCE_UNUSED_GAP_LIMIT_INITIAL : SEQUENCE_UNUSED_GAP_LIMIT_EXTERNAL) : (isAnonymous ? SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN : (isInitial ? SEQUENCE_GAP_LIMIT_INITIAL : SEQUENCE_GAP_LIMIT_EXTERNAL)); - NSUInteger internalLimit = useReduced ? (isInitial ? SEQUENCE_UNUSED_GAP_LIMIT_INITIAL : SEQUENCE_GAP_LIMIT_INTERNAL) : (isAnonymous ? SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN : (isInitial ? SEQUENCE_GAP_LIMIT_INITIAL : SEQUENCE_GAP_LIMIT_INTERNAL)); + NSUInteger externalLimit = useReduced + ? (isInitial + ? SEQUENCE_UNUSED_GAP_LIMIT_INITIAL + : SEQUENCE_UNUSED_GAP_LIMIT_EXTERNAL) + : (isAnonymous + ? SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN + : (isInitial + ? SEQUENCE_GAP_LIMIT_INITIAL + : SEQUENCE_GAP_LIMIT_EXTERNAL)); + NSUInteger internalLimit = useReduced + ? (isInitial + ? SEQUENCE_UNUSED_GAP_LIMIT_INITIAL + : SEQUENCE_GAP_LIMIT_INTERNAL) + : (isAnonymous + ? SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN + : (isInitial + ? SEQUENCE_GAP_LIMIT_INITIAL + : SEQUENCE_GAP_LIMIT_INTERNAL)); [mArray addObjectsFromArray:[path registerAddressesWithSettings:[DSGapLimitFunds external:externalLimit]]]; [mArray addObjectsFromArray:[path registerAddressesWithSettings:[DSGapLimitFunds internal:internalLimit]]]; } else if ([derivationPath isKindOfClass:[DSIncomingFundsDerivationPath class]]) { - BOOL isInitial = stage == DSGapLimitStage_Initial; NSUInteger limit = isInitial ? SEQUENCE_DASHPAY_GAP_LIMIT_INITIAL : SEQUENCE_DASHPAY_GAP_LIMIT_INCOMING; - NSArray *addresses = [derivationPath registerAddressesWithSettings:[DSGapLimit withLimit:limit]]; [mArray addObjectsFromArray:addresses]; } } - if (self.coinJoinDerivationPath) { - BOOL useReduced = [self.coinJoinDerivationPath shouldUseReducedGapLimit]; - BOOL isInitial = stage == DSGapLimitStage_Initial; - NSUInteger externalLimit = useReduced ? (isInitial ? SEQUENCE_UNUSED_GAP_LIMIT_INITIAL : SEQUENCE_UNUSED_GAP_LIMIT_EXTERNAL) : SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN; - NSUInteger internalLimit = useReduced ? (isInitial ? SEQUENCE_UNUSED_GAP_LIMIT_INITIAL : SEQUENCE_GAP_LIMIT_INTERNAL) : SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN; - - [mArray addObjectsFromArray:[self.coinJoinDerivationPath registerAddressesWithSettings:[DSGapLimitFunds external:externalLimit]]]; - [mArray addObjectsFromArray:[self.coinJoinDerivationPath registerAddressesWithSettings:[DSGapLimitFunds internal:internalLimit]]]; - } return [mArray copy]; } @@ -524,8 +520,6 @@ - (NSArray *)externalAddresses { for (DSDerivationPath *derivationPath in self.fundDerivationPaths) { [mSet addObjectsFromArray:[(id)derivationPath allReceiveAddresses]]; } - if (self.coinJoinDerivationPath) - [mSet addObjectsFromArray:[self.coinJoinDerivationPath allReceiveAddresses]]; if ([mSet containsObject:[NSNull null]]) { [mSet removeObject:[NSNull null]]; @@ -541,8 +535,6 @@ - (NSArray *)internalAddresses { [mSet addObjectsFromArray:[(DSFundsDerivationPath *)derivationPath allChangeAddresses]]; } } - if (self.coinJoinDerivationPath) - [mSet addObjectsFromArray:[self.coinJoinDerivationPath allChangeAddresses]]; if ([mSet containsObject:[NSNull null]]) { [mSet removeObject:[NSNull null]]; } @@ -554,8 +546,6 @@ - (NSSet *)allAddresses { for (DSFundsDerivationPath *derivationPath in self.fundDerivationPaths) { [mSet unionSet:[derivationPath allAddresses]]; } - if (self.coinJoinDerivationPath) - [mSet unionSet:[self.coinJoinDerivationPath allAddresses]]; return mSet; } @@ -564,8 +554,6 @@ - (NSSet *)usedAddresses { for (DSFundsDerivationPath *derivationPath in self.fundDerivationPaths) { [mSet unionSet:[derivationPath usedAddresses]]; } - if (self.coinJoinDerivationPath) - [mSet unionSet:[self.coinJoinDerivationPath usedAddresses]]; return mSet; } @@ -580,7 +568,6 @@ - (BOOL)containsAddress:(NSString *)address { for (DSFundsDerivationPath *derivationPath in self.fundDerivationPaths) { if ([derivationPath containsAddress:address]) return TRUE; } - if (self.coinJoinDerivationPath && [self.coinJoinDerivationPath containsAddress:address]) return YES; return FALSE; } @@ -610,7 +597,6 @@ - (BOOL)containsInternalAddress:(NSString *)address { return TRUE; } } - if (self.coinJoinDerivationPath && [self.coinJoinDerivationPath containsChangeAddress:address]) return YES; return FALSE; } @@ -626,7 +612,6 @@ - (BOOL)baseDerivationPathsContainAddress:(NSString *)address { return TRUE; } } - if (self.coinJoinDerivationPath && [self.coinJoinDerivationPath containsAddress:address]) return YES; return FALSE; } @@ -645,7 +630,6 @@ - (BOOL)containsExternalAddress:(NSString *)address { if ([(DSIncomingFundsDerivationPath *)derivationPath containsAddress:address]) return TRUE; //!OCLINT } } - if (self.coinJoinDerivationPath && [self.coinJoinDerivationPath containsReceiveAddress:address]) return TRUE; return FALSE; } @@ -673,7 +657,6 @@ - (BOOL)addressIsUsed:(NSString *)address { for (DSFundsDerivationPath *derivationPath in self.fundDerivationPaths) { if ([derivationPath addressIsUsed:address]) return TRUE; } - if (self.coinJoinDerivationPath && [self.coinJoinDerivationPath addressIsUsed:address]) return YES; return FALSE; } @@ -704,9 +687,7 @@ - (void)updateBalance { for (DSFundsDerivationPath *derivationPath in self.fundDerivationPaths) { derivationPath.balance = 0; } - if (self.coinJoinDerivationPath) - self.coinJoinDerivationPath.balance = 0; - + for (DSTransaction *tx in [self.transactions reverseObjectEnumerator]) { #if LOG_BALANCE_UPDATE DSLogPrivate(@"updating balance after transaction %@", [NSData dataWithUInt256:tx.txHash].reverse.hexString); @@ -810,16 +791,6 @@ - (void)updateBalance { balance += amount; } } - if (self.coinJoinDerivationPath) { - if ([self.coinJoinDerivationPath containsAddress:output.address]) { - [self.coinJoinDerivationPath setHasKnownBalance]; - uint64_t amount = output.amount; - self.coinJoinDerivationPath.balance += amount; - [utxos addObject:dsutxo_obj(((DSUTXO){tx.txHash, n}))]; - balance += amount; - } - } - n++; } @@ -843,9 +814,6 @@ - (void)updateBalance { break; } } - if (self.coinJoinDerivationPath && [self.coinJoinDerivationPath containsAddress:output.address]) { - self.coinJoinDerivationPath.balance -= amount; - } } if (prevBalance < balance) totalReceived += balance - prevBalance; @@ -1035,17 +1003,11 @@ - (BOOL)canContainTransaction:(DSTransaction *)transaction { - (BOOL)canContainTransactionOutputAddresses:(DSTransaction *)transaction { return [[NSSet setWithArray:transaction.outputAddresses] intersectsSet:self.allAddresses]; } -- (BOOL)canContainTransactionOutputAddressesIncludingCoinjoin:(DSTransaction *)transaction { - for (NSString *address in transaction.outputAddresses) { - if ([self containsCoinJoinAddress:address]) return YES; - } - return NO; -} - (BOOL)canContainTransactionIncludingCoinjoin:(DSTransaction *)transaction { NSParameterAssert(transaction); @synchronized (self) { - if ([self canContainTransactionOutputAddresses:transaction] || [self canContainTransactionOutputAddressesIncludingCoinjoin:transaction]) return YES; + if ([self canContainTransactionOutputAddresses:transaction]) return YES; for (DSTransactionInput *input in transaction.inputs) { DSTransaction *tx = self.allTx[uint256_obj(input.inputHash)]; uint32_t n = input.index; @@ -1685,21 +1647,15 @@ - (BOOL)registerTransaction:(DSTransaction *)transaction for (DSFundsDerivationPath *derivationPath in self.fundDerivationPaths) { [derivationPath registerTransactionAddress:address]; //only will register if derivation path contains address } - if (self.coinJoinDerivationPath) - [self.coinJoinDerivationPath registerTransactionAddress:address]; } for (DSTransactionOutput *output in transaction.outputs) { for (DSFundsDerivationPath *derivationPath in self.fundDerivationPaths) { [derivationPath registerTransactionAddress:output.address]; //only will register if derivation path contains address } - if (self.coinJoinDerivationPath) - [self.coinJoinDerivationPath registerTransactionAddress:output.address]; //only will register if derivation path contains address } [transaction loadIdentitiesFromDerivationPaths:self.fundDerivationPaths]; [transaction loadIdentitiesFromDerivationPaths:self.outgoingFundDerivationPaths]; - if (self.coinJoinDerivationPath) - [transaction loadIdentitiesFromDerivationPaths:@[self.coinJoinDerivationPath]]; [self updateBalance]; if (saveImmediately) { if (!self.wallet.isTransient) { diff --git a/DashSync/shared/Models/Wallet/DSWallet+Identity.m b/DashSync/shared/Models/Wallet/DSWallet+Identity.m index ffb0cd6b..b1802253 100644 --- a/DashSync/shared/Models/Wallet/DSWallet+Identity.m +++ b/DashSync/shared/Models/Wallet/DSWallet+Identity.m @@ -131,7 +131,6 @@ - (void)loadIdentities { for (DSTransaction *transaction in account.allTransactions) { [transaction loadIdentitiesFromDerivationPaths:account.fundDerivationPaths]; [transaction loadIdentitiesFromDerivationPaths:account.outgoingFundDerivationPaths]; - [transaction loadIdentitiesFromDerivationPaths:@[account.coinJoinDerivationPath]]; } } }]; @@ -189,7 +188,7 @@ - (BOOL)registerIdentity:(DSIdentity *)identity verify:(BOOL)verify { NSParameterAssert(identity); if (verify && ![identity verifyKeysForWallet:self]) { - identity.isLocal = FALSE; + dash_spv_platform_identity_model_IdentityModel_set_is_local(identity.model, NO); return FALSE; } if ([self.mIdentities objectForKey:identity.uniqueIDData] == nil) @@ -336,11 +335,11 @@ - (NSMutableDictionary *)identities { } } else { // We also don't have the registration funding transaction - identity = [[DSIdentity alloc] initAtIndex:index withUniqueId:uniqueIdData.UInt256 inWallet:self]; + identity = [[DSIdentity alloc] initAtIndex:index uniqueId:uniqueIdData.UInt256 inWallet:self]; [identity registerInWalletForIdentityUniqueId:uniqueIdData.UInt256]; } } else { - identity = [[DSIdentity alloc] initAtIndex:index withUniqueId:uniqueIdData.UInt256 inWallet:self]; + identity = [[DSIdentity alloc] initAtIndex:index uniqueId:uniqueIdData.UInt256 inWallet:self]; [identity registerInWalletForIdentityUniqueId:uniqueIdData.UInt256]; } if (identity) { @@ -371,14 +370,14 @@ - (DSIdentity *)createIdentityUsingDerivationIndex:(uint32_t)index { - (DSIdentity *)createIdentityForUsername:(NSString *)username { DSIdentity *identity = [self createIdentity]; - [identity addDashpayUsername:username save:NO]; + [identity addDashpayUsername:username]; return identity; } - (DSIdentity *)createIdentityForUsername:(NSString *)username usingDerivationIndex:(uint32_t)index { DSIdentity *identity = [self createIdentityUsingDerivationIndex:index]; - [identity addDashpayUsername:username save:NO]; + [identity addDashpayUsername:username]; return identity; } diff --git a/DashSync/shared/Models/Wallet/DSWallet+Tests.m b/DashSync/shared/Models/Wallet/DSWallet+Tests.m index 0b7c696d..f8e3060f 100644 --- a/DashSync/shared/Models/Wallet/DSWallet+Tests.m +++ b/DashSync/shared/Models/Wallet/DSWallet+Tests.m @@ -64,8 +64,6 @@ + (NSString *)setTransientDerivedKeyData:(NSData *)derivedKeyData withAccounts:( for (DSDerivationPath *derivationPath in account.fundDerivationPaths) { [derivationPath generateExtendedPublicKeyFromSeed:derivedKeyData storeUnderWalletUniqueId:nil]; } - if (account.coinJoinDerivationPath) - [account.coinJoinDerivationPath generateExtendedPublicKeyFromSeed:derivedKeyData storeUnderWalletUniqueId:nil]; if ([chain isEvolutionEnabled]) { [account.masterContactsDerivationPath generateExtendedPublicKeyFromSeed:derivedKeyData storeUnderWalletUniqueId:nil]; } diff --git a/DashSync/shared/Models/Wallet/DSWallet.m b/DashSync/shared/Models/Wallet/DSWallet.m index 325e97a7..b74a9074 100644 --- a/DashSync/shared/Models/Wallet/DSWallet.m +++ b/DashSync/shared/Models/Wallet/DSWallet.m @@ -303,13 +303,10 @@ - (DSAccount *)addAnotherAccountIfAuthenticated { [derivationPath generateExtendedPublicKeyFromSeed:derivedKeyData storeUnderWalletUniqueId:self.uniqueIDString]; } - if (addAccount.coinJoinDerivationPath) - [addAccount.coinJoinDerivationPath generateExtendedPublicKeyFromSeed:derivedKeyData storeUnderWalletUniqueId:self.uniqueIDString]; - if ([self.chain isEvolutionEnabled]) { + if ([self.chain isEvolutionEnabled]) [addAccount.masterContactsDerivationPath generateExtendedPublicKeyFromSeed:derivedKeyData storeUnderWalletUniqueId:self.uniqueIDString]; - } [self addAccount:addAccount]; [addAccount loadDerivationPaths]; @@ -621,8 +618,6 @@ + (NSString *)setSeedPhrase:(NSString *)seedPhrase createdAt:(NSTimeInterval)cre for (DSDerivationPath *derivationPath in account.fundDerivationPaths) { [derivationPath generateExtendedPublicKeyFromSeed:derivedKeyData storeUnderWalletUniqueId:storeOnUniqueId]; } - if (account.coinJoinDerivationPath) - [account.coinJoinDerivationPath generateExtendedPublicKeyFromSeed:derivedKeyData storeUnderWalletUniqueId:storeOnUniqueId]; if ([chain isEvolutionEnabled]) { [account.masterContactsDerivationPath generateExtendedPublicKeyFromSeed:derivedKeyData storeUnderWalletUniqueId:storeOnUniqueId]; } @@ -641,7 +636,6 @@ - (void)seedWithPrompt:(NSString *)authprompt forAmount:(uint64_t)amount complet BOOL usingBiometricAuthentication = amount ? [[DSAuthenticationManager sharedInstance] canUseBiometricAuthenticationForAmount:amount] : NO; -// __weak typeof(self) weakSelf = self; [[DSAuthenticationManager sharedInstance] authenticateWithPrompt:authprompt usingBiometricAuthentication:usingBiometricAuthentication alertIfLockout:YES @@ -1001,8 +995,6 @@ - (void)reloadDerivationPaths { for (DSDerivationPath *derivationPath in account.fundDerivationPaths) { [derivationPath reloadAddresses]; } - if (account.coinJoinDerivationPath) - [account.coinJoinDerivationPath reloadAddresses]; } for (DSDerivationPath *derivationPath in self.specializedDerivationPaths) { [derivationPath reloadAddresses]; diff --git a/Example/DashSync/DSAccountsDerivationPathsViewController.m b/Example/DashSync/DSAccountsDerivationPathsViewController.m index 0500167e..1d3df9c8 100644 --- a/Example/DashSync/DSAccountsDerivationPathsViewController.m +++ b/Example/DashSync/DSAccountsDerivationPathsViewController.m @@ -35,17 +35,15 @@ - (void)didReceiveMemoryWarning { #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return 3; + return 2; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { switch (section) { case 0: return [self.account.fundDerivationPaths count]; - case 1: - return [self.account.outgoingFundDerivationPaths count]; default: - return 1; //coinjoin + return [self.account.outgoingFundDerivationPaths count]; } } @@ -54,10 +52,8 @@ - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInte switch (section) { case 0: return @"Funds"; - case 1: - return @"Friends"; default: - return @"CoinJoin"; + return @"Friends"; } } @@ -66,14 +62,12 @@ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPa case 0: { DSDerivationPath *derivationPath = (DSDerivationPath *)[self.account.fundDerivationPaths objectAtIndex:indexPath.row]; if ([derivationPath isKindOfClass:[DSFundsDerivationPath class]]) { - return 299; + return 320; } else { - return 237; + return 260; } } - case 1: return 237; - default: - return 299; + default: return 260; } } @@ -91,14 +85,8 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N return cell; } } - case 1: { - DSDerivationPathTableViewCell *cell = (DSDerivationPathTableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"IncomingDerivationPathCellIdentifier" forIndexPath:indexPath]; - [self configureCell:cell atIndexPath:indexPath]; - return cell; - } default: { - DSDerivationPath *derivationPath = (DSDerivationPath *)self.account.coinJoinDerivationPath; - DSDerivationPathTableViewCell *cell = (DSDerivationPathTableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"DerivationPathCellIdentifier" forIndexPath:indexPath]; + DSDerivationPathTableViewCell *cell = (DSDerivationPathTableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"IncomingDerivationPathCellIdentifier" forIndexPath:indexPath]; [self configureCell:cell atIndexPath:indexPath]; return cell; } @@ -110,8 +98,6 @@ - (void)configureCell:(DSDerivationPathTableViewCell *)cell atIndexPath:(NSIndex DSDerivationPath *derivationPath; if (indexPath.section == 1) { derivationPath = (DSDerivationPath *)[self.account.outgoingFundDerivationPaths objectAtIndex:indexPath.row]; - } else if (indexPath.section == 2) { - derivationPath = (DSDerivationPath *)self.account.coinJoinDerivationPath; } else { derivationPath = (DSDerivationPath *)[self.account.fundDerivationPaths objectAtIndex:indexPath.row]; } @@ -120,15 +106,17 @@ - (void)configureCell:(DSDerivationPathTableViewCell *)cell atIndexPath:(NSIndex cell.balanceLabel.text = [[DSPriceManager sharedInstance] stringForDashAmount:derivationPath.balance]; cell.referenceNameLabel.text = derivationPath.referenceName; if ([derivationPath isKindOfClass:[DSFundsDerivationPath class]]) { - DSFundsDerivationPath *fundsDerivationPath = (DSFundsDerivationPath *)derivationPath; - cell.knownAddressesLabel.text = [NSString stringWithFormat:@"%lu", (unsigned long)fundsDerivationPath.allReceiveAddresses.count]; - cell.usedAddressesLabel.text = [NSString stringWithFormat:@"%lu", (unsigned long)fundsDerivationPath.usedReceiveAddresses.count]; - cell.knownInternalAddressesLabel.text = [NSString stringWithFormat:@"%lu", (unsigned long)fundsDerivationPath.allChangeAddresses.count]; - cell.usedInternalAddressesLabel.text = [NSString stringWithFormat:@"%lu", (unsigned long)fundsDerivationPath.usedChangeAddresses.count]; + DSFundsDerivationPath *path = (DSFundsDerivationPath *)derivationPath; + cell.knownAddressesLabel.text = [NSString stringWithFormat:@"%lu", path.allReceiveAddresses.count]; + cell.usedAddressesLabel.text = [NSString stringWithFormat:@"%lu", path.usedReceiveAddresses.count]; + cell.knownInternalAddressesLabel.text = [NSString stringWithFormat:@"%lu", path.allChangeAddresses.count]; + cell.usedInternalAddressesLabel.text = [NSString stringWithFormat:@"%lu", path.usedChangeAddresses.count]; + cell.balanceLabel.text = [NSString stringWithFormat:@"%llu", path.balance]; } else if ([derivationPath isKindOfClass:[DSIncomingFundsDerivationPath class]]) { - DSIncomingFundsDerivationPath *incomingFundsDerivationPath = (DSIncomingFundsDerivationPath *)derivationPath; - cell.knownAddressesLabel.text = [NSString stringWithFormat:@"%lu", (unsigned long)incomingFundsDerivationPath.allReceiveAddresses.count]; - cell.usedAddressesLabel.text = [NSString stringWithFormat:@"%lu", (unsigned long)incomingFundsDerivationPath.usedReceiveAddresses.count]; + DSIncomingFundsDerivationPath *path = (DSIncomingFundsDerivationPath *)derivationPath; + cell.knownAddressesLabel.text = [NSString stringWithFormat:@"%lu", path.allReceiveAddresses.count]; + cell.usedAddressesLabel.text = [NSString stringWithFormat:@"%lu", path.usedReceiveAddresses.count]; + cell.balanceLabel.text = [NSString stringWithFormat:@"%llu", path.balance]; } } @@ -173,8 +161,6 @@ - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { DSDoubleDerivationPathsAddressesViewController *derivationPathsAddressesViewController = (DSDoubleDerivationPathsAddressesViewController *)segue.destinationViewController; if (indexPath.section == 0) { derivationPathsAddressesViewController.derivationPath = (DSFundsDerivationPath *)[self.account.fundDerivationPaths objectAtIndex:indexPath.row]; - } else if (indexPath.section == 2) { - derivationPathsAddressesViewController.derivationPath = (DSFundsDerivationPath *)self.account.coinJoinDerivationPath; } else { derivationPathsAddressesViewController.derivationPath = (DSFundsDerivationPath *)[self.account.outgoingFundDerivationPaths objectAtIndex:indexPath.row]; } diff --git a/Example/DashSync/DSAccountsViewController.m b/Example/DashSync/DSAccountsViewController.m index 36c32c98..564fbd27 100644 --- a/Example/DashSync/DSAccountsViewController.m +++ b/Example/DashSync/DSAccountsViewController.m @@ -115,8 +115,6 @@ - (IBAction)addAccount:(id)sender { [derivationPath generateExtendedPublicKeyFromSeed:derivedKeyData storeUnderWalletUniqueId:walletUniqueId]; } - if (addAccount.coinJoinDerivationPath) - [addAccount.coinJoinDerivationPath generateExtendedPublicKeyFromSeed:derivedKeyData storeUnderWalletUniqueId:walletUniqueId]; if ([chain isEvolutionEnabled]) [addAccount.masterContactsDerivationPath generateExtendedPublicKeyFromSeed:derivedKeyData storeUnderWalletUniqueId:walletUniqueId]; diff --git a/Example/DashSync/DSCreateIdentityViewController.m b/Example/DashSync/DSCreateIdentityViewController.m index b785bc2b..85009be9 100644 --- a/Example/DashSync/DSCreateIdentityViewController.m +++ b/Example/DashSync/DSCreateIdentityViewController.m @@ -153,15 +153,15 @@ - (IBAction)done:(id)sender { steps |= DSIdentityRegistrationStep_Username; } [identity generateIdentityExtendedPublicKeysWithPrompt:@"Update wallet to allow for Evolution features?" - completion:^(BOOL registered) { + completion:^(BOOL registered) { [identity createFundingPrivateKeyWithPrompt:@"Register?" completion:^(BOOL success, BOOL cancelled) { if (success && !cancelled) { [identity registerOnNetwork:steps - withFundingAccount:self.fundingAccount - forTopupAmount:topupAmount - pinPrompt:@"Enter your PIN?" - stepCompletion:^(DSIdentityRegistrationStep stepCompleted) {} - completion:^(DSIdentityRegistrationStep stepsCompleted, NSArray *errors) { + withFundingAccount:self.fundingAccount + forTopupAmount:topupAmount + pinPrompt:@"Enter your PIN?" + stepCompletion:^(DSIdentityRegistrationStep stepCompleted) {} + completion:^(DSIdentityRegistrationStep stepsCompleted, NSArray *errors) { if ([errors count]) { [self raiseIssue:@"Error" message:[NSError errorsDescription:errors]]; return; diff --git a/Example/DashSync/DSIdentityActionsViewController.m b/Example/DashSync/DSIdentityActionsViewController.m index ebf538d1..543b1215 100644 --- a/Example/DashSync/DSIdentityActionsViewController.m +++ b/Example/DashSync/DSIdentityActionsViewController.m @@ -46,7 +46,8 @@ - (void)viewDidLoad { } }]; } - DUsernameStatusDtor(username_status); + if (username_status) + DUsernameStatusDtor(username_status); [self reloadKeyInfo]; [self reloadContactInfo]; diff --git a/Example/DashSync/DSIdentityKeysViewController.m b/Example/DashSync/DSIdentityKeysViewController.m index df0f40ba..bc1af4b8 100644 --- a/Example/DashSync/DSIdentityKeysViewController.m +++ b/Example/DashSync/DSIdentityKeysViewController.m @@ -61,7 +61,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N // Override to support conditional editing of the table view. - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { DIdentityKeyStatus *status = [self.identity statusOfKeyAtIndex:indexPath.row]; - BOOL is_registered = dash_spv_platform_identity_model_IdentityKeyStatus_is_not_registered(status); + BOOL is_registered = dash_spv_platform_identity_key_status_IdentityKeyStatus_is_not_registered(status); DIdentityKeyStatusDtor(status); return is_registered; } diff --git a/Example/DashSync/DSTopupIdentityViewController.m b/Example/DashSync/DSTopupIdentityViewController.m index 93016002..adec532f 100644 --- a/Example/DashSync/DSTopupIdentityViewController.m +++ b/Example/DashSync/DSTopupIdentityViewController.m @@ -100,7 +100,10 @@ - (IBAction)done:(id)sender { return; } - [self.identity createAndPublishTopUpTransitionForAmount:topupAmount fundedByAccount:self.fundingAccount pinPrompt:@"Fund Transaction" withCompletion:^(BOOL success, NSError * _Nonnull error) { + [self.identity createAndPublishTopUpTransitionForAmount:topupAmount + fundedByAccount:self.fundingAccount + pinPrompt:@"Fund Transaction" + withCompletion:^(BOOL success, NSError * _Nonnull error) { if (error) { [self raiseIssue:@"Error" message:error.localizedDescription]; } else if (!success) { diff --git a/Example/DashSync/DSTransactionsViewController.m b/Example/DashSync/DSTransactionsViewController.m index e2f5e90a..9753dfed 100644 --- a/Example/DashSync/DSTransactionsViewController.m +++ b/Example/DashSync/DSTransactionsViewController.m @@ -364,7 +364,45 @@ - (void)configureCell:(DSTransactionTableViewCell *)cell atIndexPath:(NSIndexPat cell.confirmationsLabel.hidden = YES; cell.directionLabel.hidden = NO; } - + NSString *txTypeString = @""; + switch (tx.type) { + case DSTransactionType_ProviderRegistration: + txTypeString = @"pro reg"; + break; + case DSTransactionType_ProviderUpdateService: + txTypeString = @"pro up srv"; + break; + case DSTransactionType_ProviderUpdateRegistrar: + txTypeString = @"pro up reg"; + break; + case DSTransactionType_ProviderUpdateRevocation: + txTypeString = @"pro up revoke"; + break; + case DSTransactionType_Coinbase: + txTypeString = @"reward"; + break; + case DSTransactionType_QuorumCommitment: + txTypeString = @"llmq"; + break; + case DSTransactionType_AssetLock: + txTypeString = @"asset lock"; + break; + case DSTransactionType_AssetUnlock: + txTypeString = @"asset unlock"; + break; + case DSTransactionType_SubscriptionResetKey: + txTypeString = @"sub reset"; + break; + case DSTransactionType_SubscriptionCloseAccount: + txTypeString = @"sub close"; + break; + case DSTransactionType_Transition: + txTypeString = @"transition"; + break; + default: + break; + } + DCoinJoinTransactionType *type = [[DSCoinJoinManager sharedInstanceForChain:self.chainManager.chain] coinJoinTxTypeForTransaction:tx]; DCoinJoinTransactionType type_index = DCoinJoinTransactionTypeIndex(type); if (type_index != dash_spv_coinjoin_models_coinjoin_tx_type_CoinJoinTransactionType_None) { @@ -399,19 +437,19 @@ - (void)configureCell:(DSTransactionTableViewCell *)cell atIndexPath:(NSIndexPat cell.amountLabel.attributedText = [priceManager attributedStringForDashAmount:sent]; cell.fiatAmountLabel.text = [NSString stringWithFormat:@"(%@)", [priceManager localCurrencyStringForDashAmount:sent]]; - cell.directionLabel.text = NSLocalizedString(@"moved", nil); + cell.directionLabel.text = DSLocalizedFormat(@"moved | %@", nil, txTypeString); cell.directionLabel.textColor = [UIColor blackColor]; } else if (sent > 0) { cell.amountLabel.attributedText = [priceManager attributedStringForDashAmount:received - sent]; cell.fiatAmountLabel.text = [NSString stringWithFormat:@"(%@)", [priceManager localCurrencyStringForDashAmount:received - sent]]; - cell.directionLabel.text = NSLocalizedString(@"sent", nil); + cell.directionLabel.text = DSLocalizedFormat(@"sent | %@", nil, txTypeString); cell.directionLabel.textColor = [UIColor colorWithRed:1.0 green:0.33 blue:0.33 alpha:1.0]; } else { cell.amountLabel.attributedText = [priceManager attributedStringForDashAmount:received]; cell.fiatAmountLabel.text = [NSString stringWithFormat:@"(%@)", [priceManager localCurrencyStringForDashAmount:received]]; - cell.directionLabel.text = NSLocalizedString(@"received", nil); + cell.directionLabel.text = DSLocalizedFormat(@"received | %@", nil, txTypeString); cell.directionLabel.textColor = [UIColor colorWithRed:0.0 green:0.75 blue:0.0 alpha:1.0]; } } @@ -499,6 +537,7 @@ - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { DSTransactionDetailViewController *transactionDetailViewController = (DSTransactionDetailViewController *)segue.destinationViewController; DSTransactionEntity *transactionEntity = [self.fetchedResultsController objectAtIndexPath:[self.tableView indexPathForCell:cell]]; DSTransaction *transaction = self.transactions[transactionEntity.transactionHash.txHash]; + DSLog(@"TransactionDetailSegue -> %@ %@ %@", transactionEntity.transactionHash.txHash.hexString, uint256_hex(transaction.txHash), transaction.data.hexString); transactionDetailViewController.transaction = transaction; transactionDetailViewController.txDateString = [self dateForTx:transaction]; } diff --git a/Example/DashSync/Wallets.storyboard b/Example/DashSync/Wallets.storyboard index c1f3148c..6e62ce2e 100644 --- a/Example/DashSync/Wallets.storyboard +++ b/Example/DashSync/Wallets.storyboard @@ -340,11 +340,11 @@ - - + + - + + + + @@ -449,15 +462,17 @@ + + - + @@ -467,6 +482,7 @@ + @@ -478,6 +494,7 @@ + @@ -489,11 +506,11 @@ - - + + - + + + - + + + + @@ -589,12 +621,14 @@ + + diff --git a/Example/Podfile b/Example/Podfile index a668da23..d91112d2 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -1,7 +1,7 @@ def common_pods # pod 'DashSharedCore', :git => 'https://github.com/dashpay/dash-shared-core.git', :branch => 'fix/core-20-test-additional' - pod 'DashSharedCore', :git => 'https://github.com/dashpay/dash-shared-core.git', :commit => '6426b8ab1fcce4cb0898228ca340946dfc7d5cb4' -# pod 'DashSharedCore', :path => '../../dash-shared-core-ferment/' +# pod 'DashSharedCore', :git => 'https://github.com/dashpay/dash-shared-core.git', :commit => '6426b8ab1fcce4cb0898228ca340946dfc7d5cb4' + pod 'DashSharedCore', :path => '../../dash-shared-core/' pod 'DashSync', :path => '../' pod 'SDWebImage', '5.21.0' pod 'CocoaImageHashing', :git => 'https://github.com/ameingast/cocoaimagehashing.git', :commit => 'ad01eee' diff --git a/Example/Podfile.lock b/Example/Podfile.lock index c917da49..41e33486 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -18,7 +18,7 @@ PODS: DEPENDENCIES: - CocoaImageHashing (from `https://github.com/ameingast/cocoaimagehashing.git`, commit `ad01eee`) - - DashSharedCore (from `../../dash-shared-core-ferment/`) + - DashSharedCore (from `../../dash-shared-core/`) - DashSync (from `../`) - KVO-MVVM (= 0.5.1) - SDWebImage (= 5.21.0) @@ -36,7 +36,7 @@ EXTERNAL SOURCES: :commit: ad01eee :git: https://github.com/ameingast/cocoaimagehashing.git DashSharedCore: - :path: "../../dash-shared-core-ferment/" + :path: "../../dash-shared-core/" DashSync: :path: "../" @@ -55,6 +55,6 @@ SPEC CHECKSUMS: KVO-MVVM: 4df3afd1f7ebcb69735458b85db59c4271ada7c6 SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868 -PODFILE CHECKSUM: 45f72cc47868b7f63c3c149723d1e03b70c88dec +PODFILE CHECKSUM: ac4ee8d15a12817fc10676b31e9e06c3d05e290a COCOAPODS: 1.15.2 diff --git a/Example/Tests/DSTransitionTests.m b/Example/Tests/DSTransitionTests.m index e06ae1ad..745d1eb9 100644 --- a/Example/Tests/DSTransitionTests.m +++ b/Example/Tests/DSTransitionTests.m @@ -78,7 +78,7 @@ - (void)setUp { self.identity = [[DSIdentity alloc] initAtIndex:0 withAssetLockTransaction:fundingTransaction inWallet:self.testWallet]; - dash_spv_platform_identity_model_IdentityModel_set_username_status(self.identity.identity_model, DChar(@"Bob"), dash_spv_platform_document_usernames_UsernameStatus_Initial_ctor()); + dash_spv_platform_identity_model_IdentityModel_set_username_status(self.identity.model, DChar(@"Bob"), dash_spv_platform_document_usernames_UsernameStatus_Initial_ctor()); } - (void)tearDown {