From a7e815fb2ea259f66db50104b988ced13dba46bf Mon Sep 17 00:00:00 2001 From: pankcuf Date: Fri, 10 Jan 2025 20:26:56 +0800 Subject: [PATCH 01/37] chore: dirty --- DashSync.podspec | 48 + DashSync/shared/Categories/NSArray+Dash.h | 23 + DashSync/shared/Categories/NSArray+Dash.m | 72 + .../shared/Categories/NSData/NSData+DSHash.m | 5 +- .../shared/Categories/NSData/NSData+Dash.m | 6 + .../Categories/NSData/NSMutableData+Dash.m | 2 +- .../shared/Categories/NSDictionary+Dash.h | 64 + DashSync/shared/Categories/NSIndexPath+FFI.h | 8 +- DashSync/shared/Categories/NSIndexPath+FFI.m | 53 +- .../shared/Categories/NSMutableArray+Dash.h | 1 + DashSync/shared/Categories/NSString+Bitcoin.m | 2 +- DashSync/shared/Categories/NSString+Dash.m | 2 +- DashSync/shared/DSDashSharedCore.h | 49 + DashSync/shared/DSDashSharedCore.m | 547 ++ DashSync/shared/DashSync.h | 31 +- DashSync/shared/DashSync.m | 2 + .../DashSync.xcdatamodeld/.xccurrentversion | 2 +- .../DashSync 21.xcdatamodel/contents | 2 +- .../DashSync 22.xcdatamodel/contents | 528 ++ .../AdvancedOperations/Others/NSError+Dash.h | 3 + .../Others/NSError+Platform.h | 33 + .../Others/NSError+Platform.m | 64 + .../shared/Libraries/DSUInt256IndexPath.h | 2 - DashSync/shared/Models/Chain/DSBlock.m | 1 + .../shared/Models/Chain/DSChain+Checkpoint.h | 69 + .../shared/Models/Chain/DSChain+Checkpoint.m | 147 + .../shared/Models/Chain/DSChain+Identity.h | 63 + .../shared/Models/Chain/DSChain+Identity.m | 174 + DashSync/shared/Models/Chain/DSChain+Params.h | 148 + DashSync/shared/Models/Chain/DSChain+Params.m | 685 ++ .../shared/Models/Chain/DSChain+Protected.h | 26 +- .../shared/Models/Chain/DSChain+Transaction.h | 54 + .../shared/Models/Chain/DSChain+Transaction.m | 329 + DashSync/shared/Models/Chain/DSChain+Wallet.h | 105 + DashSync/shared/Models/Chain/DSChain+Wallet.m | 352 + DashSync/shared/Models/Chain/DSChain.h | 255 +- DashSync/shared/Models/Chain/DSChain.m | 1574 +--- .../shared/Models/Chain/DSChainConstants.h | 9 +- DashSync/shared/Models/Chain/DSChainLock.h | 9 +- DashSync/shared/Models/Chain/DSChainLock.m | 94 +- DashSync/shared/Models/Chain/DSFullBlock.m | 1 + DashSync/shared/Models/DAPI/DSDAPIClient.h | 8 +- DashSync/shared/Models/DAPI/DSDAPIClient.m | 210 +- .../shared/Models/DAPI/DSPlatformTreeQuery.h | 1 + .../Networking/DSDAPICoreNetworkService.m | 1 + .../Networking/DSDAPIGRPCResponseHandler.h | 11 +- .../Networking/DSDAPIGRPCResponseHandler.m | 80 +- .../Networking/DSDAPIPlatformNetworkService.m | 112 +- .../DSDAPIPlatformNetworkServiceProtocol.h | 6 +- .../DSAssetLockDerivationPath+Protected.h | 30 + ...tionPath.h => DSAssetLockDerivationPath.h} | 10 +- .../DSAssetLockDerivationPath.m | 100 + ...thenticationKeysDerivationPath+Protected.h | 4 +- .../DSAuthenticationKeysDerivationPath.h | 11 +- .../DSAuthenticationKeysDerivationPath.m | 220 +- .../DSCreditFundingDerivationPath+Protected.h | 30 - .../DSCreditFundingDerivationPath.m | 91 - .../DSDerivationPath+Protected.h | 24 +- .../Derivation Paths/DSDerivationPath.h | 93 +- .../Derivation Paths/DSDerivationPath.m | 552 +- .../DSDerivationPathFactory.h | 44 +- .../DSDerivationPathFactory.m | 320 +- .../Derivation Paths/DSFundsDerivationPath.h | 4 - .../Derivation Paths/DSFundsDerivationPath.m | 154 +- .../DSIncomingFundsDerivationPath.h | 31 +- .../DSIncomingFundsDerivationPath.m | 239 +- .../DSMasternodeHoldingsDerivationPath.h | 5 +- .../DSMasternodeHoldingsDerivationPath.m | 39 +- .../DSSimpleIndexedDerivationPath.h | 5 +- .../DSSimpleIndexedDerivationPath.m | 109 +- .../Entities/DSAccountEntity+CoreDataClass.m | 5 +- ...LockTransactionEntity+CoreDataProperties.h | 3 + ...LockTransactionEntity+CoreDataProperties.m | 1 + ...DSBlockchainIdentityEntity+CoreDataClass.h | 4 +- ...DSBlockchainIdentityEntity+CoreDataClass.m | 12 +- ...ckchainIdentityEntity+CoreDataProperties.h | 14 +- ...IdentityKeyPathEntity+CoreDataProperties.h | 2 +- .../Entities/DSChainEntity+CoreDataClass.h | 3 +- .../Entities/DSChainEntity+CoreDataClass.m | 30 +- .../DSChainLockEntity+CoreDataClass.m | 14 +- .../shared/Models/Entities/DSContactRequest.h | 12 +- .../shared/Models/Entities/DSContactRequest.m | 45 +- .../Entities/DSContractEntity+CoreDataClass.h | 7 +- .../Entities/DSContractEntity+CoreDataClass.m | 7 + ...itFundingTransactionEntity+CoreDataClass.m | 6 +- .../DSDashpayUserEntity+CoreDataClass.h | 2 +- .../DSDerivationPathEntity+CoreDataClass.m | 19 +- .../DSFriendRequestEntity+CoreDataClass.m | 2 +- .../DSInstantSendLockEntity+CoreDataClass.m | 8 +- .../DSMasternodeListEntity+CoreDataClass.h | 10 +- .../DSMasternodeListEntity+CoreDataClass.m | 61 +- .../DSMerkleBlockEntity+CoreDataClass.h | 8 +- .../DSMerkleBlockEntity+CoreDataClass.m | 9 +- .../Entities/DSPeerEntity+CoreDataClass.m | 1 + .../DSQuorumEntryEntity+CoreDataClass.h | 22 +- .../DSQuorumEntryEntity+CoreDataClass.m | 89 +- .../DSQuorumEntryEntity+CoreDataProperties.h | 2 +- .../DSQuorumSnapshotEntity+CoreDataClass.h | 12 +- .../DSQuorumSnapshotEntity+CoreDataClass.m | 77 +- ...ifiedMasternodeEntryEntity+CoreDataClass.h | 45 +- ...ifiedMasternodeEntryEntity+CoreDataClass.m | 472 +- ...MasternodeEntryEntity+CoreDataProperties.h | 1 + ...MasternodeEntryEntity+CoreDataProperties.m | 1 + .../DSTransactionEntity+CoreDataClass.m | 1 + .../Models/Governance/DSGovernanceObject.m | 5 +- .../Models/Governance/DSGovernanceVote.h | 5 +- .../Models/Governance/DSGovernanceVote.m | 26 +- .../Identity/DSBlockchainIdentity+Protected.h | 91 - .../Models/Identity/DSBlockchainIdentity.h | 409 -- .../Models/Identity/DSBlockchainIdentity.m | 4849 ------------- .../DSBlockchainInvitation+Protected.h | 45 - .../Models/Identity/DSBlockchainInvitation.m | 457 -- .../Identity/DSIdentity+ContactRequest.h | 37 + .../Identity/DSIdentity+ContactRequest.m | 553 ++ .../Models/Identity/DSIdentity+Friendship.h | 48 + .../Models/Identity/DSIdentity+Friendship.m | 392 + .../Models/Identity/DSIdentity+Profile.h | 102 + .../Models/Identity/DSIdentity+Profile.m | 597 ++ .../Models/Identity/DSIdentity+Protected.h | 173 + .../Models/Identity/DSIdentity+Username.h | 63 + .../Models/Identity/DSIdentity+Username.m | 951 +++ DashSync/shared/Models/Identity/DSIdentity.h | 292 + DashSync/shared/Models/Identity/DSIdentity.m | 2814 ++++++++ .../Models/Identity/DSInvitation+Protected.h | 55 + ...SBlockchainInvitation.h => DSInvitation.h} | 29 +- .../shared/Models/Identity/DSInvitation.m | 465 ++ .../Models/Identity/DSPotentialContact.h | 10 +- .../Models/Identity/DSPotentialContact.m | 16 +- .../Identity/DSPotentialOneWayFriendship.h | 27 +- .../Identity/DSPotentialOneWayFriendship.m | 170 +- .../shared/Models/Keys/NSData+Encryption.h | 8 +- .../shared/Models/Keys/NSData+Encryption.mm | 40 +- .../Chain Managers/DSChainManager+Protected.h | 1 + .../DSChainManager+Transactions.m | 1 + .../Managers/Chain Managers/DSChainManager.h | 1 + .../Managers/Chain Managers/DSChainManager.m | 14 +- .../Managers/Chain Managers/DSChainsManager.h | 2 +- .../Managers/Chain Managers/DSChainsManager.m | 33 +- .../Chain Managers/DSGovernanceSyncManager.m | 1 + .../DSIdentitiesManager+CoreData.h | 35 + .../DSIdentitiesManager+CoreData.m | 87 + .../DSIdentitiesManager+Protected.h | 2 +- .../Chain Managers/DSIdentitiesManager.h | 67 +- .../Chain Managers/DSIdentitiesManager.m | 603 +- .../Managers/Chain Managers/DSKeyManager.h | 345 +- .../Managers/Chain Managers/DSKeyManager.m | 628 +- .../DSMasternodeManager+LocalMasternode.h | 13 +- .../DSMasternodeManager+LocalMasternode.m | 126 +- .../DSMasternodeManager+Mndiff.h | 92 +- .../DSMasternodeManager+Mndiff.m | 550 +- .../DSMasternodeManager+Protected.h | 18 +- .../Chain Managers/DSMasternodeManager.h | 63 +- .../Chain Managers/DSMasternodeManager.m | 780 +- .../Chain Managers/DSPeerManager+Protected.h | 5 +- .../Managers/Chain Managers/DSPeerManager.m | 87 +- .../Managers/Chain Managers/DSSporkManager.m | 1 + .../Chain Managers/DSTransactionManager.h | 8 +- .../Chain Managers/DSTransactionManager.m | 237 +- .../Auth/DSAuthenticationManager.m | 2 + .../Service Managers/DSInsightManager.h | 2 + .../Service Managers/DSInsightManager.m | 46 + .../Service Managers/DSOptionsManager.h | 4 +- .../Service Managers/DSVersionManager.m | 1 + .../Models/Masternode/DSLocalMasternode.h | 18 +- .../Models/Masternode/DSLocalMasternode.m | 72 +- .../Masternode/DSMasternodeList+Mndiff.h | 17 +- .../Masternode/DSMasternodeList+Mndiff.m | 188 +- .../Models/Masternode/DSMasternodeList.h | 78 +- .../Models/Masternode/DSMasternodeList.m | 1362 ++-- .../Masternode/DSMasternodeListDiffService.h | 2 +- .../Masternode/DSMasternodeListDiffService.m | 39 +- .../DSMasternodeListService+Protected.h | 16 +- .../Masternode/DSMasternodeListService.h | 42 +- .../Masternode/DSMasternodeListService.m | 274 +- .../DSMasternodeListStore+Protected.h | 23 +- .../Models/Masternode/DSMasternodeListStore.h | 50 +- .../Models/Masternode/DSMasternodeListStore.m | 768 +- .../Masternode/DSMasternodeProcessorContext.h | 76 +- .../Masternode/DSMasternodeProcessorContext.m | 180 +- .../Masternode/DSMnDiffProcessingResult.h | 54 +- .../Masternode/DSMnDiffProcessingResult.m | 156 +- .../Masternode/DSQRInfoProcessingResult.h | 60 +- .../Masternode/DSQRInfoProcessingResult.m | 156 +- .../Models/Masternode/DSQuorumEntry+Mndiff.h | 38 +- .../Models/Masternode/DSQuorumEntry+Mndiff.m | 140 +- .../shared/Models/Masternode/DSQuorumEntry.h | 20 +- .../shared/Models/Masternode/DSQuorumEntry.m | 551 +- .../Masternode/DSQuorumRotationService.h | 13 +- .../Masternode/DSQuorumRotationService.m | 87 +- .../Masternode/DSQuorumSnapshot+Mndiff.h | 36 +- .../Masternode/DSQuorumSnapshot+Mndiff.m | 90 +- .../Models/Masternode/DSQuorumSnapshot.h | 36 +- .../Models/Masternode/DSQuorumSnapshot.m | 22 +- .../DSSimplifiedMasternodeEntry+Mndiff.h | 11 +- .../DSSimplifiedMasternodeEntry+Mndiff.m | 358 +- .../Masternode/DSSimplifiedMasternodeEntry.h | 2 +- .../Masternode/DSSimplifiedMasternodeEntry.m | 166 +- DashSync/shared/Models/Network/DSPeer.h | 13 +- DashSync/shared/Models/Network/DSPeer.m | 17 +- .../shared/Models/Payment/DSPaymentProtocol.m | 1 + .../shared/Models/Payment/DSPaymentRequest.h | 28 +- .../shared/Models/Payment/DSPaymentRequest.m | 34 +- .../Internal/DSCoreDataMigrationVersion.h | 1 + .../Internal/DSCoreDataMigrationVersion.m | 3 +- .../DSMerkleBlockEntity6To7MigrationPolicy.m | 6 +- .../Models/Platform/Base/DPBaseObject.m | 1 + .../Platform/Contract/DPContract+Protected.h | 7 +- .../Models/Platform/Contract/DPContract.h | 29 +- .../Models/Platform/Contract/DPContract.m | 384 +- .../shared/Models/Platform/DSDashPlatform.h | 7 +- .../shared/Models/Platform/DSDashPlatform.m | 24 +- .../Platform/Document/DPDocumentFactory.h | 4 +- .../Platform/Document/DPDocumentFactory.m | 6 +- .../DSBlockchainIdentityCloseTransition.h | 22 - ...BlockchainIdentityRegistrationTransition.h | 23 - .../DSBlockchainIdentityUpdateTransition.h | 38 - .../DSIdentityCloseTransition.h | 22 + ...ansition.m => DSIdentityCloseTransition.m} | 26 +- .../DSIdentityRegistrationTransition.h | 26 + ...n.m => DSIdentityRegistrationTransition.m} | 52 +- ...ansition.h => DSIdentityTopupTransition.h} | 6 +- ...ansition.m => DSIdentityTopupTransition.m} | 8 +- .../DSIdentityUpdateTransition.h | 38 + ...nsition.m => DSIdentityUpdateTransition.m} | 42 +- .../Transitions/DSContractTransition.h | 23 +- .../Transitions/DSContractTransition.m | 56 +- .../Transitions/DSDocumentTransition.h | 6 +- .../Transitions/DSDocumentTransition.m | 13 +- .../Transitions/DSTransition+Protected.h | 4 +- .../Platform/Transitions/DSTransition.h | 21 +- .../Platform/Transitions/DSTransition.m | 33 +- DashSync/shared/Models/Spork/DSSpork.m | 71 +- DashSync/shared/Models/System/DSEnvironment.m | 1 + .../Base/DSAssetLockTransaction.h | 8 + .../Base/DSAssetLockTransaction.m | 81 + .../Base/DSCreditFundingTransaction.m | 21 +- .../Base/DSInstantSendTransactionLock.h | 9 +- .../Base/DSInstantSendTransactionLock.m | 117 +- .../Models/Transactions/Base/DSTransaction.h | 26 +- .../Models/Transactions/Base/DSTransaction.m | 178 +- .../Transactions/Base/DSTransactionInput.h | 6 +- .../Coinbase/DSCoinbaseTransaction.m | 1 + .../Transactions/DSTransactionFactory.m | 19 +- .../DSProviderRegistrationTransaction.m | 22 +- .../DSProviderUpdateRegistrarTransaction.h | 4 +- .../DSProviderUpdateRegistrarTransaction.m | 27 +- .../DSProviderUpdateRevocationTransaction.h | 4 +- .../DSProviderUpdateRevocationTransaction.m | 25 +- .../DSProviderUpdateServiceTransaction.h | 7 +- .../DSProviderUpdateServiceTransaction.m | 25 +- DashSync/shared/Models/Wallet/DSAccount.h | 135 +- DashSync/shared/Models/Wallet/DSAccount.m | 301 +- .../DSSpecialTransactionsWalletHolder.h | 13 +- .../DSSpecialTransactionsWalletHolder.m | 130 +- .../shared/Models/Wallet/DSWallet+Identity.h | 66 + .../shared/Models/Wallet/DSWallet+Identity.m | 378 + .../Models/Wallet/DSWallet+Invitation.h | 45 + .../Models/Wallet/DSWallet+Invitation.m | 182 + .../shared/Models/Wallet/DSWallet+Protected.h | 2 - DashSync/shared/Models/Wallet/DSWallet.h | 144 +- DashSync/shared/Models/Wallet/DSWallet.m | 765 +- .../shared/Models/Wallet/DSWalletConstants.m | 28 +- Example/DashSync.xcodeproj/project.pbxproj | 168 +- .../xcschemes/DashSync-Example.xcscheme | 12 +- Example/DashSync/BRCopyLabel.m | 6 +- Example/DashSync/Base.lproj/Main.storyboard | 178 +- .../DashSync/BlockchainIdentities.storyboard | 117 +- .../DSAccountsDerivationPathsViewController.m | 2 +- Example/DashSync/DSAddDevnetViewController.m | 28 +- .../DSAddressesTransactionsViewController.m | 8 +- ...ysDerivationPathsAddressesViewController.m | 5 +- .../DSBlockchainIdentitiesViewController.m | 168 - Example/DashSync/DSChainsViewController.m | 17 +- .../DSClaimMasternodeViewController.h | 3 +- .../DSClaimMasternodeViewController.m | 23 +- Example/DashSync/DSContactProfileAvatarView.m | 2 +- .../DashSync/DSContactProfileViewController.h | 2 +- .../DashSync/DSContactProfileViewController.m | 22 +- ...tReceivedTransactionsTableViewController.h | 4 +- ...tReceivedTransactionsTableViewController.m | 10 +- ...ContactRelationshipActionsViewController.h | 2 +- .../DSContactRelationshipInfoViewController.h | 2 +- .../DSContactRelationshipInfoViewController.m | 12 +- .../DSContactSendDashViewController.h | 2 +- .../DSContactSendDashViewController.m | 6 +- ...ntactSentTransactionsTableViewController.h | 4 +- ...ntactSentTransactionsTableViewController.m | 10 +- .../DashSync/DSContactsNavigationController.h | 4 +- .../DashSync/DSContactsNavigationController.m | 4 +- .../DashSync/DSContactsTabBarViewController.h | 4 +- .../DashSync/DSContactsTabBarViewController.m | 10 +- Example/DashSync/DSContactsViewController.h | 4 +- Example/DashSync/DSContactsViewController.m | 22 +- ...ateIdentityFromInvitationViewController.h} | 2 +- ...ateIdentityFromInvitationViewController.m} | 21 +- ...ler.h => DSCreateIdentityViewController.h} | 4 +- ...ler.m => DSCreateIdentityViewController.m} | 31 +- .../DSCreateInvitationViewController.m | 23 +- .../DSDAPIGetUserInfoViewController.m | 6 +- ...troller.h => DSIdentitiesViewController.h} | 4 +- Example/DashSync/DSIdentitiesViewController.m | 149 + ...er.h => DSIdentityActionsViewController.h} | 6 +- ...er.m => DSIdentityActionsViewController.m} | 175 +- ...onDerivationPathsAddressesViewController.m | 8 +- ...ell.h => DSIdentityChooserTableViewCell.h} | 2 +- ...ell.m => DSIdentityChooserTableViewCell.m} | 4 +- .../DSIdentityChooserViewController.h | 2 +- .../DSIdentityChooserViewController.m | 44 +- ...iewCell.h => DSIdentityKeyTableViewCell.h} | 2 +- ...iewCell.m => DSIdentityKeyTableViewCell.m} | 4 +- ...oller.h => DSIdentityKeysViewController.h} | 4 +- ...oller.m => DSIdentityKeysViewController.m} | 24 +- ...Cell.h => DSIdentitySearchTableViewCell.h} | 2 +- ...Cell.m => DSIdentitySearchTableViewCell.m} | 4 +- ...leViewCell.h => DSIdentityTableViewCell.h} | 4 +- ...leViewCell.m => DSIdentityTableViewCell.m} | 6 +- ... => DSIdentityTransitionsViewController.h} | 4 +- ... => DSIdentityTransitionsViewController.m} | 12 +- .../DSIncomingContactsTableViewController.h | 4 +- .../DSIncomingContactsTableViewController.m | 12 +- .../DSInvitationDetailViewController.h | 2 +- .../DSInvitationDetailViewController.m | 74 +- .../DashSync/DSInvitationsViewController.h | 2 +- .../DashSync/DSInvitationsViewController.m | 89 +- .../DSMasternodeDetailViewController.h | 5 +- .../DSMasternodeDetailViewController.m | 24 +- .../DSMasternodeListsViewController.m | 24 +- Example/DashSync/DSMasternodeViewController.h | 4 +- Example/DashSync/DSMasternodeViewController.m | 11 +- Example/DashSync/DSNetworkActivityView.h | 31 + Example/DashSync/DSNetworkActivityView.m | 82 + .../DSOutgoingContactsTableViewController.h | 4 +- .../DSOutgoingContactsTableViewController.m | 10 +- Example/DashSync/DSQuorumListViewController.m | 28 +- .../DSReclaimMasternodeViewController.m | 83 +- .../DSRegisterContractsViewController.h | 2 +- .../DSRegisterContractsViewController.m | 28 +- .../DashSync/DSRegisterTLDViewController.h | 2 +- .../DashSync/DSRegisterTLDViewController.m | 8 +- ...r.h => DSSearchIdentitiesViewController.h} | 2 +- ...r.m => DSSearchIdentitiesViewController.m} | 28 +- Example/DashSync/DSSendAmountViewController.m | 2 +- Example/DashSync/DSSettingsViewController.m | 2 +- ...SpecializedDerivationPathsViewController.m | 4 +- ...DSStandaloneDerivationPathViewController.m | 2 +- Example/DashSync/DSSyncViewController.m | 72 +- ...ller.h => DSTopupIdentityViewController.h} | 6 +- ...ller.m => DSTopupIdentityViewController.m} | 20 +- .../DSTransactionDetailViewController.m | 52 +- ...SUpdateMasternodeRegistrarViewController.h | 3 +- ...SUpdateMasternodeRegistrarViewController.m | 4 +- Example/DashSync/DSWalletViewController.m | 2 +- Example/DashSync/Helpers.storyboard | 13 +- Example/DashSync/SearchIdentity.storyboard | 13 +- Example/Podfile | 4 +- Example/Podfile.lock | 679 +- Example/Tests/DSAttackTests.m | 7 +- Example/Tests/DSBIP32Tests.m | 427 +- Example/Tests/DSChainTests.m | 2 +- Example/Tests/DSChainedSigningTests.m | 21 +- Example/Tests/DSDIP14Tests.m | 37 +- .../DSDeterministicMasternodeListTests.m | 6311 +++++++++-------- Example/Tests/DSIESEncryptedDataTests.mm | 218 +- Example/Tests/DSInstantSendLockTests.m | 22 +- Example/Tests/DSInvitationsTests.m | 2 +- Example/Tests/DSKeyTests.m | 150 +- Example/Tests/DSMainnetMetricSyncTests.m | 1 + Example/Tests/DSMainnetSyncTests.m | 1 + Example/Tests/DSMiningTests.m | 2 +- Example/Tests/DSProviderTransactionsTests.m | 105 +- Example/Tests/DSSparseMerkleTreeTests.m | 4 +- Example/Tests/DSTestnetE2ETests.m | 100 +- Example/Tests/DSTestnetMetricSyncTests.m | 2 +- Example/Tests/DSTestnetSyncTests.m | 1 + Example/Tests/DSTransactionTests.m | 160 +- Example/Tests/DSTransitionTests.m | 89 +- 376 files changed, 25228 insertions(+), 20124 deletions(-) create mode 100644 DashSync/shared/DSDashSharedCore.h create mode 100644 DashSync/shared/DSDashSharedCore.m create mode 100644 DashSync/shared/DashSync.xcdatamodeld/DashSync 22.xcdatamodel/contents create mode 100644 DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Platform.h create mode 100644 DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Platform.m create mode 100644 DashSync/shared/Models/Chain/DSChain+Checkpoint.h create mode 100644 DashSync/shared/Models/Chain/DSChain+Checkpoint.m create mode 100644 DashSync/shared/Models/Chain/DSChain+Identity.h create mode 100644 DashSync/shared/Models/Chain/DSChain+Identity.m create mode 100644 DashSync/shared/Models/Chain/DSChain+Params.h create mode 100644 DashSync/shared/Models/Chain/DSChain+Params.m create mode 100644 DashSync/shared/Models/Chain/DSChain+Transaction.h create mode 100644 DashSync/shared/Models/Chain/DSChain+Transaction.m create mode 100644 DashSync/shared/Models/Chain/DSChain+Wallet.h create mode 100644 DashSync/shared/Models/Chain/DSChain+Wallet.m create mode 100644 DashSync/shared/Models/Derivation Paths/DSAssetLockDerivationPath+Protected.h rename DashSync/shared/Models/Derivation Paths/{DSCreditFundingDerivationPath.h => DSAssetLockDerivationPath.h} (60%) create mode 100644 DashSync/shared/Models/Derivation Paths/DSAssetLockDerivationPath.m delete mode 100644 DashSync/shared/Models/Derivation Paths/DSCreditFundingDerivationPath+Protected.h delete mode 100644 DashSync/shared/Models/Derivation Paths/DSCreditFundingDerivationPath.m delete mode 100644 DashSync/shared/Models/Identity/DSBlockchainIdentity+Protected.h delete mode 100644 DashSync/shared/Models/Identity/DSBlockchainIdentity.h delete mode 100644 DashSync/shared/Models/Identity/DSBlockchainIdentity.m delete mode 100644 DashSync/shared/Models/Identity/DSBlockchainInvitation+Protected.h delete mode 100644 DashSync/shared/Models/Identity/DSBlockchainInvitation.m create mode 100644 DashSync/shared/Models/Identity/DSIdentity+ContactRequest.h create mode 100644 DashSync/shared/Models/Identity/DSIdentity+ContactRequest.m create mode 100644 DashSync/shared/Models/Identity/DSIdentity+Friendship.h create mode 100644 DashSync/shared/Models/Identity/DSIdentity+Friendship.m create mode 100644 DashSync/shared/Models/Identity/DSIdentity+Profile.h create mode 100644 DashSync/shared/Models/Identity/DSIdentity+Profile.m create mode 100644 DashSync/shared/Models/Identity/DSIdentity+Protected.h create mode 100644 DashSync/shared/Models/Identity/DSIdentity+Username.h create mode 100644 DashSync/shared/Models/Identity/DSIdentity+Username.m create mode 100644 DashSync/shared/Models/Identity/DSIdentity.h create mode 100644 DashSync/shared/Models/Identity/DSIdentity.m create mode 100644 DashSync/shared/Models/Identity/DSInvitation+Protected.h rename DashSync/shared/Models/Identity/{DSBlockchainInvitation.h => DSInvitation.h} (73%) create mode 100644 DashSync/shared/Models/Identity/DSInvitation.m create mode 100644 DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager+CoreData.h create mode 100644 DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager+CoreData.m delete mode 100644 DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityCloseTransition.h delete mode 100644 DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityRegistrationTransition.h delete mode 100644 DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityUpdateTransition.h create mode 100644 DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityCloseTransition.h rename DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/{DSBlockchainIdentityCloseTransition.m => DSIdentityCloseTransition.m} (70%) create mode 100644 DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityRegistrationTransition.h rename DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/{DSBlockchainIdentityRegistrationTransition.m => DSIdentityRegistrationTransition.m} (60%) rename DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/{DSBlockchainIdentityTopupTransition.h => DSIdentityTopupTransition.h} (50%) rename DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/{DSBlockchainIdentityTopupTransition.m => DSIdentityTopupTransition.m} (50%) create mode 100644 DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityUpdateTransition.h rename DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/{DSBlockchainIdentityUpdateTransition.m => DSIdentityUpdateTransition.m} (72%) create mode 100644 DashSync/shared/Models/Wallet/DSWallet+Identity.h create mode 100644 DashSync/shared/Models/Wallet/DSWallet+Identity.m create mode 100644 DashSync/shared/Models/Wallet/DSWallet+Invitation.h create mode 100644 DashSync/shared/Models/Wallet/DSWallet+Invitation.m delete mode 100644 Example/DashSync/DSBlockchainIdentitiesViewController.m rename Example/DashSync/{DSCreateBlockchainIdentityFromInvitationViewController.h => DSCreateIdentityFromInvitationViewController.h} (87%) rename Example/DashSync/{DSCreateBlockchainIdentityFromInvitationViewController.m => DSCreateIdentityFromInvitationViewController.m} (86%) rename Example/DashSync/{DSCreateBlockchainIdentityViewController.h => DSCreateIdentityViewController.h} (63%) rename Example/DashSync/{DSCreateBlockchainIdentityViewController.m => DSCreateIdentityViewController.m} (84%) rename Example/DashSync/{DSBlockchainIdentitiesViewController.h => DSIdentitiesViewController.h} (68%) create mode 100644 Example/DashSync/DSIdentitiesViewController.m rename Example/DashSync/{DSBlockchainIdentityActionsViewController.h => DSIdentityActionsViewController.h} (56%) rename Example/DashSync/{DSBlockchainIdentityActionsViewController.m => DSIdentityActionsViewController.m} (52%) rename Example/DashSync/{DSBlockchainIdentityChooserTableViewCell.h => DSIdentityChooserTableViewCell.h} (92%) rename Example/DashSync/{DSBlockchainIdentityChooserTableViewCell.m => DSIdentityChooserTableViewCell.m} (89%) rename Example/DashSync/{DSBlockchainIdentityKeyTableViewCell.h => DSIdentityKeyTableViewCell.h} (93%) rename Example/DashSync/{DSBlockchainIdentityKeyTableViewCell.m => DSIdentityKeyTableViewCell.m} (89%) rename Example/DashSync/{DSBlockchainIdentityKeysViewController.h => DSIdentityKeysViewController.h} (83%) rename Example/DashSync/{DSBlockchainIdentityKeysViewController.m => DSIdentityKeysViewController.m} (66%) rename Example/DashSync/{DSBlockchainIdentitySearchTableViewCell.h => DSIdentitySearchTableViewCell.h} (93%) rename Example/DashSync/{DSBlockchainIdentitySearchTableViewCell.m => DSIdentitySearchTableViewCell.m} (89%) rename Example/DashSync/{DSBlockchainIdentityTableViewCell.h => DSIdentityTableViewCell.h} (83%) rename Example/DashSync/{DSBlockchainIdentityTableViewCell.m => DSIdentityTableViewCell.m} (73%) rename Example/DashSync/{DSBlockchainIdentityTransitionsViewController.h => DSIdentityTransitionsViewController.h} (83%) rename Example/DashSync/{DSBlockchainIdentityTransitionsViewController.m => DSIdentityTransitionsViewController.m} (92%) create mode 100644 Example/DashSync/DSNetworkActivityView.h create mode 100644 Example/DashSync/DSNetworkActivityView.m rename Example/DashSync/{DSSearchBlockchainIdentitiesViewController.h => DSSearchIdentitiesViewController.h} (88%) rename Example/DashSync/{DSSearchBlockchainIdentitiesViewController.m => DSSearchIdentitiesViewController.m} (76%) rename Example/DashSync/{DSTopupBlockchainIdentityViewController.h => DSTopupIdentityViewController.h} (55%) rename Example/DashSync/{DSTopupBlockchainIdentityViewController.m => DSTopupIdentityViewController.m} (85%) diff --git a/DashSync.podspec b/DashSync.podspec index b185dc97d..53767b781 100644 --- a/DashSync.podspec +++ b/DashSync.podspec @@ -38,8 +38,56 @@ Pod::Spec.new do |s| s.dependency 'CocoaLumberjack', '3.7.2' s.ios.dependency 'DWAlertController', '0.2.1' s.dependency 'DSDynamicOptions', '0.1.2' + s.dependency 'DAPI-GRPC', '0.22.0-dev.8' s.dependency 'TinyCborObjc', '0.4.6' s.prefix_header_contents = '#import "DSEnvironment.h"' end +#Pod::Spec.new do |s| +# s.name = 'DashSync' +# s.version = '0.1.0' +# s.summary = 'Dash Sync is a light and configurable blockchain client that you can embed into your iOS Application.' +# s.description = 'Dash Sync is a light blockchain client that you can embed into your iOS Application. It is fully customizable to make the type of node you are interested in.' +# +# s.homepage = 'https://github.com/dashevo/dashsync-ios.git' +# s.license = { :type => 'MIT', :file => 'LICENSE' } +# s.author = { 'quantumexplorer' => 'quantum@dash.org' } +# s.source = { :git => 'https://github.com/dashevo/dashsync-iOS.git', :tag => s.version.to_s } +# +# s.ios.deployment_target = '13.0' +# s.osx.deployment_target = '10.15' +# +# s.requires_arc = true +# +# s.source_files = "DashSync/shared/**/*.{h,m,mm}", "../../dash-shared-core-ferment/dash_spv_apple_bindings/target/include/*.{h,m,mm}" +# s.public_header_files = 'DashSync/shared/**/*.h', "../../dash-shared-core-ferment/dash_spv_apple_bindings/target/include/*.{h,m,mm}" +# s.ios.source_files = "DashSync/iOS/**/*.{h,m,mm}", "../../dash-shared-core-ferment/dash_spv_apple_bindings/target/include/*.{h,m,mm}" +# s.ios.public_header_files = 'DashSync/iOS/**/*.h', "../../dash-shared-core-ferment/dash_spv_apple_bindings/target/include/*.{h,m,mm}" +# s.macos.source_files = "DashSync/macOS/**/*.{h,m,mm}", "../../dash-shared-core-ferment/dash_spv_apple_bindings/target/include/*.{h,m,mm}" +# s.macos.public_header_files = 'DashSync/macOS/**/*.h', "../../dash-shared-core-ferment/dash_spv_apple_bindings/target/include/*.{h,m,mm}" +# s.libraries = 'resolv', 'bz2', 'sqlite3' +## s.ios.libraries = 'dash_spv_apple_bindings_ios' +## s.macos.libraries = 'dash_spv_apple_bindings_macos' +# s.resource_bundles = {'DashSync' => ['DashSync/shared/*.xcdatamodeld', 'DashSync/shared/MappingModels/*.xcmappingmodel', 'DashSync/shared/*.plist', 'DashSync/shared/*.lproj', 'DashSync/shared/MasternodeLists/*.dat', 'DashSync/shared/*.json']} +# +# s.framework = 'Foundation', 'SystemConfiguration', 'CoreData', 'BackgroundTasks', 'Security' +# s.ios.framework = 'UIKit' +# s.macos.framework = 'Cocoa' +# s.compiler_flags = '-Wno-comma' +## s.dependency 'DashSharedCore', '0.4.19' +# s.dependency 'CocoaLumberjack', '3.7.2' +# s.ios.dependency 'DWAlertController', '0.2.1' +# s.dependency 'DSDynamicOptions', '0.1.2' +# s.dependency 'DAPI-GRPC', '0.22.0-dev.8' +# s.dependency 'TinyCborObjc', '0.4.6' +# s.prefix_header_contents = '#import "DSEnvironment.h"' +# s.ios.vendored_libraries = '../../dash-shared-core-ferment/dash_spv_apple_bindings/lib/ios/libdash_spv_apple_bindings_ios.a' +# s.macos.vendored_libraries = '../../dash-shared-core-ferment/dash_spv_apple_bindings/lib/ios/libdash_spv_apple_bindings_macos.a' +# +## s.vendored_frameworks = '../../dash-shared-core-ferment/dash_spv_apple_bindings/target/framework/DashSharedCore.xcframework' +## s.public_header_files += '../../dash-shared-core-ferment/dash_spv_apple_bindings/target/framework/DashSharedCore.xcframework/**/*.h' +##s.public_header_files = '../../dash-shared-core-ferment/dash_spv_apple_bindings/target/framework/DashSharedCore.xcframework/**/*.h' +# +#end +# diff --git a/DashSync/shared/Categories/NSArray+Dash.h b/DashSync/shared/Categories/NSArray+Dash.h index c9028fe2c..56b9706c1 100644 --- a/DashSync/shared/Categories/NSArray+Dash.h +++ b/DashSync/shared/Categories/NSArray+Dash.h @@ -6,6 +6,8 @@ // #import "BigIntTypes.h" +#import "dash_shared_core.h" +#import "DSKeyManager.h" #import "NSData+Dash.h" #import @@ -23,4 +25,25 @@ NS_ASSUME_NONNULL_BEGIN @end +@interface NSArray (HashSet_u8_32) ++ (NSArray *)ffi_from_hash_set:(std_collections_HashSet_u8_32 *)ffi_ref; ++ (std_collections_HashSet_u8_32 *)ffi_to_hash_set:(NSArray *)obj; ++ (void)ffi_destroy_hash_set:(std_collections_HashSet_u8_32 *)ffi_ref; +@end + +@interface NSArray (_) + ++ (NSArray *)ffi_from_vec:(Vec_ *)ffi_ref; ++ (Vec_ *)ffi_to_vec:(NSArray *)obj; ++ (void)ffi_destroy_vec:(Vec_ *)ffi_ref; + +@end + +@interface NSArray (Vec_u8_32) + ++ (NSArray *)ffi_from_vec_u256:(Vec_u8_32 *)ffi_ref; ++ (Vec_u8_32 *)ffi_to_vec_u256:(NSArray *)obj; ++ (void)ffi_destroy_vec_u256:(Vec_u8_32 *)ffi_ref; +@end + NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Categories/NSArray+Dash.m b/DashSync/shared/Categories/NSArray+Dash.m index 989c098d4..0432a263e 100644 --- a/DashSync/shared/Categories/NSArray+Dash.m +++ b/DashSync/shared/Categories/NSArray+Dash.m @@ -68,3 +68,75 @@ - (NSArray *)map:(id (^)(id obj))block { } @end + +@implementation NSArray (HashSet_u8_32) + ++ (NSArray *)ffi_from_hash_set:(std_collections_HashSet_u8_32 *)ffi_ref { + NSMutableArray *arr = [NSMutableArray arrayWithCapacity:ffi_ref->count]; + for (int i = 0; i < ffi_ref->count; i++) { + u256 *chunk = ffi_ref->values[i]; + NSData *data = NSDataFromPtr(chunk); + [arr addObject:data]; + } + return arr; +} ++ (std_collections_HashSet_u8_32 *)ffi_to_hash_set:(NSArray *)obj { + std_collections_HashSet_u8_32 *set = malloc(sizeof(std_collections_HashSet_u8_32)); + u256 **values = malloc(obj.count * sizeof(u256 *)); + for (NSUInteger i = 0; i < obj.count; i++) { + NSData *data = obj[i]; + values[i] = u256_ctor(data); + } + set->count = obj.count; + set->values = values; + return set; +} ++ (void)ffi_destroy_hash_set:(std_collections_HashSet_u8_32 *)ffi_ref { + std_collections_HashSet_u8_32_destroy(ffi_ref); +} +@end + +@implementation NSArray (_) + ++ (NSArray *)ffi_from_vec:(Vec_ *)ffi_ref { + NSMutableArray *arr = [NSMutableArray arrayWithCapacity:ffi_ref->count]; + for (int i = 0; i < ffi_ref->count; i++) { + [arr addObject:[NSString stringWithUTF8String:ffi_ref->values[i]]]; + } + return arr; +} ++ (Vec_ *)ffi_to_vec:(NSArray *)obj { + NSUInteger count = obj.count; + char **values = malloc(count * sizeof(char *)); + for (NSUInteger i = 0; i < count; i++) { + values[i] = strdup([obj[i] UTF8String]); + } + return Vec__ctor(count, values); +} ++ (void)ffi_destroy_vec:(Vec_ *)ffi_ref { + Vec__destroy(ffi_ref); +} +@end + +@implementation NSArray (Vec_u8_32) + ++ (NSArray *)ffi_from_vec_u256:(Vec_u8_32 *)ffi_ref { + NSMutableArray *arr = [NSMutableArray arrayWithCapacity:ffi_ref->count]; + for (int i = 0; i < ffi_ref->count; i++) { + [arr addObject:NSDataFromPtr(ffi_ref->values[i])]; + } + return arr; +} ++ (Vec_u8_32 *)ffi_to_vec_u256:(NSArray *)obj { + NSUInteger count = obj.count; + u256 **values = malloc(count * sizeof(u256 *)); + for (NSUInteger i = 0; i < count; i++) { + values[i] = u256_ctor(obj[i]); + } + return Vec_u8_32_ctor(count, values); +} ++ (void)ffi_destroy_vec_u256:(Vec_u8_32 *)ffi_ref { + Vec_u8_32_destroy(ffi_ref); +} +@end + diff --git a/DashSync/shared/Categories/NSData/NSData+DSHash.m b/DashSync/shared/Categories/NSData/NSData+DSHash.m index a6f8e1294..6eddd9474 100644 --- a/DashSync/shared/Categories/NSData/NSData+DSHash.m +++ b/DashSync/shared/Categories/NSData/NSData+DSHash.m @@ -30,7 +30,10 @@ @implementation NSData (DSHash) - (NSData *)blake3Data { - return [DSKeyManager NSDataFrom:processor_blake3(self.bytes, self.length)]; + SLICE *slice = slice_ctor(self); + u256 *result = dash_spv_crypto_blake3(slice); + NSData *data = [DSKeyManager NSDataFromArr_u8_32:result]; + return data; } diff --git a/DashSync/shared/Categories/NSData/NSData+Dash.m b/DashSync/shared/Categories/NSData/NSData+Dash.m index ae9ddb2a4..309f92153 100644 --- a/DashSync/shared/Categories/NSData/NSData+Dash.m +++ b/DashSync/shared/Categories/NSData/NSData+Dash.m @@ -95,6 +95,7 @@ BOOL hasKeychainData(NSString *key, NSError **error) { } NSData *getKeychainData(NSString *key, NSError **error) { +// NSLog(@"getKeychainData: %@", key); NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrService: SEC_ATTR_SERVICE, (__bridge id)kSecAttrAccount: key, @@ -119,6 +120,7 @@ BOOL setKeychainInt(int64_t i, NSString *key, BOOL authenticated) { } int64_t getKeychainInt(NSString *key, NSError **error) { +// NSLog(@"getKeychainInt: %@", key); @autoreleasepool { NSData *d = getKeychainData(key, error); @@ -137,6 +139,7 @@ BOOL setKeychainString(NSString *s, NSString *key, BOOL authenticated) { } NSString *getKeychainString(NSString *key, NSError **error) { +// NSLog(@"getKeychainString: %@", key); @autoreleasepool { NSData *d = getKeychainData(key, error); @@ -155,6 +158,7 @@ BOOL setKeychainDict(NSDictionary *dict, NSString *key, BOOL authenticated) { } NSDictionary *getKeychainDict(NSString *key, NSArray *classes, NSError **error) { +// NSLog(@"getKeychainDict: %@", key); //@autoreleasepool { NSData *d = getKeychainData(key, error); if (d == nil) return nil; @@ -180,6 +184,7 @@ BOOL setKeychainArray(NSArray *array, NSString *key, BOOL authenticated) { } NSArray *getKeychainArray(NSString *key, NSArray *classes, NSError **error) { +// NSLog(@"getKeychainArray: %@", key); @autoreleasepool { NSData *d = getKeychainData(key, error); if (d == nil) return nil; @@ -197,6 +202,7 @@ BOOL setKeychainArray(NSArray *array, NSString *key, BOOL authenticated) { } NSOrderedSet *getKeychainOrderedSet(NSString *key, NSError **error) { +// NSLog(@"getKeychainOrderedSet: %@", key); @autoreleasepool { NSData *d = getKeychainData(key, error); diff --git a/DashSync/shared/Categories/NSData/NSMutableData+Dash.m b/DashSync/shared/Categories/NSData/NSMutableData+Dash.m index 4a79f9667..9fb5a2bbe 100644 --- a/DashSync/shared/Categories/NSData/NSMutableData+Dash.m +++ b/DashSync/shared/Categories/NSData/NSMutableData+Dash.m @@ -26,7 +26,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#import "DSChain.h" +#import "DSChain+Params.h" #import "NSData+DSHash.h" #import "NSMutableData+Dash.h" #import "NSString+Dash.h" diff --git a/DashSync/shared/Categories/NSDictionary+Dash.h b/DashSync/shared/Categories/NSDictionary+Dash.h index 22a20cae8..8931aeff7 100644 --- a/DashSync/shared/Categories/NSDictionary+Dash.h +++ b/DashSync/shared/Categories/NSDictionary+Dash.h @@ -19,6 +19,68 @@ NS_ASSUME_NONNULL_BEGIN +#define FFIMapConversion(TYPE, \ + KeyTypeC, KeyTypeObjC, KeyCtor, KeyDtor, KeyFrom, KeyTo, \ + ValueTypeC, ValueTypeObjC, ValueCtor, ValueDtor, ValueFrom, ValueTo) \ +@implementation NSDictionary (Conversions_##TYPE) \ +- (TYPE *)ffi_to:(NSDictionary *)obj { \ + NSUInteger i = 0, count = [obj count]; \ + TYPE *ffi_ref = malloc(sizeof(TYPE)); \ + KeyTypeC *keys = malloc(count * sizeof(KeyTypeC)); \ + ValueTypeC *values = malloc(count * sizeof(ValueTypeC)); \ + for (id key in obj) { \ + keys[i] = KeyTo; \ + values[i] = ValueTo; \ + i++; \ + } \ + ffi_ref->count = count; \ + ffi_ref->keys = keys; \ + ffi_ref->values = values; \ + return ffi_ref; \ +} \ ++ (TYPE *)ffi_to_opt:(NSDictionary * _Nullable)obj { \ + return obj ? [self ffi_to:obj] : nil; \ +} \ +- (NSDictionary *)ffi_from:(TYPE *)ffi_ref { \ + uintptr_t count = ffi_ref->count; \ + NSMutableDictionary *obj = [NSMutableDictionary dictionaryWithCapacity:count]; \ + for (int i = 0; i < count; i++) { \ + [obj setObject:ValueFrom forKey:KeyFrom]; \ + } \ + return obj; \ +} \ ++ (NSDictionary * _Nullable)ffi_from_opt:(TYPE *)ffi_ref { \ + return ffi_ref ? [self ffi_from:ffi_ref] : nil; \ +} \ ++ (void)ffi_destroy:(TYPE *)ffi_ref { \ + if (!ffi_ref) return; \ + if (ffi_ref->count > 0) { \ + for (int i = 0; i < ffi_ref->count; i++) { \ + KeyDtor\ + ValueDtor\ + } \ + free(ffi_ref->keys); \ + free(ffi_ref->values); \ + } \ + free(ffi_ref); \ +} \ +@end \ +@implementation NSDictionary (Bindings_##TYPE) \ ++ (TYPE *)ffi_ctor:(NSDictionary *)obj { \ + NSUInteger i = 0, count = [obj count]; \ + KeyTypeC *keys = malloc(count * sizeof(KeyTypeC)); \ + ValueTypeC *values = malloc(count * sizeof(ValueTypeC)); \ + for (id key in obj) { \ + keys[i] = KeyTo; \ + values[i] = ValueTo; \ + i++; \ + } \ + return ##TYPE_ctor(count, keys, values); \ +} \ ++ (void)ffi_dtor:(TYPE *)ffi_ref { \ + ##TYPE_destroy(ffi_ref); \ +} \ +@end @interface NSDictionary (Dash) - (NSDictionary *)transformToDictionaryOfHexStringsToHexStrings; @@ -26,4 +88,6 @@ NS_ASSUME_NONNULL_BEGIN @end +//FFIMapConversion(<#TYPE#>, <#KeyTypeC#>, <#KeyTypeObjC#>, <#KeyCtor#>, <#KeyDtor#>, <#KeyFrom#>, <#KeyTo#>, <#ValueTypeC#>, <#ValueTypeObjC#>, <#ValueCtor#>, <#ValueDtor#>, <#ValueFrom#>, <#ValueTo#>) + NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Categories/NSIndexPath+FFI.h b/DashSync/shared/Categories/NSIndexPath+FFI.h index a249373a6..a6fa176c1 100644 --- a/DashSync/shared/Categories/NSIndexPath+FFI.h +++ b/DashSync/shared/Categories/NSIndexPath+FFI.h @@ -20,11 +20,11 @@ NS_ASSUME_NONNULL_BEGIN -@interface NSIndexPath (FFI) - -- (IndexPathData *)ffi_malloc; -+ (void)ffi_free:(IndexPathData *)entry; +@interface NSIndexPath (Vec_u32) ++ (NSIndexPath *)ffi_from:(Vec_u32 *)ffi_ref; ++ (Vec_u32 *)ffi_to:(NSIndexPath *)obj; ++ (void)ffi_destroy:(Vec_u32 *)ffi_ref; @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Categories/NSIndexPath+FFI.m b/DashSync/shared/Categories/NSIndexPath+FFI.m index 9580cc859..6c5dba2d9 100644 --- a/DashSync/shared/Categories/NSIndexPath+FFI.m +++ b/DashSync/shared/Categories/NSIndexPath+FFI.m @@ -17,22 +17,49 @@ #import "NSIndexPath+FFI.h" -@implementation NSIndexPath (FFI) +//@implementation NSIndexPath (FFI) +// +//- (Vec *)ffi_malloc { +// DIndexPathU32 *obj = malloc(sizeof(dash_spv_crypto_keys_key_IndexPathU32)); +// NSUInteger *indexes = calloc(self.length, sizeof(NSUInteger)); +// [self getIndexes:indexes]; +// obj->indexes = Vec_u32_ctor(self.length, (uint32_t *) indexes); +//// obj->len = self.length; +// return obj; +//} +// +//+ (void)ffi_free:(DIndexPathU32 *)entry { +// if (entry->indexes > 0) { +// free((void *) entry->indexes); +// } +// if (entry->hardened > 0) { +// free((void *) entry->hardened); +// } +// free(entry); +//// if (entry->len > 0) { +//// free((void *) entry->indexes); +//// } +//// free(entry); +//} +// +//@end -- (IndexPathData *)ffi_malloc { - IndexPathData *obj = malloc(sizeof(IndexPathData)); - NSUInteger *indexes = calloc(self.length, sizeof(NSUInteger)); - [self getIndexes:indexes]; - obj->indexes = indexes; - obj->len = self.length; - return obj; +@implementation NSIndexPath (Vec_u32) + ++ (NSIndexPath *)ffi_from:(Vec_u32 *)ffi_ref { + return [NSIndexPath indexPathWithIndexes:(NSUInteger *) ffi_ref->values length:ffi_ref->count]; } -+ (void)ffi_free:(IndexPathData *)entry { - if (entry->len > 0) { - free((void *) entry->indexes); ++ (Vec_u32 *)ffi_to:(NSIndexPath *)obj { + NSUInteger length = obj.length; + uint32_t *indexes = malloc(sizeof(uint32_t) * length); + for (NSUInteger i = 0; i < length; i++) { + indexes[i] = (uint32_t)[obj indexAtPosition:i]; } - free(entry); + return Vec_u32_ctor(length, indexes); + +} ++ (void)ffi_destroy:(Vec_u32 *)ffi_ref { + Vec_u32_destroy(ffi_ref); } - @end diff --git a/DashSync/shared/Categories/NSMutableArray+Dash.h b/DashSync/shared/Categories/NSMutableArray+Dash.h index 63fbd66df..818145b8c 100644 --- a/DashSync/shared/Categories/NSMutableArray+Dash.h +++ b/DashSync/shared/Categories/NSMutableArray+Dash.h @@ -15,6 +15,7 @@ // limitations under the License. // + #import NS_ASSUME_NONNULL_BEGIN diff --git a/DashSync/shared/Categories/NSString+Bitcoin.m b/DashSync/shared/Categories/NSString+Bitcoin.m index b48e2f8a2..bd178cc65 100644 --- a/DashSync/shared/Categories/NSString+Bitcoin.m +++ b/DashSync/shared/Categories/NSString+Bitcoin.m @@ -26,7 +26,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#import "DSChain.h" +#import "DSChain+Params.h" #import "NSData+Dash.h" #import "NSMutableData+Dash.h" #import "NSString+Bitcoin.h" diff --git a/DashSync/shared/Categories/NSString+Dash.m b/DashSync/shared/Categories/NSString+Dash.m index a9dd9e8cd..efb8af32b 100644 --- a/DashSync/shared/Categories/NSString+Dash.m +++ b/DashSync/shared/Categories/NSString+Dash.m @@ -26,7 +26,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#import "DSChain.h" +#import "DSChain+Params.h" #import "DSDerivationPath.h" #import "DSPriceManager.h" #import "NSData+DSHash.h" diff --git a/DashSync/shared/DSDashSharedCore.h b/DashSync/shared/DSDashSharedCore.h new file mode 100644 index 000000000..f81a5f25a --- /dev/null +++ b/DashSync/shared/DSDashSharedCore.h @@ -0,0 +1,49 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "dash_shared_core.h" +#import "DSChain.h" + +NS_ASSUME_NONNULL_BEGIN + +#define DArcProcessor std_sync_Arc_dash_spv_masternode_processor_processing_processor_MasternodeProcessor +#define DArcCache std_sync_Arc_dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache +#define DArcPlatformSDK std_sync_Arc_dash_spv_platform_PlatformSDK +#define DArcIdentitiesManager std_sync_Arc_dash_spv_platform_identity_manager_IdentitiesManager +#define DArcContractsManager std_sync_Arc_dash_spv_platform_contract_manager_ContractsManager +#define DArcDocumentsManager std_sync_Arc_dash_spv_platform_document_manager_DocumentsManager + +@class DSChain; + +@interface DSDashSharedCore : NSObject + +- (instancetype)initOnChain:(DSChain *)chain; + +- (DArcProcessor *)processor; +- (DArcCache *)cache; +- (DArcPlatformSDK *)platform; +- (Runtime *)runtime; +- (DArcIdentitiesManager *)identitiesManager; +- (DArcContractsManager *)contractsManager; +- (DArcDocumentsManager *)documentsManager; + +@property (nonatomic, readonly) BOOL hasMasternodeListCurrentlyBeingSaved; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/DSDashSharedCore.m b/DashSync/shared/DSDashSharedCore.m new file mode 100644 index 000000000..1995838c8 --- /dev/null +++ b/DashSync/shared/DSDashSharedCore.m @@ -0,0 +1,547 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DSBlock.h" +#import "DSChain+Params.h" +#import "DSChain+Protected.h" +#import "DSChainLock.h" +#import "DSChainManager+Protected.h" +#import "DSDashSharedCore.h" +#import "DSKeyManager.h" +#import "DSMasternodeListService.h" +#import "DSMasternodeListDiffService.h" +#import "DSQuorumRotationService.h" +#import "DSMasternodeManager+Protected.h" +#import "NSArray+Dash.h" + +@class DSPeer; + +#define AS_OBJC(context) ((__bridge DSDashSharedCore *)(context)) +#define AS_RUST(context) ((__bridge void *)(context)) + +#define GetDataContract Fn_ARGS_std_os_raw_c_void_platform_value_types_identifier_Identifier_RTRN_Result_ok_Option_std_sync_Arc_dpp_data_contract_DataContract_err_drive_proof_verifier_error_ContextProviderError + +#define SignerCallback Fn_ARGS_std_os_raw_c_void_dpp_identity_identity_public_key_IdentityPublicKey_Vec_u8_RTRN_Result_ok_platform_value_types_binary_data_BinaryData_err_dpp_errors_protocol_error_ProtocolError +#define GetPlatformActivationHeight Fn_ARGS_std_os_raw_c_void_RTRN_Result_ok_dpp_prelude_CoreBlockHeight_err_drive_proof_verifier_error_ContextProviderError + +#define CanSign Fn_ARGS_std_os_raw_c_void_dpp_identity_identity_public_key_IdentityPublicKey_RTRN_bool + +#define GetBlockHeightByHash Fn_ARGS_std_os_raw_c_void_Arr_u8_32_RTRN_u32 +#define GetBlockHashByHeight Fn_ARGS_std_os_raw_c_void_u32_RTRN_Arr_u8_32 + +#define MerkleBlockByBlockHash Fn_ARGS_std_os_raw_c_void_Arr_u8_32_RTRN_Result_ok_dash_spv_masternode_processor_common_block_MBlock_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError +#define LastMerkleBlockByBlockHashForPeer Fn_ARGS_std_os_raw_c_void_Arr_u8_32_std_os_raw_c_void_RTRN_Result_ok_dash_spv_masternode_processor_common_block_MBlock_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError + + +#define AddInsight Fn_ARGS_std_os_raw_c_void_Arr_u8_32_RTRN_ + +#define HasPersistInRetrieval Fn_ARGS_std_os_raw_c_void_Arr_u8_32_RTRN_bool +#define GetBlockHeightOrLastTerminal Fn_ARGS_std_os_raw_c_void_u32_RTRN_Result_ok_dash_spv_masternode_processor_common_block_Block_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError + +#define FnMaybeCLSignature Fn_ARGS_std_os_raw_c_void_Arr_u8_32_RTRN_Result_ok_u8_arr_96_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError +#define DMaybeCLSignature Result_ok_u8_arr_96_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError +#define DMaybeCLSignatureCtor(ok, err) Result_ok_u8_arr_96_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError_ctor(ok, err) +#define LoadMasternodeList Fn_ARGS_std_os_raw_c_void_Arr_u8_32_RTRN_Result_ok_std_sync_Arc_dash_spv_masternode_processor_models_masternode_list_MasternodeList_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError +#define SaveMasternodeList Fn_ARGS_std_os_raw_c_void_std_sync_Arc_dash_spv_masternode_processor_models_masternode_list_MasternodeList_std_collections_Map_keys_u8_arr_32_values_dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_RTRN_Result_ok_bool_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError +#define LoadLLMQSnapshot Fn_ARGS_std_os_raw_c_void_Arr_u8_32_RTRN_Result_ok_dash_spv_masternode_processor_models_snapshot_LLMQSnapshot_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError +#define SaveLLMQSnapshot Fn_ARGS_std_os_raw_c_void_Arr_u8_32_dash_spv_masternode_processor_models_snapshot_LLMQSnapshot_RTRN_Result_ok_bool_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError + +#define UpdateMasternodesAddressUsage Fn_ARGS_std_os_raw_c_void_Vec_dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_RTRN_ + + +@interface DSDashSharedCore () + +@property (nonatomic) DSChain *chain; +@property (nonatomic, assign) DashSPVCore *core; +@property (nonatomic, strong) NSMutableDictionary *devnetSharedCoreDictionary; +@property (atomic, assign) uint32_t masternodeListCurrentlyBeingSavedCount; + +@end + +@implementation DSDashSharedCore + ++ (instancetype)sharedCore { + static DSDashSharedCore *_sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _sharedInstance = [[self alloc] init]; + }); + return _sharedInstance; +} + +- (DSDashSharedCore *)mainnetSharedCore { + static id _core = nil; + static dispatch_once_t mainnetToken = 0; + dispatch_once(&mainnetToken, ^{ + DSChain *mainnet = [DSChain mainnet]; + _core = [[DSDashSharedCore alloc] initOnChain:mainnet]; + mainnet.shareCore = _core; + }); + return _core; +} + +- (DSDashSharedCore *)testnetSharedCore { + static id core = nil; + static dispatch_once_t testnetToken = 0; + + dispatch_once(&testnetToken, ^{ + DSChain *testnet = [DSChain testnet]; + core = [[DSDashSharedCore alloc] initOnChain:testnet]; + testnet.shareCore = core; + }); + return core; +} + +- (instancetype)devnetSharedCore:(DSChain *)chain { + static dispatch_once_t devnetToken = 0; + dispatch_once(&devnetToken, ^{ + self.devnetSharedCoreDictionary = [NSMutableDictionary dictionary]; + }); + NSValue *genesisValue = uint256_obj(chain.genesisHash); + DSDashSharedCore *core = nil; + @synchronized(self) { + if (![self.devnetSharedCoreDictionary objectForKey:genesisValue]) { + core = [[DSDashSharedCore alloc] initOnChain:chain]; + chain.shareCore = core; + } else { + core = [self.devnetSharedCoreDictionary objectForKey:genesisValue]; + } + } + return core; +} +- (DArcProcessor *)processor { + return dash_spv_apple_bindings_DashSPVCore_processor(self.core); +} +- (DArcCache *)cache { + return dash_spv_apple_bindings_DashSPVCore_cache(self.core); +} +- (DArcPlatformSDK *)platform { + return dash_spv_apple_bindings_DashSPVCore_platform(self.core); +} +- (Runtime *)runtime { + return dash_spv_apple_bindings_DashSPVCore_runtime(self.core); +} + +- (DArcIdentitiesManager *)identitiesManager { + return dash_spv_platform_PlatformSDK_identity_manager(self.platform->obj); +} +- (DArcContractsManager *)contractsManager { + return dash_spv_platform_PlatformSDK_contract_manager(self.platform->obj); +} +- (DArcDocumentsManager *)documentsManager { + return dash_spv_platform_PlatformSDK_doc_manager(self.platform->obj); +} + +- (instancetype)initOnChain:(DSChain *)chain { + if (!(self = [super init])) return nil; + self.chain = chain; + _masternodeListCurrentlyBeingSavedCount = 0; + + const void *context = AS_RUST(self); + + GetDataContract get_data_contract = { + .caller = &get_data_contract_caller, + .destructor = &get_data_contract_dtor + }; + SignerCallback callback_signer = { + .caller = &callback_signer_caller, + .destructor = &callback_signer_dtor + }; + GetPlatformActivationHeight get_platform_activation_height = { + .caller = &get_platform_activation_height_caller, + .destructor = &get_platform_activation_height_dtor + }; + CanSign callback_can_sign = { + .caller = &callback_can_sign_caller, + .destructor = &callback_can_sign_dtor + }; + + GetBlockHeightByHash get_block_height_by_hash = { + .caller = &get_block_height_by_hash_caller, + .destructor = &get_block_height_by_hash_dtor + }; + + GetBlockHashByHeight get_block_hash_by_height = { + .caller = &get_block_hash_by_height_caller, + .destructor = &get_block_hash_by_height_dtor + }; + MerkleBlockByBlockHash block_by_block_hash = { + .caller = &block_by_block_hash_caller, + .destructor = &block_by_block_hash_dtor + }; + LastMerkleBlockByBlockHashForPeer last_block_for_block_hash = { + .caller = &last_block_by_block_hash_caller, + .destructor = &last_block_by_block_hash_dtor + }; + + AddInsight add_insight = { + .caller = &add_insight_caller + }; + + GetBlockHeightOrLastTerminal get_block_by_height_or_last_terminal = { + .caller = &get_block_by_height_or_last_terminal_caller, + .destructor = &get_block_by_height_or_last_terminal_dtor + }; + + LoadMasternodeList load_masternode_list_from_db = { + .caller = &load_masternode_list_from_db_caller, + .destructor = &load_masternode_list_from_db_dtor + }; + + SaveMasternodeList save_masternode_list_into_db = { + .caller = &save_masternode_list_into_db_caller, + .destructor = &save_masternode_list_into_db_destructor + }; + + LoadLLMQSnapshot load_llmq_snapshot_from_db = { + .caller = &load_llmq_snapshot_from_db_caller, + .destructor = &load_llmq_snapshot_from_db_dtor + }; + SaveLLMQSnapshot save_llmq_snapshot_into_db = { + .caller = &save_llmq_snapshot_into_db_caller, + .destructor = &save_llmq_snapshot_into_db_dtor + }; + + UpdateMasternodesAddressUsage update_address_usage_of_masternodes = { + .caller = &update_address_usage_of_masternodes_caller + }; + Fn_ARGS_std_os_raw_c_void_bool_Arr_u8_32_Arr_u8_32_RTRN_bool remove_request_in_retrieval = { + .caller = &remove_request_in_retrieval_caller, + .destructor = &remove_request_in_retrieval_dtor + }; + + Fn_ARGS_std_os_raw_c_void_bool_std_os_raw_c_void_RTRN_ issue_with_masternode_list_from_peer = { + .caller = &issue_with_masternode_list_from_peer_caller + }; + FnMaybeCLSignature get_cl_signature_by_block_hash = { + .caller = &get_cl_signature_by_block_hash_caller, + .destructor = &get_cl_signature_by_block_hash_dtor + }; + + Fn_ARGS_std_os_raw_c_void_bool_RTRN_ dequeue_masternode_list = { + .caller = &dequeue_masternode_list_caller, + }; + Fn_ARGS_std_os_raw_c_void_dash_spv_masternode_processor_models_sync_state_SyncState_RTRN_ notify_sync_state = { + .caller = ¬ify_sync_state_caller, + }; + + NSArray *addresses = @[@"127.0.0.1"]; + switch (chain.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + addresses = @[ + @"149.28.241.190", @"216.238.75.46", @"134.255.182.186", @"66.245.196.52", @"178.157.91.186", @"157.66.81.162", @"213.199.34.250", @"157.90.238.161", @"5.182.33.231", @"185.198.234.68", @"37.60.236.212", @"207.244.247.40", @"45.32.70.131", @"158.220.122.76", @"52.33.9.172", @"185.158.107.124", @"185.198.234.17", @"93.190.140.101", @"194.163.153.225", @"194.146.13.7", @"93.190.140.112", @"75.119.132.2", @"65.108.74.95", @"44.240.99.214", @"5.75.133.148", @"192.248.178.237", @"95.179.159.65", @"139.84.232.129", @"37.60.243.119", @"194.195.87.34", @"46.254.241.7", @"45.77.77.195", @"65.108.246.145", @"64.176.10.71", @"158.247.247.241", @"37.60.244.220", @"2.58.82.231", @"139.180.143.115", @"185.198.234.54", @"213.199.44.112", @"37.27.67.154", @"134.255.182.185", @"86.107.168.28", @"139.84.137.143", @"173.212.239.124", @"157.10.199.77", @"5.189.186.78", @"139.84.170.10", @"173.249.53.139", @"37.60.236.151", @"37.27.67.159", @"104.200.24.196", @"37.60.236.225", @"172.104.90.249", @"57.128.212.163", @"37.60.236.249", @"158.220.122.74", @"185.198.234.25", @"148.113.201.221", @"134.255.183.250", @"185.192.96.70", @"134.255.183.248", @"52.36.102.91", @"134.255.183.247", @"49.13.28.255", @"168.119.102.10", @"86.107.168.44", @"49.13.237.193", @"37.27.83.17", @"134.255.182.187", @"142.132.165.149", @"193.203.15.209", @"38.242.198.100", @"192.175.127.198", @"37.27.67.163", @"79.137.71.84", @"198.7.115.43", @"70.34.206.123", @"163.172.20.205", @"65.108.74.78", @"108.61.165.170", @"157.10.199.79", @"31.220.88.116", @"185.166.217.154", @"37.27.67.164", @"31.220.85.180", @"161.97.170.251", @"157.10.199.82", @"91.107.226.241", @"167.88.169.16", @"216.238.99.9", @"62.169.17.112", @"52.10.213.198", @"149.28.201.164", @"198.7.115.38", @"37.60.236.161", @"49.13.193.251", @"46.254.241.9", @"65.108.74.75", @"192.99.44.64", @"95.179.241.182", @"95.216.146.18", @"185.194.216.84", @"31.220.84.93", @"185.197.250.227", @"149.28.247.165", @"86.107.168.29", @"213.199.34.251", @"108.160.135.149", @"185.198.234.12", @"87.228.24.64", @"45.32.52.10", @"91.107.204.136", @"64.176.35.235", @"167.179.90.255", @"157.66.81.130", @"157.10.199.125", @"46.254.241.8", @"49.12.102.105", @"134.255.182.189", @"81.17.101.141", @"65.108.74.79", @"64.23.134.67", @"54.69.95.118", @"158.220.122.13", @"49.13.154.121", @"75.119.149.9", @"93.190.140.111", @"93.190.140.114", @"195.201.238.55", @"135.181.110.216", @"45.76.141.74", @"65.21.145.147", @"50.116.28.103", @"188.245.90.255", @"130.162.233.186", @"65.109.65.126", @"188.208.196.183", @"178.157.91.184", @"37.60.236.201", @"95.179.139.125", @"213.199.34.248", @"178.157.91.178", @"213.199.35.18", @"213.199.35.6", @"37.60.243.59", @"37.27.67.156", @"37.60.236.247", @"159.69.204.162", @"46.254.241.11", @"173.199.71.83", @"185.215.166.126", @"91.234.35.132", @"157.66.81.218", @"213.199.35.15", @"114.132.172.215", @"93.190.140.162", @"65.108.74.109" + ]; + + break; + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + addresses = @[@"35.165.50.126", @"52.42.202.128", @"52.12.176.90", @"44.233.44.95", @"35.167.145.149", @"52.34.144.50", @"44.240.98.102"]; + break; + case dash_spv_crypto_network_chain_type_ChainType_DevNet: + break; + + default: + break; + } + Vec_ *address_list = [NSArray ffi_to_vec:addresses]; + + self.core = dash_spv_apple_bindings_DashSPVCore_with_callbacks(chain.chainType, address_list, get_data_contract, get_platform_activation_height, callback_signer, callback_can_sign, get_block_height_by_hash, get_block_hash_by_height, get_block_by_height_or_last_terminal, block_by_block_hash, last_block_for_block_hash, add_insight, get_cl_signature_by_block_hash, load_masternode_list_from_db, save_masternode_list_into_db, load_llmq_snapshot_from_db, save_llmq_snapshot_into_db, update_address_usage_of_masternodes, remove_request_in_retrieval, issue_with_masternode_list_from_peer, notify_sync_state, dequeue_masternode_list, context); + return self; +} + + + +- (void)dealloc { +// DashSPVCore_ +// if (self.core) { +// dash_spv_apple_bindings_DashSPVCore_destroy(self.core); +// } +} + + +- (BOOL)hasMasternodeListCurrentlyBeingSaved { + return !!self.masternodeListCurrentlyBeingSavedCount; +} +- (DSBlock *)lastBlockForBlockHash:(UInt256)blockHash fromPeer:(DSPeer *)peer { + DSBlock *lastBlock = nil; + if ([self.chain heightForBlockHash:blockHash]) { + lastBlock = [[peer.chain terminalBlocks] objectForKey:uint256_obj(blockHash)]; + if (!lastBlock && [peer.chain allowInsightBlocksForVerification]) { + NSData *blockHashData = uint256_data(blockHash); + lastBlock = [[peer.chain insightVerifiedBlocksByHashDictionary] objectForKey:blockHashData]; + if (!lastBlock && peer.chain.isTestnet) { + //We can trust insight if on testnet + [self.chain blockUntilGetInsightForBlockHash:blockHash]; + lastBlock = [[peer.chain insightVerifiedBlocksByHashDictionary] objectForKey:blockHashData]; + } + } + } else { + lastBlock = (DSBlock *) [peer.chain recentTerminalBlockForBlockHash:blockHash]; + } + return lastBlock; +} + +MaybeDataContract *get_data_contract_caller(const void *context, DIdentifier *identitifier) { + return NULL; +} +void get_data_contract_dtor(MaybeDataContract *result) {} + +MaybeSignedData *callback_signer_caller(const void *context, DIdentityPublicKey *identity_public_key, BYTES *arr) { + return NULL; +} +void callback_signer_dtor(MaybeSignedData *result) {} + +MaybePlatformActivationHeight *get_platform_activation_height_caller(const void *context) { + return NULL; +} +void get_platform_activation_height_dtor(MaybePlatformActivationHeight *result) {} + +bool callback_can_sign_caller(const void *context, DIdentityPublicKey *identity_public_key) { + // TODO: impl + return TRUE; +} +void callback_can_sign_dtor(bool result) {} + +uint32_t get_block_height_by_hash_caller(const void *context, u256 *block_hash) { + DSDashSharedCore *core = AS_OBJC(context); + UInt256 blockHash = u256_cast(block_hash); + uint32_t height = [core.chain heightForBlockHash:blockHash]; + if (height == UINT32_MAX && core.chain.allowInsightBlocksForVerification) { + [core.chain blockUntilGetInsightForBlockHash:blockHash]; + height = [[core.chain insightVerifiedBlocksByHashDictionary] objectForKey:NSDataFromPtr(block_hash)].height; + } + u256_dtor(block_hash); +// DSLog(@"[SDK] get_block_height_by_hash_caller: %@ = %u", uint256_hex(blockHash), height); + return height; +} +void get_block_height_by_hash_dtor(uint32_t result) {} + +u256 *get_block_hash_by_height_caller(const void *context, uint32_t block_height) { + DSDashSharedCore *core = AS_OBJC(context); + DSBlock *block = NULL; + @synchronized (context) { + block = (DSBlock *) [core.chain blockAtHeight:block_height]; + if (!block && core.chain.allowInsightBlocksForVerification) + block = [core.chain blockUntilGetInsightForBlockHeight:block_height]; + } + // DSLog(@"[SDK] get_block_hash_by_height_caller: %u = %@", block_height, uint256_hex(block.blockHash)); + UInt256 blockHash = block ? block.blockHash : UINT256_ZERO; + return u256_ctor_u(blockHash); +} + +void get_block_hash_by_height_dtor(u256 *result) {} + +DMaybeMBlock *block_by_block_hash_caller(const void *context, u256 *block_hash) { + DSDashSharedCore *core = AS_OBJC(context); + UInt256 blockHash = u256_cast(block_hash); + DMBlock *ok = NULL; + DCoreProviderError *err = NULL; + @synchronized (context) { + DSBlock *block = (DSBlock *) [core.chain blockForBlockHash:blockHash]; + DSLog(@"[SDK] block_by_hash_caller: %@ [%d] merkle_root: %@", uint256_hex(blockHash), block.height, uint256_hex(block.merkleRoot)); + if (block) { + ok = DMBlockCtor(block.height, u256_ctor_u(block.blockHash), u256_ctor_u(block.merkleRoot)); + } else { + err = DCoreProviderErrorNullResultCtor(); + } + } + u256_dtor(block_hash); + return DMaybeMBlockCtor(ok, err); +} +void block_by_block_hash_dtor(DMaybeMBlock *result) {} + +DMaybeMBlock *last_block_by_block_hash_caller(const void *context, u256 *block_hash, const void *peer_context) { + DSDashSharedCore *core = AS_OBJC(context); + DSPeer *peer = ((__bridge DSPeer *)(peer_context)); + UInt256 blockHash = u256_cast(block_hash); + u256_dtor(block_hash); + DMBlock *ok = NULL; + DCoreProviderError *err = NULL; + @synchronized (context) { + DSBlock *lastBlock = [core lastBlockForBlockHash:blockHash fromPeer:peer]; + DSLog(@"[SDK] last_block_by_hash_caller: %@ = %@", uint256_hex(blockHash), lastBlock); + if (lastBlock) { + ok = DMBlockCtor(lastBlock.height, u256_ctor_u(lastBlock.blockHash), u256_ctor_u(lastBlock.merkleRoot)); + } else { + err = DCoreProviderErrorNullResultCtor(); + } + } + return DMaybeMBlockCtor(ok, err); +} +void last_block_by_block_hash_dtor(DMaybeMBlock *result) {} + +void add_insight_caller(const void *context, u256* block_hash) { + UInt256 blockHash = u256_cast(block_hash); + @synchronized (context) { + [AS_OBJC(context).chain blockUntilGetInsightForBlockHash:blockHash]; + } + u256_dtor(block_hash); +} + +DMaybeBlock *get_block_by_height_or_last_terminal_caller(const void *context, uint32_t block_height) { + DSDashSharedCore *core = AS_OBJC(context); + DSBlock *b = (DSBlock *) [core.chain blockAtHeightOrLastTerminal:block_height]; + DBlock *ok = b ? DBlockCtor(b.height, u256_ctor_u(b.blockHash)) : NULL; + DCoreProviderError *err = b ? NULL : DCoreProviderErrorNullResultCtor(); + return DMaybeBlockCtor(ok, err); +} +void get_block_by_height_or_last_terminal_dtor(DMaybeBlock *result) { + DMaybeBlockDtor(result); +} +DMaybeCLSignature *get_cl_signature_by_block_hash_caller(const void *context, u256 *block_hash) { + DSDashSharedCore *core = AS_OBJC(context); + UInt256 blockHash = u256_cast(block_hash); + u256_dtor(block_hash); + DSChainLock *chainLock = [core.chain.chainManager chainLockForBlockHash:blockHash]; + return chainLock ? DMaybeCLSignatureCtor(u768_ctor_u(chainLock.signature), NULL) : DMaybeCLSignatureCtor(NULL, DCoreProviderErrorNullResultCtor()); +} +void get_cl_signature_by_block_hash_dtor(DMaybeCLSignature *result) {} + + +DMaybeArcMasternodeList *load_masternode_list_from_db_caller(const void *context, u256 *block_hash) { + DSDashSharedCore *core = AS_OBJC(context); + NSData *blockHashData = [DSKeyManager NSDataFromArr_u8_32:block_hash]; + DArcMasternodeList *list = [core.chain.masternodeManager.store loadMasternodeListAtBlockHash:blockHashData withBlockHeightLookup:^uint32_t(UInt256 blockHash) { + return [core.chain heightForBlockHash:blockHash]; + }]; + DSLog(@"load_masternode_list_from_db_caller (%@) %p", blockHashData.hexString, list); + return Result_ok_std_sync_Arc_dash_spv_masternode_processor_models_masternode_list_MasternodeList_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError_ctor(list, list ? NULL : DCoreProviderErrorNullResultCtor()); +} +void load_masternode_list_from_db_dtor(DMaybeArcMasternodeList *result) {} + +MaybeBool *save_masternode_list_into_db_caller(const void *context, DArcMasternodeList *masternode_list, DMasternodeEntryMap *modified_masternodes) { + DSDashSharedCore *core = AS_OBJC(context); + DSChain *chain = core.chain; + DSMasternodeManager *masternodeManager = chain.masternodeManager; + uintptr_t count = DStoredMasternodeListsCount(core.cache->obj); + uint32_t last_block_height = DLastMasternodeListBlockHeight(core.processor->obj); + [chain.chainManager notifyMasternodeSyncStateChange:last_block_height storedCount:count]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSMasternodeListDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: chain}]; + [[NSNotificationCenter defaultCenter] postNotificationName:DSQuorumListDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: chain}]; + }); + + dispatch_group_enter(core.chain.masternodeManager.store.savingGroup); + //We will want to create unknown blocks if they came from insight + BOOL createUnknownBlocks = chain.allowInsightBlocksForVerification; + core.masternodeListCurrentlyBeingSavedCount++; + //This will create a queue for masternodes to be saved without blocking the networking queue + DSLog(@"[%@] save_masternode_list_into_db --> %d", chain.name, masternode_list->obj->known_height); + NSError *error = [DSMasternodeListStore saveMasternodeList:masternode_list + toChain:chain + havingModifiedMasternodes:modified_masternodes + createUnknownBlocks:createUnknownBlocks + inContext:chain.chainManagedObjectContext]; + core.masternodeListCurrentlyBeingSavedCount--; + dispatch_group_leave(core.chain.masternodeManager.store.savingGroup); + BOOL success = !error; + DCoreProviderError *provider_err = NULL; + if (error) { + uintptr_t mn_list_count = dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_mn_list_retrieval_queue_count(core.cache->obj); + uintptr_t qr_info_count = dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_qr_info_retrieval_queue_count(core.cache->obj); + BOOL isEmptyQueue = !(mn_list_count + qr_info_count); + DSLog(@"[%@] Finished saving MNL with error: %@", chain.name, error.description); + if (!isEmptyQueue) { + [masternodeManager wipeMasternodeInfo]; + if (masternodeManager.isSyncing) + dispatch_async(chain.networkingQueue, ^{ [masternodeManager getRecentMasternodeList]; }); + } + provider_err = DCoreProviderErrorNullResultCtor(); + } + DSLog(@"•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••"); + DSLog(@"[%@] save_masternode_list_into_db <-- %d = %d", chain.name, masternode_list->obj->known_height, success); + DSLog(@"•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••"); +// dash_spv_masternode_processor_models_masternode_list_MasternodeList_print_description(masternode_list->obj); +// DSLog(@"•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••"); + return Result_ok_bool_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError_ctor(&success, provider_err); +} +void save_masternode_list_into_db_destructor(MaybeBool *result) {} + +MaybeLLMQSnapshot *load_llmq_snapshot_from_db_caller(const void *context, u256 *block_hash) { + return NULL; +} + +void load_llmq_snapshot_from_db_dtor(MaybeLLMQSnapshot *result) {} + +MaybeBool *save_llmq_snapshot_into_db_caller(const void *context, u256 *block_hash, DLLMQSnapshot *snapshot) { + DSDashSharedCore *core = AS_OBJC(context); + NSError *err = [core.chain.masternodeManager.store saveQuorumSnapshot:snapshot forBlockHash:block_hash]; + BOOL success = !err; + DCoreProviderError *provider_err = err ? DCoreProviderErrorNullResultCtor() : NULL; + u256_dtor(block_hash); + dash_spv_masternode_processor_models_snapshot_LLMQSnapshot_destroy(snapshot); + return Result_ok_bool_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError_ctor(&success, provider_err); +} +void save_llmq_snapshot_into_db_dtor(MaybeBool *result) {} + +void update_address_usage_of_masternodes_caller(const void *context, DMasternodeEntryList *masternodes) { + DSDashSharedCore *core = AS_OBJC(context); + [core.chain updateAddressUsageOfSimplifiedMasternodeEntries:masternodes]; + DMasternodeEntryListDtor(masternodes); +} + +bool remove_request_in_retrieval_caller(const void *context, bool is_dip24, u256 *base_block_hash, u256 *block_hash) { + DSDashSharedCore *core = AS_OBJC(context); + DSMasternodeListService *service = is_dip24 ? core.chain.masternodeManager.quorumRotationService : core.chain.masternodeManager.masternodeListDiffService; + BOOL hasRemovedFromRetrieval = [service removeRequestInRetrievalForBaseBlockHash:u256_cast(base_block_hash) blockHash:u256_cast(block_hash)]; + u256_dtor(base_block_hash); + u256_dtor(block_hash); + return hasRemovedFromRetrieval; +} +void remove_request_in_retrieval_dtor(bool result) {} + +void issue_with_masternode_list_from_peer_caller(const void *context, bool is_dip24, const void *peer_context) { + DSDashSharedCore *core = AS_OBJC(context); + DSPeer *peer = ((__bridge DSPeer *)(peer_context)); + if (is_dip24) { + [core.chain.masternodeManager.quorumRotationService issueWithMasternodeListFromPeer:peer]; + } else { + [core.chain.masternodeManager.masternodeListDiffService issueWithMasternodeListFromPeer:peer]; + } +} + +void notify_sync_state_caller(const void *context, dash_spv_masternode_processor_models_sync_state_SyncState *state) { + DSDashSharedCore *core = AS_OBJC(context); + DSMasternodeListSyncState *syncInfo = core.chain.chainManager.syncState.masternodeListSyncInfo; + @synchronized (syncInfo) { + switch (state->tag) { + case dash_spv_masternode_processor_models_sync_state_SyncState_QueueChanged: + syncInfo.retrievalQueueCount = (uint32_t) state->queue_changed.count; + syncInfo.retrievalQueueMaxAmount = (uint32_t) state->queue_changed.max_amount; + DSLog(@"[%@] Masternode list queue updated: %lu/%lu", core.chain.name, state->queue_changed.count, state->queue_changed.max_amount); + break; + case dash_spv_masternode_processor_models_sync_state_SyncState_StoreChanged: + syncInfo.storedCount = (uint32_t) state->store_changed.count; + syncInfo.lastBlockHeight = state->store_changed.last_block_height; + DSLog(@"[%@] Masternode list store updated: %lu/%u", core.chain.name, state->store_changed.count, state->store_changed.last_block_height); + break; + default: + break; + } + [core.chain.chainManager notifySyncStateChanged]; + } +} +void dequeue_masternode_list_caller(const void *context, bool is_dip24) { + DSDashSharedCore *core = AS_OBJC(context); + if (is_dip24) { + [core.chain.masternodeManager.masternodeListDiffService dequeueMasternodeListRequest]; + } else { + [core.chain.masternodeManager.quorumRotationService dequeueMasternodeListRequest]; + } +} + + +@end diff --git a/DashSync/shared/DashSync.h b/DashSync/shared/DashSync.h index 7f28d2347..1b4a789c5 100644 --- a/DashSync/shared/DashSync.h +++ b/DashSync/shared/DashSync.h @@ -9,15 +9,19 @@ #import "dash_shared_core.h" #import "DSError.h" -#import "DSBlockchainIdentity.h" +#import "DSIdentity.h" #import "DSChain.h" +#import "DSChain+Identity.h" +#import "DSChain+Params.h" +#import "DSChain+Wallet.h" +#import "DSChain+Transaction.h" #import "DSCheckpoint.h" #import "DSEnvironment.h" #import "DSPeerManager.h" #import "DSReachabilityManager.h" #import "DSAuthenticationKeysDerivationPath.h" -#import "DSCreditFundingDerivationPath.h" +#import "DSAssetLockDerivationPath.h" #import "DSDerivationPath.h" #import "DSDerivationPathFactory.h" #import "DSFundsDerivationPath.h" @@ -27,9 +31,12 @@ #import "DSSparseMerkleTree.h" -#import "DSBlockchainIdentity.h" -#import "DSBlockchainInvitation.h" -#import "DSCreditFundingTransaction.h" +#import "DSIdentity.h" +#import "DSIdentity+ContactRequest.h" +#import "DSIdentity+Friendship.h" +#import "DSIdentity+Profile.h" +#import "DSIdentity+Username.h" +#import "DSInvitation.h" #import "DSAccount.h" #import "DSAuthenticationManager.h" @@ -59,6 +66,9 @@ #import "DSTransactionManager.h" #import "DSVersionManager.h" #import "DSWallet.h" +#import "DSWallet+Identity.h" +#import "DSWallet+Invitation.h" + #import "NSMutableData+Dash.h" #import "NSString+Dash.h" @@ -109,12 +119,12 @@ #import "DSTransactionInput.h" #import "DSTransactionOutput.h" -#import "DSBlockchainIdentityCloseTransition.h" -#import "DSBlockchainIdentityRegistrationTransition.h" -#import "DSBlockchainIdentityTopupTransition.h" -#import "DSBlockchainIdentityUpdateTransition.h" +#import "DSIdentityCloseTransition.h" +#import "DSIdentityRegistrationTransition.h" +#import "DSIdentityTopupTransition.h" +#import "DSIdentityUpdateTransition.h" -#import "DSBlockchainIdentity.h" +#import "DSIdentity.h" #import "DSBlockchainIdentityEntity+CoreDataClass.h" #import "DSBlockchainIdentityKeyPathEntity+CoreDataClass.h" #import "DSBlockchainIdentityUsernameEntity+CoreDataClass.h" @@ -123,7 +133,6 @@ #import "DSMasternodeListEntity+CoreDataProperties.h" #import "DSPotentialContact.h" #import "DSPotentialOneWayFriendship.h" -#import "DSQuorumEntry.h" #import "DSQuorumEntryEntity+CoreDataProperties.h" #import "DSNetworking.h" diff --git a/DashSync/shared/DashSync.m b/DashSync/shared/DashSync.m index 25d8f807b..498a6dbe9 100644 --- a/DashSync/shared/DashSync.m +++ b/DashSync/shared/DashSync.m @@ -7,7 +7,9 @@ // #import "DashSync.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" +#import "DSChain+Wallet.h" #import "DSChainEntity+CoreDataClass.h" #import "DSChainManager+Protected.h" #import "DSDataController.h" diff --git a/DashSync/shared/DashSync.xcdatamodeld/.xccurrentversion b/DashSync/shared/DashSync.xcdatamodeld/.xccurrentversion index 9697cdadc..904683a5e 100644 --- a/DashSync/shared/DashSync.xcdatamodeld/.xccurrentversion +++ b/DashSync/shared/DashSync.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - DashSync 21.xcdatamodel + DashSync 22.xcdatamodel diff --git a/DashSync/shared/DashSync.xcdatamodeld/DashSync 21.xcdatamodel/contents b/DashSync/shared/DashSync.xcdatamodeld/DashSync 21.xcdatamodel/contents index e8661f27e..1cde448c8 100644 --- a/DashSync/shared/DashSync.xcdatamodeld/DashSync 21.xcdatamodel/contents +++ b/DashSync/shared/DashSync.xcdatamodeld/DashSync 21.xcdatamodel/contents @@ -1,5 +1,5 @@ - + diff --git a/DashSync/shared/DashSync.xcdatamodeld/DashSync 22.xcdatamodel/contents b/DashSync/shared/DashSync.xcdatamodeld/DashSync 22.xcdatamodel/contents new file mode 100644 index 000000000..e28851a0c --- /dev/null +++ b/DashSync/shared/DashSync.xcdatamodeld/DashSync 22.xcdatamodel/contents @@ -0,0 +1,528 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Dash.h b/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Dash.h index 627210fca..23af00c46 100644 --- a/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Dash.h +++ b/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Dash.h @@ -15,10 +15,13 @@ // limitations under the License. // +//#import "dash_shared_core.h" #import NS_ASSUME_NONNULL_BEGIN +#define ERROR_500(msg) [NSError errorWithCode:500 localizedDescriptionKey:msg] + @interface NSError (Dash) + (instancetype)errorWithCode:(NSInteger)code userInfo:(nullable NSDictionary *)dict; diff --git a/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Platform.h b/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Platform.h new file mode 100644 index 000000000..2a43b4ede --- /dev/null +++ b/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Platform.h @@ -0,0 +1,33 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2025 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "dash_shared_core.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NSError (dash_spv_platform_error_Error) ++ (NSError *)ffi_from_platform_error:(dash_spv_platform_error_Error *)ffi_ref; +@end + + +@interface NSError (dash_spv_crypto_keys_KeyError) ++ (NSError *)ffi_from_key_error:(dash_spv_crypto_keys_KeyError *)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 new file mode 100644 index 000000000..c1200d7ca --- /dev/null +++ b/DashSync/shared/Libraries/AdvancedOperations/Others/NSError+Platform.m @@ -0,0 +1,64 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2025 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "NSError+Dash.h" +#import "NSError+Platform.h" + +@implementation NSError (dash_spv_platform_error_Error) + ++ (nonnull NSError *)ffi_from_platform_error:(nonnull dash_spv_platform_error_Error *)ffi_ref { + switch (ffi_ref->tag) { + case dash_spv_platform_error_Error_KeyError: + return [NSError ffi_from_key_error:ffi_ref->key_error]; + case dash_spv_platform_error_Error_DashSDKError: + return [NSError errorWithCode:0 localizedDescriptionKey:[NSString stringWithCString:ffi_ref->dash_sdk_error encoding:NSUTF8StringEncoding]]; + case dash_spv_platform_error_Error_Any: + return [NSError errorWithCode:ffi_ref->any._0 localizedDescriptionKey:[NSString stringWithCString:ffi_ref->any._1 encoding:NSUTF8StringEncoding]]; + case dash_spv_platform_error_Error_MaxRetryExceeded: + return [NSError errorWithCode:0 localizedDescriptionKey:[NSString stringWithCString:ffi_ref->max_retry_exceeded encoding:NSUTF8StringEncoding]]; + } +} + +@end + + +@implementation NSError (dash_spv_crypto_keys_KeyError) + ++ (nonnull NSError *)ffi_from_key_error:(nonnull dash_spv_crypto_keys_KeyError *)ffi_ref { + switch (ffi_ref->tag) { + case dash_spv_crypto_keys_KeyError_WrongFormat: + return [NSError errorWithCode:0 localizedDescriptionKey:@"Wrong key format"]; + case dash_spv_crypto_keys_KeyError_WrongLength: + return [NSError errorWithCode:0 descriptionKey:[NSString stringWithFormat:DSLocalizedString(@"Wrong key length", nil), ffi_ref->wrong_length]]; + case dash_spv_crypto_keys_KeyError_Extended: + return [NSError errorWithCode:0 descriptionKey:[NSString stringWithFormat:DSLocalizedString(@"Key extended error", nil), ffi_ref->extended]]; + case dash_spv_crypto_keys_KeyError_UnableToDerive: + return [NSError errorWithCode:0 localizedDescriptionKey:@"Unable to derive key"]; + case dash_spv_crypto_keys_KeyError_DHKeyExchange: + return [NSError errorWithCode:0 localizedDescriptionKey:@"Unable to exchange key"]; + case dash_spv_crypto_keys_KeyError_CCCrypt: + return [NSError errorWithCode:0 descriptionKey:[NSString stringWithFormat:DSLocalizedString(@"CCrypt error", nil), ffi_ref->cc_crypt]]; + case dash_spv_crypto_keys_KeyError_EmptySecKey: + return [NSError errorWithCode:0 localizedDescriptionKey:@"Private key is empty"]; + case dash_spv_crypto_keys_KeyError_Product: + return [NSError errorWithCode:0 localizedDescriptionKey:@"Can't multiple keys"]; + case dash_spv_crypto_keys_KeyError_Any: + return [NSError errorWithCode:0 localizedDescriptionKey:[NSString stringWithCString:ffi_ref->any encoding:NSUTF8StringEncoding]]; + } +} + +@end diff --git a/DashSync/shared/Libraries/DSUInt256IndexPath.h b/DashSync/shared/Libraries/DSUInt256IndexPath.h index 186201057..d7edcbf34 100644 --- a/DashSync/shared/Libraries/DSUInt256IndexPath.h +++ b/DashSync/shared/Libraries/DSUInt256IndexPath.h @@ -16,9 +16,7 @@ // #import - #import "BigIntTypes.h" -#import "dash_shared_core.h" NS_ASSUME_NONNULL_BEGIN diff --git a/DashSync/shared/Models/Chain/DSBlock.m b/DashSync/shared/Models/Chain/DSBlock.m index f0400d26e..bd480480e 100644 --- a/DashSync/shared/Models/Chain/DSBlock.m +++ b/DashSync/shared/Models/Chain/DSBlock.m @@ -17,6 +17,7 @@ #import "DSBlock+Protected.h" #import "DSChain.h" +#import "DSChain+Params.h" #import "DSChainLock.h" #import "DSCheckpoint.h" #import "NSData+DSHash.h" diff --git a/DashSync/shared/Models/Chain/DSChain+Checkpoint.h b/DashSync/shared/Models/Chain/DSChain+Checkpoint.h new file mode 100644 index 000000000..095fddb57 --- /dev/null +++ b/DashSync/shared/Models/Chain/DSChain+Checkpoint.h @@ -0,0 +1,69 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "DSChain.h" +#import "DSChainCheckpoints.h" +#import "DSCheckpoint.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSChain (Checkpoint) + +@property (nonatomic, strong) NSMutableDictionary *checkpointsByHashDictionary; +@property (nonatomic, strong) NSMutableDictionary *checkpointsByHeightDictionary; +///*! @brief An array of known hard coded checkpoints for the chain. */ +@property (nonatomic, strong) NSArray *checkpoints; +@property (nonatomic, readonly) DSCheckpoint *terminalHeadersOverrideUseCheckpoint; +@property (nonatomic, readonly) DSCheckpoint *syncHeadersOverrideUseCheckpoint; +@property (nonatomic, readonly) DSCheckpoint *lastCheckpoint; + +- (BOOL)blockHeightHasCheckpoint:(uint32_t)blockHeight; + + +// MARK: - Checkpoints + +- (DSCheckpoint *_Nullable)lastTerminalCheckpoint; + +/*! @brief Returns the last checkpoint that has a masternode list attached to it. */ +- (DSCheckpoint *_Nullable)lastCheckpointHavingMasternodeList; + +/*! @brief Returns the checkpoint matching the parameter block hash, if one exists. */ +- (DSCheckpoint *_Nullable)checkpointForBlockHash:(UInt256)blockHash; + +/*! @brief Returns the checkpoint at a given block height, if one exists at that block height. */ +- (DSCheckpoint *_Nullable)checkpointForBlockHeight:(uint32_t)blockHeight; + +/*! @brief Returns the last checkpoint on or before the given height. */ +- (DSCheckpoint *)lastCheckpointOnOrBeforeHeight:(uint32_t)height; + +/*! @brief Returns the last checkpoint on or before the given timestamp. */ +- (DSCheckpoint *)lastCheckpointOnOrBeforeTimestamp:(NSTimeInterval)timestamp; + +/*! @brief When used this will change the checkpoint used for initial headers sync. This value is not persisted. */ +- (void)useCheckpointBeforeOrOnHeightForTerminalBlocksSync:(uint32_t)blockHeight; + +/*! @brief When used this will change the checkpoint used for main chain syncing. This value is not persisted. */ +- (void)useCheckpointBeforeOrOnHeightForSyncingChainBlocks:(uint32_t)blockHeight; + + +// MARK: Protected ++ (NSMutableArray *)createCheckpointsArrayFromCheckpoints:(checkpoint *)checkpoints count:(NSUInteger)checkpointCount; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Chain/DSChain+Checkpoint.m b/DashSync/shared/Models/Chain/DSChain+Checkpoint.m new file mode 100644 index 000000000..1d7191ea7 --- /dev/null +++ b/DashSync/shared/Models/Chain/DSChain+Checkpoint.m @@ -0,0 +1,147 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DSChain+Checkpoint.h" +#import "DSChain+Params.h" +#import "NSData+Dash.h" +#import "NSString+Bitcoin.h" +#import + +NSString const *checkpointsKey = @"checkpointsKey"; +NSString const *lastCheckpointKey = @"lastCheckpointKey"; +NSString const *checkpointsByHashDictionaryKey = @"checkpointsByHashDictionaryKey"; +NSString const *checkpointsByHeightDictionaryKey = @"checkpointsByHeightDictionaryKey"; +NSString const *terminalHeadersOverrideUseCheckpointKey = @"terminalHeadersOverrideUseCheckpointKey"; +NSString const *syncHeadersOverrideUseCheckpointKey = @"syncHeadersOverrideUseCheckpointKey"; + +@implementation DSChain (Checkpoint) + +// MARK: - Checkpoints +- (NSArray *)checkpoints { + return objc_getAssociatedObject(self, &checkpointsKey); +} +- (void)setCheckpoints:(NSArray *)checkpoints { + objc_setAssociatedObject(self, &checkpointsKey, checkpoints, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSMutableDictionary *)checkpointsByHashDictionary { + return objc_getAssociatedObject(self, &checkpointsByHashDictionaryKey); +} +- (void)setCheckpointsByHashDictionary:(NSMutableDictionary *)checkpointsByHashDictionary { + objc_setAssociatedObject(self, &checkpointsByHashDictionaryKey, checkpointsByHashDictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSMutableDictionary *)checkpointsByHeightDictionary { + return objc_getAssociatedObject(self, &checkpointsByHeightDictionaryKey); +} +- (void)setCheckpointsByHeightDictionary:(NSMutableDictionary *)checkpointsByHeightDictionary { + objc_setAssociatedObject(self, &checkpointsByHeightDictionaryKey, checkpointsByHeightDictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)blockHeightHasCheckpoint:(uint32_t)blockHeight { + DSCheckpoint *checkpoint = [self lastCheckpointOnOrBeforeHeight:blockHeight]; + return (checkpoint.height == blockHeight); +} + +- (DSCheckpoint *)lastCheckpoint { + DSCheckpoint *maybeLastCheckpoint = objc_getAssociatedObject(self, &lastCheckpointKey); + if (!maybeLastCheckpoint) { + maybeLastCheckpoint = [[self checkpoints] lastObject]; + objc_setAssociatedObject(self, &lastCheckpointKey, maybeLastCheckpoint, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return maybeLastCheckpoint; +} + +- (DSCheckpoint *)lastTerminalCheckpoint { + return self.terminalHeadersOverrideUseCheckpoint ? self.terminalHeadersOverrideUseCheckpoint : [self lastCheckpoint]; + +} + +- (DSCheckpoint *)lastCheckpointOnOrBeforeHeight:(uint32_t)height { + NSUInteger genesisHeight = [self isDevnetAny] ? 1 : 0; + // if we don't have any blocks yet, use the latest checkpoint that's at least a week older than earliestKeyTime + for (long i = self.checkpoints.count - 1; i >= genesisHeight; i--) { + if (i == genesisHeight || ![self syncsBlockchain] || (self.checkpoints[i].height <= height)) { + return self.checkpoints[i]; + } + } + return nil; +} + +- (DSCheckpoint *)lastCheckpointOnOrBeforeTimestamp:(NSTimeInterval)timestamp { + NSUInteger genesisHeight = [self isDevnetAny] ? 1 : 0; + // if we don't have any blocks yet, use the latest checkpoint that's at least a week older than earliestKeyTime + for (long i = self.checkpoints.count - 1; i >= genesisHeight; i--) { + if (i == genesisHeight || ![self syncsBlockchain] || (self.checkpoints[i].timestamp <= timestamp)) { + return self.checkpoints[i]; + } + } + return nil; +} + +- (DSCheckpoint *_Nullable)lastCheckpointHavingMasternodeList { + NSSet *set = [self.checkpointsByHeightDictionary keysOfEntriesPassingTest:^BOOL(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) { + DSCheckpoint *checkpoint = (DSCheckpoint *)obj; + return (checkpoint.masternodeListName && ![checkpoint.masternodeListName isEqualToString:@""]); + }]; + NSArray *numbers = [[set allObjects] sortedArrayUsingSelector:@selector(compare:)]; + if (!numbers.count) return nil; + return self.checkpointsByHeightDictionary[numbers.lastObject]; +} + +- (DSCheckpoint *)checkpointForBlockHash:(UInt256)blockHash { + return [self.checkpointsByHashDictionary objectForKey:uint256_data(blockHash)]; +} + +- (DSCheckpoint *)checkpointForBlockHeight:(uint32_t)blockHeight { + return [self.checkpointsByHeightDictionary objectForKey:@(blockHeight)]; +} + +- (DSCheckpoint *)terminalHeadersOverrideUseCheckpoint { + return objc_getAssociatedObject(self, &terminalHeadersOverrideUseCheckpointKey); +} + +- (void)useCheckpointBeforeOrOnHeightForTerminalBlocksSync:(uint32_t)blockHeight { + objc_setAssociatedObject(self, &terminalHeadersOverrideUseCheckpointKey, [self lastCheckpointOnOrBeforeHeight:blockHeight], OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (DSCheckpoint *)syncHeadersOverrideUseCheckpoint { + return objc_getAssociatedObject(self, &syncHeadersOverrideUseCheckpointKey); +} + +- (void)useCheckpointBeforeOrOnHeightForSyncingChainBlocks:(uint32_t)blockHeight { + objc_setAssociatedObject(self, &syncHeadersOverrideUseCheckpointKey, [self lastCheckpointOnOrBeforeHeight:blockHeight], OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + + ++ (NSMutableArray *)createCheckpointsArrayFromCheckpoints:(checkpoint *)checkpoints count:(NSUInteger)checkpointCount { + NSMutableArray *checkpointMutableArray = [NSMutableArray array]; + for (int i = 0; i < checkpointCount; i++) { + checkpoint cpt = checkpoints[i]; + NSString *merkleRootString = [NSString stringWithCString:cpt.merkleRoot encoding:NSUTF8StringEncoding]; + NSString *chainWorkString = [NSString stringWithCString:cpt.chainWork encoding:NSUTF8StringEncoding]; + uint32_t blockHeight = cpt.height; + UInt256 blockHash = [NSString stringWithCString:cpt.checkpointHash encoding:NSUTF8StringEncoding].hexToData.reverse.UInt256; + UInt256 chainWork = chainWorkString.hexToData.reverse.UInt256; + UInt256 merkleRoot = [merkleRootString isEqualToString:@""] ? UINT256_ZERO : merkleRootString.hexToData.reverse.UInt256; + DSCheckpoint *checkpoint = [DSCheckpoint checkpointForHeight:blockHeight blockHash:blockHash timestamp:cpt.timestamp target:cpt.target merkleRoot:merkleRoot chainWork:chainWork masternodeListName:[NSString stringWithCString:cpt.masternodeListPath encoding:NSUTF8StringEncoding]]; + [checkpointMutableArray addObject:checkpoint]; + } + return [checkpointMutableArray copy]; +} + +@end diff --git a/DashSync/shared/Models/Chain/DSChain+Identity.h b/DashSync/shared/Models/Chain/DSChain+Identity.h new file mode 100644 index 000000000..8e46c3a8f --- /dev/null +++ b/DashSync/shared/Models/Chain/DSChain+Identity.h @@ -0,0 +1,63 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "DSIdentity.h" +#import "DSChain.h" +#import "DSWallet+Identity.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSChain (Identity) + +// MARK: - Identities + +/*! @brief Returns a count of local blockchain identities. */ +@property (nonatomic, readonly) uint32_t localIdentitiesCount; + +/*! @brief Returns a count of blockchain invitations that have been created locally. */ +@property (nonatomic, readonly) uint32_t localInvitationsCount; + +/*! @brief Returns an array of all local blockchain identities. */ +@property (nonatomic, readonly) NSArray *localIdentities; + +/*! @brief Returns a dictionary of all local blockchain identities keyed by uniqueId. */ +@property (nonatomic, readonly) NSDictionary *localIdentitiesByUniqueIdDictionary; + +/*! @brief Returns a blockchain identity by uniqueId, if it exists. */ +- (DSIdentity *_Nullable)identityForUniqueId:(UInt256)uniqueId; + +/*! @brief Returns a blockchain identity that could have created this contract. */ +- (DSIdentity *_Nullable)identityThatCreatedContract:(DPContract *)contract + withContractId:(UInt256)contractId + foundInWallet:(DSWallet *_Nullable *_Nullable)foundInWallet; + +/*! @brief Returns a blockchain identity by uniqueId, if it exists. Also returns the wallet it was found in. */ +- (DSIdentity *_Nullable)identityForUniqueId:(UInt256)uniqueId + foundInWallet:(DSWallet *_Nullable *_Nullable)foundInWallet; + +/*! @brief Returns a blockchain identity by uniqueId, if it exists. Also returns the wallet it was found in. Allows to search foreign blockchain identities too */ +- (DSIdentity *)identityForUniqueId:(UInt256)uniqueId + foundInWallet:(DSWallet *_Nullable *_Nullable)foundInWallet + includeForeignIdentities:(BOOL)includeForeignIdentities; + +- (void)wipeIdentitiesPersistedDataInContext:(NSManagedObjectContext *)context; +- (void)wipeInvitationsPersistedDataInContext:(NSManagedObjectContext *)context; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Chain/DSChain+Identity.m b/DashSync/shared/Models/Chain/DSChain+Identity.m new file mode 100644 index 000000000..a1ba3ec6e --- /dev/null +++ b/DashSync/shared/Models/Chain/DSChain+Identity.m @@ -0,0 +1,174 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DSChain+Identity.h" +#import "DSChain+Wallet.h" +#import "DSWallet+Invitation.h" +#import "DSBlockchainIdentityEntity+CoreDataClass.h" +#import "DSBlockchainInvitationEntity+CoreDataClass.h" +#import "DSChainManager.h" +#import "DSIdentitiesManager+CoreData.h" +#import "NSManagedObject+Sugar.h" + +@implementation DSChain (Identity) + +// MARK: - Identities + +- (uint32_t)localIdentitiesCount { + uint32_t identitiesCount = 0; + for (DSWallet *lWallet in self.wallets) { + identitiesCount += [lWallet identitiesCount]; + } + return identitiesCount; +} + +- (NSArray *)localIdentities { + NSMutableArray *rAllIdentities = [NSMutableArray array]; + for (DSWallet *wallet in self.wallets) { + [rAllIdentities addObjectsFromArray:[wallet.identities allValues]]; + } + return rAllIdentities; +} + +- (NSDictionary *)localIdentitiesByUniqueIdDictionary { + NSMutableDictionary *rAllIdentities = [NSMutableDictionary dictionary]; + for (DSWallet *wallet in self.wallets) { + for (DSIdentity *identity in [wallet.identities allValues]) { + rAllIdentities[identity.uniqueIDData] = identity; + } + } + return rAllIdentities; +} + + +- (DSIdentity *)identityForUniqueId:(UInt256)uniqueId { + NSAssert(uint256_is_not_zero(uniqueId), @"uniqueId must not be null"); + return [self identityForUniqueId:uniqueId foundInWallet:nil includeForeignIdentities:NO]; +} + +- (DSIdentity *)identityForUniqueId:(UInt256)uniqueId + foundInWallet:(DSWallet **)foundInWallet { + NSAssert(uint256_is_not_zero(uniqueId), @"uniqueId must not be null"); + return [self identityForUniqueId:uniqueId foundInWallet:foundInWallet includeForeignIdentities:NO]; +} + +- (DSIdentity *_Nullable)identityThatCreatedContract:(DPContract *)contract + withContractId:(UInt256)contractId + foundInWallet:(DSWallet **)foundInWallet { + NSAssert(uint256_is_not_zero(contractId), @"contractId must not be null"); + for (DSWallet *wallet in self.wallets) { + DSIdentity *identity = [wallet identityThatCreatedContract:contract withContractId:contractId]; + if (identity) { + if (foundInWallet) + *foundInWallet = wallet; + return identity; + } + } + return nil; +} + +- (DSIdentity *)identityForUniqueId:(UInt256)uniqueId + foundInWallet:(DSWallet **)foundInWallet + includeForeignIdentities:(BOOL)includeForeignIdentities { + NSAssert(uint256_is_not_zero(uniqueId), @"uniqueId must not be null"); + for (DSWallet *wallet in self.wallets) { + DSIdentity *identity = [wallet identityForUniqueId:uniqueId]; + if (identity) { + if (foundInWallet) + *foundInWallet = wallet; + return identity; + } + } + return includeForeignIdentities ? [self.chainManager.identitiesManager foreignIdentityWithUniqueId:uniqueId] : nil; +} + +- (void)wipeIdentitiesPersistedDataInContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + NSArray *objects = [DSBlockchainIdentityEntity objectsInContext:context matching:@"chain == %@", [self chainEntityInContext:context]]; + [DSBlockchainIdentityEntity deleteObjects:objects inContext:context]; + }]; +} + +// MARK: - Invitations + +- (uint32_t)localInvitationsCount { + uint32_t invitationsCount = 0; + for (DSWallet *lWallet in self.wallets) { + invitationsCount += [lWallet invitationsCount]; + } + return invitationsCount; +} + +- (void)wipeInvitationsPersistedDataInContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + NSArray *objects = [DSBlockchainInvitationEntity objectsInContext:context matching:@"chain == %@", [self chainEntityInContext:context]]; + [DSBlockchainInvitationEntity deleteObjects:objects inContext:context]; + }]; +} + + +// +//-(BOOL)registerBlockchainIdentityRegistrationTransaction:(DSIdentityRegistrationTransition*)identityRegistrationTransaction { +// DSWallet * identityWallet = [self walletHavingIdentityAuthenticationHash:identityRegistrationTransaction.pubkeyHash foundAtIndex:nil]; +// BOOL registered = [odentityWallet.specialTransactionsHolder registerTransaction:identityRegistrationTransaction]; +// +// if (identityWallet) { +// DSAuthenticationKeysDerivationPath * identitiesDerivationPath = [[DSDerivationPathFactory sharedInstance] identityBLSKeysDerivationPathForWallet:identityWallet]; +// [identitiesDerivationPath registerTransactionAddress:identityRegistrationTransaction.pubkeyAddress]; +// } +// return registered; +//} +// +//-(BOOL)registerIdentityResetTransaction:(DSIdentityUpdateTransition*)identityResetTransaction { +// DSWallet * identityWallet = [self walletHavingIdentityAuthenticationHash:identityResetTransaction.replacementPublicKeyHash foundAtIndex:nil]; +// [identityWallet.specialTransactionsHolder registerTransaction:identityResetTransaction]; +// DSWallet * identityRegistrationWallet = nil; +// DSTransaction * identityRegistrationTransaction = [self transactionForHash:identityResetTransaction.registrationTransactionHash returnWallet:&identityRegistrationWallet]; +// BOOL registered = NO; +// if (identityRegistrationTransaction && identityRegistrationWallet && (identityWallet != identityRegistrationWallet)) { +// registered = [identityRegistrationWallet.specialTransactionsHolder registerTransaction:identityResetTransaction]; +// } +// +// if (identityWallet) { +// DSAuthenticationKeysDerivationPath * identitiesDerivationPath = [[DSDerivationPathFactory sharedInstance] identityBLSKeysDerivationPathForWallet:identityWallet]; +// [identitiesDerivationPath registerTransactionAddress:identityResetTransaction.replacementAddress]; +// } +// return registered; +//} +// +//-(BOOL)registerIdentityCloseTransaction:(DSIdentityCloseTransition*)identityCloseTransaction { +// DSWallet * identityRegistrationWallet = nil; +// DSTransaction * identityRegistrationTransaction = [self transactionForHash:identityCloseTransaction.registrationTransactionHash returnWallet:&identityRegistrationWallet]; +// if (identityRegistrationTransaction && identityRegistrationWallet) { +// return [identityRegistrationWallet.specialTransactionsHolder registerTransaction:identityCloseTransaction]; +// } else { +// return NO; +// } +//} +// +//-(BOOL)registerIdentityTopupTransaction:(DSIdentityTopupTransition*)identityTopupTransaction { +// DSWallet * identityRegistrationWallet = nil; +// DSTransaction * identityRegistrationTransaction = [self transactionForHash:identityTopupTransaction.registrationTransactionHash returnWallet:&identityRegistrationWallet]; +// if (identityRegistrationTransaction && identityRegistrationWallet) { +// return [identityRegistrationWallet.specialTransactionsHolder registerTransaction:identityTopupTransaction]; +// } else { +// return NO; +// } +//} +// + +@end diff --git a/DashSync/shared/Models/Chain/DSChain+Params.h b/DashSync/shared/Models/Chain/DSChain+Params.h new file mode 100644 index 000000000..02488534a --- /dev/null +++ b/DashSync/shared/Models/Chain/DSChain+Params.h @@ -0,0 +1,148 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "DSChain.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSChain (Params) + +/*! @brief The chain type (MainNet, TestNet or DevNet). */ +@property (nonatomic, assign) dash_spv_crypto_network_chain_type_ChainType *chainType; + +// MARK: Sync + +/*! @brief The genesis hash is the hash of the first block of the chain. For a devnet this is actually the second block as the first block is created in a special way for devnets. */ +@property (nonatomic, assign) UInt256 genesisHash; + +/*! @brief headersMaxAmount is the maximum amount of headers that is expected from peers. */ +@property (nonatomic, assign) uint64_t headersMaxAmount; + +/*! @brief This is the minimum amount that can be entered into an amount for a output for it not to be considered dust. */ +@property (nonatomic, readonly) uint64_t minOutputAmount; + +/*! @brief The magic number is used in message headers to indicate what network (or chain) a message is intended for. */ +@property (nonatomic, readonly) uint32_t magicNumber; + +/*! @brief The base reward is the intial mining reward at genesis for the chain. This goes down by 7% every year. A SPV client does not validate that the reward amount is correct as it would not make sense for miners to enter incorrect rewards as the blocks would be rejected by full nodes. */ +@property (nonatomic, readonly) uint64_t baseReward; +@property (nonatomic, readonly) uint32_t coinType; + +/*! @brief minProtocolVersion is the minimum protocol version that peers on this chain can communicate with. This should only be changed in the case of devnets. */ +@property (nonatomic, assign) uint32_t minProtocolVersion; + +/*! @brief protocolVersion is the protocol version that we currently use for this chain. This should only be changed in the case of devnets. */ +@property (nonatomic, assign) uint32_t protocolVersion; + +/*! @brief maxProofOfWork is the lowest amount of work effort required to mine a block on the chain. */ +@property (nonatomic, readonly) UInt256 maxProofOfWork; + +/*! @brief maxProofOfWorkTarget is the lowest amount of work effort required to mine a block on the chain. Here it is represented as the compact target. */ +@property (nonatomic, readonly) uint32_t maxProofOfWorkTarget; + +/*! @brief allowMinDifficultyBlocks is set to TRUE on networks where mining is low enough that it can be attacked by increasing difficulty with ASICs and then no longer running ASICs. This is set to NO for Mainnet, and generally should be YES on all other networks. */ +@property (nonatomic, readonly) BOOL allowMinDifficultyBlocks; + +/*! @brief The number of minimumDifficultyBlocks. */ +@property (nonatomic, assign) uint32_t minimumDifficultyBlocks; + +/*! @brief The default transaction version used when sending transactions. */ +@property (nonatomic, readonly) uint16_t transactionVersion; + +/*! @brief A threshold after which a peer will be banned. */ +@property (nonatomic, readonly) uintptr_t peerMisbehavingThreshold; + +/*! @brief The flag represents whether the quorum rotation is enabled in this chain. */ +@property (nonatomic, assign) BOOL isRotatedQuorumsPresented; + +// MARK: Ports + +/*! @brief The standard port for the chain for L1 communication. */ +@property (nonatomic, assign) uint32_t standardPort; + +/*! @brief The standard port for the chain for L2 communication through JRPC. */ +@property (nonatomic, assign) uint32_t standardDapiJRPCPort; + +/*! @brief The standard port for the chain for L2 communication through GRPC. */ +@property (nonatomic, assign) uint32_t standardDapiGRPCPort; + +// MARK: Names and Identifiers + +/*! @brief The unique identifier of the chain. This unique id follows the same chain accross devices because it is the short hex string of the genesis hash. */ +@property (nonatomic, readonly) NSString *uniqueID; + +/*! @brief The name of the chain (Mainnet-Testnet-Devnet). */ +@property (nonatomic, readonly) NSString *name; + +/*! @brief The localized name of the chain (Mainnet-Testnet-Devnet). */ +@property (nonatomic, readonly) NSString *localizedName; + +/*! @brief The network name. Currently main, test, dev or reg. */ +@property (nonatomic, readonly) NSString *networkName; + + +- (void)setDevnetNetworkName:(NSString *)networkName; + +// MARK: Sporks + +/*! @brief The spork public key as a hex string. */ +@property (nonatomic, strong, nullable) NSString *sporkPublicKeyHexString; + +/*! @brief The spork private key as a base 58 string. */ +@property (nonatomic, strong, nullable) NSString *sporkPrivateKeyBase58String; + +/*! @brief The spork address base 58 string (addresses are known to be base 58). */ +@property (nonatomic, strong, nullable) NSString *sporkAddress; + + + +// MARK: - L2 Network Chain Info + +/*! @brief platformProtocolVersion is the protocol version that we currently use for the platform chain. This should only be changed in the case of devnets. */ +@property (nonatomic, assign) uint32_t platformProtocolVersion; + +/*! @brief The dpns contract id. */ +@property (nonatomic, assign) UInt256 dpnsContractID; + +/*! @brief The dashpay contract id. */ +@property (nonatomic, assign) UInt256 dashpayContractID; + + +// MARK: Fees + +@property (nonatomic, assign) uint64_t feePerByte; + +/*! @brief The fee for transactions in L1 are now entirely dependent on their size. */ +- (uint64_t)feeForTxSize:(NSUInteger)size; + + +// MARK: - Chain Info methods + +- (BOOL)isMainnet; +- (BOOL)isTestnet; +- (BOOL)isDevnetAny; +- (BOOL)isEvolutionEnabled; +- (BOOL)isDevnetWithGenesisHash:(UInt256)genesisHash; +- (BOOL)isCore19Active; +- (BOOL)isCore20Active; +- (BOOL)isCore20ActiveAtHeight:(uint32_t)height; +//- (KeyKind)activeBLSType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Chain/DSChain+Params.m b/DashSync/shared/Models/Chain/DSChain+Params.m new file mode 100644 index 000000000..cb8dc5096 --- /dev/null +++ b/DashSync/shared/Models/Chain/DSChain+Params.m @@ -0,0 +1,685 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DSChain+Params.h" +#import "DSChainConstants.h" +#import "DSWallet.h" +#import "NSData+Dash.h" +#import "NSString+Bitcoin.h" +#import + +#define PROTOCOL_VERSION_LOCATION @"PROTOCOL_VERSION_LOCATION" +#define DEFAULT_MIN_PROTOCOL_VERSION_LOCATION @"MIN_PROTOCOL_VERSION_LOCATION" + +#define PLATFORM_PROTOCOL_VERSION_LOCATION @"PLATFORM_PROTOCOL_VERSION_LOCATION" +#define PLATFORM_DEFAULT_MIN_PROTOCOL_VERSION_LOCATION @"PLATFORM_MIN_PROTOCOL_VERSION_LOCATION" + +#define QUORUM_ROTATION_PRESENCE_KEY @"QUORUM_ROTATION_PRESENCE_KEY" + +#define STANDARD_PORT_LOCATION @"STANDARD_PORT_LOCATION" +#define JRPC_PORT_LOCATION @"JRPC_PORT_LOCATION" +#define GRPC_PORT_LOCATION @"GRPC_PORT_LOCATION" + +#define DPNS_CONTRACT_ID @"DPNS_CONTRACT_ID" +#define DASHPAY_CONTRACT_ID @"DASHPAY_CONTRACT_ID" + +#define MINIMUM_DIFFICULTY_BLOCKS_COUNT_KEY @"MINIMUM_DIFFICULTY_BLOCKS_COUNT_KEY" + +#define SPORK_PUBLIC_KEY_LOCATION @"SPORK_PUBLIC_KEY_LOCATION" +#define SPORK_ADDRESS_LOCATION @"SPORK_ADDRESS_LOCATION" +#define SPORK_PRIVATE_KEY_LOCATION @"SPORK_PRIVATE_KEY_LOCATION" + +NSString const *chainTypeKey = @"chainTypeKey"; +NSString const *protocolVersionKey = @"protocolVersionKey"; +NSString const *minProtocolVersionKey = @"minProtocolVersionKey"; +NSString const *standardPortKey = @"standardPortKey"; +NSString const *standardDapiGRPCPortKey = @"standardDapiGRPCPortKey"; +NSString const *standardDapiJRPCPortKey = @"standardDapiJRPCPortKey"; +NSString const *minimumDifficultyBlocksKey = @"minimumDifficultyBlocksKey"; +NSString const *maxProofOfWorkKey = @"maxProofOfWorkKey"; +NSString const *dpnsContractIDKey = @"dpnsContractIDKey"; +NSString const *dashpayContractIDKey = @"dashpayContractIDKey"; +NSString const *uniqueIDKey = @"uniqueIDKey"; +NSString const *networkNameKey = @"networkNameKey"; +NSString const *headersMaxAmountKey = @"headersMaxAmountKey"; +NSString const *genesisHashKey = @"genesisHashKey"; +NSString const *minOutputAmountKey = @"minOutputAmountKey"; +NSString const *cachedIsQuorumRotationPresentedKey = @"cachedIsQuorumRotationPresentedKey"; +NSString const *feePerByteKey = @"feePerByteKey"; + +@implementation DSChain (Params) + +- (dash_spv_crypto_network_chain_type_ChainType *)chainType { + NSValue *value = objc_getAssociatedObject(self, &chainTypeKey); + return value.pointerValue; +} +- (void)setChainType:(dash_spv_crypto_network_chain_type_ChainType *)chainType { + objc_setAssociatedObject(self, &chainTypeKey, [NSValue valueWithPointer:chainType], OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +// MARK: Sync Parameters + +- (uint32_t)magicNumber { + return (uint32_t) dash_spv_crypto_network_chain_type_ChainType_magic_number(self.chainType); +} + +- (void)setProtocolVersion:(uint32_t)protocolVersion { + if (dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType)) { + setKeychainInt(protocolVersion, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], PROTOCOL_VERSION_LOCATION], NO); + } +} + +- (uint32_t)protocolVersion { + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + return PROTOCOL_VERSION_MAINNET; //(70216 + (self.headersMaxAmount / 2000)); + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + return PROTOCOL_VERSION_TESTNET; + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + NSError *error = nil; + uint32_t protocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], PROTOCOL_VERSION_LOCATION], &error); + if (!error && protocolVersion) + return protocolVersion; + else + return PROTOCOL_VERSION_DEVNET; + } + } +} + +- (BOOL)isRotatedQuorumsPresented { + BOOL cachedIsQuorumRotationPresented = objc_getAssociatedObject(self, &cachedIsQuorumRotationPresentedKey); + if (cachedIsQuorumRotationPresented) return cachedIsQuorumRotationPresented; + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: { + NSError *error = nil; + BOOL isPresented = (BOOL)getKeychainInt([NSString stringWithFormat:@"MAINNET_%@", QUORUM_ROTATION_PRESENCE_KEY], &error); + objc_setAssociatedObject(self, &cachedIsQuorumRotationPresentedKey, @(!error && isPresented), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + break; + } + case dash_spv_crypto_network_chain_type_ChainType_TestNet: { + NSError *error = nil; + BOOL isPresented = (BOOL)getKeychainInt([NSString stringWithFormat:@"TESTNET_%@", QUORUM_ROTATION_PRESENCE_KEY], &error); + objc_setAssociatedObject(self, &cachedIsQuorumRotationPresentedKey, @(!error && isPresented), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + break; + } + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + NSError *error = nil; + BOOL isPresented = (BOOL)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], QUORUM_ROTATION_PRESENCE_KEY], &error); + objc_setAssociatedObject(self, &cachedIsQuorumRotationPresentedKey, @(!error && isPresented), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + break; + } + } + return objc_getAssociatedObject(self, &cachedIsQuorumRotationPresentedKey); +} + + +- (void)setIsRotatedQuorumsPresented:(BOOL)isRotatedQuorumsPresented { + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + setKeychainInt(isRotatedQuorumsPresented, [NSString stringWithFormat:@"MAINNET_%@", QUORUM_ROTATION_PRESENCE_KEY], NO); + break; + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + setKeychainInt(isRotatedQuorumsPresented, [NSString stringWithFormat:@"TESTNET_%@", QUORUM_ROTATION_PRESENCE_KEY], NO); + break; + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + setKeychainInt(isRotatedQuorumsPresented, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], QUORUM_ROTATION_PRESENCE_KEY], NO); + break; + } + } + objc_setAssociatedObject(self, &cachedIsQuorumRotationPresentedKey, @(isRotatedQuorumsPresented), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (uint32_t)minProtocolVersion { + @synchronized(self) { + NSNumber *cachedMinProtocolVersion = objc_getAssociatedObject(self, &minProtocolVersionKey); + if (cachedMinProtocolVersion) return [cachedMinProtocolVersion unsignedIntValue]; + NSError *error = nil; + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: { + uint32_t minProtocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"MAINNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], &error); + uint32_t version = !error && minProtocolVersion ? MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_MAINNET) : DEFAULT_MIN_PROTOCOL_VERSION_MAINNET; + cachedMinProtocolVersion = @(version); + break; + } + case dash_spv_crypto_network_chain_type_ChainType_TestNet: { + uint32_t minProtocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"TESTNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], &error); + uint32_t version = !error && minProtocolVersion ? MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_TESTNET) : DEFAULT_MIN_PROTOCOL_VERSION_TESTNET; + cachedMinProtocolVersion = @(version); + break; + } + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + uint32_t minProtocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], &error); + uint32_t version = !error && minProtocolVersion ? MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_DEVNET) : DEFAULT_MIN_PROTOCOL_VERSION_DEVNET; + cachedMinProtocolVersion = @(version); + break; + } + } + objc_setAssociatedObject(self, &minProtocolVersionKey, cachedMinProtocolVersion, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return [cachedMinProtocolVersion unsignedIntValue]; + } +} + + +- (void)setMinProtocolVersion:(uint32_t)minProtocolVersion { + @synchronized(self) { + if (minProtocolVersion < MIN_VALID_MIN_PROTOCOL_VERSION || minProtocolVersion > MAX_VALID_MIN_PROTOCOL_VERSION) return; + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + setKeychainInt(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_MAINNET), [NSString stringWithFormat:@"MAINNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], NO); + objc_setAssociatedObject(self, &minProtocolVersionKey, @(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_MAINNET)), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + break; + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + setKeychainInt(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_TESTNET), [NSString stringWithFormat:@"TESTNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], NO); + objc_setAssociatedObject(self, &minProtocolVersionKey, @(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_TESTNET)), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + break; + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + setKeychainInt(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_DEVNET), [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], NO); + objc_setAssociatedObject(self, &minProtocolVersionKey, @(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_DEVNET)), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + break; + } + } + } +} + +- (uint32_t)standardPort { + NSNumber *cachedStandardPort = objc_getAssociatedObject(self, &standardPortKey); + if (cachedStandardPort) return [cachedStandardPort unsignedIntValue]; + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + objc_setAssociatedObject(self, &standardPortKey, @(MAINNET_STANDARD_PORT), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return MAINNET_STANDARD_PORT; + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + objc_setAssociatedObject(self, &standardPortKey, @(TESTNET_STANDARD_PORT), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return TESTNET_STANDARD_PORT; + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + NSError *error = nil; + uint32_t port = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], STANDARD_PORT_LOCATION], &error); + if (!error && port) { + objc_setAssociatedObject(self, &standardPortKey, @(port), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return port; + } else { + return DEVNET_STANDARD_PORT; + } + } + } +} + +- (void)setStandardPort:(uint32_t)standardPort { + if (dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType)) { + objc_setAssociatedObject(self, &standardPortKey, @(standardPort), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + setKeychainInt(standardPort, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], STANDARD_PORT_LOCATION], NO); + } +} + +- (uint32_t)standardDapiGRPCPort { + NSNumber *cachedStandardDapiGRPCPort = objc_getAssociatedObject(self, &standardDapiGRPCPortKey); + if (cachedStandardDapiGRPCPort) return [cachedStandardDapiGRPCPort unsignedIntValue]; + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + objc_setAssociatedObject(self, &standardDapiGRPCPortKey, @(MAINNET_DAPI_GRPC_STANDARD_PORT), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return MAINNET_DAPI_GRPC_STANDARD_PORT; + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + objc_setAssociatedObject(self, &standardDapiGRPCPortKey, @(TESTNET_DAPI_GRPC_STANDARD_PORT), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return TESTNET_DAPI_GRPC_STANDARD_PORT; + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + NSError *error = nil; + uint32_t cachedStandardDapiGRPCPort = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], GRPC_PORT_LOCATION], &error); + if (!error && cachedStandardDapiGRPCPort) { + objc_setAssociatedObject(self, &standardDapiGRPCPortKey, @(cachedStandardDapiGRPCPort), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return cachedStandardDapiGRPCPort; + } else + return DEVNET_DAPI_GRPC_STANDARD_PORT; + } + } +} + +- (void)setStandardDapiGRPCPort:(uint32_t)standardDapiGRPCPort { + if (dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType)) { + objc_setAssociatedObject(self, &standardDapiGRPCPortKey, @(standardDapiGRPCPort), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + setKeychainInt(standardDapiGRPCPort, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], GRPC_PORT_LOCATION], NO); + } +} +- (uint32_t)standardDapiJRPCPort { + NSNumber *cached = objc_getAssociatedObject(self, &standardDapiJRPCPortKey); + if (cached) return [cached unsignedIntValue]; + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + objc_setAssociatedObject(self, &standardDapiJRPCPortKey, @(MAINNET_DAPI_JRPC_STANDARD_PORT), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return MAINNET_DAPI_JRPC_STANDARD_PORT; + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + objc_setAssociatedObject(self, &standardDapiJRPCPortKey, @(TESTNET_DAPI_JRPC_STANDARD_PORT), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return TESTNET_DAPI_JRPC_STANDARD_PORT; + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + NSError *error = nil; + uint32_t cachedStandardDapiJRPCPort = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], JRPC_PORT_LOCATION], &error); + if (!error && cachedStandardDapiJRPCPort) { + objc_setAssociatedObject(self, &standardDapiJRPCPortKey, @(cachedStandardDapiJRPCPort), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return cachedStandardDapiJRPCPort; + } else + return DEVNET_DAPI_JRPC_STANDARD_PORT; + } + } +} + +- (void)setStandardDapiJRPCPort:(uint32_t)standardDapiJRPCPort { + if (dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType)) { + objc_setAssociatedObject(self, &standardDapiJRPCPortKey, @(standardDapiJRPCPort), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + setKeychainInt(standardDapiJRPCPort, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], JRPC_PORT_LOCATION], NO); + } +} + +// MARK: Mining and Dark Gravity Wave Parameters + +- (UInt256)maxProofOfWork { + NSData *cachedMaxProofOfWork = objc_getAssociatedObject(self, &maxProofOfWorkKey); + if (cachedMaxProofOfWork != nil) { + UInt256 work = cachedMaxProofOfWork.UInt256; + if (uint256_is_not_zero(work)) + return work; + } + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + cachedMaxProofOfWork = MAX_PROOF_OF_WORK_MAINNET_DATA; + break; + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + cachedMaxProofOfWork = MAX_PROOF_OF_WORK_TESTNET_DATA; + break; + case dash_spv_crypto_network_chain_type_ChainType_DevNet: + cachedMaxProofOfWork = MAX_PROOF_OF_WORK_DEVNET_DATA; + break; + } + objc_setAssociatedObject(self, &maxProofOfWorkKey, cachedMaxProofOfWork, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return cachedMaxProofOfWork.UInt256; +} + +- (uint32_t)maxProofOfWorkTarget { + return dash_spv_crypto_network_chain_type_ChainType_max_proof_of_work_target(self.chainType); +} + +- (BOOL)allowMinDifficultyBlocks { + return dash_spv_crypto_network_chain_type_ChainType_allow_min_difficulty_blocks(self.chainType); +} + +- (uint64_t)baseReward { + if (dash_spv_crypto_network_chain_type_ChainType_is_mainnet(self.chainType)) return 5 * DUFFS; + return 50 * DUFFS; +} +- (uint32_t)coinType { + return dash_spv_crypto_network_chain_type_ChainType_coin_type(self.chainType); +} + +// MARK: Spork Parameters + +- (NSString *)sporkPublicKeyHexString { + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + return SPORK_PUBLIC_KEY_MAINNET; + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + return SPORK_PUBLIC_KEY_TESTNET; + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + NSError *error = nil; + NSString *publicKey = getKeychainString([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], SPORK_PUBLIC_KEY_LOCATION], &error); + if (!error && publicKey) { + return publicKey; + } else { + return nil; + } + } + } + return nil; +} + +- (void)setSporkPublicKeyHexString:(NSString *)sporkPublicKey { + if (dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType)) { + setKeychainString(sporkPublicKey, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], SPORK_PUBLIC_KEY_LOCATION], NO); + } +} + +- (NSString *)sporkPrivateKeyBase58String { + if (dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType)) { + NSError *error = nil; + NSString *publicKey = getKeychainString([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], SPORK_PRIVATE_KEY_LOCATION], &error); + if (!error && publicKey) { + return publicKey; + } + } + return nil; +} + +- (void)setSporkPrivateKeyBase58String:(NSString *)sporkPrivateKey { + if (dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType)) { + setKeychainString(sporkPrivateKey, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], SPORK_PRIVATE_KEY_LOCATION], YES); + } +} + +- (NSString *)sporkAddress { + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + return SPORK_ADDRESS_MAINNET; + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + return SPORK_ADDRESS_TESTNET; + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + NSError *error = nil; + NSString *publicKey = getKeychainString([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], SPORK_ADDRESS_LOCATION], &error); + if (!error && publicKey) { + return publicKey; + } else { + return nil; + } + } + } + return nil; +} + +- (void)setSporkAddress:(NSString *)sporkAddress { + if (dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType)) { + setKeychainString(sporkAddress, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], SPORK_ADDRESS_LOCATION], NO); + } +} + +// MARK: - L2 Chain Parameters + +- (uint32_t)platformProtocolVersion { + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + return PLATFORM_PROTOCOL_VERSION_MAINNET; //(70216 + (self.headersMaxAmount / 2000)); + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + return PLATFORM_PROTOCOL_VERSION_TESTNET; + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + NSError *error = nil; + uint32_t platformProtocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], PLATFORM_PROTOCOL_VERSION_LOCATION], &error); + if (!error && platformProtocolVersion) + return platformProtocolVersion; + else + return PLATFORM_PROTOCOL_VERSION_DEVNET; + } + } +} + +- (void)setPlatformProtocolVersion:(uint32_t)platformProtocolVersion { + if (dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType)) { + setKeychainInt(platformProtocolVersion, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], PLATFORM_PROTOCOL_VERSION_LOCATION], NO); + } +} + +- (UInt256)dpnsContractID { + if (!self.isEvolutionEnabled) return UINT256_ZERO; + NSData *cachedDpnsContractIDData = objc_getAssociatedObject(self, &dpnsContractIDKey); + if (cachedDpnsContractIDData != nil) { + UInt256 cachedDpnsContractID = cachedDpnsContractIDData.UInt256; + if (uint256_is_not_zero(cachedDpnsContractID)) + return cachedDpnsContractID; + } + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + cachedDpnsContractIDData = MAINNET_DPNS_CONTRACT_ID.base58ToData; + break; + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + cachedDpnsContractIDData = TESTNET_DPNS_CONTRACT_ID.base58ToData; + break; + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + NSError *error = NULL; + NSData *data = getKeychainData([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DPNS_CONTRACT_ID], &error); + if (!error && data) { + cachedDpnsContractIDData = data; + break; + } else { + return UINT256_ZERO; + } + } + } + objc_setAssociatedObject(self, &dpnsContractIDKey, cachedDpnsContractIDData, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return cachedDpnsContractIDData.UInt256; + +} + +- (void)setDpnsContractID:(UInt256)dpnsContractID { + if (dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType)) { + objc_setAssociatedObject(self, &dpnsContractIDKey, uint256_data(dpnsContractID), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + NSString *identifier = [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DPNS_CONTRACT_ID]; + if (uint256_is_zero(dpnsContractID)) { + NSError *error = nil; + BOOL hasDashpayContractID = (getKeychainData(identifier, &error) != nil); + if (hasDashpayContractID) { + setKeychainData(nil, identifier, NO); + } + } else { + setKeychainData(uint256_data(dpnsContractID), identifier, NO); + } + } +} + +- (UInt256)dashpayContractID { + if (!self.isEvolutionEnabled) return UINT256_ZERO; + NSData *cachedData = objc_getAssociatedObject(self, &dashpayContractIDKey); + if (cachedData != nil) { + UInt256 cachedDpnsContractID = cachedData.UInt256; + if (uint256_is_not_zero(cachedDpnsContractID)) + return cachedDpnsContractID; + } + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + cachedData = MAINNET_DASHPAY_CONTRACT_ID.base58ToData; + break; + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + cachedData = TESTNET_DASHPAY_CONTRACT_ID.base58ToData; + break; + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + NSError *error = NULL; + NSData *data = getKeychainData([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DASHPAY_CONTRACT_ID], &error); + + if (!error && data) { + cachedData = data; + break; + } else { + return UINT256_ZERO; + } + } + } + objc_setAssociatedObject(self, &dashpayContractIDKey, cachedData, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return cachedData.UInt256; +} + +- (void)setDashpayContractID:(UInt256)dashpayContractID { + if (dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType)) { + objc_setAssociatedObject(self, &dashpayContractIDKey, uint256_data(dashpayContractID), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + if (uint256_is_zero(dashpayContractID)) { + NSError *error = nil; + NSString *identifier = [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DASHPAY_CONTRACT_ID]; + BOOL hasDashpayContractID = (getKeychainData(identifier, &error) != nil); + if (hasDashpayContractID) { + setKeychainData(nil, identifier, NO); + } + } else { + setKeychainData(uint256_data(dashpayContractID), [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DASHPAY_CONTRACT_ID], NO); + } + } +} + +- (void)setMinimumDifficultyBlocks:(uint32_t)minimumDifficultyBlocks { + if (dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType)) { + objc_setAssociatedObject(self, &minimumDifficultyBlocksKey, @(minimumDifficultyBlocks), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + setKeychainInt(minimumDifficultyBlocks, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], MINIMUM_DIFFICULTY_BLOCKS_COUNT_KEY], NO); + } +} + +- (uint32_t)minimumDifficultyBlocks { + NSNumber *cached = objc_getAssociatedObject(self, &minimumDifficultyBlocksKey); + if (cached) return [cached unsignedIntValue]; + if (dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType)) { + NSError *error = nil; + uint32_t cachedMinimumDifficultyBlocks = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], MINIMUM_DIFFICULTY_BLOCKS_COUNT_KEY], &error); + + if (!error && cachedMinimumDifficultyBlocks) { + objc_setAssociatedObject(self, &minimumDifficultyBlocksKey, @(cachedMinimumDifficultyBlocks), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return cachedMinimumDifficultyBlocks; + } else { + return 0; + } + } else { + objc_setAssociatedObject(self, &minimumDifficultyBlocksKey, @(0), OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return 0; + } +} + +// MARK: - Names and Identifiers + +- (NSString *)uniqueID { + NSString *cached = objc_getAssociatedObject(self, &uniqueIDKey); + if (!cached) { + cached = [[NSData dataWithUInt256:[self genesisHash]] shortHexString]; + objc_setAssociatedObject(self, &uniqueIDKey, cached, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return cached; +} + + +- (NSString *)networkName { + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + return @"main"; + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + return @"test"; + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + NSString *cached = objc_getAssociatedObject(self, &networkNameKey); + if (cached) return cached; + return @"dev"; + } + } +} + +- (NSString *)name { + return [DSKeyManager NSStringFrom:dash_spv_crypto_network_chain_type_ChainType_name(self.chainType)]; +} + +- (NSString *)localizedName { + switch (self.chainType->tag) { + case dash_spv_crypto_network_chain_type_ChainType_MainNet: + return DSLocalizedString(@"Mainnet", nil); + case dash_spv_crypto_network_chain_type_ChainType_TestNet: + return DSLocalizedString(@"Testnet", nil); + case dash_spv_crypto_network_chain_type_ChainType_DevNet: { + NSString *cached = objc_getAssociatedObject(self, &networkNameKey); + if (cached) return cached; + cached = [DSKeyManager NSStringFrom:dash_spv_crypto_network_chain_type_DevnetType_name(self.chainType->dev_net)]; + objc_setAssociatedObject(self, &networkNameKey, cached, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return cached; + } + } +} + +- (void)setDevnetNetworkName:(NSString *)networkName { + if (dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType)) { + objc_setAssociatedObject(self, &networkNameKey, @"Evonet", OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } +} + +- (uint16_t)transactionVersion { + return dash_spv_crypto_network_chain_type_ChainType_transaction_version(self.chainType); +} + +- (uintptr_t)peerMisbehavingThreshold { + return dash_spv_crypto_network_chain_type_ChainType_peer_misbehaving_threshold(self.chainType); +} + + +- (uint64_t)headersMaxAmount { + NSNumber *cached = objc_getAssociatedObject(self, &headersMaxAmountKey); + return [cached unsignedLongValue]; +} +- (void)setHeadersMaxAmount:(uint64_t)headersMaxAmount { + objc_setAssociatedObject(self, &headersMaxAmountKey, @(headersMaxAmount), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (UInt256)genesisHash { + NSData *data = objc_getAssociatedObject(self, &genesisHashKey); + return data.UInt256; +} + +- (void)setGenesisHash:(UInt256)genesisHash { + objc_setAssociatedObject(self, &genesisHashKey, uint256_data(genesisHash), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +// outputs below this amount are uneconomical due to fees +- (uint64_t)minOutputAmount { + uint64_t amount = (TX_MIN_OUTPUT_AMOUNT * self.feePerByte + MIN_FEE_PER_B - 1) / MIN_FEE_PER_B; + return (amount > TX_MIN_OUTPUT_AMOUNT) ? amount : TX_MIN_OUTPUT_AMOUNT; + +} + +// MARK: Fee Parameters + +// fee that will be added for a transaction of the given size in bytes +- (uint64_t)feeForTxSize:(NSUInteger)size { + uint64_t standardFee = size * TX_FEE_PER_B; //!OCLINT // standard fee based on tx size +#if (!!FEE_PER_KB_URL) + uint64_t fee = ((size * self.feePerByte + 99) / 100) * 100; // fee using feePerByte, rounded up to nearest 100 satoshi + return (fee > standardFee) ? fee : standardFee; +#else + return standardFee; +#endif +} + +- (uint64_t)feePerByte { + NSNumber *value = objc_getAssociatedObject(self, &feePerByteKey); + return [value unsignedLongValue]; +} +- (void)setFeePerByte:(uint64_t)feePerByte { + objc_setAssociatedObject(self, &feePerByteKey, @(feePerByte), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +// MARK: - Check Type + +- (BOOL)isMainnet { + return dash_spv_crypto_network_chain_type_ChainType_is_mainnet(self.chainType); +} + +- (BOOL)isTestnet { + return dash_spv_crypto_network_chain_type_ChainType_is_testnet(self.chainType); +} + +- (BOOL)isDevnetAny { + return dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType); +} + +- (BOOL)isEvolutionEnabled { + return YES; +} + +- (BOOL)isDevnetWithGenesisHash:(UInt256)genesisHash { + return [self isDevnetAny] && uint256_eq([self genesisHash], genesisHash); +} + +// MARK: - Helpers + +- (BOOL)isCore19Active { + return self.lastTerminalBlockHeight >= dash_spv_crypto_network_chain_type_ChainType_core19_activation_height(self.chainType); +} + +- (BOOL)isCore20Active { + return self.lastTerminalBlockHeight >= dash_spv_crypto_network_chain_type_ChainType_core20_activation_height(self.chainType); +} + +- (BOOL)isCore20ActiveAtHeight:(uint32_t)height { + return height >= dash_spv_crypto_network_chain_type_ChainType_core20_activation_height(self.chainType); +} + +//- (KeyKind)activeBLSType { +// return [self isCore19Active] ? KeyKind_BLSBasic : KeyKind_BLS; +//} + +@end diff --git a/DashSync/shared/Models/Chain/DSChain+Protected.h b/DashSync/shared/Models/Chain/DSChain+Protected.h index d69e39732..f26fc0724 100644 --- a/DashSync/shared/Models/Chain/DSChain+Protected.h +++ b/DashSync/shared/Models/Chain/DSChain+Protected.h @@ -46,6 +46,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)setBlockHeight:(int32_t)height andTimestamp:(NSTimeInterval)timestamp forTransactionHashes:(NSArray *)txHashes; - (void)clearOrphans; - (void)blockUntilGetInsightForBlockHash:(UInt256)blockHash; +- (DSBlock *_Nullable)blockUntilGetInsightForBlockHeight:(uint32_t)blockHeight; + - (void)addInsightVerifiedBlock:(DSBlock *)block forBlockHash:(UInt256)blockHash; @property (nonatomic, readonly) BOOL allowInsightBlocksForVerification; @@ -72,25 +74,9 @@ NS_ASSUME_NONNULL_BEGIN // MARK: - Wallet, Accounts and Transactions -/*! @brief Add a wallet to the chain. It is only temporarily in the chain if externaly added this way. */ -- (BOOL)addWallet:(DSWallet *)wallet; - -- (BOOL)registerSpecialTransaction:(DSTransaction *)transaction saveImmediately:(BOOL)saveImmediately; - -- (void)triggerUpdatesForLocalReferences:(DSTransaction *)transaction; -- (void)reloadDerivationPaths; -// MARK: Wallet Discovery -- (DSWallet *_Nullable)walletHavingBlockchainIdentityCreditFundingRegistrationHash:(UInt160)creditFundingRegistrationHash foundAtIndex:(uint32_t *_Nullable)rIndex; -- (DSWallet *_Nullable)walletHavingBlockchainIdentityCreditFundingTopupHash:(UInt160)creditFundingTopupHash foundAtIndex:(uint32_t *)rIndex; -- (DSWallet *_Nullable)walletHavingBlockchainIdentityCreditFundingInvitationHash:(UInt160)creditFundingInvitationHash foundAtIndex:(uint32_t *)rIndex; -- (DSWallet *_Nullable)walletHavingProviderVotingAuthenticationHash:(UInt160)votingAuthenticationHash foundAtIndex:(uint32_t *_Nullable)rIndex; -- (DSWallet *_Nullable)walletHavingProviderOwnerAuthenticationHash:(UInt160)owningAuthenticationHash foundAtIndex:(uint32_t *_Nullable)rIndex; -- (DSWallet *_Nullable)walletHavingProviderOperatorAuthenticationKey:(UInt384)providerOperatorAuthenticationKey foundAtIndex:(uint32_t *_Nullable)rIndex; -- (DSWallet *_Nullable)walletHavingPlatformNodeAuthenticationHash:(UInt160)platformNodeAuthenticationHash foundAtIndex:(uint32_t *_Nullable)rIndex; -- (DSWallet *_Nullable)walletContainingMasternodeHoldingAddressForProviderRegistrationTransaction:(DSProviderRegistrationTransaction *_Nonnull)transaction foundAtIndex:(uint32_t *_Nullable)rIndex; // MARK: - Standalone Derivation Paths @@ -101,7 +87,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) UInt256 masternodeBaseBlockHash; -- (void)updateAddressUsageOfSimplifiedMasternodeEntries:(NSArray *)simplifiedMasternodeEntries; +- (void)updateAddressUsageOfSimplifiedMasternodeEntries:(DMasternodeEntryList *)simplifiedMasternodeEntries; /*! @brief The header locator array is an array of the 10 most recent block hashes in decending order followed by block hashes that double the step back each iteration in decending order and finishing with the previous known checkpoint after that last hash. Something like (top, -1, -2, -3, -4, -5, -6, -7, -8, -9, -11, -15, -23, -39, -71, -135, ..., 0). */ @property (nonatomic, readonly, nullable) NSArray *terminalBlocksLocatorArray; @@ -127,6 +113,12 @@ NS_ASSUME_NONNULL_BEGIN - (void)saveBlockLocators; - (void)saveTerminalBlocks; +// MARK: Notifications + +- (void)notify:(NSNotificationName)name userInfo:(NSDictionary *_Nullable)userInfo; + + +- (void)resetLastSyncBlock; @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Chain/DSChain+Transaction.h b/DashSync/shared/Models/Chain/DSChain+Transaction.h new file mode 100644 index 000000000..4805b554c --- /dev/null +++ b/DashSync/shared/Models/Chain/DSChain+Transaction.h @@ -0,0 +1,54 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "DSChain.h" +#import "DSTransaction.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSChain (Transaction) + +// MARK: - Transactions + +/*! @brief Returns all wallet transactions sorted by date, most recent first. */ +@property (nonatomic, readonly) NSArray *allTransactions; + +/*! @brief Returns the transaction with the given hash if it's been registered in any wallet on the chain (might also return non-registered) */ +- (DSTransaction *_Nullable)transactionForHash:(UInt256)txHash; + +///*! @brief Returns the direction of a transaction for the chain (Sent - Received - Moved - Not Account Funds) */ +//- (DSTransactionDirection)directionOfTransaction:(DSTransaction *)transaction; + +/*! @brief Returns the amount received globally from the transaction (total outputs to change and/or receive addresses) */ +- (uint64_t)amountReceivedFromTransaction:(DSTransaction *)transaction; + +/*! @brief Returns the amount sent globally by the trasaction (total wallet outputs consumed, change and fee included) */ +- (uint64_t)amountSentByTransaction:(DSTransaction *)transaction; + +/*! @brief Returns if this transaction has any local references. Local references are a pubkey hash contained in a wallet, pubkeys in wallets special derivation paths, or anything that would make the transaction relevant for this device. */ +- (BOOL)transactionHasLocalReferences:(DSTransaction *)transaction; + + +// MARK: Protected +- (BOOL)registerSpecialTransaction:(DSTransaction *)transaction saveImmediately:(BOOL)saveImmediately; + +- (void)triggerUpdatesForLocalReferences:(DSTransaction *)transaction; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Chain/DSChain+Transaction.m b/DashSync/shared/Models/Chain/DSChain+Transaction.m new file mode 100644 index 000000000..ea32e50be --- /dev/null +++ b/DashSync/shared/Models/Chain/DSChain+Transaction.m @@ -0,0 +1,329 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DSAccount.h" +#import "DSAssetLockTransaction.h" +#import "DSAssetUnlockTransaction.h" +#import "DSAuthenticationKeysDerivationPath.h" +#import "DSIdentity+Protected.h" +#import "DSIdentityRegistrationTransition.h" +#import "DSInvitation+Protected.h" +#import "DSChain+Transaction.h" +#import "DSChain+Wallet.h" +#import "DSChainManager.h" +#import "DSDerivationPathFactory.h" +#import "DSLocalMasternode+Protected.h" +#import "DSMasternodeHoldingsDerivationPath.h" +#import "DSMasternodeManager+LocalMasternode.h" +#import "DSProviderRegistrationTransaction.h" +#import "DSProviderUpdateRegistrarTransaction.h" +#import "DSProviderUpdateRevocationTransaction.h" +#import "DSProviderUpdateServiceTransaction.h" +#import "DSSpecialTransactionsWalletHolder.h" +#import "DSWallet.h" +#import "DSWallet+Identity.h" +#import "DSWallet+Invitation.h" + +@implementation DSChain (Transaction) + +// MARK: - Transactions + +- (DSTransaction *)transactionForHash:(UInt256)txHash { + return [self transactionForHash:txHash returnWallet:nil]; +} + +- (DSTransaction *)transactionForHash:(UInt256)txHash returnWallet:(DSWallet **)rWallet { + for (DSWallet *wallet in self.wallets) { + DSTransaction *transaction = [wallet transactionForHash:txHash]; + if (transaction) { + if (rWallet) *rWallet = wallet; + return transaction; + } + } + return nil; +} + +- (NSArray *)allTransactions { + NSMutableArray *mArray = [NSMutableArray array]; + for (DSWallet *wallet in self.wallets) { + [mArray addObjectsFromArray:wallet.allTransactions]; + } + return mArray; +} + +// retuns the amount sent globally by the trasaction (total wallet outputs consumed, change and fee included) +- (uint64_t)amountReceivedFromTransaction:(DSTransaction *)transaction { + NSParameterAssert(transaction); + + uint64_t received = 0; + for (DSWallet *wallet in self.wallets) { + received += [wallet amountReceivedFromTransaction:transaction]; + } + return received; +} + +// retuns the amount sent globally by the trasaction (total wallet outputs consumed, change and fee included) +- (uint64_t)amountSentByTransaction:(DSTransaction *)transaction { + NSParameterAssert(transaction); + + uint64_t sent = 0; + for (DSWallet *wallet in self.wallets) { + sent += [wallet amountSentByTransaction:transaction]; + } + return sent; +} + +//- (DSTransactionDirection)directionOfTransaction:(DSTransaction *)transaction { +// const uint64_t sent = [self amountSentByTransaction:transaction]; +// const uint64_t received = [self amountReceivedFromTransaction:transaction]; +// const uint64_t fee = transaction.feeUsed; +// +// if (sent > 0 && (received + fee) == sent) { +// // moved +// return DSTransactionDirection_Moved; +// } else if (sent > 0) { +// // sent +// return DSTransactionDirection_Sent; +// } else if (received > 0) { +// // received +// return DSTransactionDirection_Received; +// } else { +// // no funds moved on this account +// return DSTransactionDirection_NotAccountFunds; +// } +//} + +// MARK: - Special Transactions + +//Does the chain mat +- (BOOL)transactionHasLocalReferences:(DSTransaction *)transaction { + if ([self firstAccountThatCanContainTransaction:transaction]) return TRUE; + + //PROVIDERS + if ([transaction isKindOfClass:[DSProviderRegistrationTransaction class]]) { + DSProviderRegistrationTransaction *tx = (DSProviderRegistrationTransaction *)transaction; + if ([self walletHavingProviderOwnerAuthenticationHash:tx.ownerKeyHash foundAtIndex:nil]) return TRUE; + if ([self walletHavingProviderVotingAuthenticationHash:tx.votingKeyHash foundAtIndex:nil]) return TRUE; + if ([self walletHavingProviderOperatorAuthenticationKey:tx.operatorKey foundAtIndex:nil]) return TRUE; + if ([self walletHavingPlatformNodeAuthenticationHash:tx.platformNodeID foundAtIndex:nil]) return TRUE; + if ([self walletContainingMasternodeHoldingAddressForProviderRegistrationTransaction:tx foundAtIndex:nil]) return TRUE; + if ([self accountContainingAddress:tx.payoutAddress]) return TRUE; + } else if ([transaction isKindOfClass:[DSProviderUpdateServiceTransaction class]]) { + DSProviderUpdateServiceTransaction *tx = (DSProviderUpdateServiceTransaction *)transaction; + if ([self transactionForHash:tx.providerRegistrationTransactionHash]) return TRUE; + if ([self accountContainingAddress:tx.payoutAddress]) return TRUE; + } else if ([transaction isKindOfClass:[DSProviderUpdateRegistrarTransaction class]]) { + DSProviderUpdateRegistrarTransaction *tx = (DSProviderUpdateRegistrarTransaction *)transaction; + if ([self walletHavingProviderVotingAuthenticationHash:tx.votingKeyHash foundAtIndex:nil]) return TRUE; + if ([self walletHavingProviderOperatorAuthenticationKey:tx.operatorKey foundAtIndex:nil]) return TRUE; + if ([self transactionForHash:tx.providerRegistrationTransactionHash]) return TRUE; + if ([self accountContainingAddress:tx.payoutAddress]) return TRUE; + } else if ([transaction isKindOfClass:[DSProviderUpdateRevocationTransaction class]]) { + DSProviderUpdateRevocationTransaction *tx = (DSProviderUpdateRevocationTransaction *)transaction; + if ([self transactionForHash:tx.providerRegistrationTransactionHash]) return TRUE; + + //BLOCKCHAIN USERS + } + // else if ([transaction isKindOfClass:[DSIdentityRegistrationTransition class]]) { + // DSIdentityRegistrationTransition * identityRegistrationTransaction = (DSIdentityRegistrationTransition *)transaction; + // if ([self walletHavingIdentityAuthenticationHash:identityRegistrationTransaction.pubkeyHash foundAtIndex:nil]) return TRUE; + // } else if ([transaction isKindOfClass:[DSIdentityUpdateTransition class]]) { + // DSIdentityUpdateTransition * identityResetTransaction = (DSIdentityUpdateTransition *)transaction; + // if ([self walletHavingIdentityAuthenticationHash:identityResetTransaction.replacementPublicKeyHash foundAtIndex:nil]) return TRUE; + // if ([self transactionForHash:identityResetTransaction.registrationTransactionHash]) return TRUE; + // } else if ([transaction isKindOfClass:[DSIdentityCloseTransition class]]) { + // DSIdentityCloseTransition * identityCloseTransaction = (DSIdentityCloseTransition *)transaction; + // if ([self transactionForHash:identityCloseTransaction.registrationTransactionHash]) return TRUE; + // } else if ([transaction isKindOfClass:[DSIdentityTopupTransition class]]) { + // DSIdentityTopupTransition * identityTopupTransaction = (DSIdentityTopupTransition *)transaction; + // if ([self transactionForHash:identityTopupTransaction.registrationTransactionHash]) return TRUE; + // } + return FALSE; +} + +// MARK: - Registering special transactions + + +- (BOOL)registerProviderRegistrationTransaction:(DSProviderRegistrationTransaction *)providerRegistrationTransaction + saveImmediately:(BOOL)saveImmediately { + DSWallet *ownerWallet = [self walletHavingProviderOwnerAuthenticationHash:providerRegistrationTransaction.ownerKeyHash foundAtIndex:nil]; + DSWallet *votingWallet = [self walletHavingProviderVotingAuthenticationHash:providerRegistrationTransaction.votingKeyHash foundAtIndex:nil]; + DSWallet *operatorWallet = [self walletHavingProviderOperatorAuthenticationKey:providerRegistrationTransaction.operatorKey foundAtIndex:nil]; + DSWallet *holdingWallet = [self walletContainingMasternodeHoldingAddressForProviderRegistrationTransaction:providerRegistrationTransaction foundAtIndex:nil]; + DSWallet *platformNodeWallet = [self walletHavingPlatformNodeAuthenticationHash:providerRegistrationTransaction.platformNodeID foundAtIndex:nil]; + DSAccount *account = [self accountContainingAddress:providerRegistrationTransaction.payoutAddress]; + BOOL registered = NO; + registered |= [account registerTransaction:providerRegistrationTransaction saveImmediately:saveImmediately]; + registered |= [ownerWallet.specialTransactionsHolder registerTransaction:providerRegistrationTransaction saveImmediately:saveImmediately]; + registered |= [votingWallet.specialTransactionsHolder registerTransaction:providerRegistrationTransaction saveImmediately:saveImmediately]; + registered |= [operatorWallet.specialTransactionsHolder registerTransaction:providerRegistrationTransaction saveImmediately:saveImmediately]; + registered |= [holdingWallet.specialTransactionsHolder registerTransaction:providerRegistrationTransaction saveImmediately:saveImmediately]; + registered |= [platformNodeWallet.specialTransactionsHolder registerTransaction:providerRegistrationTransaction saveImmediately:saveImmediately]; + + if (ownerWallet) { + DSAuthenticationKeysDerivationPath *ownerDerivationPath = [[DSDerivationPathFactory sharedInstance] providerOwnerKeysDerivationPathForWallet:ownerWallet]; + [ownerDerivationPath registerTransactionAddress:providerRegistrationTransaction.ownerAddress]; + } + + if (votingWallet) { + DSAuthenticationKeysDerivationPath *votingDerivationPath = [[DSDerivationPathFactory sharedInstance] providerVotingKeysDerivationPathForWallet:votingWallet]; + [votingDerivationPath registerTransactionAddress:providerRegistrationTransaction.votingAddress]; + } + + if (operatorWallet) { + DSAuthenticationKeysDerivationPath *operatorDerivationPath = [[DSDerivationPathFactory sharedInstance] providerOperatorKeysDerivationPathForWallet:operatorWallet]; + [operatorDerivationPath registerTransactionAddress:providerRegistrationTransaction.operatorAddress]; + } + + if (holdingWallet) { + DSMasternodeHoldingsDerivationPath *holdingDerivationPath = [[DSDerivationPathFactory sharedInstance] providerFundsDerivationPathForWallet:holdingWallet]; + [holdingDerivationPath registerTransactionAddress:providerRegistrationTransaction.holdingAddress]; + } + + if (platformNodeWallet) { + DSAuthenticationKeysDerivationPath *platformNodeDerivationPath = [[DSDerivationPathFactory sharedInstance] platformNodeKeysDerivationPathForWallet:platformNodeWallet]; + [platformNodeDerivationPath registerTransactionAddress:providerRegistrationTransaction.platformNodeAddress]; + } + + return registered; +} + +- (BOOL)registerProviderUpdateServiceTransaction:(DSProviderUpdateServiceTransaction *)providerUpdateServiceTransaction saveImmediately:(BOOL)saveImmediately { + DSWallet *providerRegistrationWallet = nil; + DSTransaction *providerRegistrationTransaction = [self transactionForHash:providerUpdateServiceTransaction.providerRegistrationTransactionHash returnWallet:&providerRegistrationWallet]; + DSAccount *account = [self accountContainingAddress:providerUpdateServiceTransaction.payoutAddress]; + BOOL registered = [account registerTransaction:providerUpdateServiceTransaction saveImmediately:saveImmediately]; + if (providerRegistrationTransaction && providerRegistrationWallet) { + registered |= [providerRegistrationWallet.specialTransactionsHolder registerTransaction:providerUpdateServiceTransaction saveImmediately:saveImmediately]; + } + return registered; +} + + +- (BOOL)registerProviderUpdateRegistrarTransaction:(DSProviderUpdateRegistrarTransaction *)providerUpdateRegistrarTransaction saveImmediately:(BOOL)saveImmediately { + DSWallet *votingWallet = [self walletHavingProviderVotingAuthenticationHash:providerUpdateRegistrarTransaction.votingKeyHash foundAtIndex:nil]; + DSWallet *operatorWallet = [self walletHavingProviderOperatorAuthenticationKey:providerUpdateRegistrarTransaction.operatorKey foundAtIndex:nil]; + [votingWallet.specialTransactionsHolder registerTransaction:providerUpdateRegistrarTransaction saveImmediately:saveImmediately]; + [operatorWallet.specialTransactionsHolder registerTransaction:providerUpdateRegistrarTransaction saveImmediately:saveImmediately]; + DSWallet *providerRegistrationWallet = nil; + DSTransaction *providerRegistrationTransaction = [self transactionForHash:providerUpdateRegistrarTransaction.providerRegistrationTransactionHash returnWallet:&providerRegistrationWallet]; + DSAccount *account = [self accountContainingAddress:providerUpdateRegistrarTransaction.payoutAddress]; + BOOL registered = [account registerTransaction:providerUpdateRegistrarTransaction saveImmediately:saveImmediately]; + if (providerRegistrationTransaction && providerRegistrationWallet) { + registered |= [providerRegistrationWallet.specialTransactionsHolder registerTransaction:providerUpdateRegistrarTransaction saveImmediately:saveImmediately]; + } + + if (votingWallet) { + DSAuthenticationKeysDerivationPath *votingDerivationPath = [[DSDerivationPathFactory sharedInstance] providerVotingKeysDerivationPathForWallet:votingWallet]; + [votingDerivationPath registerTransactionAddress:providerUpdateRegistrarTransaction.votingAddress]; + } + + if (operatorWallet) { + DSAuthenticationKeysDerivationPath *operatorDerivationPath = [[DSDerivationPathFactory sharedInstance] providerOperatorKeysDerivationPathForWallet:operatorWallet]; + [operatorDerivationPath registerTransactionAddress:providerUpdateRegistrarTransaction.operatorAddress]; + } + return registered; +} + +- (BOOL)registerProviderUpdateRevocationTransaction:(DSProviderUpdateRevocationTransaction *)providerUpdateRevocationTransaction saveImmediately:(BOOL)saveImmediately { + DSWallet *providerRegistrationWallet = nil; + DSTransaction *providerRegistrationTransaction = [self transactionForHash:providerUpdateRevocationTransaction.providerRegistrationTransactionHash returnWallet:&providerRegistrationWallet]; + if (providerRegistrationTransaction && providerRegistrationWallet) { + return [providerRegistrationWallet.specialTransactionsHolder registerTransaction:providerUpdateRevocationTransaction saveImmediately:saveImmediately]; + } else { + return NO; + } +} + +//-(BOOL)registerTransition:(DSTransition*)transition { +// DSWallet * identityRegistrationWallet = nil; +// DSTransaction * identityRegistrationTransaction = [self transactionForHash:transition.registrationTransactionHash returnWallet:&identityRegistrationWallet]; +// if (identityRegistrationTransaction && identityRegistrationWallet) { +// return [identityRegistrationWallet.specialTransactionsHolder registerTransaction:transition]; +// } else { +// return NO; +// } +//} + +- (BOOL)registerSpecialTransaction:(DSTransaction *)transaction saveImmediately:(BOOL)saveImmediately { + if ([transaction isKindOfClass:[DSProviderRegistrationTransaction class]]) { + DSProviderRegistrationTransaction *providerRegistrationTransaction = (DSProviderRegistrationTransaction *)transaction; + return [self registerProviderRegistrationTransaction:providerRegistrationTransaction saveImmediately:saveImmediately]; + } else if ([transaction isKindOfClass:[DSProviderUpdateServiceTransaction class]]) { + DSProviderUpdateServiceTransaction *providerUpdateServiceTransaction = (DSProviderUpdateServiceTransaction *)transaction; + return [self registerProviderUpdateServiceTransaction:providerUpdateServiceTransaction saveImmediately:saveImmediately]; + } else if ([transaction isKindOfClass:[DSProviderUpdateRegistrarTransaction class]]) { + DSProviderUpdateRegistrarTransaction *providerUpdateRegistrarTransaction = (DSProviderUpdateRegistrarTransaction *)transaction; + return [self registerProviderUpdateRegistrarTransaction:providerUpdateRegistrarTransaction saveImmediately:saveImmediately]; + } else if ([transaction isKindOfClass:[DSProviderUpdateRevocationTransaction class]]) { + DSProviderUpdateRevocationTransaction *providerUpdateRevocationTransaction = (DSProviderUpdateRevocationTransaction *)transaction; + return [self registerProviderUpdateRevocationTransaction:providerUpdateRevocationTransaction saveImmediately:saveImmediately]; + } + return FALSE; +} + + + +- (void)triggerUpdatesForLocalReferences:(DSTransaction *)transaction { + if ([transaction isKindOfClass:[DSProviderRegistrationTransaction class]]) { + DSProviderRegistrationTransaction *providerRegistrationTransaction = (DSProviderRegistrationTransaction *)transaction; + if ([self walletHavingProviderOwnerAuthenticationHash:providerRegistrationTransaction.ownerKeyHash foundAtIndex:nil] || + [self walletHavingProviderVotingAuthenticationHash:providerRegistrationTransaction.votingKeyHash foundAtIndex:nil] || + [self walletHavingProviderOperatorAuthenticationKey:providerRegistrationTransaction.operatorKey foundAtIndex:nil] || + [self walletHavingPlatformNodeAuthenticationHash:providerRegistrationTransaction.platformNodeID foundAtIndex:nil]) { + [self.chainManager.masternodeManager localMasternodeFromProviderRegistrationTransaction:providerRegistrationTransaction save:TRUE]; + } + } else if ([transaction isKindOfClass:[DSProviderUpdateServiceTransaction class]]) { + DSProviderUpdateServiceTransaction *tx = (DSProviderUpdateServiceTransaction *)transaction; + DSLocalMasternode *localMasternode = [self.chainManager.masternodeManager localMasternodeHavingProviderRegistrationTransactionHash:tx.providerRegistrationTransactionHash]; + [localMasternode updateWithUpdateServiceTransaction:tx save:TRUE]; + } else if ([transaction isKindOfClass:[DSProviderUpdateRegistrarTransaction class]]) { + DSProviderUpdateRegistrarTransaction *tx = (DSProviderUpdateRegistrarTransaction *)transaction; + DSLocalMasternode *localMasternode = [self.chainManager.masternodeManager localMasternodeHavingProviderRegistrationTransactionHash:tx.providerRegistrationTransactionHash]; + [localMasternode updateWithUpdateRegistrarTransaction:tx save:TRUE]; + } else if ([transaction isKindOfClass:[DSProviderUpdateRevocationTransaction class]]) { + DSProviderUpdateRevocationTransaction *tx = (DSProviderUpdateRevocationTransaction *)transaction; + DSLocalMasternode *localMasternode = [self.chainManager.masternodeManager localMasternodeHavingProviderRegistrationTransactionHash:tx.providerRegistrationTransactionHash]; + [localMasternode updateWithUpdateRevocationTransaction:tx save:TRUE]; + } else if ([transaction isKindOfClass:[DSAssetLockTransaction class]]) { + DSAssetLockTransaction *tx = (DSAssetLockTransaction *)transaction; + uint32_t index; + DSWallet *wallet = [self walletHavingIdentityAssetLockRegistrationHash:tx.creditBurnPublicKeyHash foundAtIndex:&index]; + if (wallet) { + DSIdentity *identity = [wallet identityForUniqueId:tx.creditBurnIdentityIdentifier]; + if (!identity) { + identity = [[DSIdentity alloc] initAtIndex:index withAssetLockTransaction:tx withUsernameDictionary:nil inWallet:wallet]; + [identity registerInWalletForAssetLockTransaction:tx]; + } + } else { + wallet = [self walletHavingIdentityAssetLockInvitationHash:tx.creditBurnPublicKeyHash foundAtIndex:&index]; + if (wallet) { + DSInvitation *invitation = [wallet invitationForUniqueId:tx.creditBurnIdentityIdentifier]; + if (!invitation) { + invitation = [[DSInvitation alloc] initAtIndex:index withAssetLockTransaction:tx inWallet:wallet]; + [invitation registerInWalletForAssetLockTransaction:tx]; + } + } + } + + } +// else if ([transaction isKindOfClass:[DSAssetUnlockTransaction class]]) { +// DSAssetUnlockTransaction *tx = (DSAssetUnlockTransaction *)transaction; +// } +} + +@end diff --git a/DashSync/shared/Models/Chain/DSChain+Wallet.h b/DashSync/shared/Models/Chain/DSChain+Wallet.h new file mode 100644 index 000000000..9982b4e7b --- /dev/null +++ b/DashSync/shared/Models/Chain/DSChain+Wallet.h @@ -0,0 +1,105 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "DSWallet.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSChain (Wallet) + +// MARK: - Wallets + +/*! @brief The wallets in the chain. */ +@property (nonatomic, readonly) NSArray *wallets; +@property (nonatomic, strong) NSMutableArray *mWallets; + +/*! @brief Conveniance method. Does this walleet have a chain? */ +@property (nonatomic, readonly) BOOL hasAWallet; + +/*! @brief Conveniance method. The earliest known creation time for any wallet in this chain. */ +@property (nonatomic, readonly) NSTimeInterval earliestWalletCreationTime; + +/*! @brief Add a wallet to the chain. It is only temporarily in the chain if externaly added this way. */ +- (BOOL)addWallet:(DSWallet *)wallet; + +/*! @brief Unregister a wallet from the chain, it will no longer be loaded or used. */ +- (void)unregisterWallet:(DSWallet *)wallet; + +/*! @brief Register a wallet to the chain. */ +- (void)registerWallet:(DSWallet *)wallet; + +/*! @brief Unregister all wallets from the chain, they will no longer be loaded or used. */ +- (void)unregisterAllWallets; + +/*! @brief Unregister all wallets from the chain that don't have an extended public key in one of their derivation paths, they will no longer be loaded or used. */ +- (void)unregisterAllWalletsMissingExtendedPublicKeys; + +// MARK: Wallet Discovery + +- (DSWallet *_Nullable)walletHavingIdentityAssetLockRegistrationHash:(UInt160)hash + foundAtIndex:(uint32_t *_Nullable)rIndex; +- (DSWallet *_Nullable)walletHavingIdentityAssetLockTopupHash:(UInt160)hash + foundAtIndex:(uint32_t *)rIndex; +- (DSWallet *_Nullable)walletHavingIdentityAssetLockInvitationHash:(UInt160)hash + foundAtIndex:(uint32_t *)rIndex; +- (DSWallet *_Nullable)walletHavingProviderVotingAuthenticationHash:(UInt160)hash + foundAtIndex:(uint32_t *_Nullable)rIndex; +- (DSWallet *_Nullable)walletHavingProviderOwnerAuthenticationHash:(UInt160)hash + foundAtIndex:(uint32_t *_Nullable)rIndex; +- (DSWallet *_Nullable)walletHavingProviderOperatorAuthenticationKey:(UInt384)providerOperatorAuthenticationKey + foundAtIndex:(uint32_t *_Nullable)rIndex; +- (DSWallet *_Nullable)walletHavingPlatformNodeAuthenticationHash:(UInt160)hash + foundAtIndex:(uint32_t *_Nullable)rIndex; +- (DSWallet *_Nullable)walletContainingMasternodeHoldingAddressForProviderRegistrationTransaction:(DSProviderRegistrationTransaction *_Nonnull)transaction + foundAtIndex:(uint32_t *_Nullable)rIndex; + +// MARK: - Accounts and Balances + +/*! @brief The current wallet balance excluding transactions known to be invalid. */ +@property (nonatomic, readonly) uint64_t balance; + +/*! @brief All accounts that contain the specified transaction hash. The transaction is also returned if it is found. */ +- (NSArray *)accountsForTransactionHash:(UInt256)txHash + transaction:(DSTransaction *_Nullable *_Nullable)transaction; + +/*! @brief Returns the first account with a balance. */ +- (DSAccount *_Nullable)firstAccountWithBalance; + +/*! @brief Returns an account to which the given transaction is or can be associated with (even if it hasn't been registered), no account if the transaction is not associated with the wallet. */ +- (DSAccount *_Nullable)firstAccountThatCanContainTransaction:(DSTransaction *)transaction; + +/*! @brief Returns all accounts to which the given transaction is or can be associated with (even if it hasn't been registered) */ +- (NSArray *)accountsThatCanContainTransaction:(DSTransaction *_Nonnull)transaction; + +/*! @brief Returns an account to which the given transaction hash is associated with, no account if the transaction hash is not associated with the wallet. */ +- (DSAccount *_Nullable)firstAccountForTransactionHash:(UInt256)txHash + transaction:(DSTransaction *_Nullable *_Nullable)transaction + wallet:(DSWallet *_Nullable *_Nullable)wallet; + +/*! @brief Returns an account to which the given address is contained in a derivation path. */ +- (DSAccount *_Nullable)accountContainingAddress:(NSString *)address; + +/*! @brief Returns an account to which the given address is known by a dashpay outgoing derivation path. */ +- (DSAccount *_Nullable)accountContainingDashpayExternalDerivationPathAddress:(NSString *)address; + +// MARK: Protected +- (void)reloadDerivationPaths; +- (void)retrieveWallets; +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Chain/DSChain+Wallet.m b/DashSync/shared/Models/Chain/DSChain+Wallet.m new file mode 100644 index 000000000..22aa5c916 --- /dev/null +++ b/DashSync/shared/Models/Chain/DSChain+Wallet.m @@ -0,0 +1,352 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DSAccount.h" +#import "DSChain+Params.h" +#import "DSChain+Protected.h" +#import "DSChain+Wallet.h" +#import "DSChainManager.h" +#import "DSProviderRegistrationTransaction.h" +#import "DSTransactionOutput.h" +#import "DSWallet+Identity.h" +#import "DSWallet+Protected.h" + +#define CHAIN_WALLETS_KEY @"CHAIN_WALLETS_KEY" + +NSString const *mWalletsKey = @"mWalletsKey"; + +@implementation DSChain (Wallet) + +- (NSString *)chainWalletsKey { + return [NSString stringWithFormat:@"%@_%@", CHAIN_WALLETS_KEY, [self uniqueID]]; +} + +// This is a time interval since 1970 +- (NSTimeInterval)earliestWalletCreationTime { + if (![self.wallets count]) return BIP39_CREATION_TIME; + NSTimeInterval timeInterval = [[NSDate date] timeIntervalSince1970]; + for (DSWallet *wallet in self.wallets) { + if (timeInterval > wallet.walletCreationTime) { + timeInterval = wallet.walletCreationTime; + } + } + return timeInterval; +} + +- (void)reloadDerivationPaths { + for (DSWallet *wallet in self.mWallets) { + if (!wallet.isTransient) { //no need to reload transient wallets (those are for testing purposes) + [wallet reloadDerivationPaths]; + } + } +} + +// MARK: - Wallet + +- (NSMutableArray *)mWallets { + return objc_getAssociatedObject(self, &mWalletsKey); +} +- (void)setMWallets:(NSMutableArray *)mWallets { + objc_setAssociatedObject(self, &mWalletsKey, mWallets, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + + +- (BOOL)hasAWallet { + return [self.mWallets count] > 0; +} + +- (NSArray *)wallets { + return [self.mWallets copy]; +} + +- (void)unregisterAllWallets { + for (DSWallet *wallet in [self.mWallets copy]) { + [self unregisterWallet:wallet]; + } +} + +- (void)unregisterAllWalletsMissingExtendedPublicKeys { + for (DSWallet *wallet in [self.mWallets copy]) { + if ([wallet hasAnExtendedPublicKeyMissing]) { + [self unregisterWallet:wallet]; + } + } +} + +- (void)unregisterWallet:(DSWallet *)wallet { + NSAssert(wallet.chain == self, @"the wallet you are trying to remove is not on this chain"); + [wallet wipeBlockchainInfoInContext:self.chainManagedObjectContext]; + [wallet wipeWalletInfo]; + [self.mWallets removeObject:wallet]; + NSError *error = nil; + NSMutableArray *keyChainArray = [getKeychainArray(self.chainWalletsKey, @[[NSString class]], &error) mutableCopy]; + if (!keyChainArray) keyChainArray = [NSMutableArray array]; + [keyChainArray removeObject:wallet.uniqueIDString]; + setKeychainArray(keyChainArray, self.chainWalletsKey, NO); + [self notify:DSChainWalletsDidChangeNotification userInfo:@{DSChainManagerNotificationChainKey: self}]; +} + +- (BOOL)addWallet:(DSWallet *)walletToAdd { + BOOL alreadyPresent = FALSE; + for (DSWallet *cWallet in self.mWallets) { + if ([cWallet.uniqueIDString isEqual:walletToAdd.uniqueIDString]) + alreadyPresent = TRUE; + } + if (!alreadyPresent) { + [self.mWallets addObject:walletToAdd]; + return TRUE; + } + return FALSE; +} + +- (void)registerWallet:(DSWallet *)wallet { + BOOL firstWallet = !self.mWallets.count; + if ([self.mWallets indexOfObject:wallet] == NSNotFound) { + [self addWallet:wallet]; + } + + if (firstWallet) { + //this is the first wallet, we should reset the last block height to the most recent checkpoint. + //it will lazy load later + [self resetLastSyncBlock]; + } + + NSError *error = nil; + NSMutableArray *keyChainArray = [getKeychainArray(self.chainWalletsKey, @[[NSString class]], &error) mutableCopy]; + if (!keyChainArray) keyChainArray = [NSMutableArray array]; + if (![keyChainArray containsObject:wallet.uniqueIDString]) { + [keyChainArray addObject:wallet.uniqueIDString]; + setKeychainArray(keyChainArray, self.chainWalletsKey, NO); + [self notify:DSChainWalletsDidChangeNotification userInfo:@{DSChainManagerNotificationChainKey: self}]; + } +} + +- (void)retrieveWallets { + NSError *error = nil; + NSArray *walletIdentifiers = getKeychainArray(self.chainWalletsKey, @[[NSString class]], &error); + if (!error && walletIdentifiers) { + for (NSString *uniqueID in walletIdentifiers) { + DSWallet *wallet = [[DSWallet alloc] initWithUniqueID:uniqueID forChain:self]; + [self addWallet:wallet]; + } + //we should load blockchain identies after all wallets are in the chain, as blockchain identities might be on different wallets and have interactions between each other + for (DSWallet *wallet in self.wallets) { + [wallet loadIdentities]; + } + } +} + +// MARK: - Merging Wallets + +- (DSWallet *)walletHavingIdentityAssetLockRegistrationHash:(UInt160)hash + foundAtIndex:(uint32_t *)rIndex { + for (DSWallet *wallet in self.wallets) { + NSUInteger index = [wallet indexOfIdentityAssetLockRegistrationHash:hash]; + if (index != NSNotFound) { + if (rIndex) *rIndex = (uint32_t)index; + return wallet; + } + } + if (rIndex) *rIndex = UINT32_MAX; + return nil; +} + +- (DSWallet *)walletHavingIdentityAssetLockTopupHash:(UInt160)hash + foundAtIndex:(uint32_t *)rIndex { + for (DSWallet *wallet in self.wallets) { + NSUInteger index = [wallet indexOfIdentityAssetLockTopupHash:hash]; + if (index != NSNotFound) { + if (rIndex) *rIndex = (uint32_t)index; + return wallet; + } + } + if (rIndex) *rIndex = UINT32_MAX; + return nil; +} + +- (DSWallet *)walletHavingIdentityAssetLockInvitationHash:(UInt160)hash + foundAtIndex:(uint32_t *)rIndex { + for (DSWallet *wallet in self.wallets) { + NSUInteger index = [wallet indexOfIdentityAssetLockInvitationHash:hash]; + if (index != NSNotFound) { + if (rIndex) *rIndex = (uint32_t)index; + return wallet; + } + } + if (rIndex) *rIndex = UINT32_MAX; + return nil; +} + +- (DSWallet *)walletHavingProviderVotingAuthenticationHash:(UInt160)hash + foundAtIndex:(uint32_t *)rIndex { + for (DSWallet *wallet in self.wallets) { + NSUInteger index = [wallet indexOfProviderVotingAuthenticationHash:hash]; + if (index != NSNotFound) { + if (rIndex) *rIndex = (uint32_t)index; + return wallet; + } + } + if (rIndex) *rIndex = UINT32_MAX; + return nil; +} + +- (DSWallet *_Nullable)walletHavingProviderOwnerAuthenticationHash:(UInt160)hash + foundAtIndex:(uint32_t *)rIndex { + for (DSWallet *wallet in self.wallets) { + NSUInteger index = [wallet indexOfProviderOwningAuthenticationHash:hash]; + if (index != NSNotFound) { + if (rIndex) *rIndex = (uint32_t)index; + return wallet; + } + } + if (rIndex) *rIndex = UINT32_MAX; + return nil; +} + +- (DSWallet *_Nullable)walletHavingProviderOperatorAuthenticationKey:(UInt384)providerOperatorAuthenticationKey + foundAtIndex:(uint32_t *)rIndex { + for (DSWallet *wallet in self.wallets) { + NSUInteger index = [wallet indexOfProviderOperatorAuthenticationKey:providerOperatorAuthenticationKey]; + if (index != NSNotFound) { + if (rIndex) *rIndex = (uint32_t)index; + return wallet; + } + } + if (rIndex) *rIndex = UINT32_MAX; + return nil; +} + +- (DSWallet *_Nullable)walletHavingPlatformNodeAuthenticationHash:(UInt160)hash + foundAtIndex:(uint32_t *)rIndex { + for (DSWallet *wallet in self.wallets) { + NSUInteger index = [wallet indexOfPlatformNodeAuthenticationHash:hash]; + if (index != NSNotFound) { + if (rIndex) *rIndex = (uint32_t)index; + return wallet; + } + } + if (rIndex) *rIndex = UINT32_MAX; + return nil; +} + +- (DSWallet *_Nullable)walletContainingMasternodeHoldingAddressForProviderRegistrationTransaction:(DSProviderRegistrationTransaction *_Nonnull)transaction + foundAtIndex:(uint32_t *)rIndex { + for (DSWallet *wallet in self.wallets) { + for (DSTransactionOutput *output in transaction.outputs) { + NSString *address = output.address; + if (!address || address == (id)[NSNull null]) continue; + NSUInteger index = [wallet indexOfHoldingAddress:address]; + if (index != NSNotFound) { + if (rIndex) *rIndex = (uint32_t)index; + return wallet; + } + } + } + if (rIndex) *rIndex = UINT32_MAX; + return nil; +} + +// MARK: - Accounts + +- (uint64_t)balance { + uint64_t rBalance = 0; + for (DSWallet *wallet in self.wallets) { + rBalance += wallet.balance; + } + for (DSDerivationPath *standaloneDerivationPath in self.standaloneDerivationPaths) { + rBalance += standaloneDerivationPath.balance; + } + return rBalance; +} + +- (DSAccount *_Nullable)firstAccountWithBalance { + for (DSWallet *wallet in self.wallets) { + DSAccount *account = [wallet firstAccountWithBalance]; + if (account) return account; + } + return nil; +} + +- (DSAccount *_Nullable)firstAccountThatCanContainTransaction:(DSTransaction *)transaction { + if (!transaction) return nil; + for (DSWallet *wallet in self.wallets) { + DSAccount *account = [wallet firstAccountThatCanContainTransaction:transaction]; + if (account) return account; + } + return nil; +} + +- (NSArray *)accountsThatCanContainTransaction:(DSTransaction *)transaction { + NSMutableArray *mArray = [NSMutableArray array]; + if (!transaction) return @[]; + for (DSWallet *wallet in self.wallets) { + [mArray addObjectsFromArray:[wallet accountsThatCanContainTransaction:transaction]]; + } + return [mArray copy]; +} + +- (DSAccount *_Nullable)accountContainingAddress:(NSString *)address { + if (!address) return nil; + for (DSWallet *wallet in self.wallets) { + DSAccount *account = [wallet accountForAddress:address]; + if (account) return account; + } + return nil; +} + +- (DSAccount *_Nullable)accountContainingDashpayExternalDerivationPathAddress:(NSString *)address { + if (!address) return nil; + for (DSWallet *wallet in self.wallets) { + DSAccount *account = [wallet accountForDashpayExternalDerivationPathAddress:address]; + if (account) return account; + } + return nil; +} + +// returns an account to which the given transaction hash is associated with, no account if the transaction hash is not associated with the wallet +- (DSAccount *_Nullable)firstAccountForTransactionHash:(UInt256)txHash + transaction:(DSTransaction **)transaction + wallet:(DSWallet **)wallet { + for (DSWallet *lWallet in self.wallets) { + for (DSAccount *account in lWallet.accounts) { + DSTransaction *lTransaction = [account transactionForHash:txHash]; + if (lTransaction) { + if (transaction) *transaction = lTransaction; + if (wallet) *wallet = lWallet; + return account; + } + } + } + return nil; +} + +// returns an account to which the given transaction hash is associated with, no account if the transaction hash is not associated with the wallet +- (NSArray *)accountsForTransactionHash:(UInt256)txHash + transaction:(DSTransaction **)transaction { + NSMutableArray *accounts = [NSMutableArray array]; + for (DSWallet *lWallet in self.wallets) { + for (DSAccount *account in lWallet.accounts) { + DSTransaction *lTransaction = [account transactionForHash:txHash]; + if (lTransaction) { + if (transaction) *transaction = lTransaction; + [accounts addObject:account]; + } + } + } + return [accounts copy]; +} +@end diff --git a/DashSync/shared/Models/Chain/DSChain.h b/DashSync/shared/Models/Chain/DSChain.h index 4537474e8..39033d39b 100644 --- a/DashSync/shared/Models/Chain/DSChain.h +++ b/DashSync/shared/Models/Chain/DSChain.h @@ -26,6 +26,8 @@ #import "BigIntTypes.h" #import "dash_shared_core.h" #import "DSChainConstants.h" +#import "DSDashSharedCore.h" +#import "DSTransaction.h" #import #import @@ -41,13 +43,13 @@ FOUNDATION_EXPORT NSString *const DSChainNotificationBlockKey; FOUNDATION_EXPORT NSString *const DSChainInitialHeadersDidFinishSyncingNotification; FOUNDATION_EXPORT NSString *const DSChainBlocksDidFinishSyncingNotification; -typedef NS_ENUM(NSUInteger, DSTransactionDirection) -{ - DSTransactionDirection_Sent, - DSTransactionDirection_Received, - DSTransactionDirection_Moved, - DSTransactionDirection_NotAccountFunds, -}; +//typedef NS_ENUM(NSUInteger, DSTransactionDirection) +//{ +// DSTransactionDirection_Sent, +// DSTransactionDirection_Received, +// DSTransactionDirection_Moved, +// DSTransactionDirection_NotAccountFunds, +//}; typedef NS_ENUM(uint16_t, DSChainSyncPhase) { @@ -57,7 +59,7 @@ typedef NS_ENUM(uint16_t, DSChainSyncPhase) DSChainSyncPhase_Synced, }; -@class DSChain, DSChainEntity, DSChainManager, DSWallet, DSMerkleBlock, DSBlock, DSFullBlock, DSPeer, DSDerivationPath, DSTransaction, DSAccount, DSSimplifiedMasternodeEntry, DSBlockchainIdentity, DSBloomFilter, DSProviderRegistrationTransaction, DSMasternodeList, DPContract, DSCheckpoint, DSChainLock; +@class DSChain, DSChainEntity, DSChainManager, DSWallet, DSMerkleBlock, DSBlock, DSFullBlock, DSPeer, DSDerivationPath, DSTransaction, DSAccount, DSIdentity, DSBloomFilter, DSProviderRegistrationTransaction, DPContract, DSCheckpoint, DSChainLock, DSDashSharedCore, DSMasternodeManager; @protocol DSChainDelegate; @@ -67,6 +69,11 @@ typedef NS_ENUM(uint16_t, DSChainSyncPhase) /*! @brief The chain manager is a container for all managers (peer, identity, governance, masternode, spork and transition). It also is used to control the sync process. */ @property (nonatomic, weak, nullable) DSChainManager *chainManager; +@property (nonatomic, weak, nullable) DSMasternodeManager *masternodeManager; +// MARK: - Shortcuts + +/*! @brief The shared core is a container for all stuff related to rust dash-shared-core. */ +@property (nonatomic, nullable) DSDashSharedCore *shareCore; /*! @brief The chain entity associated in Core Data in the required context. */ - (DSChainEntity *)chainEntityInContext:(NSManagedObjectContext *)context; @@ -76,91 +83,14 @@ typedef NS_ENUM(uint16_t, DSChainSyncPhase) // MARK: - L1 Network Chain Info -/*! @brief The network name. Currently main, test, dev or reg. */ -@property (nonatomic, readonly) NSString *networkName; - -/*! @brief An array of known hard coded checkpoints for the chain. */ -@property (nonatomic, readonly) NSArray *checkpoints; - -// MARK: Sync - -/*! @brief The genesis hash is the hash of the first block of the chain. For a devnet this is actually the second block as the first block is created in a special way for devnets. */ -@property (nonatomic, readonly) UInt256 genesisHash; - -/*! @brief The magic number is used in message headers to indicate what network (or chain) a message is intended for. */ -@property (nonatomic, readonly) uint32_t magicNumber; - -/*! @brief The base reward is the intial mining reward at genesis for the chain. This goes down by 7% every year. A SPV client does not validate that the reward amount is correct as it would not make sense for miners to enter incorrect rewards as the blocks would be rejected by full nodes. */ -@property (nonatomic, readonly) uint64_t baseReward; - -/*! @brief minProtocolVersion is the minimum protocol version that peers on this chain can communicate with. This should only be changed in the case of devnets. */ -@property (nonatomic, assign) uint32_t minProtocolVersion; - -/*! @brief protocolVersion is the protocol version that we currently use for this chain. This should only be changed in the case of devnets. */ -@property (nonatomic, assign) uint32_t protocolVersion; - -/*! @brief headersMaxAmount is the maximum amount of headers that is expected from peers. */ -@property (nonatomic, assign) uint64_t headersMaxAmount; - -/*! @brief maxProofOfWork is the lowest amount of work effort required to mine a block on the chain. */ -@property (nonatomic, readonly) UInt256 maxProofOfWork; - -/*! @brief maxProofOfWorkTarget is the lowest amount of work effort required to mine a block on the chain. Here it is represented as the compact target. */ -@property (nonatomic, readonly) uint32_t maxProofOfWorkTarget; - -/*! @brief allowMinDifficultyBlocks is set to TRUE on networks where mining is low enough that it can be attacked by increasing difficulty with ASICs and then no longer running ASICs. This is set to NO for Mainnet, and generally should be YES on all other networks. */ -@property (nonatomic, readonly) BOOL allowMinDifficultyBlocks; - -/*! @brief This is the minimum amount that can be entered into an amount for a output for it not to be considered dust. */ -@property (nonatomic, readonly) uint64_t minOutputAmount; - -// MARK: Fees - -@property (nonatomic, assign) uint64_t feePerByte; - -/*! @brief The fee for transactions in L1 are now entirely dependent on their size. */ -- (uint64_t)feeForTxSize:(NSUInteger)size; +///*! @brief An array of known hard coded checkpoints for the chain. */ +//@property (nonatomic, readonly) NSArray *checkpoints; -// MARK: Ports -/*! @brief The standard port for the chain for L1 communication. */ -@property (nonatomic, assign) uint32_t standardPort; -/*! @brief The standard port for the chain for L2 communication through JRPC. */ -@property (nonatomic, assign) uint32_t standardDapiJRPCPort; - -/*! @brief The standard port for the chain for L2 communication through GRPC. */ -@property (nonatomic, assign) uint32_t standardDapiGRPCPort; - -// MARK: Sporks - -/*! @brief The spork public key as a hex string. */ -@property (nonatomic, strong, nullable) NSString *sporkPublicKeyHexString; - -/*! @brief The spork private key as a base 58 string. */ -@property (nonatomic, strong, nullable) NSString *sporkPrivateKeyBase58String; - -/*! @brief The spork address base 58 string (addresses are known to be base 58). */ -@property (nonatomic, strong, nullable) NSString *sporkAddress; - -// MARK: - L2 Network Chain Info - -/*! @brief platformProtocolVersion is the protocol version that we currently use for the platform chain. This should only be changed in the case of devnets. */ -@property (nonatomic, assign) uint32_t platformProtocolVersion; - -/*! @brief The dpns contract id. */ -@property (nonatomic, assign) UInt256 dpnsContractID; - -/*! @brief The dashpay contract id. */ -@property (nonatomic, assign) UInt256 dashpayContractID; // MARK: - DashSync Chain Info -/*! @brief The chain type (MainNet, TestNet or DevNet). */ -@property (nonatomic, readonly) ChainType chainType; - -/*! @brief A threshold after which a peer will be banned. */ -@property (nonatomic, readonly) uintptr_t peerMisbehavingThreshold; /*! @brief True if this chain syncs the blockchain. All Chains currently sync the blockchain. */ @property (nonatomic, readonly) BOOL syncsBlockchain; @@ -168,53 +98,10 @@ typedef NS_ENUM(uint16_t, DSChainSyncPhase) /*! @brief True if this chain should sync headers first for masternode list verification. */ @property (nonatomic, readonly) BOOL needsInitialTerminalHeadersSync; -/*! @brief The default transaction version used when sending transactions. */ -@property (nonatomic, readonly) uint16_t transactionVersion; - -/*! @brief The number of minimumDifficultyBlocks. */ -@property (nonatomic, assign) uint32_t minimumDifficultyBlocks; - -/*! @brief The flag represents whether the quorum rotation is enabled in this chain. */ -@property (nonatomic, assign) BOOL isRotatedQuorumsPresented; /*! @brief Returns all standard derivaton paths used for the chain based on the account number. */ - (NSArray *)standardDerivationPathsForAccountNumber:(uint32_t)accountNumber; -// MARK: Names and Identifiers - -/*! @brief The unique identifier of the chain. This unique id follows the same chain accross devices because it is the short hex string of the genesis hash. */ -@property (nonatomic, readonly) NSString *uniqueID; - -/*! @brief The name of the chain (Mainnet-Testnet-Devnet). */ -@property (nonatomic, readonly) NSString *name; - -/*! @brief The localized name of the chain (Mainnet-Testnet-Devnet). */ -@property (nonatomic, readonly) NSString *localizedName; - -- (void)setDevnetNetworkName:(NSString *)networkName; - -// MARK: - Wallets - -/*! @brief The wallets in the chain. */ -@property (nonatomic, readonly) NSArray *wallets; - -/*! @brief Conveniance method. Does this walleet have a chain? */ -@property (nonatomic, readonly) BOOL hasAWallet; - -/*! @brief Conveniance method. The earliest known creation time for any wallet in this chain. */ -@property (nonatomic, readonly) NSTimeInterval earliestWalletCreationTime; - -/*! @brief Unregister a wallet from the chain, it will no longer be loaded or used. */ -- (void)unregisterWallet:(DSWallet *)wallet; - -/*! @brief Register a wallet to the chain. */ -- (void)registerWallet:(DSWallet *)wallet; - -/*! @brief Unregister all wallets from the chain, they will no longer be loaded or used. */ -- (void)unregisterAllWallets; - -/*! @brief Unregister all wallets from the chain that don't have an extended public key in one of their derivation paths, they will no longer be loaded or used. */ -- (void)unregisterAllWalletsMissingExtendedPublicKeys; // MARK: - Standalone Derivation Paths @@ -230,28 +117,6 @@ typedef NS_ENUM(uint16_t, DSChainSyncPhase) /*! @brief Register a standalone derivation path to the chain. Standalone derivation paths are currently an experimental feature */ - (void)registerStandaloneDerivationPath:(DSDerivationPath *)derivationPath; -// MARK: - Checkpoints - -/*! @brief Returns the last checkpoint that has a masternode list attached to it. */ -- (DSCheckpoint *_Nullable)lastCheckpointHavingMasternodeList; - -/*! @brief Returns the checkpoint matching the parameter block hash, if one exists. */ -- (DSCheckpoint *_Nullable)checkpointForBlockHash:(UInt256)blockHash; - -/*! @brief Returns the checkpoint at a given block height, if one exists at that block height. */ -- (DSCheckpoint *_Nullable)checkpointForBlockHeight:(uint32_t)blockHeight; - -/*! @brief Returns the last checkpoint on or before the given height. */ -- (DSCheckpoint *)lastCheckpointOnOrBeforeHeight:(uint32_t)height; - -/*! @brief Returns the last checkpoint on or before the given timestamp. */ -- (DSCheckpoint *)lastCheckpointOnOrBeforeTimestamp:(NSTimeInterval)timestamp; - -/*! @brief When used this will change the checkpoint used for initial headers sync. This value is not persisted. */ -- (void)useCheckpointBeforeOrOnHeightForTerminalBlocksSync:(uint32_t)blockHeight; - -/*! @brief When used this will change the checkpoint used for main chain syncing. This value is not persisted. */ -- (void)useCheckpointBeforeOrOnHeightForSyncingChainBlocks:(uint32_t)blockHeight; // MARK: - Blocks and Headers @@ -354,25 +219,6 @@ typedef NS_ENUM(uint16_t, DSChainSyncPhase) /*! @brief Returns if there is a block at the following height that is confirmed. */ - (BOOL)blockHeightChainLocked:(uint32_t)height; -// MARK: - Transactions - -/*! @brief Returns all wallet transactions sorted by date, most recent first. */ -@property (nonatomic, readonly) NSArray *allTransactions; - -/*! @brief Returns the transaction with the given hash if it's been registered in any wallet on the chain (might also return non-registered) */ -- (DSTransaction *_Nullable)transactionForHash:(UInt256)txHash; - -/*! @brief Returns the direction of a transaction for the chain (Sent - Received - Moved - Not Account Funds) */ -- (DSTransactionDirection)directionOfTransaction:(DSTransaction *)transaction; - -/*! @brief Returns the amount received globally from the transaction (total outputs to change and/or receive addresses) */ -- (uint64_t)amountReceivedFromTransaction:(DSTransaction *)transaction; - -/*! @brief Returns the amount sent globally by the trasaction (total wallet outputs consumed, change and fee included) */ -- (uint64_t)amountSentByTransaction:(DSTransaction *)transaction; - -/*! @brief Returns if this transaction has any local references. Local references are a pubkey hash contained in a wallet, pubkeys in wallets special derivation paths, or anything that would make the transaction relevant for this device. */ -- (BOOL)transactionHasLocalReferences:(DSTransaction *)transaction; // MARK: - Bloom Filter @@ -382,62 +228,12 @@ typedef NS_ENUM(uint16_t, DSChainSyncPhase) /*! @brief Returns a bloom filter with the given false positive rate tweaked with the value tweak. The value tweak is generally peer specific. */ - (DSBloomFilter *)bloomFilterWithFalsePositiveRate:(double)falsePositiveRate withTweak:(uint32_t)tweak; -// MARK: - Accounts and Balances - -/*! @brief The current wallet balance excluding transactions known to be invalid. */ -@property (nonatomic, readonly) uint64_t balance; - -/*! @brief All accounts that contain the specified transaction hash. The transaction is also returned if it is found. */ -- (NSArray *)accountsForTransactionHash:(UInt256)txHash transaction:(DSTransaction *_Nullable *_Nullable)transaction; - -/*! @brief Returns the first account with a balance. */ -- (DSAccount *_Nullable)firstAccountWithBalance; - -/*! @brief Returns an account to which the given transaction is or can be associated with (even if it hasn't been registered), no account if the transaction is not associated with the wallet. */ -- (DSAccount *_Nullable)firstAccountThatCanContainTransaction:(DSTransaction *)transaction; - -/*! @brief Returns all accounts to which the given transaction is or can be associated with (even if it hasn't been registered) */ -- (NSArray *)accountsThatCanContainTransaction:(DSTransaction *_Nonnull)transaction; - -/*! @brief Returns an account to which the given transaction hash is associated with, no account if the transaction hash is not associated with the wallet. */ -- (DSAccount *_Nullable)firstAccountForTransactionHash:(UInt256)txHash transaction:(DSTransaction *_Nullable *_Nullable)transaction wallet:(DSWallet *_Nullable *_Nullable)wallet; - -/*! @brief Returns an account to which the given address is contained in a derivation path. */ -- (DSAccount *_Nullable)accountContainingAddress:(NSString *)address; - -/*! @brief Returns an account to which the given address is known by a dashpay outgoing derivation path. */ -- (DSAccount *_Nullable)accountContainingDashpayExternalDerivationPathAddress:(NSString *)address; // MARK: - Governance /*! @brief Returns a count of all governance objects. */ @property (nonatomic, assign) uint32_t totalGovernanceObjectsCount; -// MARK: - Identities - -/*! @brief Returns a count of local blockchain identities. */ -@property (nonatomic, readonly) uint32_t localBlockchainIdentitiesCount; - -/*! @brief Returns a count of blockchain invitations that have been created locally. */ -@property (nonatomic, readonly) uint32_t localBlockchainInvitationsCount; - -/*! @brief Returns an array of all local blockchain identities. */ -@property (nonatomic, readonly) NSArray *localBlockchainIdentities; - -/*! @brief Returns a dictionary of all local blockchain identities keyed by uniqueId. */ -@property (nonatomic, readonly) NSDictionary *localBlockchainIdentitiesByUniqueIdDictionary; - -/*! @brief Returns a blockchain identity by uniqueId, if it exists. */ -- (DSBlockchainIdentity *_Nullable)blockchainIdentityForUniqueId:(UInt256)uniqueId; - -/*! @brief Returns a blockchain identity that could have created this contract. */ -- (DSBlockchainIdentity *_Nullable)blockchainIdentityThatCreatedContract:(DPContract *)contract withContractId:(UInt256)contractId foundInWallet:(DSWallet *_Nullable *_Nullable)foundInWallet; - -/*! @brief Returns a blockchain identity by uniqueId, if it exists. Also returns the wallet it was found in. */ -- (DSBlockchainIdentity *_Nullable)blockchainIdentityForUniqueId:(UInt256)uniqueId foundInWallet:(DSWallet *_Nullable *_Nullable)foundInWallet; - -/*! @brief Returns a blockchain identity by uniqueId, if it exists. Also returns the wallet it was found in. Allows to search foreign blockchain identities too */ -- (DSBlockchainIdentity *)blockchainIdentityForUniqueId:(UInt256)uniqueId foundInWallet:(DSWallet *_Nullable *_Nullable)foundInWallet includeForeignBlockchainIdentities:(BOOL)includeForeignBlockchainIdentities; // MARK: - Chain Retrieval methods @@ -451,7 +247,7 @@ typedef NS_ENUM(uint16_t, DSChainSyncPhase) + (DSChain *_Nullable)devnetWithIdentifier:(NSString *)identifier; /*! @brief Set up a given devnet with an identifier, checkpoints, default L1, JRPC and GRPC ports, a dpns contractId and a dashpay contract id. minimumDifficultyBlocks are used to speed up the initial chain creation. This devnet will be registered on the keychain. The additional isTransient property allows for test usage where you do not wish to persist the devnet. */ -+ (DSChain *)setUpDevnetWithIdentifier:(DevnetType)devnetType ++ (DSChain *)setUpDevnetWithIdentifier:(dash_spv_crypto_network_chain_type_DevnetType *)devnetType protocolVersion:(uint32_t)protocolVersion minProtocolVersion:(uint32_t)minProtocolVersion withCheckpoints:(NSArray *_Nullable)checkpointArray @@ -464,22 +260,11 @@ typedef NS_ENUM(uint16_t, DSChainSyncPhase) isTransient:(BOOL)isTransient; /*! @brief Retrieve from the keychain a devnet with an identifier and add given checkpoints. */ -+ (DSChain *)recoverKnownDevnetWithIdentifier:(DevnetType)devnetType withCheckpoints:(NSArray *)checkpointArray performSetup:(BOOL)performSetup; ++ (DSChain *)recoverKnownDevnetWithIdentifier:(dash_spv_crypto_network_chain_type_DevnetType *)devnetType withCheckpoints:(NSArray *)checkpointArray performSetup:(BOOL)performSetup; /*! @brief Retrieve a chain having the specified network name. */ + (DSChain *_Nullable)chainForNetworkName:(NSString *_Nullable)networkName; -// MARK: - Chain Info methods - -- (BOOL)isMainnet; -- (BOOL)isTestnet; -- (BOOL)isDevnetAny; -- (BOOL)isEvolutionEnabled; -- (BOOL)isDevnetWithGenesisHash:(UInt256)genesisHash; -- (BOOL)isCore19Active; -- (BOOL)isCore20Active; -- (BOOL)isCore20ActiveAtHeight:(uint32_t)height; -- (KeyKind)activeBLSType; @end @@ -493,7 +278,7 @@ typedef NS_ENUM(uint16_t, DSChainSyncPhase) @protocol DSChainIdentitiesDelegate @required -- (void)chain:(DSChain *)chain didFinishInChainSyncPhaseFetchingBlockchainIdentityDAPInformation:(DSBlockchainIdentity *)blockchainIdentity; +- (void)chain:(DSChain *)chain didFinishInChainSyncPhaseFetchingIdentityDAPInformation:(DSIdentity *)identity; @end diff --git a/DashSync/shared/Models/Chain/DSChain.m b/DashSync/shared/Models/Chain/DSChain.m index 834465ffc..3f1cf39c8 100644 --- a/DashSync/shared/Models/Chain/DSChain.m +++ b/DashSync/shared/Models/Chain/DSChain.m @@ -22,104 +22,57 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#import "BigIntTypes.h" #import "DSAccount.h" #import "DSAuthenticationKeysDerivationPath.h" -#import "DSBIP39Mnemonic.h" #import "DSBlock+Protected.h" -#import "DSBlockchainIdentity+Protected.h" -#import "DSBlockchainIdentityCloseTransition.h" -#import "DSBlockchainIdentityEntity+CoreDataClass.h" -#import "DSBlockchainIdentityRegistrationTransition.h" -#import "DSBlockchainIdentityTopupTransition.h" -#import "DSBlockchainIdentityUpdateTransition.h" -#import "DSBlockchainInvitation+Protected.h" #import "DSBloomFilter.h" +#import "DSChain.h" +#import "DSChain+Checkpoint.h" +#import "DSChain+Identity.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" +#import "DSChain+Transaction.h" +#import "DSChain+Wallet.h" #import "DSChainCheckpoints.h" #import "DSChainEntity+CoreDataClass.h" #import "DSChainLock.h" #import "DSChainManager+Protected.h" #import "DSChainsManager.h" #import "DSCheckpoint.h" -#import "DSCreditFundingTransaction.h" -#import "DSDerivationPath.h" #import "DSDerivationPathEntity+CoreDataProperties.h" #import "DSDerivationPathFactory.h" -#import "DSEventManager.h" #import "DSFullBlock.h" #import "DSFundsDerivationPath.h" #import "DSIdentitiesManager+Protected.h" #import "DSInsightManager.h" -#import "DSKeyManager.h" #import "DSLocalMasternode+Protected.h" #import "DSLocalMasternodeEntity+CoreDataProperties.h" -#import "DSMasternodeHoldingsDerivationPath.h" #import "DSMasternodeListEntity+CoreDataProperties.h" -#import "DSMasternodeManager+LocalMasternode.h" #import "DSMasternodeManager+Protected.h" #import "DSMerkleBlock.h" #import "DSMerkleBlockEntity+CoreDataClass.h" #import "DSOptionsManager.h" -#import "DSPeer.h" #import "DSPeerManager.h" -#import "DSPriceManager.h" -#import "DSProviderRegistrationTransaction.h" -#import "DSProviderUpdateRegistrarTransaction.h" -#import "DSProviderUpdateRevocationTransaction.h" -#import "DSProviderUpdateServiceTransaction.h" #import "DSQuorumEntryEntity+CoreDataProperties.h" #import "DSQuorumSnapshotEntity+CoreDataClass.h" -#import "DSSimplifiedMasternodeEntry.h" #import "DSSimplifiedMasternodeEntryEntity+CoreDataProperties.h" -#import "DSSpecialTransactionsWalletHolder.h" -#import "DSSporkManager.h" -#import "DSTransaction.h" -#import "DSTransactionEntity+CoreDataClass.h" #import "DSTransactionHashEntity+CoreDataProperties.h" #import "DSTransactionInput.h" #import "DSTransactionOutput.h" -#import "DSTransition.h" -#import "DSWallet+Protected.h" -#import "NSCoder+Dash.h" -#import "NSData+DSHash.h" -#import "NSData+Dash.h" -#import "NSManagedObject+Sugar.h" #import "NSMutableData+Dash.h" #import "NSString+Bitcoin.h" #define FEE_PER_BYTE_KEY @"FEE_PER_BYTE" -#define CHAIN_WALLETS_KEY @"CHAIN_WALLETS_KEY" #define CHAIN_STANDALONE_DERIVATIONS_KEY @"CHAIN_STANDALONE_DERIVATIONS_KEY" #define REGISTERED_PEERS_KEY @"REGISTERED_PEERS_KEY" -#define PROTOCOL_VERSION_LOCATION @"PROTOCOL_VERSION_LOCATION" -#define DEFAULT_MIN_PROTOCOL_VERSION_LOCATION @"MIN_PROTOCOL_VERSION_LOCATION" - -#define PLATFORM_PROTOCOL_VERSION_LOCATION @"PLATFORM_PROTOCOL_VERSION_LOCATION" -#define PLATFORM_DEFAULT_MIN_PROTOCOL_VERSION_LOCATION @"PLATFORM_MIN_PROTOCOL_VERSION_LOCATION" - #define ISLOCK_QUORUM_TYPE @"ISLOCK_QUORUM_TYPE" #define ISDLOCK_QUORUM_TYPE @"ISDLOCK_QUORUM_TYPE" #define CHAINLOCK_QUORUM_TYPE @"CHAINLOCK_QUORUM_TYPE" #define PLATFORM_QUORUM_TYPE @"PLATFORM_QUORUM_TYPE" -#define STANDARD_PORT_LOCATION @"STANDARD_PORT_LOCATION" -#define JRPC_PORT_LOCATION @"JRPC_PORT_LOCATION" -#define GRPC_PORT_LOCATION @"GRPC_PORT_LOCATION" - -#define DPNS_CONTRACT_ID @"DPNS_CONTRACT_ID" -#define DASHPAY_CONTRACT_ID @"DASHPAY_CONTRACT_ID" - -#define MINIMUM_DIFFICULTY_BLOCKS_COUNT_KEY @"MINIMUM_DIFFICULTY_BLOCKS_COUNT_KEY" - -#define SPORK_PUBLIC_KEY_LOCATION @"SPORK_PUBLIC_KEY_LOCATION" -#define SPORK_ADDRESS_LOCATION @"SPORK_ADDRESS_LOCATION" -#define SPORK_PRIVATE_KEY_LOCATION @"SPORK_PRIVATE_KEY_LOCATION" - #define CHAIN_VOTING_KEYS_KEY @"CHAIN_VOTING_KEYS_KEY" -#define QUORUM_ROTATION_PRESENCE_KEY @"QUORUM_ROTATION_PRESENCE_KEY" #define LOG_PREV_BLOCKS_ON_ORPHAN 0 @@ -137,34 +90,15 @@ @interface DSChain () @property (nonatomic, strong) DSBlock *lastSyncBlock, *lastTerminalBlock, *lastOrphan; @property (nonatomic, strong) NSMutableDictionary *mSyncBlocks, *mTerminalBlocks, *mOrphans; -@property (nonatomic, strong) NSMutableDictionary *checkpointsByHashDictionary; -@property (nonatomic, strong) NSMutableDictionary *checkpointsByHeightDictionary; -@property (nonatomic, strong) NSArray *checkpoints; -@property (nonatomic, copy) NSString *uniqueID; -@property (nonatomic, copy) NSString *networkName; -@property (nonatomic, strong) NSMutableArray *mWallets; +//@property (nonatomic, copy) NSString *uniqueID; +//@property (nonatomic, copy) NSString *networkName; @property (nonatomic, strong) DSAccount *viewingAccount; @property (nonatomic, strong) NSMutableDictionary *> *estimatedBlockHeights; -@property (nonatomic, assign) uint32_t cachedMinimumDifficultyBlocks; @property (nonatomic, assign) uint32_t bestEstimatedBlockHeight; -@property (nonatomic, assign) uint32_t cachedMinProtocolVersion; -@property (nonatomic, assign) uint32_t cachedProtocolVersion; -@property (nonatomic, assign) UInt256 cachedMaxProofOfWork; -@property (nonatomic, assign) uint32_t cachedStandardPort; -@property (nonatomic, assign) uint32_t cachedStandardDapiJRPCPort; -@property (nonatomic, assign) uint32_t cachedStandardDapiGRPCPort; -@property (nonatomic, assign) UInt256 genesisHash; -@property (nonatomic, assign) UInt256 cachedDpnsContractID; -@property (nonatomic, assign) UInt256 cachedDashpayContractID; @property (nonatomic, strong) NSMutableDictionary *transactionHashHeights; @property (nonatomic, strong) NSMutableDictionary *transactionHashTimestamps; @property (nonatomic, strong) NSManagedObjectContext *chainManagedObjectContext; -@property (nonatomic, strong) DSCheckpoint *terminalHeadersOverrideUseCheckpoint; -@property (nonatomic, strong) DSCheckpoint *syncHeadersOverrideUseCheckpoint; -@property (nonatomic, strong) DSCheckpoint *lastCheckpoint; @property (nonatomic, assign, getter=isTransient) BOOL transient; -@property (nonatomic, assign) BOOL cachedIsQuorumRotationPresented; -@property (nonatomic, readonly) NSString *chainWalletsKey; @end @@ -194,34 +128,36 @@ - (instancetype)init { self.feePerByte = DEFAULT_FEE_PER_B; uint64_t feePerByte = [[NSUserDefaults standardUserDefaults] doubleForKey:FEE_PER_BYTE_KEY]; if (feePerByte >= MIN_FEE_PER_B && feePerByte <= MAX_FEE_PER_B) self.feePerByte = feePerByte; - + return self; } -- (instancetype)initWithType:(ChainType)type checkpoints:(NSArray *)checkpoints { +- (instancetype)initWithType:(DChainType *)type checkpoints:(NSArray *)checkpoints { if (!(self = [self init])) return nil; - NSAssert(!chain_type_is_devnet_any(type), @"DevNet should be configured with initAsDevnetWithIdentifier:version:checkpoints:port:dapiPort:dapiGRPCPort:dpnsContractID:dashpayContractID:"); - _chainType = type; - self.standardPort = chain_standard_port(type); - self.standardDapiJRPCPort = chain_standard_dapi_jrpc_port(type); - self.headersMaxAmount = chain_headers_max_amount(type); + + NSAssert(!dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(type), @"DevNet should be configured with initAsDevnetWithIdentifier:version:checkpoints:port:dapiPort:dapiGRPCPort:dpnsContractID:dashpayContractID:"); + self.chainType = type; + self.standardPort = dash_spv_crypto_network_chain_type_ChainType_standard_port(type); + self.standardDapiJRPCPort = dash_spv_crypto_network_chain_type_ChainType_standard_dapi_jrpc_port(type); + self.headersMaxAmount = dash_spv_crypto_network_chain_type_ChainType_header_max_amount(type); self.checkpoints = checkpoints; self.genesisHash = self.checkpoints[0].blockHash; - _checkpointsByHashDictionary = [NSMutableDictionary dictionary]; - _checkpointsByHeightDictionary = [NSMutableDictionary dictionary]; + self.checkpointsByHashDictionary = [NSMutableDictionary dictionary]; + self.checkpointsByHeightDictionary = [NSMutableDictionary dictionary]; dispatch_sync(self.networkingQueue, ^{ self.chainManagedObjectContext = [NSManagedObjectContext chainContext]; }); - + self.shareCore = [[DSDashSharedCore alloc] initOnChain:self]; + return self; } -- (instancetype)initAsDevnetWithIdentifier:(DevnetType)devnetType +- (instancetype)initAsDevnetWithIdentifier:(dash_spv_crypto_network_chain_type_DevnetType *)devnetType onProtocolVersion:(uint32_t)protocolVersion checkpoints:(NSArray *)checkpoints { //for devnet the genesis checkpoint is really the second block if (!(self = [self init])) return nil; - _chainType = chain_type_for_devnet_type(devnetType); + self.chainType = dash_spv_crypto_network_chain_type_ChainType_DevNet_ctor(devnetType); if (!checkpoints || ![checkpoints count]) { DSCheckpoint *genesisCheckpoint = [DSCheckpoint genesisDevnetCheckpoint]; DSCheckpoint *secondCheckpoint = [self createDevNetGenesisBlockCheckpointForParentCheckpoint:genesisCheckpoint withIdentifier:devnetType onProtocolVersion:protocolVersion]; @@ -234,11 +170,13 @@ - (instancetype)initAsDevnetWithIdentifier:(DevnetType)devnetType dispatch_sync(self.networkingQueue, ^{ self.chainManagedObjectContext = [NSManagedObjectContext chainContext]; }); - self.headersMaxAmount = chain_headers_max_amount(_chainType); + self.headersMaxAmount = dash_spv_crypto_network_chain_type_ChainType_header_max_amount(self.chainType); + self.shareCore = [[DSDashSharedCore alloc] initOnChain:self]; + return self; } -- (instancetype)initAsDevnetWithIdentifier:(DevnetType)devnetType +- (instancetype)initAsDevnetWithIdentifier:(dash_spv_crypto_network_chain_type_DevnetType *)devnetType protocolVersion:(uint32_t)protocolVersion minProtocolVersion:(uint32_t)minProtocolVersion checkpoints:(NSArray *)checkpoints @@ -266,7 +204,7 @@ + (DSChain *)mainnet { static dispatch_once_t mainnetToken = 0; __block BOOL inSetUp = FALSE; dispatch_once(&mainnetToken, ^{ - _mainnet = [[DSChain alloc] initWithType:chain_type_from_index(ChainType_MainNet) checkpoints:[DSChain createCheckpointsArrayFromCheckpoints:mainnet_checkpoint_array count:(sizeof(mainnet_checkpoint_array) / sizeof(*mainnet_checkpoint_array))]]; + _mainnet = [[DSChain alloc] initWithType:dash_spv_crypto_network_chain_type_ChainType_MainNet_ctor() checkpoints:[DSChain createCheckpointsArrayFromCheckpoints:mainnet_checkpoint_array count:(sizeof(mainnet_checkpoint_array) / sizeof(*mainnet_checkpoint_array))]]; inSetUp = TRUE; }); if (inSetUp) { @@ -290,7 +228,7 @@ + (DSChain *)testnet { static dispatch_once_t testnetToken = 0; __block BOOL inSetUp = FALSE; dispatch_once(&testnetToken, ^{ - _testnet = [[DSChain alloc] initWithType:chain_type_from_index(ChainType_TestNet) checkpoints:[DSChain createCheckpointsArrayFromCheckpoints:testnet_checkpoint_array count:(sizeof(testnet_checkpoint_array) / sizeof(*testnet_checkpoint_array))]]; + _testnet = [[DSChain alloc] initWithType:dash_spv_crypto_network_chain_type_ChainType_TestNet_ctor() checkpoints:[DSChain createCheckpointsArrayFromCheckpoints:testnet_checkpoint_array count:(sizeof(testnet_checkpoint_array) / sizeof(*testnet_checkpoint_array))]]; inSetUp = TRUE; }); if (inSetUp) { @@ -320,14 +258,17 @@ + (DSChain *)devnetWithIdentifier:(NSString *)identifier { return devnetChain; } -+ (DSChain *)recoverKnownDevnetWithIdentifier:(DevnetType)devnetType withCheckpoints:(NSArray *)checkpointArray performSetup:(BOOL)performSetup { ++ (DSChain *)recoverKnownDevnetWithIdentifier:(dash_spv_crypto_network_chain_type_DevnetType *)devnetType + withCheckpoints:(NSArray *)checkpointArray + performSetup:(BOOL)performSetup { dispatch_once(&devnetToken, ^{ _devnetDictionary = [NSMutableDictionary dictionary]; }); DSChain *devnetChain = nil; __block BOOL inSetUp = FALSE; +// char *identifier = dash_spv_crypto_network_chain_type_DevnetType_identifier(devnetType); @synchronized(self) { - NSString *devnetIdentifier = [DSKeyManager NSStringFrom:chain_devnet_identifier(devnetType)]; + NSString *devnetIdentifier = [DSKeyManager NSStringFrom:dash_spv_crypto_network_chain_type_DevnetType_identifier(devnetType)]; if (![_devnetDictionary objectForKey:devnetIdentifier]) { devnetChain = [[DSChain alloc] initAsDevnetWithIdentifier:devnetType onProtocolVersion:PROTOCOL_VERSION_DEVNET checkpoints:checkpointArray]; _devnetDictionary[devnetIdentifier] = devnetChain; @@ -345,7 +286,7 @@ + (DSChain *)recoverKnownDevnetWithIdentifier:(DevnetType)devnetType withCheckpo return devnetChain; } -+ (DSChain *)setUpDevnetWithIdentifier:(DevnetType)devnetType ++ (DSChain *)setUpDevnetWithIdentifier:(dash_spv_crypto_network_chain_type_DevnetType *)devnetType protocolVersion:(uint32_t)protocolVersion minProtocolVersion:(uint32_t)minProtocolVersion withCheckpoints:(NSArray *_Nullable)checkpointArray @@ -362,7 +303,7 @@ + (DSChain *)setUpDevnetWithIdentifier:(DevnetType)devnetType DSChain *devnetChain = nil; __block BOOL inSetUp = FALSE; @synchronized(self) { - NSString *devnetIdentifier = [DSKeyManager NSStringFrom:chain_devnet_identifier(devnetType)]; + NSString *devnetIdentifier = [DSKeyManager NSStringFrom:dash_spv_crypto_network_chain_type_DevnetType_identifier(devnetType)]; if (![_devnetDictionary objectForKey:devnetIdentifier]) { devnetChain = [[DSChain alloc] initAsDevnetWithIdentifier:devnetType protocolVersion:protocolVersion minProtocolVersion:minProtocolVersion checkpoints:checkpointArray minimumDifficultyBlocks:minimumDifficultyBlocks port:port dapiJRPCPort:dapiJRPCPort dapiGRPCPort:dapiGRPCPort dpnsContractID:dpnsContractID dashpayContractID:dashpayContractID isTransient:isTransient]; _devnetDictionary[devnetIdentifier] = devnetChain; @@ -403,23 +344,6 @@ - (void)setUp { [self retrieveStandaloneDerivationPaths]; } -// MARK: - Helpers - -- (BOOL)isCore19Active { - return self.lastTerminalBlockHeight >= chain_core19_activation_height(self.chainType); -} - -- (BOOL)isCore20Active { - return self.lastTerminalBlockHeight >= chain_core20_activation_height(self.chainType); -} - -- (BOOL)isCore20ActiveAtHeight:(uint32_t)height { - return height >= chain_core20_activation_height(self.chainType); -} - -- (KeyKind)activeBLSType { - return [self isCore19Active] ? KeyKind_BLSBasic : KeyKind_BLS; -} - (NSDictionary *)syncBlocks { return [self.mSyncBlocks copy]; @@ -477,25 +401,13 @@ - (DSChainManager *)chainManager { - (DSKeyManager *)keyManager { return [[self chainManager] keyManager]; } - -+ (NSMutableArray *)createCheckpointsArrayFromCheckpoints:(checkpoint *)checkpoints count:(NSUInteger)checkpointCount { - NSMutableArray *checkpointMutableArray = [NSMutableArray array]; - for (int i = 0; i < checkpointCount; i++) { - checkpoint cpt = checkpoints[i]; - NSString *merkleRootString = [NSString stringWithCString:cpt.merkleRoot encoding:NSUTF8StringEncoding]; - NSString *chainWorkString = [NSString stringWithCString:cpt.chainWork encoding:NSUTF8StringEncoding]; - uint32_t blockHeight = cpt.height; - UInt256 blockHash = [NSString stringWithCString:cpt.checkpointHash encoding:NSUTF8StringEncoding].hexToData.reverse.UInt256; - UInt256 chainWork = chainWorkString.hexToData.reverse.UInt256; - UInt256 merkleRoot = [merkleRootString isEqualToString:@""] ? UINT256_ZERO : merkleRootString.hexToData.reverse.UInt256; - DSCheckpoint *checkpoint = [DSCheckpoint checkpointForHeight:blockHeight blockHash:blockHash timestamp:cpt.timestamp target:cpt.target merkleRoot:merkleRoot chainWork:chainWork masternodeListName:[NSString stringWithCString:cpt.masternodeListPath encoding:NSUTF8StringEncoding]]; - [checkpointMutableArray addObject:checkpoint]; - } - return [checkpointMutableArray copy]; +- (DSMasternodeManager *)masternodeManager { + return [[self chainManager] masternodeManager]; } + - (BOOL)isEqual:(id)obj { - return self == obj || ([obj isKindOfClass:[DSChain class]] && uint256_eq([obj genesisHash], _genesisHash)); + return self == obj || ([obj isKindOfClass:[DSChain class]] && uint256_eq([obj genesisHash], self.genesisHash)); } - (NSUInteger)hash { @@ -515,7 +427,9 @@ - (UInt256)blockHashForDevNetGenesisBlockWithVersion:(uint32_t)version prevHash: return [DSKeyManager x11:d]; } -- (DSCheckpoint *)createDevNetGenesisBlockCheckpointForParentCheckpoint:(DSCheckpoint *)checkpoint withIdentifier:(DevnetType)identifier onProtocolVersion:(uint32_t)protocolVersion { +- (DSCheckpoint *)createDevNetGenesisBlockCheckpointForParentCheckpoint:(DSCheckpoint *)checkpoint + withIdentifier:(dash_spv_crypto_network_chain_type_DevnetType *)identifier + onProtocolVersion:(uint32_t)protocolVersion { uint32_t nTime = checkpoint.timestamp + 1; uint32_t nBits = checkpoint.target; UInt256 fullTarget = setCompactLE(nBits); @@ -549,34 +463,9 @@ - (dispatch_queue_t)dapiMetadataQueue { return _dapiMetadataQueue; } -// MARK: - Check Type - -- (BOOL)isMainnet { - return self.chainType.tag == ChainType_MainNet; -} - -- (BOOL)isTestnet { - return self.chainType.tag == ChainType_TestNet; -} - -- (BOOL)isDevnetAny { - return chain_type_is_devnet_any(self.chainType); -} - -- (BOOL)isEvolutionEnabled { - return NO; - // return [self isDevnetAny] || [self isTestnet]; -} - -- (BOOL)isDevnetWithGenesisHash:(UInt256)genesisHash { - return chain_type_is_devnet_any(self.chainType) && uint256_eq([self genesisHash], genesisHash); -} // MARK: - Keychain Strings -- (NSString *)chainWalletsKey { - return [NSString stringWithFormat:@"%@_%@", CHAIN_WALLETS_KEY, [self uniqueID]]; -} - (NSString *)chainStandaloneDerivationPathsKey { return [NSString stringWithFormat:@"%@_%@", CHAIN_STANDALONE_DERIVATIONS_KEY, [self uniqueID]]; @@ -586,65 +475,6 @@ - (NSString *)registeredPeersKey { return [NSString stringWithFormat:@"%@_%@", REGISTERED_PEERS_KEY, [self uniqueID]]; } -- (NSString *)votingKeysKey { - return [NSString stringWithFormat:@"%@_%@", CHAIN_VOTING_KEYS_KEY, [self uniqueID]]; -} - - -// MARK: - Names and Identifiers - -- (NSString *)uniqueID { - if (!_uniqueID) { - _uniqueID = [[NSData dataWithUInt256:[self genesisHash]] shortHexString]; - } - return _uniqueID; -} - - -- (NSString *)networkName { - switch (self.chainType.tag) { - case ChainType_MainNet: - return @"main"; - case ChainType_TestNet: - return @"test"; - case ChainType_DevNet: - if (_networkName) return _networkName; - return @"dev"; - } - if (_networkName) return _networkName; -} - -- (NSString *)name { - switch (self.chainType.tag) { - case ChainType_MainNet: - return @"Mainnet"; - case ChainType_TestNet: - return @"Testnet"; - case ChainType_DevNet: - if (_networkName) return _networkName; - return [NSString stringWithFormat:@"Devnet - %@.%u", [DSKeyManager devnetIdentifierFor:self.chainType], devnet_version_for_chain_type(self.chainType)]; - } - if (_networkName) return _networkName; -} - -- (NSString *)localizedName { - switch (self.chainType.tag) { - case ChainType_MainNet: - return DSLocalizedString(@"Mainnet", nil); - case ChainType_TestNet: - return DSLocalizedString(@"Testnet", nil); - case ChainType_DevNet: - if (_networkName) return _networkName; - return [NSString stringWithFormat:@"%@ - %@.%u", DSLocalizedString(@"Devnet", nil), [DSKeyManager devnetIdentifierFor:self.chainType], devnet_version_for_chain_type(self.chainType)]; - } - if (_networkName) return _networkName; -} - -- (void)setDevnetNetworkName:(NSString *)networkName { - if (chain_type_is_devnet_any(self.chainType)) { - _networkName = @"Evonet"; - } -} // MARK: - L1 Chain Parameters @@ -652,20 +482,13 @@ - (void)setDevnetNetworkName:(NSString *)networkName { - (NSArray *)standardDerivationPathsForAccountNumber:(uint32_t)accountNumber { if (accountNumber == 0) { - return @[[DSFundsDerivationPath bip32DerivationPathForAccountNumber:accountNumber onChain:self], [DSFundsDerivationPath bip44DerivationPathForAccountNumber:accountNumber onChain:self], [DSDerivationPath masterBlockchainIdentityContactsDerivationPathForAccountNumber:accountNumber onChain:self]]; + return @[[DSFundsDerivationPath bip32DerivationPathForAccountNumber:accountNumber onChain:self], [DSFundsDerivationPath bip44DerivationPathForAccountNumber:accountNumber onChain:self], [DSDerivationPath masterIdentityContactsDerivationPathForAccountNumber:accountNumber onChain:self]]; } else { //don't include BIP32 derivation path on higher accounts - return @[[DSFundsDerivationPath bip44DerivationPathForAccountNumber:accountNumber onChain:self], [DSDerivationPath masterBlockchainIdentityContactsDerivationPathForAccountNumber:accountNumber onChain:self]]; + return @[[DSFundsDerivationPath bip44DerivationPathForAccountNumber:accountNumber onChain:self], [DSDerivationPath masterIdentityContactsDerivationPathForAccountNumber:accountNumber onChain:self]]; } } -- (uint16_t)transactionVersion { - return chain_transaction_version(self.chainType); -} - -- (uintptr_t)peerMisbehavingThreshold { - return chain_peer_misbehaving_threshold(self.chainType); -} - (BOOL)syncsBlockchain { //required for SPV wallets return ([[DSOptionsManager sharedInstance] syncType] & DSSyncType_NeedsWalletSyncType) != 0; @@ -675,17 +498,6 @@ - (BOOL)needsInitialTerminalHeadersSync { return !(self.estimatedBlockHeight == self.lastTerminalBlockHeight); } -// This is a time interval since 1970 -- (NSTimeInterval)earliestWalletCreationTime { - if (![self.wallets count]) return BIP39_CREATION_TIME; - NSTimeInterval timeInterval = [[NSDate date] timeIntervalSince1970]; - for (DSWallet *wallet in self.wallets) { - if (timeInterval > wallet.walletCreationTime) { - timeInterval = wallet.walletCreationTime; - } - } - return timeInterval; -} - (NSTimeInterval)startSyncFromTime { if ([self syncsBlockchain]) { @@ -699,465 +511,6 @@ - (NSString *)chainTip { return [NSData dataWithUInt256:self.lastTerminalBlock.blockHash].shortHexString; } -// MARK: Sync Parameters - -- (uint32_t)magicNumber { - return chain_magic_number(_chainType); -} - -- (uint32_t)protocolVersion { - switch (self.chainType.tag) { - case ChainType_MainNet: - return PROTOCOL_VERSION_MAINNET; //(70216 + (self.headersMaxAmount / 2000)); - case ChainType_TestNet: - return PROTOCOL_VERSION_TESTNET; - case ChainType_DevNet: { - NSError *error = nil; - uint32_t protocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], PROTOCOL_VERSION_LOCATION], &error); - if (!error && protocolVersion) - return protocolVersion; - else - return PROTOCOL_VERSION_DEVNET; - } - } -} - -- (void)setProtocolVersion:(uint32_t)protocolVersion { - if (chain_type_is_devnet_any(self.chainType)) { - setKeychainInt(protocolVersion, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], PROTOCOL_VERSION_LOCATION], NO); - } -} - -- (BOOL)isRotatedQuorumsPresented { - if (_cachedIsQuorumRotationPresented) return _cachedIsQuorumRotationPresented; - switch (self.chainType.tag) { - case ChainType_MainNet: { - NSError *error = nil; - BOOL isPresented = (BOOL)getKeychainInt([NSString stringWithFormat:@"MAINNET_%@", QUORUM_ROTATION_PRESENCE_KEY], &error); - _cachedIsQuorumRotationPresented = !error && isPresented; - break; - } - case ChainType_TestNet: { - NSError *error = nil; - BOOL isPresented = (BOOL)getKeychainInt([NSString stringWithFormat:@"TESTNET_%@", QUORUM_ROTATION_PRESENCE_KEY], &error); - _cachedIsQuorumRotationPresented = !error && isPresented; - break; - } - case ChainType_DevNet: { - NSError *error = nil; - BOOL isPresented = (BOOL)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], QUORUM_ROTATION_PRESENCE_KEY], &error); - _cachedIsQuorumRotationPresented = !error && isPresented; - break; - } - } - return _cachedIsQuorumRotationPresented; -} - - -- (void)setIsRotatedQuorumsPresented:(BOOL)isRotatedQuorumsPresented { - switch (self.chainType.tag) { - case ChainType_MainNet: - setKeychainInt(isRotatedQuorumsPresented, [NSString stringWithFormat:@"MAINNET_%@", QUORUM_ROTATION_PRESENCE_KEY], NO); - break; - case ChainType_TestNet: - setKeychainInt(isRotatedQuorumsPresented, [NSString stringWithFormat:@"TESTNET_%@", QUORUM_ROTATION_PRESENCE_KEY], NO); - break; - case ChainType_DevNet: { - setKeychainInt(isRotatedQuorumsPresented, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], QUORUM_ROTATION_PRESENCE_KEY], NO); - break; - } - } - _cachedIsQuorumRotationPresented = isRotatedQuorumsPresented; -} - -- (uint32_t)minProtocolVersion { - @synchronized(self) { - if (_cachedMinProtocolVersion) return _cachedMinProtocolVersion; - switch (self.chainType.tag) { - case ChainType_MainNet: { - NSError *error = nil; - uint32_t minProtocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"MAINNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], &error); - if (!error && minProtocolVersion) - _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_MAINNET); - else - _cachedMinProtocolVersion = DEFAULT_MIN_PROTOCOL_VERSION_MAINNET; - break; - } - case ChainType_TestNet: { - NSError *error = nil; - uint32_t minProtocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"TESTNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], &error); - if (!error && minProtocolVersion) - _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_TESTNET); - else - _cachedMinProtocolVersion = DEFAULT_MIN_PROTOCOL_VERSION_TESTNET; - break; - } - case ChainType_DevNet: { - NSError *error = nil; - uint32_t minProtocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], &error); - if (!error && minProtocolVersion) - _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_DEVNET); - else - _cachedMinProtocolVersion = DEFAULT_MIN_PROTOCOL_VERSION_DEVNET; - break; - } - } - return _cachedMinProtocolVersion; - } -} - - -- (void)setMinProtocolVersion:(uint32_t)minProtocolVersion { - @synchronized(self) { - if (minProtocolVersion < MIN_VALID_MIN_PROTOCOL_VERSION || minProtocolVersion > MAX_VALID_MIN_PROTOCOL_VERSION) return; - switch (self.chainType.tag) { - case ChainType_MainNet: - setKeychainInt(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_MAINNET), [NSString stringWithFormat:@"MAINNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], NO); - _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_MAINNET); - break; - case ChainType_TestNet: - setKeychainInt(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_TESTNET), [NSString stringWithFormat:@"TESTNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], NO); - _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_TESTNET); - break; - case ChainType_DevNet: { - setKeychainInt(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_DEVNET), [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], NO); - _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_DEVNET); - break; - } - } - } -} - -- (uint32_t)standardPort { - if (_cachedStandardPort) return _cachedStandardPort; - switch (self.chainType.tag) { - case ChainType_MainNet: - _cachedStandardPort = MAINNET_STANDARD_PORT; - return MAINNET_STANDARD_PORT; - case ChainType_TestNet: - _cachedStandardPort = TESTNET_STANDARD_PORT; - return TESTNET_STANDARD_PORT; - case ChainType_DevNet: { - NSError *error = nil; - uint32_t cachedStandardPort = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], STANDARD_PORT_LOCATION], &error); - if (!error && cachedStandardPort) { - _cachedStandardPort = cachedStandardPort; - return _cachedStandardPort; - } - return DEVNET_STANDARD_PORT; - } - } -} - -- (void)setStandardPort:(uint32_t)standardPort { - if (chain_type_is_devnet_any(self.chainType)) { - _cachedStandardPort = standardPort; - setKeychainInt(standardPort, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], STANDARD_PORT_LOCATION], NO); - } -} - -- (uint32_t)standardDapiGRPCPort { - if (_cachedStandardDapiGRPCPort) return _cachedStandardDapiGRPCPort; - switch (self.chainType.tag) { - case ChainType_MainNet: - _cachedStandardDapiGRPCPort = MAINNET_DAPI_GRPC_STANDARD_PORT; - return MAINNET_DAPI_GRPC_STANDARD_PORT; - case ChainType_TestNet: - _cachedStandardDapiGRPCPort = TESTNET_DAPI_GRPC_STANDARD_PORT; - return TESTNET_DAPI_GRPC_STANDARD_PORT; - case ChainType_DevNet: { - NSError *error = nil; - uint32_t cachedStandardDapiGRPCPort = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], GRPC_PORT_LOCATION], &error); - if (!error && cachedStandardDapiGRPCPort) { - _cachedStandardDapiGRPCPort = cachedStandardDapiGRPCPort; - return _cachedStandardDapiGRPCPort; - } else - return DEVNET_DAPI_GRPC_STANDARD_PORT; - } - } -} - -- (void)setStandardDapiGRPCPort:(uint32_t)standardDapiGRPCPort { - if (chain_type_is_devnet_any(self.chainType)) { - _cachedStandardDapiGRPCPort = standardDapiGRPCPort; - setKeychainInt(standardDapiGRPCPort, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], GRPC_PORT_LOCATION], NO); - } -} - -// MARK: Mining and Dark Gravity Wave Parameters - -- (UInt256)maxProofOfWork { - if (uint256_is_not_zero(_cachedMaxProofOfWork)) return _cachedMaxProofOfWork; - switch (self.chainType.tag) { - case ChainType_MainNet: - _cachedMaxProofOfWork = MAX_PROOF_OF_WORK_MAINNET; - break; - case ChainType_TestNet: - _cachedMaxProofOfWork = MAX_PROOF_OF_WORK_TESTNET; - break; - case ChainType_DevNet: - _cachedMaxProofOfWork = MAX_PROOF_OF_WORK_DEVNET; - break; - } - return _cachedMaxProofOfWork; -} - -- (uint32_t)maxProofOfWorkTarget { - return chain_max_proof_of_work_target(self.chainType); -} - -- (BOOL)allowMinDifficultyBlocks { - return chain_allow_min_difficulty_blocks(self.chainType); -} - -- (uint64_t)baseReward { - if (self.chainType.tag == ChainType_MainNet) return 5 * DUFFS; - return 50 * DUFFS; -} - -// MARK: Spork Parameters - -- (NSString *)sporkPublicKeyHexString { - switch (self.chainType.tag) { - case ChainType_MainNet: - return SPORK_PUBLIC_KEY_MAINNET; - case ChainType_TestNet: - return SPORK_PUBLIC_KEY_TESTNET; - case ChainType_DevNet: { - NSError *error = nil; - NSString *publicKey = getKeychainString([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], SPORK_PUBLIC_KEY_LOCATION], &error); - if (!error && publicKey) { - return publicKey; - } else { - return nil; - } - } - } - return nil; -} - -- (void)setSporkPublicKeyHexString:(NSString *)sporkPublicKey { - if (chain_type_is_devnet_any(self.chainType)) { - setKeychainString(sporkPublicKey, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], SPORK_PUBLIC_KEY_LOCATION], NO); - } -} - -- (NSString *)sporkPrivateKeyBase58String { - if (chain_type_is_devnet_any(self.chainType)) { - NSError *error = nil; - NSString *publicKey = getKeychainString([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], SPORK_PRIVATE_KEY_LOCATION], &error); - if (!error && publicKey) { - return publicKey; - } - } - return nil; -} - -- (void)setSporkPrivateKeyBase58String:(NSString *)sporkPrivateKey { - if (chain_type_is_devnet_any(self.chainType)) { - setKeychainString(sporkPrivateKey, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], SPORK_PRIVATE_KEY_LOCATION], YES); - } -} - -- (NSString *)sporkAddress { - switch (self.chainType.tag) { - case ChainType_MainNet: - return SPORK_ADDRESS_MAINNET; - case ChainType_TestNet: - return SPORK_ADDRESS_TESTNET; - case ChainType_DevNet: { - NSError *error = nil; - NSString *publicKey = getKeychainString([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], SPORK_ADDRESS_LOCATION], &error); - if (!error && publicKey) { - return publicKey; - } else { - return nil; - } - } - } - return nil; -} - -- (void)setSporkAddress:(NSString *)sporkAddress { - if (chain_type_is_devnet_any(self.chainType)) { - setKeychainString(sporkAddress, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], SPORK_ADDRESS_LOCATION], NO); - } -} - -// MARK: Fee Parameters - -// fee that will be added for a transaction of the given size in bytes -- (uint64_t)feeForTxSize:(NSUInteger)size { - uint64_t standardFee = size * TX_FEE_PER_B; //!OCLINT // standard fee based on tx size -#if (!!FEE_PER_KB_URL) - uint64_t fee = ((size * self.feePerByte + 99) / 100) * 100; // fee using feePerByte, rounded up to nearest 100 satoshi - return (fee > standardFee) ? fee : standardFee; -#else - return standardFee; -#endif -} - -// outputs below this amount are uneconomical due to fees -- (uint64_t)minOutputAmount { - uint64_t amount = (TX_MIN_OUTPUT_AMOUNT * self.feePerByte + MIN_FEE_PER_B - 1) / MIN_FEE_PER_B; - return (amount > TX_MIN_OUTPUT_AMOUNT) ? amount : TX_MIN_OUTPUT_AMOUNT; -} - -// MARK: - L2 Chain Parameters - -- (uint32_t)platformProtocolVersion { - switch (self.chainType.tag) { - case ChainType_MainNet: - return PLATFORM_PROTOCOL_VERSION_MAINNET; //(70216 + (self.headersMaxAmount / 2000)); - case ChainType_TestNet: - return PLATFORM_PROTOCOL_VERSION_TESTNET; - case ChainType_DevNet: { - NSError *error = nil; - uint32_t platformProtocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], PLATFORM_PROTOCOL_VERSION_LOCATION], &error); - if (!error && platformProtocolVersion) - return platformProtocolVersion; - else - return PLATFORM_PROTOCOL_VERSION_DEVNET; - } - } -} - -- (void)setPlatformProtocolVersion:(uint32_t)platformProtocolVersion { - if (chain_type_is_devnet_any(self.chainType)) { - setKeychainInt(platformProtocolVersion, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], PLATFORM_PROTOCOL_VERSION_LOCATION], NO); - } -} - -- (UInt256)dpnsContractID { - if (uint256_is_not_zero(_cachedDpnsContractID)) return _cachedDpnsContractID; - switch (self.chainType.tag) { - case ChainType_MainNet: - if (!self.isEvolutionEnabled) return UINT256_ZERO; - _cachedDpnsContractID = MAINNET_DPNS_CONTRACT_ID.base58ToData.UInt256; - return _cachedDpnsContractID; - case ChainType_TestNet: - if (!self.isEvolutionEnabled) return UINT256_ZERO; - _cachedDpnsContractID = TESTNET_DPNS_CONTRACT_ID.base58ToData.UInt256; - return _cachedDpnsContractID; - case ChainType_DevNet: { - NSError *error = nil; - NSData *cachedDpnsContractIDData = getKeychainData([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DPNS_CONTRACT_ID], &error); - if (!error && cachedDpnsContractIDData) { - _cachedDpnsContractID = cachedDpnsContractIDData.UInt256; - return _cachedDpnsContractID; - } - return UINT256_ZERO; - } - } -} - -- (void)setDpnsContractID:(UInt256)dpnsContractID { - if (chain_type_is_devnet_any(self.chainType)) { - _cachedDpnsContractID = dpnsContractID; - if (uint256_is_zero(dpnsContractID)) { - NSError *error = nil; - NSString *identifier = [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DPNS_CONTRACT_ID]; - BOOL hasDashpayContractID = (getKeychainData(identifier, &error) != nil); - if (hasDashpayContractID) { - setKeychainData(nil, identifier, NO); - } - } else { - setKeychainData(uint256_data(dpnsContractID), [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DPNS_CONTRACT_ID], NO); - } - } -} - -- (UInt256)dashpayContractID { - if (uint256_is_not_zero(_cachedDashpayContractID)) return _cachedDashpayContractID; - switch (self.chainType.tag) { - case ChainType_MainNet: - if (!self.isEvolutionEnabled) return UINT256_ZERO; - _cachedDashpayContractID = MAINNET_DASHPAY_CONTRACT_ID.base58ToData.UInt256; - return _cachedDashpayContractID; - case ChainType_TestNet: - if (!self.isEvolutionEnabled) return UINT256_ZERO; - _cachedDashpayContractID = TESTNET_DASHPAY_CONTRACT_ID.base58ToData.UInt256; - return _cachedDashpayContractID; - case ChainType_DevNet: { - NSError *error = nil; - NSData *cachedDashpayContractIDData = getKeychainData([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DASHPAY_CONTRACT_ID], &error); - if (!error && cachedDashpayContractIDData) { - _cachedDashpayContractID = cachedDashpayContractIDData.UInt256; - return _cachedDashpayContractID; - } - return UINT256_ZERO; - } - } -} - -- (void)setDashpayContractID:(UInt256)dashpayContractID { - if (chain_type_is_devnet_any(self.chainType)) { - _cachedDashpayContractID = dashpayContractID; - if (uint256_is_zero(dashpayContractID)) { - NSError *error = nil; - NSString *identifier = [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DASHPAY_CONTRACT_ID]; - BOOL hasDashpayContractID = (getKeychainData(identifier, &error) != nil); - if (hasDashpayContractID) { - setKeychainData(nil, identifier, NO); - } - } else { - setKeychainData(uint256_data(dashpayContractID), [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DASHPAY_CONTRACT_ID], NO); - } - } -} - -- (void)setMinimumDifficultyBlocks:(uint32_t)minimumDifficultyBlocks { - if (chain_type_is_devnet_any(self.chainType)) { - _cachedMinimumDifficultyBlocks = minimumDifficultyBlocks; - setKeychainInt(minimumDifficultyBlocks, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], MINIMUM_DIFFICULTY_BLOCKS_COUNT_KEY], NO); - } -} - -- (uint32_t)minimumDifficultyBlocks { - if (_cachedMinimumDifficultyBlocks) return _cachedMinimumDifficultyBlocks; - if (chain_type_is_devnet_any(self.chainType)) { - NSError *error = nil; - uint32_t cachedMinimumDifficultyBlocks = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], MINIMUM_DIFFICULTY_BLOCKS_COUNT_KEY], &error); - if (!error && cachedMinimumDifficultyBlocks) { - _cachedMinimumDifficultyBlocks = cachedMinimumDifficultyBlocks; - return _cachedMinimumDifficultyBlocks; - } else { - return 0; - } - } else { - _cachedMinimumDifficultyBlocks = 0; - return 0; - } -} - - -- (uint32_t)standardDapiJRPCPort { - if (_cachedStandardDapiJRPCPort) return _cachedStandardDapiJRPCPort; - switch (self.chainType.tag) { - case ChainType_MainNet: - _cachedStandardDapiJRPCPort = MAINNET_DAPI_JRPC_STANDARD_PORT; - return MAINNET_DAPI_JRPC_STANDARD_PORT; - case ChainType_TestNet: - _cachedStandardDapiJRPCPort = TESTNET_DAPI_JRPC_STANDARD_PORT; - return TESTNET_DAPI_JRPC_STANDARD_PORT; - case ChainType_DevNet: { - NSError *error = nil; - uint32_t cachedStandardDapiJRPCPort = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], JRPC_PORT_LOCATION], &error); - if (!error && cachedStandardDapiJRPCPort) { - _cachedStandardDapiJRPCPort = cachedStandardDapiJRPCPort; - return _cachedStandardDapiJRPCPort; - } else - return DEVNET_DAPI_JRPC_STANDARD_PORT; - } - } -} - -- (void)setStandardDapiJRPCPort:(uint32_t)standardDapiJRPCPort { - if (chain_type_is_devnet_any(self.chainType)) { - _cachedStandardDapiJRPCPort = standardDapiJRPCPort; - setKeychainInt(standardDapiJRPCPort, [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], JRPC_PORT_LOCATION], NO); - } -} // MARK: - Standalone Derivation Paths @@ -1235,7 +588,7 @@ - (DSBloomFilter *)bloomFilterWithFalsePositiveRate:(double)falsePositiveRate wi [allUTXOs addObjectsFromArray:wallet.unspentOutputs]; //we should also add the blockchain user public keys to the filter - //[allAddresses addObjectsFromArray:[wallet blockchainIdentityAddresses]]; + //[allAddresses addObjectsFromArray:[wallet identityAddresses]]; [allAddresses addObjectsFromArray:[wallet providerOwnerAddresses]]; [allAddresses addObjectsFromArray:[wallet providerVotingAddresses]]; [allAddresses addObjectsFromArray:[wallet providerOperatorAddresses]]; @@ -1307,157 +660,8 @@ - (BOOL)canConstructAFilter { return [self hasAStandaloneDerivationPath] || [self hasAWallet]; } -// MARK: - Checkpoints - -- (BOOL)blockHeightHasCheckpoint:(uint32_t)blockHeight { - DSCheckpoint *checkpoint = [self lastCheckpointOnOrBeforeHeight:blockHeight]; - return (checkpoint.height == blockHeight); -} - -- (DSCheckpoint *)lastCheckpoint { - if (!_lastCheckpoint) { - _lastCheckpoint = [[self checkpoints] lastObject]; - } - return _lastCheckpoint; -} - -- (DSCheckpoint *)lastCheckpointOnOrBeforeHeight:(uint32_t)height { - NSUInteger genesisHeight = [self isDevnetAny] ? 1 : 0; - // if we don't have any blocks yet, use the latest checkpoint that's at least a week older than earliestKeyTime - for (long i = self.checkpoints.count - 1; i >= genesisHeight; i--) { - if (i == genesisHeight || ![self syncsBlockchain] || (self.checkpoints[i].height <= height)) { - return self.checkpoints[i]; - } - } - return nil; -} - -- (DSCheckpoint *)lastCheckpointOnOrBeforeTimestamp:(NSTimeInterval)timestamp { - NSUInteger genesisHeight = [self isDevnetAny] ? 1 : 0; - // if we don't have any blocks yet, use the latest checkpoint that's at least a week older than earliestKeyTime - for (long i = self.checkpoints.count - 1; i >= genesisHeight; i--) { - if (i == genesisHeight || ![self syncsBlockchain] || (self.checkpoints[i].timestamp <= timestamp)) { - return self.checkpoints[i]; - } - } - return nil; -} - -- (DSCheckpoint *_Nullable)lastCheckpointHavingMasternodeList { - NSSet *set = [self.checkpointsByHeightDictionary keysOfEntriesPassingTest:^BOOL(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) { - DSCheckpoint *checkpoint = (DSCheckpoint *)obj; - return (checkpoint.masternodeListName && ![checkpoint.masternodeListName isEqualToString:@""]); - }]; - NSArray *numbers = [[set allObjects] sortedArrayUsingSelector:@selector(compare:)]; - if (!numbers.count) return nil; - return self.checkpointsByHeightDictionary[numbers.lastObject]; -} - -- (DSCheckpoint *)checkpointForBlockHash:(UInt256)blockHash { - return [self.checkpointsByHashDictionary objectForKey:uint256_data(blockHash)]; -} - -- (DSCheckpoint *)checkpointForBlockHeight:(uint32_t)blockHeight { - return [self.checkpointsByHeightDictionary objectForKey:@(blockHeight)]; -} - -- (void)useCheckpointBeforeOrOnHeightForTerminalBlocksSync:(uint32_t)blockHeight { - DSCheckpoint *checkpoint = [self lastCheckpointOnOrBeforeHeight:blockHeight]; - self.terminalHeadersOverrideUseCheckpoint = checkpoint; -} - -- (void)useCheckpointBeforeOrOnHeightForSyncingChainBlocks:(uint32_t)blockHeight { - DSCheckpoint *checkpoint = [self lastCheckpointOnOrBeforeHeight:blockHeight]; - self.syncHeadersOverrideUseCheckpoint = checkpoint; -} - - -// MARK: - Wallet - -- (BOOL)hasAWallet { - return [self.mWallets count] > 0; -} - -- (NSArray *)wallets { - return [self.mWallets copy]; -} - -- (void)unregisterAllWallets { - for (DSWallet *wallet in [self.mWallets copy]) { - [self unregisterWallet:wallet]; - } -} - -- (void)unregisterAllWalletsMissingExtendedPublicKeys { - for (DSWallet *wallet in [self.mWallets copy]) { - if ([wallet hasAnExtendedPublicKeyMissing]) { - [self unregisterWallet:wallet]; - } - } -} - -- (void)unregisterWallet:(DSWallet *)wallet { - NSAssert(wallet.chain == self, @"the wallet you are trying to remove is not on this chain"); - [wallet wipeBlockchainInfoInContext:self.chainManagedObjectContext]; - [wallet wipeWalletInfo]; - [self.mWallets removeObject:wallet]; - NSError *error = nil; - NSMutableArray *keyChainArray = [getKeychainArray(self.chainWalletsKey, @[[NSString class]], &error) mutableCopy]; - if (!keyChainArray) keyChainArray = [NSMutableArray array]; - [keyChainArray removeObject:wallet.uniqueIDString]; - setKeychainArray(keyChainArray, self.chainWalletsKey, NO); - [self notify:DSChainWalletsDidChangeNotification userInfo:@{DSChainManagerNotificationChainKey: self}]; -} - -- (BOOL)addWallet:(DSWallet *)walletToAdd { - BOOL alreadyPresent = FALSE; - for (DSWallet *cWallet in self.mWallets) { - if ([cWallet.uniqueIDString isEqual:walletToAdd.uniqueIDString]) { - alreadyPresent = TRUE; - } - } - if (!alreadyPresent) { - [self.mWallets addObject:walletToAdd]; - return TRUE; - } - return FALSE; -} -- (void)registerWallet:(DSWallet *)wallet { - BOOL firstWallet = !self.mWallets.count; - if ([self.mWallets indexOfObject:wallet] == NSNotFound) { - [self addWallet:wallet]; - } - - if (firstWallet) { - //this is the first wallet, we should reset the last block height to the most recent checkpoint. - _lastSyncBlock = nil; //it will lazy load later - } - - NSError *error = nil; - NSMutableArray *keyChainArray = [getKeychainArray(self.chainWalletsKey, @[[NSString class]], &error) mutableCopy]; - if (!keyChainArray) keyChainArray = [NSMutableArray array]; - if (![keyChainArray containsObject:wallet.uniqueIDString]) { - [keyChainArray addObject:wallet.uniqueIDString]; - setKeychainArray(keyChainArray, self.chainWalletsKey, NO); - [self notify:DSChainWalletsDidChangeNotification userInfo:@{DSChainManagerNotificationChainKey: self}]; - } -} -- (void)retrieveWallets { - NSError *error = nil; - NSArray *walletIdentifiers = getKeychainArray(self.chainWalletsKey, @[[NSString class]], &error); - if (!error && walletIdentifiers) { - for (NSString *uniqueID in walletIdentifiers) { - DSWallet *wallet = [[DSWallet alloc] initWithUniqueID:uniqueID forChain:self]; - [self addWallet:wallet]; - } - //we should load blockchain identies after all wallets are in the chain, as blockchain identities might be on different wallets and have interactions between each other - for (DSWallet *wallet in self.wallets) { - [wallet loadBlockchainIdentities]; - } - } -} // MARK: - Blocks @@ -1493,7 +697,7 @@ - (DSBlock *)lastBlockOnOrBeforeTimestamp:(NSTimeInterval)timestamp { } - (void)setLastTerminalBlockFromCheckpoints { - DSCheckpoint *checkpoint = self.terminalHeadersOverrideUseCheckpoint ? self.terminalHeadersOverrideUseCheckpoint : [self lastCheckpoint]; + DSCheckpoint *checkpoint = [self lastTerminalCheckpoint]; if (checkpoint) { if (self.mTerminalBlocks[uint256_obj(checkpoint.blockHash)]) { _lastTerminalBlock = self.mSyncBlocks[uint256_obj(checkpoint.blockHash)]; @@ -1544,6 +748,11 @@ - (DSBlock *)lastSyncBlock { return [self lastSyncBlockWithUseCheckpoints:YES]; } +- (void)resetLastSyncBlock { + _lastSyncBlock = nil; +} + + - (DSBlock *)lastSyncBlockWithUseCheckpoints:(BOOL)useCheckpoints { if (_lastSyncBlock) return _lastSyncBlock; @@ -1575,8 +784,8 @@ - (NSMutableDictionary *)mSyncBlocks { UInt256 checkpointHash = checkpoint.blockHash; self->_mSyncBlocks[uint256_obj(checkpointHash)] = [[DSBlock alloc] initWithCheckpoint:checkpoint onChain:self]; - self->_checkpointsByHeightDictionary[@(checkpoint.height)] = checkpoint; - self->_checkpointsByHashDictionary[uint256_data(checkpointHash)] = checkpoint; + self.checkpointsByHeightDictionary[@(checkpoint.height)] = checkpoint; + self.checkpointsByHashDictionary[uint256_data(checkpointHash)] = checkpoint; } }]; @@ -1585,7 +794,8 @@ - (NSMutableDictionary *)mSyncBlocks { } - (NSArray *)chainSyncBlockLocatorArray { - if (_lastSyncBlock && !(_lastSyncBlock.height == 1 && self.chainType.tag == ChainType_DevNet)) { + + if (_lastSyncBlock && !(_lastSyncBlock.height == 1 && dash_spv_crypto_network_chain_type_ChainType_is_devnet_any(self.chainType))) { return [self blockLocatorArrayForBlock:_lastSyncBlock]; } else if (!_lastPersistedChainSyncLocators) { _lastPersistedChainSyncLocators = [self blockLocatorArrayOnOrBeforeTimestamp:BIP39_CREATION_TIME includeInitialTerminalBlocks:NO]; @@ -1729,6 +939,19 @@ - (void)blockUntilGetInsightForBlockHash:(UInt256)blockHash { }]; dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); } +- (DSBlock *_Nullable)blockUntilGetInsightForBlockHeight:(uint32_t)blockHeight { + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + __block DSBlock *b = NULL; + [[DSInsightManager sharedInstance] blockForBlockHeight:blockHeight onChain:self completion:^(DSBlock *_Nullable block, NSError *_Nullable error) { + if (!error && block) { + [self addInsightVerifiedBlock:block forBlockHash:block.blockHash]; + b = block; + } + dispatch_semaphore_signal(sem); + }]; + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + return b; +} - (void)addInsightVerifiedBlock:(DSBlock *)block forBlockHash:(UInt256)blockHash { if ([self allowInsightBlocksForVerification]) { @@ -1956,7 +1179,7 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:( onMainChain = TRUE; if ([self blockHeightHasCheckpoint:h] || - ((h % 1000 == 0) && (h + BLOCK_NO_FORK_DEPTH < self.lastTerminalBlockHeight) && !self.chainManager.masternodeManager.hasMasternodeListCurrentlyBeingSaved)) { + ((h % 1000 == 0) && (h + BLOCK_NO_FORK_DEPTH < self.lastTerminalBlockHeight) && !self.shareCore.hasMasternodeListCurrentlyBeingSaved)) { [self saveBlockLocators]; } @@ -2209,8 +1432,8 @@ - (NSMutableDictionary *)mTerminalBlocks { UInt256 checkpointHash = checkpoint.blockHash; self->_mTerminalBlocks[uint256_obj(checkpointHash)] = [[DSBlock alloc] initWithCheckpoint:checkpoint onChain:self]; - self->_checkpointsByHeightDictionary[@(checkpoint.height)] = checkpoint; - self->_checkpointsByHashDictionary[uint256_data(checkpointHash)] = checkpoint; + self.checkpointsByHeightDictionary[@(checkpoint.height)] = checkpoint; + self.checkpointsByHashDictionary[uint256_data(checkpointHash)] = checkpoint; } for (DSMerkleBlockEntity *e in [DSMerkleBlockEntity lastTerminalBlocks:KEEP_RECENT_TERMINAL_BLOCKS onChainEntity:[self chainEntityInContext:self.chainManagedObjectContext]]) { @autoreleasepool { @@ -2242,7 +1465,7 @@ - (DSBlock *)lastTerminalBlock { @synchronized (self) { if (!_lastTerminalBlock) { // if we don't have any headers yet, use the latest checkpoint - DSCheckpoint *lastCheckpoint = self.terminalHeadersOverrideUseCheckpoint ? self.terminalHeadersOverrideUseCheckpoint : self.lastCheckpoint; + DSCheckpoint *lastCheckpoint = [self lastTerminalCheckpoint]; uint32_t lastSyncBlockHeight = self.lastSyncBlockHeight; if (lastCheckpoint.height >= lastSyncBlockHeight) { @@ -2618,13 +1841,6 @@ - (void)setBlockHeight:(int32_t)height andTimestamp:(NSTimeInterval)timestamp fo updatedTransactions:updatedTransactions]; } -- (void)reloadDerivationPaths { - for (DSWallet *wallet in self.mWallets) { - if (!wallet.isTransient) { //no need to reload transient wallets (those are for testing purposes) - [wallet reloadDerivationPaths]; - } - } -} - (uint32_t)estimatedBlockHeight { @synchronized (self) { @@ -2710,159 +1926,9 @@ - (void)removeEstimatedBlockHeightOfPeer:(DSPeer *)peer { } } -// MARK: - Accounts - -- (uint64_t)balance { - uint64_t rBalance = 0; - for (DSWallet *wallet in self.wallets) { - rBalance += wallet.balance; - } - for (DSDerivationPath *standaloneDerivationPath in self.standaloneDerivationPaths) { - rBalance += standaloneDerivationPath.balance; - } - return rBalance; -} - -- (DSAccount *_Nullable)firstAccountWithBalance { - for (DSWallet *wallet in self.wallets) { - DSAccount *account = [wallet firstAccountWithBalance]; - if (account) return account; - } - return nil; -} - -- (DSAccount *_Nullable)firstAccountThatCanContainTransaction:(DSTransaction *)transaction { - if (!transaction) return nil; - for (DSWallet *wallet in self.wallets) { - DSAccount *account = [wallet firstAccountThatCanContainTransaction:transaction]; - if (account) return account; - } - return nil; -} - -- (NSArray *)accountsThatCanContainTransaction:(DSTransaction *)transaction { - NSMutableArray *mArray = [NSMutableArray array]; - if (!transaction) return @[]; - for (DSWallet *wallet in self.wallets) { - [mArray addObjectsFromArray:[wallet accountsThatCanContainTransaction:transaction]]; - } - return [mArray copy]; -} - -- (DSAccount *_Nullable)accountContainingAddress:(NSString *)address { - if (!address) return nil; - for (DSWallet *wallet in self.wallets) { - DSAccount *account = [wallet accountForAddress:address]; - if (account) return account; - } - return nil; -} - -- (DSAccount *_Nullable)accountContainingDashpayExternalDerivationPathAddress:(NSString *)address { - if (!address) return nil; - for (DSWallet *wallet in self.wallets) { - DSAccount *account = [wallet accountForDashpayExternalDerivationPathAddress:address]; - if (account) return account; - } - return nil; -} - -// returns an account to which the given transaction hash is associated with, no account if the transaction hash is not associated with the wallet -- (DSAccount *_Nullable)firstAccountForTransactionHash:(UInt256)txHash transaction:(DSTransaction **)transaction wallet:(DSWallet **)wallet { - for (DSWallet *lWallet in self.wallets) { - for (DSAccount *account in lWallet.accounts) { - DSTransaction *lTransaction = [account transactionForHash:txHash]; - if (lTransaction) { - if (transaction) *transaction = lTransaction; - if (wallet) *wallet = lWallet; - return account; - } - } - } - return nil; -} - -// returns an account to which the given transaction hash is associated with, no account if the transaction hash is not associated with the wallet -- (NSArray *)accountsForTransactionHash:(UInt256)txHash transaction:(DSTransaction **)transaction { - NSMutableArray *accounts = [NSMutableArray array]; - for (DSWallet *lWallet in self.wallets) { - for (DSAccount *account in lWallet.accounts) { - DSTransaction *lTransaction = [account transactionForHash:txHash]; - if (lTransaction) { - if (transaction) *transaction = lTransaction; - [accounts addObject:account]; - } - } - } - return [accounts copy]; -} -// MARK: - Transactions - -- (DSTransaction *)transactionForHash:(UInt256)txHash { - return [self transactionForHash:txHash returnWallet:nil]; -} - -- (DSTransaction *)transactionForHash:(UInt256)txHash returnWallet:(DSWallet **)rWallet { - for (DSWallet *wallet in self.wallets) { - DSTransaction *transaction = [wallet transactionForHash:txHash]; - if (transaction) { - if (rWallet) *rWallet = wallet; - return transaction; - } - } - return nil; -} - -- (NSArray *)allTransactions { - NSMutableArray *mArray = [NSMutableArray array]; - for (DSWallet *wallet in self.wallets) { - [mArray addObjectsFromArray:wallet.allTransactions]; - } - return mArray; -} - -// retuns the amount sent globally by the trasaction (total wallet outputs consumed, change and fee included) -- (uint64_t)amountReceivedFromTransaction:(DSTransaction *)transaction { - NSParameterAssert(transaction); - - uint64_t received = 0; - for (DSWallet *wallet in self.wallets) { - received += [wallet amountReceivedFromTransaction:transaction]; - } - return received; -} -// retuns the amount sent globally by the trasaction (total wallet outputs consumed, change and fee included) -- (uint64_t)amountSentByTransaction:(DSTransaction *)transaction { - NSParameterAssert(transaction); - - uint64_t sent = 0; - for (DSWallet *wallet in self.wallets) { - sent += [wallet amountSentByTransaction:transaction]; - } - return sent; -} -- (DSTransactionDirection)directionOfTransaction:(DSTransaction *)transaction { - const uint64_t sent = [self amountSentByTransaction:transaction]; - const uint64_t received = [self amountReceivedFromTransaction:transaction]; - const uint64_t fee = transaction.feeUsed; - - if (sent > 0 && (received + fee) == sent) { - // moved - return DSTransactionDirection_Moved; - } else if (sent > 0) { - // sent - return DSTransactionDirection_Sent; - } else if (received > 0) { - // received - return DSTransactionDirection_Received; - } else { - // no funds moved on this account - return DSTransactionDirection_NotAccountFunds; - } -} // MARK: - Wiping @@ -2871,10 +1937,10 @@ - (void)wipeBlockchainInfoInContext:(NSManagedObjectContext *)context { for (DSWallet *wallet in self.wallets) { [wallet wipeBlockchainInfoInContext:context]; } - [self wipeBlockchainIdentitiesPersistedDataInContext:context]; - [self wipeBlockchainInvitationsPersistedDataInContext:context]; + [self wipeIdentitiesPersistedDataInContext:context]; + [self wipeInvitationsPersistedDataInContext:context]; [self.viewingAccount wipeBlockchainInfo]; - [self.chainManager.identitiesManager clearExternalBlockchainIdentities]; + [self.chainManager.identitiesManager clearExternalIdentities]; _bestBlockHeight = 0; @synchronized (_mSyncBlocks) { _mSyncBlocks = [NSMutableDictionary dictionary]; @@ -2899,10 +1965,10 @@ - (void)wipeBlockchainNonTerminalInfoInContext:(NSManagedObjectContext *)context for (DSWallet *wallet in self.wallets) { [wallet wipeBlockchainInfoInContext:context]; } - [self wipeBlockchainIdentitiesPersistedDataInContext:context]; - [self wipeBlockchainInvitationsPersistedDataInContext:context]; + [self wipeIdentitiesPersistedDataInContext:context]; + [self wipeInvitationsPersistedDataInContext:context]; [self.viewingAccount wipeBlockchainInfo]; - [self.chainManager.identitiesManager clearExternalBlockchainIdentities]; + [self.chainManager.identitiesManager clearExternalIdentities]; _bestBlockHeight = 0; @synchronized (_mSyncBlocks) { _mSyncBlocks = [NSMutableDictionary dictionary]; @@ -2937,368 +2003,16 @@ - (void)wipeWalletsAndDerivatives { self.viewingAccount = nil; } -// MARK: - Identities - -- (uint32_t)localBlockchainIdentitiesCount { - uint32_t blockchainIdentitiesCount = 0; - for (DSWallet *lWallet in self.wallets) { - blockchainIdentitiesCount += [lWallet blockchainIdentitiesCount]; - } - return blockchainIdentitiesCount; -} - -- (NSArray *)localBlockchainIdentities { - NSMutableArray *rAllBlockchainIdentities = [NSMutableArray array]; - for (DSWallet *wallet in self.wallets) { - [rAllBlockchainIdentities addObjectsFromArray:[wallet.blockchainIdentities allValues]]; - } - return rAllBlockchainIdentities; -} - -- (NSDictionary *)localBlockchainIdentitiesByUniqueIdDictionary { - NSMutableDictionary *rAllBlockchainIdentities = [NSMutableDictionary dictionary]; - for (DSWallet *wallet in self.wallets) { - for (DSBlockchainIdentity *blockchainIdentity in [wallet.blockchainIdentities allValues]) { - rAllBlockchainIdentities[blockchainIdentity.uniqueIDData] = blockchainIdentity; - } - } - return rAllBlockchainIdentities; -} - - -- (DSBlockchainIdentity *)blockchainIdentityForUniqueId:(UInt256)uniqueId { - NSAssert(uint256_is_not_zero(uniqueId), @"uniqueId must not be null"); - return [self blockchainIdentityForUniqueId:uniqueId foundInWallet:nil includeForeignBlockchainIdentities:NO]; -} - -- (DSBlockchainIdentity *)blockchainIdentityForUniqueId:(UInt256)uniqueId foundInWallet:(DSWallet **)foundInWallet { - NSAssert(uint256_is_not_zero(uniqueId), @"uniqueId must not be null"); - return [self blockchainIdentityForUniqueId:uniqueId foundInWallet:foundInWallet includeForeignBlockchainIdentities:NO]; -} - -- (DSBlockchainIdentity *_Nullable)blockchainIdentityThatCreatedContract:(DPContract *)contract withContractId:(UInt256)contractId foundInWallet:(DSWallet **)foundInWallet { - NSAssert(uint256_is_not_zero(contractId), @"contractId must not be null"); - for (DSWallet *wallet in self.wallets) { - DSBlockchainIdentity *blockchainIdentity = [wallet blockchainIdentityThatCreatedContract:contract withContractId:contractId]; - if (blockchainIdentity) { - if (foundInWallet) { - *foundInWallet = wallet; - } - return blockchainIdentity; - } - } - return nil; -} - -- (DSBlockchainIdentity *)blockchainIdentityForUniqueId:(UInt256)uniqueId foundInWallet:(DSWallet **)foundInWallet includeForeignBlockchainIdentities:(BOOL)includeForeignBlockchainIdentities { - NSAssert(uint256_is_not_zero(uniqueId), @"uniqueId must not be null"); - for (DSWallet *wallet in self.wallets) { - DSBlockchainIdentity *blockchainIdentity = [wallet blockchainIdentityForUniqueId:uniqueId]; - if (blockchainIdentity) { - if (foundInWallet) { - *foundInWallet = wallet; - } - return blockchainIdentity; - } - } - if (includeForeignBlockchainIdentities) { - return [self.chainManager.identitiesManager foreignBlockchainIdentityWithUniqueId:uniqueId]; - } else { - return nil; - } -} - -- (void)wipeBlockchainIdentitiesPersistedDataInContext:(NSManagedObjectContext *)context { - [context performBlockAndWait:^{ - NSArray *objects = [DSBlockchainIdentityEntity objectsInContext:context matching:@"chain == %@", [self chainEntityInContext:context]]; - [DSBlockchainIdentityEntity deleteObjects:objects inContext:context]; - }]; -} - -// MARK: - Invitations - -- (uint32_t)localBlockchainInvitationsCount { - uint32_t blockchainInvitationsCount = 0; - for (DSWallet *lWallet in self.wallets) { - blockchainInvitationsCount += [lWallet blockchainInvitationsCount]; - } - return blockchainInvitationsCount; -} - -- (void)wipeBlockchainInvitationsPersistedDataInContext:(NSManagedObjectContext *)context { - [context performBlockAndWait:^{ - NSArray *objects = [DSBlockchainInvitationEntity objectsInContext:context matching:@"chain == %@", [self chainEntityInContext:context]]; - [DSBlockchainInvitationEntity deleteObjects:objects inContext:context]; - }]; -} - - -// MARK: - Registering special transactions - - -- (BOOL)registerProviderRegistrationTransaction:(DSProviderRegistrationTransaction *)providerRegistrationTransaction saveImmediately:(BOOL)saveImmediately { - DSWallet *ownerWallet = [self walletHavingProviderOwnerAuthenticationHash:providerRegistrationTransaction.ownerKeyHash foundAtIndex:nil]; - DSWallet *votingWallet = [self walletHavingProviderVotingAuthenticationHash:providerRegistrationTransaction.votingKeyHash foundAtIndex:nil]; - DSWallet *operatorWallet = [self walletHavingProviderOperatorAuthenticationKey:providerRegistrationTransaction.operatorKey foundAtIndex:nil]; - DSWallet *holdingWallet = [self walletContainingMasternodeHoldingAddressForProviderRegistrationTransaction:providerRegistrationTransaction foundAtIndex:nil]; - DSWallet *platformNodeWallet = [self walletHavingPlatformNodeAuthenticationHash:providerRegistrationTransaction.platformNodeID foundAtIndex:nil]; - DSAccount *account = [self accountContainingAddress:providerRegistrationTransaction.payoutAddress]; - BOOL registered = NO; - registered |= [account registerTransaction:providerRegistrationTransaction saveImmediately:saveImmediately]; - registered |= [ownerWallet.specialTransactionsHolder registerTransaction:providerRegistrationTransaction saveImmediately:saveImmediately]; - registered |= [votingWallet.specialTransactionsHolder registerTransaction:providerRegistrationTransaction saveImmediately:saveImmediately]; - registered |= [operatorWallet.specialTransactionsHolder registerTransaction:providerRegistrationTransaction saveImmediately:saveImmediately]; - registered |= [holdingWallet.specialTransactionsHolder registerTransaction:providerRegistrationTransaction saveImmediately:saveImmediately]; - registered |= [platformNodeWallet.specialTransactionsHolder registerTransaction:providerRegistrationTransaction saveImmediately:saveImmediately]; - - if (ownerWallet) { - DSAuthenticationKeysDerivationPath *ownerDerivationPath = [[DSDerivationPathFactory sharedInstance] providerOwnerKeysDerivationPathForWallet:ownerWallet]; - [ownerDerivationPath registerTransactionAddress:providerRegistrationTransaction.ownerAddress]; - } - - if (votingWallet) { - DSAuthenticationKeysDerivationPath *votingDerivationPath = [[DSDerivationPathFactory sharedInstance] providerVotingKeysDerivationPathForWallet:votingWallet]; - [votingDerivationPath registerTransactionAddress:providerRegistrationTransaction.votingAddress]; - } - - if (operatorWallet) { - DSAuthenticationKeysDerivationPath *operatorDerivationPath = [[DSDerivationPathFactory sharedInstance] providerOperatorKeysDerivationPathForWallet:operatorWallet]; - [operatorDerivationPath registerTransactionAddress:providerRegistrationTransaction.operatorAddress]; - } - - if (holdingWallet) { - DSMasternodeHoldingsDerivationPath *holdingDerivationPath = [[DSDerivationPathFactory sharedInstance] providerFundsDerivationPathForWallet:holdingWallet]; - [holdingDerivationPath registerTransactionAddress:providerRegistrationTransaction.holdingAddress]; - } - - if (platformNodeWallet) { - DSAuthenticationKeysDerivationPath *platformNodeDerivationPath = [[DSDerivationPathFactory sharedInstance] platformNodeKeysDerivationPathForWallet:platformNodeWallet]; - [platformNodeDerivationPath registerTransactionAddress:providerRegistrationTransaction.platformNodeAddress]; - } - - return registered; -} - -- (BOOL)registerProviderUpdateServiceTransaction:(DSProviderUpdateServiceTransaction *)providerUpdateServiceTransaction saveImmediately:(BOOL)saveImmediately { - DSWallet *providerRegistrationWallet = nil; - DSTransaction *providerRegistrationTransaction = [self transactionForHash:providerUpdateServiceTransaction.providerRegistrationTransactionHash returnWallet:&providerRegistrationWallet]; - DSAccount *account = [self accountContainingAddress:providerUpdateServiceTransaction.payoutAddress]; - BOOL registered = [account registerTransaction:providerUpdateServiceTransaction saveImmediately:saveImmediately]; - if (providerRegistrationTransaction && providerRegistrationWallet) { - registered |= [providerRegistrationWallet.specialTransactionsHolder registerTransaction:providerUpdateServiceTransaction saveImmediately:saveImmediately]; - } - return registered; -} -- (BOOL)registerProviderUpdateRegistrarTransaction:(DSProviderUpdateRegistrarTransaction *)providerUpdateRegistrarTransaction saveImmediately:(BOOL)saveImmediately { - DSWallet *votingWallet = [self walletHavingProviderVotingAuthenticationHash:providerUpdateRegistrarTransaction.votingKeyHash foundAtIndex:nil]; - DSWallet *operatorWallet = [self walletHavingProviderOperatorAuthenticationKey:providerUpdateRegistrarTransaction.operatorKey foundAtIndex:nil]; - [votingWallet.specialTransactionsHolder registerTransaction:providerUpdateRegistrarTransaction saveImmediately:saveImmediately]; - [operatorWallet.specialTransactionsHolder registerTransaction:providerUpdateRegistrarTransaction saveImmediately:saveImmediately]; - DSWallet *providerRegistrationWallet = nil; - DSTransaction *providerRegistrationTransaction = [self transactionForHash:providerUpdateRegistrarTransaction.providerRegistrationTransactionHash returnWallet:&providerRegistrationWallet]; - DSAccount *account = [self accountContainingAddress:providerUpdateRegistrarTransaction.payoutAddress]; - BOOL registered = [account registerTransaction:providerUpdateRegistrarTransaction saveImmediately:saveImmediately]; - if (providerRegistrationTransaction && providerRegistrationWallet) { - registered |= [providerRegistrationWallet.specialTransactionsHolder registerTransaction:providerUpdateRegistrarTransaction saveImmediately:saveImmediately]; - } - - if (votingWallet) { - DSAuthenticationKeysDerivationPath *votingDerivationPath = [[DSDerivationPathFactory sharedInstance] providerVotingKeysDerivationPathForWallet:votingWallet]; - [votingDerivationPath registerTransactionAddress:providerUpdateRegistrarTransaction.votingAddress]; - } - - if (operatorWallet) { - DSAuthenticationKeysDerivationPath *operatorDerivationPath = [[DSDerivationPathFactory sharedInstance] providerOperatorKeysDerivationPathForWallet:operatorWallet]; - [operatorDerivationPath registerTransactionAddress:providerUpdateRegistrarTransaction.operatorAddress]; - } - return registered; -} - -- (BOOL)registerProviderUpdateRevocationTransaction:(DSProviderUpdateRevocationTransaction *)providerUpdateRevocationTransaction saveImmediately:(BOOL)saveImmediately { - DSWallet *providerRegistrationWallet = nil; - DSTransaction *providerRegistrationTransaction = [self transactionForHash:providerUpdateRevocationTransaction.providerRegistrationTransactionHash returnWallet:&providerRegistrationWallet]; - if (providerRegistrationTransaction && providerRegistrationWallet) { - return [providerRegistrationWallet.specialTransactionsHolder registerTransaction:providerUpdateRevocationTransaction saveImmediately:saveImmediately]; - } else { - return NO; - } -} -// -//-(BOOL)registerBlockchainIdentityRegistrationTransaction:(DSBlockchainIdentityRegistrationTransition*)blockchainIdentityRegistrationTransaction { -// DSWallet * blockchainIdentityWallet = [self walletHavingBlockchainIdentityAuthenticationHash:blockchainIdentityRegistrationTransaction.pubkeyHash foundAtIndex:nil]; -// BOOL registered = [blockchainIdentityWallet.specialTransactionsHolder registerTransaction:blockchainIdentityRegistrationTransaction]; -// -// if (blockchainIdentityWallet) { -// DSAuthenticationKeysDerivationPath * blockchainIdentitiesDerivationPath = [[DSDerivationPathFactory sharedInstance] blockchainIdentityBLSKeysDerivationPathForWallet:blockchainIdentityWallet]; -// [blockchainIdentitiesDerivationPath registerTransactionAddress:blockchainIdentityRegistrationTransaction.pubkeyAddress]; -// } -// return registered; -//} -// -//-(BOOL)registerBlockchainIdentityResetTransaction:(DSBlockchainIdentityUpdateTransition*)blockchainIdentityResetTransaction { -// DSWallet * blockchainIdentityWallet = [self walletHavingBlockchainIdentityAuthenticationHash:blockchainIdentityResetTransaction.replacementPublicKeyHash foundAtIndex:nil]; -// [blockchainIdentityWallet.specialTransactionsHolder registerTransaction:blockchainIdentityResetTransaction]; -// DSWallet * blockchainIdentityRegistrationWallet = nil; -// DSTransaction * blockchainIdentityRegistrationTransaction = [self transactionForHash:blockchainIdentityResetTransaction.registrationTransactionHash returnWallet:&blockchainIdentityRegistrationWallet]; -// BOOL registered = NO; -// if (blockchainIdentityRegistrationTransaction && blockchainIdentityRegistrationWallet && (blockchainIdentityWallet != blockchainIdentityRegistrationWallet)) { -// registered = [blockchainIdentityRegistrationWallet.specialTransactionsHolder registerTransaction:blockchainIdentityResetTransaction]; -// } -// -// if (blockchainIdentityWallet) { -// DSAuthenticationKeysDerivationPath * blockchainIdentitiesDerivationPath = [[DSDerivationPathFactory sharedInstance] blockchainIdentityBLSKeysDerivationPathForWallet:blockchainIdentityWallet]; -// [blockchainIdentitiesDerivationPath registerTransactionAddress:blockchainIdentityResetTransaction.replacementAddress]; -// } -// return registered; -//} -// -//-(BOOL)registerBlockchainIdentityCloseTransaction:(DSBlockchainIdentityCloseTransition*)blockchainIdentityCloseTransaction { -// DSWallet * blockchainIdentityRegistrationWallet = nil; -// DSTransaction * blockchainIdentityRegistrationTransaction = [self transactionForHash:blockchainIdentityCloseTransaction.registrationTransactionHash returnWallet:&blockchainIdentityRegistrationWallet]; -// if (blockchainIdentityRegistrationTransaction && blockchainIdentityRegistrationWallet) { -// return [blockchainIdentityRegistrationWallet.specialTransactionsHolder registerTransaction:blockchainIdentityCloseTransaction]; -// } else { -// return NO; -// } -//} -// -//-(BOOL)registerBlockchainIdentityTopupTransaction:(DSBlockchainIdentityTopupTransition*)blockchainIdentityTopupTransaction { -// DSWallet * blockchainIdentityRegistrationWallet = nil; -// DSTransaction * blockchainIdentityRegistrationTransaction = [self transactionForHash:blockchainIdentityTopupTransaction.registrationTransactionHash returnWallet:&blockchainIdentityRegistrationWallet]; -// if (blockchainIdentityRegistrationTransaction && blockchainIdentityRegistrationWallet) { -// return [blockchainIdentityRegistrationWallet.specialTransactionsHolder registerTransaction:blockchainIdentityTopupTransaction]; -// } else { -// return NO; -// } -//} -// -//-(BOOL)registerTransition:(DSTransition*)transition { -// DSWallet * blockchainIdentityRegistrationWallet = nil; -// DSTransaction * blockchainIdentityRegistrationTransaction = [self transactionForHash:transition.registrationTransactionHash returnWallet:&blockchainIdentityRegistrationWallet]; -// if (blockchainIdentityRegistrationTransaction && blockchainIdentityRegistrationWallet) { -// return [blockchainIdentityRegistrationWallet.specialTransactionsHolder registerTransaction:transition]; -// } else { -// return NO; -// } -//} - -- (BOOL)registerSpecialTransaction:(DSTransaction *)transaction saveImmediately:(BOOL)saveImmediately { - if ([transaction isKindOfClass:[DSProviderRegistrationTransaction class]]) { - DSProviderRegistrationTransaction *providerRegistrationTransaction = (DSProviderRegistrationTransaction *)transaction; - return [self registerProviderRegistrationTransaction:providerRegistrationTransaction saveImmediately:saveImmediately]; - } else if ([transaction isKindOfClass:[DSProviderUpdateServiceTransaction class]]) { - DSProviderUpdateServiceTransaction *providerUpdateServiceTransaction = (DSProviderUpdateServiceTransaction *)transaction; - return [self registerProviderUpdateServiceTransaction:providerUpdateServiceTransaction saveImmediately:saveImmediately]; - } else if ([transaction isKindOfClass:[DSProviderUpdateRegistrarTransaction class]]) { - DSProviderUpdateRegistrarTransaction *providerUpdateRegistrarTransaction = (DSProviderUpdateRegistrarTransaction *)transaction; - return [self registerProviderUpdateRegistrarTransaction:providerUpdateRegistrarTransaction saveImmediately:saveImmediately]; - } else if ([transaction isKindOfClass:[DSProviderUpdateRevocationTransaction class]]) { - DSProviderUpdateRevocationTransaction *providerUpdateRevocationTransaction = (DSProviderUpdateRevocationTransaction *)transaction; - return [self registerProviderUpdateRevocationTransaction:providerUpdateRevocationTransaction saveImmediately:saveImmediately]; - } - return FALSE; -} - -// MARK: - Special Transactions - -//Does the chain mat -- (BOOL)transactionHasLocalReferences:(DSTransaction *)transaction { - if ([self firstAccountThatCanContainTransaction:transaction]) return TRUE; - - //PROVIDERS - if ([transaction isKindOfClass:[DSProviderRegistrationTransaction class]]) { - DSProviderRegistrationTransaction *providerRegistrationTransaction = (DSProviderRegistrationTransaction *)transaction; - if ([self walletHavingProviderOwnerAuthenticationHash:providerRegistrationTransaction.ownerKeyHash foundAtIndex:nil]) return TRUE; - if ([self walletHavingProviderVotingAuthenticationHash:providerRegistrationTransaction.votingKeyHash foundAtIndex:nil]) return TRUE; - if ([self walletHavingProviderOperatorAuthenticationKey:providerRegistrationTransaction.operatorKey foundAtIndex:nil]) return TRUE; - if ([self walletHavingPlatformNodeAuthenticationHash:providerRegistrationTransaction.platformNodeID foundAtIndex:nil]) return TRUE; - if ([self walletContainingMasternodeHoldingAddressForProviderRegistrationTransaction:providerRegistrationTransaction foundAtIndex:nil]) return TRUE; - if ([self accountContainingAddress:providerRegistrationTransaction.payoutAddress]) return TRUE; - } else if ([transaction isKindOfClass:[DSProviderUpdateServiceTransaction class]]) { - DSProviderUpdateServiceTransaction *providerUpdateServiceTransaction = (DSProviderUpdateServiceTransaction *)transaction; - if ([self transactionForHash:providerUpdateServiceTransaction.providerRegistrationTransactionHash]) return TRUE; - if ([self accountContainingAddress:providerUpdateServiceTransaction.payoutAddress]) return TRUE; - } else if ([transaction isKindOfClass:[DSProviderUpdateRegistrarTransaction class]]) { - DSProviderUpdateRegistrarTransaction *providerUpdateRegistrarTransaction = (DSProviderUpdateRegistrarTransaction *)transaction; - if ([self walletHavingProviderVotingAuthenticationHash:providerUpdateRegistrarTransaction.votingKeyHash foundAtIndex:nil]) return TRUE; - if ([self walletHavingProviderOperatorAuthenticationKey:providerUpdateRegistrarTransaction.operatorKey foundAtIndex:nil]) return TRUE; - if ([self transactionForHash:providerUpdateRegistrarTransaction.providerRegistrationTransactionHash]) return TRUE; - if ([self accountContainingAddress:providerUpdateRegistrarTransaction.payoutAddress]) return TRUE; - } else if ([transaction isKindOfClass:[DSProviderUpdateRevocationTransaction class]]) { - DSProviderUpdateRevocationTransaction *providerUpdateRevocationTransaction = (DSProviderUpdateRevocationTransaction *)transaction; - if ([self transactionForHash:providerUpdateRevocationTransaction.providerRegistrationTransactionHash]) return TRUE; - - //BLOCKCHAIN USERS - } - // else if ([transaction isKindOfClass:[DSBlockchainIdentityRegistrationTransition class]]) { - // DSBlockchainIdentityRegistrationTransition * blockchainIdentityRegistrationTransaction = (DSBlockchainIdentityRegistrationTransition *)transaction; - // if ([self walletHavingBlockchainIdentityAuthenticationHash:blockchainIdentityRegistrationTransaction.pubkeyHash foundAtIndex:nil]) return TRUE; - // } else if ([transaction isKindOfClass:[DSBlockchainIdentityUpdateTransition class]]) { - // DSBlockchainIdentityUpdateTransition * blockchainIdentityResetTransaction = (DSBlockchainIdentityUpdateTransition *)transaction; - // if ([self walletHavingBlockchainIdentityAuthenticationHash:blockchainIdentityResetTransaction.replacementPublicKeyHash foundAtIndex:nil]) return TRUE; - // if ([self transactionForHash:blockchainIdentityResetTransaction.registrationTransactionHash]) return TRUE; - // } else if ([transaction isKindOfClass:[DSBlockchainIdentityCloseTransition class]]) { - // DSBlockchainIdentityCloseTransition * blockchainIdentityCloseTransaction = (DSBlockchainIdentityCloseTransition *)transaction; - // if ([self transactionForHash:blockchainIdentityCloseTransaction.registrationTransactionHash]) return TRUE; - // } else if ([transaction isKindOfClass:[DSBlockchainIdentityTopupTransition class]]) { - // DSBlockchainIdentityTopupTransition * blockchainIdentityTopupTransaction = (DSBlockchainIdentityTopupTransition *)transaction; - // if ([self transactionForHash:blockchainIdentityTopupTransaction.registrationTransactionHash]) return TRUE; - // } - return FALSE; -} - -- (void)triggerUpdatesForLocalReferences:(DSTransaction *)transaction { - if ([transaction isKindOfClass:[DSProviderRegistrationTransaction class]]) { - DSProviderRegistrationTransaction *providerRegistrationTransaction = (DSProviderRegistrationTransaction *)transaction; - if ([self walletHavingProviderOwnerAuthenticationHash:providerRegistrationTransaction.ownerKeyHash foundAtIndex:nil] || - [self walletHavingProviderVotingAuthenticationHash:providerRegistrationTransaction.votingKeyHash foundAtIndex:nil] || - [self walletHavingProviderOperatorAuthenticationKey:providerRegistrationTransaction.operatorKey foundAtIndex:nil] || - [self walletHavingPlatformNodeAuthenticationHash:providerRegistrationTransaction.platformNodeID foundAtIndex:nil]) { - [self.chainManager.masternodeManager localMasternodeFromProviderRegistrationTransaction:providerRegistrationTransaction save:TRUE]; - } - } else if ([transaction isKindOfClass:[DSProviderUpdateServiceTransaction class]]) { - DSProviderUpdateServiceTransaction *providerUpdateServiceTransaction = (DSProviderUpdateServiceTransaction *)transaction; - DSLocalMasternode *localMasternode = [self.chainManager.masternodeManager localMasternodeHavingProviderRegistrationTransactionHash:providerUpdateServiceTransaction.providerRegistrationTransactionHash]; - [localMasternode updateWithUpdateServiceTransaction:providerUpdateServiceTransaction save:TRUE]; - } else if ([transaction isKindOfClass:[DSProviderUpdateRegistrarTransaction class]]) { - DSProviderUpdateRegistrarTransaction *providerUpdateRegistrarTransaction = (DSProviderUpdateRegistrarTransaction *)transaction; - DSLocalMasternode *localMasternode = [self.chainManager.masternodeManager localMasternodeHavingProviderRegistrationTransactionHash:providerUpdateRegistrarTransaction.providerRegistrationTransactionHash]; - [localMasternode updateWithUpdateRegistrarTransaction:providerUpdateRegistrarTransaction save:TRUE]; - } else if ([transaction isKindOfClass:[DSProviderUpdateRevocationTransaction class]]) { - DSProviderUpdateRevocationTransaction *providerUpdateRevocationTransaction = (DSProviderUpdateRevocationTransaction *)transaction; - DSLocalMasternode *localMasternode = [self.chainManager.masternodeManager localMasternodeHavingProviderRegistrationTransactionHash:providerUpdateRevocationTransaction.providerRegistrationTransactionHash]; - [localMasternode updateWithUpdateRevocationTransaction:providerUpdateRevocationTransaction save:TRUE]; - } else if ([transaction isKindOfClass:[DSCreditFundingTransaction class]]) { - DSCreditFundingTransaction *creditFundingTransaction = (DSCreditFundingTransaction *)transaction; - uint32_t index; - DSWallet *wallet = [self walletHavingBlockchainIdentityCreditFundingRegistrationHash:creditFundingTransaction.creditBurnPublicKeyHash foundAtIndex:&index]; - if (wallet) { - DSBlockchainIdentity *blockchainIdentity = [wallet blockchainIdentityForUniqueId:creditFundingTransaction.creditBurnIdentityIdentifier]; - if (!blockchainIdentity) { - blockchainIdentity = [[DSBlockchainIdentity alloc] initAtIndex:index withFundingTransaction:creditFundingTransaction withUsernameDictionary:nil inWallet:wallet]; - [blockchainIdentity registerInWalletForRegistrationFundingTransaction:creditFundingTransaction]; - } - } else { - wallet = [self walletHavingBlockchainIdentityCreditFundingInvitationHash:creditFundingTransaction.creditBurnPublicKeyHash foundAtIndex:&index]; - if (wallet) { - DSBlockchainInvitation *blockchainInvitation = [wallet blockchainInvitationForUniqueId:creditFundingTransaction.creditBurnIdentityIdentifier]; - if (!blockchainInvitation) { - blockchainInvitation = [[DSBlockchainInvitation alloc] initAtIndex:index withFundingTransaction:creditFundingTransaction inWallet:wallet]; - [blockchainInvitation registerInWalletForRegistrationFundingTransaction:creditFundingTransaction]; - } - } - } - } -} -- (void)updateAddressUsageOfSimplifiedMasternodeEntries:(NSArray *)simplifiedMasternodeEntries { - for (DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry in simplifiedMasternodeEntries) { - NSString *votingAddress = simplifiedMasternodeEntry.votingAddress; - NSString *operatorAddress = simplifiedMasternodeEntry.operatorAddress; - NSString *platformNodeAddress = simplifiedMasternodeEntry.platformNodeAddress; +- (void)updateAddressUsageOfSimplifiedMasternodeEntries:(DMasternodeEntryList *)simplifiedMasternodeEntries { + for (int i = 0; i < simplifiedMasternodeEntries->count; i++) { + DMasternodeEntry *entry = simplifiedMasternodeEntries->values[i]; + NSString *votingAddress = [DSKeyManager NSStringFrom:DMasternodeEntryVotingAddress(entry, self.chainType)]; + NSString *operatorAddress = [DSKeyManager NSStringFrom:DMasternodeEntryOperatorPublicKeyAddress(entry, self.chainType)]; + NSString *platformNodeAddress = [DSKeyManager NSStringFrom:DMasternodeEntryEvoNodeAddress(entry, self.chainType)]; for (DSWallet *wallet in self.wallets) { DSAuthenticationKeysDerivationPath *providerOperatorKeysDerivationPath = [[DSDerivationPathFactory sharedInstance] providerOperatorKeysDerivationPathForWallet:wallet]; if ([providerOperatorKeysDerivationPath containsAddress:operatorAddress]) { @@ -3313,110 +2027,30 @@ - (void)updateAddressUsageOfSimplifiedMasternodeEntries:(NSArray *)simplifiedMas [platformNodeKeysDerivationPath registerTransactionAddress:platformNodeAddress]; } } - } -} - -// MARK: - Merging Wallets - -- (DSWallet *)walletHavingBlockchainIdentityCreditFundingRegistrationHash:(UInt160)creditFundingRegistrationHash foundAtIndex:(uint32_t *)rIndex { - for (DSWallet *wallet in self.wallets) { - NSUInteger index = [wallet indexOfBlockchainIdentityCreditFundingRegistrationHash:creditFundingRegistrationHash]; - if (index != NSNotFound) { - if (rIndex) *rIndex = (uint32_t)index; - return wallet; - } - } - if (rIndex) *rIndex = UINT32_MAX; - return nil; -} - -- (DSWallet *)walletHavingBlockchainIdentityCreditFundingTopupHash:(UInt160)creditFundingTopupHash foundAtIndex:(uint32_t *)rIndex { - for (DSWallet *wallet in self.wallets) { - NSUInteger index = [wallet indexOfBlockchainIdentityCreditFundingTopupHash:creditFundingTopupHash]; - if (index != NSNotFound) { - if (rIndex) *rIndex = (uint32_t)index; - return wallet; - } - } - if (rIndex) *rIndex = UINT32_MAX; - return nil; -} - -- (DSWallet *)walletHavingBlockchainIdentityCreditFundingInvitationHash:(UInt160)creditFundingInvitationHash foundAtIndex:(uint32_t *)rIndex { - for (DSWallet *wallet in self.wallets) { - NSUInteger index = [wallet indexOfBlockchainIdentityCreditFundingInvitationHash:creditFundingInvitationHash]; - if (index != NSNotFound) { - if (rIndex) *rIndex = (uint32_t)index; - return wallet; - } - } - if (rIndex) *rIndex = UINT32_MAX; - return nil; -} - -- (DSWallet *)walletHavingProviderVotingAuthenticationHash:(UInt160)votingAuthenticationHash foundAtIndex:(uint32_t *)rIndex { - for (DSWallet *wallet in self.wallets) { - NSUInteger index = [wallet indexOfProviderVotingAuthenticationHash:votingAuthenticationHash]; - if (index != NSNotFound) { - if (rIndex) *rIndex = (uint32_t)index; - return wallet; - } - } - if (rIndex) *rIndex = UINT32_MAX; - return nil; -} - -- (DSWallet *_Nullable)walletHavingProviderOwnerAuthenticationHash:(UInt160)owningAuthenticationHash foundAtIndex:(uint32_t *)rIndex { - for (DSWallet *wallet in self.wallets) { - NSUInteger index = [wallet indexOfProviderOwningAuthenticationHash:owningAuthenticationHash]; - if (index != NSNotFound) { - if (rIndex) *rIndex = (uint32_t)index; - return wallet; - } - } - if (rIndex) *rIndex = UINT32_MAX; - return nil; -} -- (DSWallet *_Nullable)walletHavingProviderOperatorAuthenticationKey:(UInt384)providerOperatorAuthenticationKey foundAtIndex:(uint32_t *)rIndex { - for (DSWallet *wallet in self.wallets) { - NSUInteger index = [wallet indexOfProviderOperatorAuthenticationKey:providerOperatorAuthenticationKey]; - if (index != NSNotFound) { - if (rIndex) *rIndex = (uint32_t)index; - return wallet; - } } - if (rIndex) *rIndex = UINT32_MAX; - return nil; -} - -- (DSWallet *_Nullable)walletHavingPlatformNodeAuthenticationHash:(UInt160)platformNodeAuthenticationHash foundAtIndex:(uint32_t *)rIndex { - for (DSWallet *wallet in self.wallets) { - NSUInteger index = [wallet indexOfPlatformNodeAuthenticationHash:platformNodeAuthenticationHash]; - if (index != NSNotFound) { - if (rIndex) *rIndex = (uint32_t)index; - return wallet; - } - } - if (rIndex) *rIndex = UINT32_MAX; - return nil; + +// for (DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry in simplifiedMasternodeEntries) { +// NSString *votingAddress = simplifiedMasternodeEntry.votingAddress; +// NSString *operatorAddress = simplifiedMasternodeEntry.operatorAddress; +// NSString *platformNodeAddress = simplifiedMasternodeEntry.platformNodeAddress; +// for (DSWallet *wallet in self.wallets) { +// DSAuthenticationKeysDerivationPath *providerOperatorKeysDerivationPath = [[DSDerivationPathFactory sharedInstance] providerOperatorKeysDerivationPathForWallet:wallet]; +// if ([providerOperatorKeysDerivationPath containsAddress:operatorAddress]) { +// [providerOperatorKeysDerivationPath registerTransactionAddress:operatorAddress]; +// } +// DSAuthenticationKeysDerivationPath *providerVotingKeysDerivationPath = [[DSDerivationPathFactory sharedInstance] providerVotingKeysDerivationPathForWallet:wallet]; +// if ([providerVotingKeysDerivationPath containsAddress:votingAddress]) { +// [providerVotingKeysDerivationPath registerTransactionAddress:votingAddress]; +// } +// DSAuthenticationKeysDerivationPath *platformNodeKeysDerivationPath = [[DSDerivationPathFactory sharedInstance] platformNodeKeysDerivationPathForWallet:wallet]; +// if ([platformNodeKeysDerivationPath containsAddress:platformNodeAddress]) { +// [platformNodeKeysDerivationPath registerTransactionAddress:platformNodeAddress]; +// } +// } +// } } -- (DSWallet *_Nullable)walletContainingMasternodeHoldingAddressForProviderRegistrationTransaction:(DSProviderRegistrationTransaction *_Nonnull)transaction foundAtIndex:(uint32_t *)rIndex { - for (DSWallet *wallet in self.wallets) { - for (DSTransactionOutput *output in transaction.outputs) { - NSString *address = output.address; - if (!address || address == (id)[NSNull null]) continue; - NSUInteger index = [wallet indexOfHoldingAddress:address]; - if (index != NSNotFound) { - if (rIndex) *rIndex = (uint32_t)index; - return wallet; - } - } - } - if (rIndex) *rIndex = UINT32_MAX; - return nil; -} // MARK: - Persistence diff --git a/DashSync/shared/Models/Chain/DSChainConstants.h b/DashSync/shared/Models/Chain/DSChainConstants.h index 1a8b5f9b5..beeaac7d5 100644 --- a/DashSync/shared/Models/Chain/DSChainConstants.h +++ b/DashSync/shared/Models/Chain/DSChainConstants.h @@ -58,9 +58,12 @@ #define MAX_TARGET_PROOF_OF_WORK_TESTNET 0x1e0fffffu #define MAX_TARGET_PROOF_OF_WORK_DEVNET 0x207fffffu -#define MAX_PROOF_OF_WORK_MAINNET @"00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".hexToData.reverse.UInt256 // highest value for difficulty target (higher values are less difficult) -#define MAX_PROOF_OF_WORK_TESTNET @"00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".hexToData.reverse.UInt256 -#define MAX_PROOF_OF_WORK_DEVNET @"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".hexToData.reverse.UInt256 +#define MAX_PROOF_OF_WORK_MAINNET_DATA @"00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".hexToData.reverse // highest value for difficulty target (higher values are less difficult) +#define MAX_PROOF_OF_WORK_MAINNET MAX_PROOF_OF_WORK_MAINNET_DATA.UInt256 // highest value for difficulty target (higher values are less difficult) +#define MAX_PROOF_OF_WORK_TESTNET_DATA @"00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".hexToData.reverse +#define MAX_PROOF_OF_WORK_TESTNET MAX_PROOF_OF_WORK_TESTNET_DATA.UInt256 +#define MAX_PROOF_OF_WORK_DEVNET_DATA @"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".hexToData.reverse +#define MAX_PROOF_OF_WORK_DEVNET MAX_PROOF_OF_WORK_DEVNET_DATA.UInt256 #define SPORK_PUBLIC_KEY_MAINNET @"04549ac134f694c0243f503e8c8a9a986f5de6610049c40b07816809b0d1d06a21b07be27b9bb555931773f62ba6cf35a25fd52f694d4e1106ccd237a7bb899fdd" diff --git a/DashSync/shared/Models/Chain/DSChainLock.h b/DashSync/shared/Models/Chain/DSChainLock.h index 4feee31ef..cfe0902db 100644 --- a/DashSync/shared/Models/Chain/DSChainLock.h +++ b/DashSync/shared/Models/Chain/DSChainLock.h @@ -23,21 +23,24 @@ // THE SOFTWARE. #import "BigIntTypes.h" +#import "DSKeyManager.h" #import NS_ASSUME_NONNULL_BEGIN -@class DSChain, DSQuorumEntry, DSMasternodeList; +@class DSChain; @interface DSChainLock : NSObject +@property (nonatomic, readonly) DSChain *chain; @property (nonatomic, readonly) uint32_t height; @property (nonatomic, readonly) UInt256 blockHash; @property (nonatomic, readonly) UInt256 requestID; @property (nonatomic, readonly) UInt768 signature; @property (nonatomic, readonly) BOOL signatureVerified; @property (nonatomic, readonly) BOOL saved; -@property (nonatomic, readonly) DSQuorumEntry *intendedQuorum; +//@property (nonatomic, readonly) DSQuorumEntry *intendedQuorum; +@property (nonatomic, readonly) u384 *intendedQuorumPublicKey; // message can be either a merkleblock or header message + (instancetype)chainLockWithMessage:(NSData *)message onChain:(DSChain *)chain; @@ -48,7 +51,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; -- (DSQuorumEntry *)findSigningQuorumReturnMasternodeList:(DSMasternodeList *_Nullable *_Nullable)returnMasternodeList; +//- (DSQuorumEntry *)findSigningQuorumReturnMasternodeList:(DSMasternodeList *_Nullable *_Nullable)returnMasternodeList; - (BOOL)verifySignature; diff --git a/DashSync/shared/Models/Chain/DSChainLock.m b/DashSync/shared/Models/Chain/DSChainLock.m index 3d9af3dea..f1861be3a 100644 --- a/DashSync/shared/Models/Chain/DSChainLock.m +++ b/DashSync/shared/Models/Chain/DSChainLock.m @@ -23,15 +23,13 @@ // THE SOFTWARE. #import "DSChainLock.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" #import "DSChainEntity+CoreDataClass.h" #import "DSChainLockEntity+CoreDataClass.h" #import "DSChainManager.h" -#import "DSMasternodeList.h" #import "DSMasternodeManager.h" #import "DSMerkleBlockEntity+CoreDataClass.h" -#import "DSQuorumEntry.h" -#import "DSSimplifiedMasternodeEntry.h" #import "DSSporkManager.h" #import "NSData+DSHash.h" #import "NSData+Dash.h" @@ -49,13 +47,18 @@ @interface DSChainLock () @property (nonatomic, strong) NSArray *inputOutpoints; @property (nonatomic, assign) BOOL signatureVerified; @property (nonatomic, assign) BOOL quorumVerified; -@property (nonatomic, strong) DSQuorumEntry *intendedQuorum; +//@property (nonatomic, strong) DSQuorumEntry *intendedQuorum; +@property (nonatomic, assign) u384 *intendedQuorumPublicKey; @property (nonatomic, assign) BOOL saved; @end @implementation DSChainLock - +- (void)dealloc { + if (self.intendedQuorumPublicKey) { + u384_dtor(self.intendedQuorumPublicKey); + } +} // message can be either a merkleblock or header message + (instancetype)chainLockWithMessage:(NSData *)message onChain:(DSChain *)chain { return [[self alloc] initWithMessage:message onChain:chain]; @@ -87,7 +90,11 @@ - (instancetype)initOnChain:(DSChain *)chain { return self; } -- (instancetype)initWithBlockHash:(UInt256)blockHash signature:(UInt768)signature signatureVerified:(BOOL)signatureVerified quorumVerified:(BOOL)quorumVerified onChain:(DSChain *)chain { +- (instancetype)initWithBlockHash:(UInt256)blockHash + signature:(UInt768)signature + signatureVerified:(BOOL)signatureVerified + quorumVerified:(BOOL)quorumVerified + onChain:(DSChain *)chain { if (!(self = [self initOnChain:chain])) return nil; self.blockHash = blockHash; self.signatureVerified = signatureVerified; @@ -106,46 +113,40 @@ - (UInt256)requestID { return _requestID; } -- (UInt256)signIDForQuorumEntry:(DSQuorumEntry *)quorumEntry { - NSMutableData *data = [NSMutableData data]; - [data appendVarInt:quorum_type_for_chain_locks(self.chain.chainType)]; - [data appendUInt256:quorumEntry.quorumHash]; - [data appendUInt256:self.requestID]; - [data appendUInt256:self.blockHash]; - return [data SHA256_2]; -} - -- (BOOL)verifySignatureAgainstQuorum:(DSQuorumEntry *)quorumEntry { - UInt256 signId = [self signIDForQuorumEntry:quorumEntry]; - BOOL verified = key_bls_verify(quorumEntry.quorumPublicKey.u8, quorumEntry.useLegacyBLSScheme, signId.u8, self.signature.u8); -#if DEBUG - DSLog(@"[%@] verifySignatureAgainstQuorum (%u): %u: %u: %@: %@: %@: %u", self.chain.name, verified, quorumEntry.llmqType, quorumEntry.verified, @"", uint384_hex(quorumEntry.quorumPublicKey), @"", quorumEntry.useLegacyBLSScheme); -#else - DSLogPrivate(@"[%@] verifySignatureAgainstQuorum (%u): %u: %u: %@: %@: %@: %u", self.chain.name, verified, quorumEntry.llmqType, quorumEntry.verified, uint256_hex(signId), uint384_hex(quorumEntry.quorumPublicKey), uint768_hex(self.signature), quorumEntry.useLegacyBLSScheme); -#endif +- (BOOL)verifySignatureAgainstQuorum:(DLLMQEntry *)quorumEntry { + u256 *request_id = u256_ctor_u(self.requestID); + u256 *block_hash = u256_ctor_u(self.blockHash); + u768 *sig = u768_ctor_u(self.signature); + u256 *sign_id = DLLMQEntrySignID(quorumEntry, request_id, block_hash); + bool verified = DLLMQEntryVerifySignature(quorumEntry, sign_id, sig); return verified; } -- (DSQuorumEntry *)findSigningQuorumReturnMasternodeList:(DSMasternodeList **)returnMasternodeList { - DSQuorumEntry *foundQuorum = nil; - for (DSMasternodeList *masternodeList in self.chain.chainManager.masternodeManager.recentMasternodeLists) { - for (DSQuorumEntry *quorumEntry in [[masternodeList quorumsOfType:quorum_type_for_chain_locks(self.chain.chainType)] allValues]) { - BOOL signatureVerified = [self verifySignatureAgainstQuorum:quorumEntry]; - if (signatureVerified) { - foundQuorum = quorumEntry; - if (returnMasternodeList) *returnMasternodeList = masternodeList; - break; - } - } - if (foundQuorum) break; - } - return foundQuorum; -} +//- (DSQuorumEntry *)findSigningQuorumReturnMasternodeList:(DSMasternodeList **)returnMasternodeList { +// DSQuorumEntry *foundQuorum = nil; +// for (DSMasternodeList *masternodeList in self.chain.chainManager.masternodeManager.recentMasternodeLists) { +// for (DSQuorumEntry *quorumEntry in [[masternodeList quorumsOfType:quorum_type_for_chain_locks(self.chain.chainType)] allValues]) { +// BOOL signatureVerified = [self verifySignatureAgainstQuorum:quorumEntry]; +// if (signatureVerified) { +// foundQuorum = quorumEntry; +// if (returnMasternodeList) *returnMasternodeList = masternodeList; +// break; +// } +// } +// if (foundQuorum) break; +// } +// return foundQuorum; +//} - (BOOL)verifySignatureWithQuorumOffset:(uint32_t)offset { - DSQuorumEntry *quorumEntry = [self.chain.chainManager.masternodeManager quorumEntryForChainLockRequestID:[self requestID] forBlockHeight:self.height - offset]; - if (quorumEntry && quorumEntry.verified) { - self.signatureVerified = [self verifySignatureAgainstQuorum:quorumEntry]; + DLLMQEntry *quorumEntry = [self.chain.chainManager.masternodeManager quorumEntryForChainLockRequestID:[self requestID] forBlockHeight:self.height - offset]; + if (quorumEntry && quorumEntry->verified) { + u256 *request_id = u256_ctor_u(self.requestID); + u256 *block_hash = u256_ctor_u(self.blockHash); + u768 *sig = u768_ctor_u(self.signature); + u256 *sign_id = DLLMQEntrySignID(quorumEntry, request_id, block_hash); + self.signatureVerified = DLLMQEntryVerifySignature(quorumEntry, sign_id, sig); + if (!self.signatureVerified) { DSLog(@"[%@] unable to verify signature with offset %d", self.chain.name, offset); } else { @@ -153,22 +154,23 @@ - (BOOL)verifySignatureWithQuorumOffset:(uint32_t)offset { } } else if (quorumEntry) { - DSLog(@"[%@] quorum entry %@ found but is not yet verified", self.chain.name, uint256_hex(quorumEntry.quorumHash)); + DSLog(@"[%@] quorum entry %@ found but is not yet verified", self.chain.name, + [NSString stringWithUTF8String:DLLMQEntryHashHex(quorumEntry)]); } else { DSLog(@"[%@] no quorum entry found", self.chain.name); } if (self.signatureVerified) { - self.intendedQuorum = quorumEntry; - self.quorumVerified = self.intendedQuorum.verified; + self.intendedQuorumPublicKey = quorumEntry->public_key; + self.quorumVerified = quorumEntry->verified; //We should also set the chain's last chain lock if (!self.chain.lastChainLock || self.chain.lastChainLock.height < self.height) { self.chain.lastChainLock = self; } - } else if (quorumEntry.verified && offset == 8) { + } else if (quorumEntry && quorumEntry->verified && offset == 8) { //try again a few blocks more in the past DSLog(@"[%@] trying with offset 0", self.chain.name); return [self verifySignatureWithQuorumOffset:0]; - } else if (quorumEntry.verified && offset == 0) { + } else if (quorumEntry && quorumEntry->verified && offset == 0) { //try again a few blocks more in the future DSLog(@"[%@] trying with offset 16", self.chain.name); return [self verifySignatureWithQuorumOffset:16]; diff --git a/DashSync/shared/Models/Chain/DSFullBlock.m b/DashSync/shared/Models/Chain/DSFullBlock.m index 585f6f9f4..5e26adeff 100644 --- a/DashSync/shared/Models/Chain/DSFullBlock.m +++ b/DashSync/shared/Models/Chain/DSFullBlock.m @@ -18,6 +18,7 @@ #import "DSFullBlock.h" #import "DSBlock+Protected.h" #import "DSChain.h" +#import "DSChain+Params.h" #import "DSChainLock.h" #import "DSKeyManager.h" #import "DSTransactionFactory.h" diff --git a/DashSync/shared/Models/DAPI/DSDAPIClient.h b/DashSync/shared/Models/DAPI/DSDAPIClient.h index 713fc1da3..3401e10ef 100644 --- a/DashSync/shared/Models/DAPI/DSDAPIClient.h +++ b/DashSync/shared/Models/DAPI/DSDAPIClient.h @@ -27,7 +27,7 @@ typedef NS_ENUM(NSUInteger, DSDAPIClientErrorCode) DSDAPIClientErrorCodeNoKnownDAPINodes = 2, }; -@class DSChain, DSBlockchainIdentity, DPDocument, DSTransition, DPSTPacket, DPContract, DSDAPIPlatformNetworkService, DSDAPICoreNetworkService, DSPeer, DSSimplifiedMasternodeEntry; +@class DSChain, DSIdentity, DPDocument, DSTransition, DPSTPacket, DPContract, DSDAPIPlatformNetworkService, DSDAPICoreNetworkService, DSPeer; @interface DSDAPIClient : NSObject @@ -45,10 +45,10 @@ typedef NS_ENUM(NSUInteger, DSDAPIClientErrorCode) - (void)removeDAPINodeByAddress:(NSString *)host; -- (void)getAllStateTransitionsForUser:(DSBlockchainIdentity *)blockchainIdentity completion:(void (^)(NSError *_Nullable error))completion; +- (void)getAllStateTransitionsForUser:(DSIdentity *)identity completion:(void (^)(NSError *_Nullable error))completion; - (void)sendDocument:(DPDocument *)document - forIdentity:(DSBlockchainIdentity *)blockchainIdentity + forIdentity:(DSIdentity *)identity contract:(DPContract *)contract completion:(void (^)(NSError *_Nullable error))completion; @@ -61,7 +61,7 @@ typedef NS_ENUM(NSUInteger, DSDAPIClientErrorCode) success:(void (^)(NSDictionary *successDictionary, BOOL added))success failure:(void (^)(NSError *error))failure; -- (void)checkPingTimesForMasternodes:(NSArray *)masternodes completion:(void (^)(NSMutableDictionary *pingTimes, NSMutableDictionary *))completion; +//- (void)checkPingTimesForMasternodes:(NSArray *)masternodes completion:(void (^)(NSMutableDictionary *pingTimes, NSMutableDictionary *))completion; @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/DAPI/DSDAPIClient.m b/DashSync/shared/Models/DAPI/DSDAPIClient.m index 17526ea26..9f2cb57ed 100644 --- a/DashSync/shared/Models/DAPI/DSDAPIClient.m +++ b/DashSync/shared/Models/DAPI/DSDAPIClient.m @@ -62,7 +62,7 @@ - (instancetype)initWithChain:(DSChain *)chain { } //- (void)sendDocument:(DPDocument *)document -// forUser:(DSBlockchainIdentity*)blockchainIdentity +// forUser:(DSIdentity*)identity // contract:(DPContract *)contract // completion:(void (^)(NSError *_Nullable error))completion { // NSParameterAssert(document); @@ -72,56 +72,36 @@ - (instancetype)initWithChain:(DSChain *)chain { // // DSDashPlatform *platform = [DSDashPlatform sharedInstanceForChain:self.chain]; // -// DSDocumentTransition *transition = [blockchainIdentity documentTransition]; +// DSDocumentTransition *transition = [identity documentTransition]; // // DPSTPacket *stPacket = [platform.stPacketFactory packetWithContractId:contract.identifier documents:documents]; -// [self sendPacket:stPacket forUser:blockchainIdentity completion:completion]; +// [self sendPacket:stPacket forUser:identity completion:completion]; //} - (void)sendDocument:(DPDocument *)document - forIdentity:(DSBlockchainIdentity *)blockchainIdentity + forIdentity:(DSIdentity *)identity contract:(DPContract *)contract completion:(void (^)(NSError *_Nullable error))completion { NSParameterAssert(document); NSParameterAssert(contract); - DSDocumentTransition *documentTransition = [[DSDocumentTransition alloc] initForDocuments:@[document] withTransitionVersion:1 blockchainIdentityUniqueId:blockchainIdentity.uniqueID onChain:self.chain]; - - __weak typeof(self) weakSelf = self; - [blockchainIdentity signStateTransition:documentTransition - completion:^(BOOL success) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - completion([NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]); - } - return; - } - - if (success) { - [strongSelf publishTransition:documentTransition - success:^(NSDictionary *_Nonnull successDictionary, BOOL added) { - if (completion) { - completion(nil); - } - } - failure:^(NSError *_Nonnull error) { - if (completion) { - completion(error); - } - }]; - } else { - if (completion) { - NSError *error = [NSError errorWithDomain:DSDAPIClientErrorDomain - code:DSDAPIClientErrorCodeSignTransitionFailed - userInfo:nil]; - completion(error); - } - } - }]; + DSDocumentTransition *documentTransition = [[DSDocumentTransition alloc] initForDocuments:@[document] withTransitionVersion:1 identityUniqueId:identity.uniqueID onChain:self.chain]; + if ([identity signStateTransition:documentTransition]) { + [self publishTransition:documentTransition + success:^(NSDictionary *_Nonnull successDictionary, BOOL added) { + if (completion) completion(nil); + } + failure:^(NSError *_Nonnull error) { + if (completion) completion(error); + }]; + } else if (completion) { + completion([NSError errorWithDomain:DSDAPIClientErrorDomain + code:DSDAPIClientErrorCodeSignTransitionFailed + userInfo:nil]); + } } -- (void)getAllStateTransitionsForUser:(DSBlockchainIdentity *)blockchainIdentity completion:(void (^)(NSError *_Nullable error))completion { +- (void)getAllStateTransitionsForUser:(DSIdentity *)identity completion:(void (^)(NSError *_Nullable error))completion { // DSDAPINetworkService * service = self.DAPINetworkService; // if (!service) { // completion([NSError errorWithDomain:DSDAPIClientErrorDomain @@ -129,11 +109,11 @@ - (void)getAllStateTransitionsForUser:(DSBlockchainIdentity *)blockchainIdentity // userInfo:@{NSLocalizedDescriptionKey:@"No known DAPI Nodes"}]); // return; // } - // [service getUserById:uint256_reverse_hex(blockchainIdentity.registrationTransitionHash) success:^(NSDictionary * _Nonnull blockchainIdentityDictionary) { - // if ([blockchainIdentityDictionary objectForKey:@"subtx"] && [[blockchainIdentityDictionary objectForKey:@"subtx"] isKindOfClass:[NSArray class]]) { - // NSArray * subscriptionTransactions = [blockchainIdentityDictionary objectForKey:@"subtx"]; + // [service getUserById:uint256_reverse_hex(identity.registrationTransitionHash) success:^(NSDictionary * _Nonnull identityDictionary) { + // if ([identityDictionary objectForKey:@"subtx"] && [[identityDictionary objectForKey:@"subtx"] isKindOfClass:[NSArray class]]) { + // NSArray * subscriptionTransactions = [identityDictionary objectForKey:@"subtx"]; // NSMutableArray * oldSubscriptionTransactionHashes = [NSMutableArray array]; - // for (DSTransaction * transaction in blockchainIdentity.allTransitions) { + // for (DSTransaction * transaction in identity.allTransitions) { // [oldSubscriptionTransactionHashes addObject:[NSData dataWithUInt256:transaction.txHash]]; // } // NSMutableArray * novelSubscriptionTransactionHashes = [NSMutableArray array]; @@ -151,10 +131,10 @@ - (void)getAllStateTransitionsForUser:(DSBlockchainIdentity *)blockchainIdentity // //this is a transition // NSString * extraPayload = tx[@"extraPayload"]; // uint16_t version = [tx[@"version"] shortValue]; - // DSTransition * transition = [[DSTransition alloc] initWithVersion:version payloadData:extraPayload.hexToData onChain:blockchainIdentity.wallet.chain]; + // DSTransition * transition = [[DSTransition alloc] initWithVersion:version payloadData:extraPayload.hexToData onChain:identity.wallet.chain]; // transition.blockHeight = [tx[@"blockheight"] unsignedIntValue]; - // [blockchainIdentity.wallet.specialTransactionsHolder registerTransaction:transition]; - // [blockchainIdentity updateWithTransition:transition save:TRUE]; + // [identity.wallet.specialTransactionsHolder registerTransaction:transition]; + // [identity updateWithTransition:transition save:TRUE]; // if (completion) { // completion(nil); // } @@ -179,44 +159,45 @@ - (void)getAllStateTransitionsForUser:(DSBlockchainIdentity *)blockchainIdentity } //check ping times of all DAPI nodes -- (void)checkPingTimesForMasternodes:(NSArray *)masternodes completion:(void (^)(NSMutableDictionary *pingTimes, NSMutableDictionary *))completion { - dispatch_async(self.platformMetadataDispatchQueue, ^{ - HTTPLoaderFactory *loaderFactory = [DSNetworkingCoordinator sharedInstance].loaderFactory; - __block dispatch_group_t dispatch_group = dispatch_group_create(); - __block NSMutableDictionary *errorDictionary = [NSMutableDictionary dictionary]; - __block NSMutableDictionary *pingTimeDictionary = [NSMutableDictionary dictionary]; - dispatch_semaphore_t dispatchSemaphore = dispatch_semaphore_create(32); - - for (DSSimplifiedMasternodeEntry *masternode in masternodes) { - if (uint128_is_zero(masternode.address)) continue; - if (!masternode.isValid) continue; - dispatch_semaphore_wait(dispatchSemaphore, DISPATCH_TIME_FOREVER); - dispatch_group_enter(dispatch_group); - DSDAPICoreNetworkService *coreNetworkService = [[DSDAPICoreNetworkService alloc] initWithDAPINodeIPAddress:masternode.ipAddressString httpLoaderFactory:loaderFactory usingGRPCDispatchQueue:self.coreNetworkingDispatchQueue onChain:self.chain]; - __block NSDate *time = [NSDate date]; - [coreNetworkService - getStatusWithCompletionQueue:self.platformMetadataDispatchQueue - success:^(NSDictionary *_Nonnull status) { - NSTimeInterval platformPing = -[time timeIntervalSinceNow] * 1000; - pingTimeDictionary[uint256_data(masternode.providerRegistrationTransactionHash)] = @(platformPing); - [masternode setPlatformPing:platformPing at:[NSDate date]]; - dispatch_semaphore_signal(dispatchSemaphore); - dispatch_group_leave(dispatch_group); - } - failure:^(NSError *_Nonnull error) { - errorDictionary[uint256_data(masternode.providerRegistrationTransactionHash)] = error; - dispatch_semaphore_signal(dispatchSemaphore); - dispatch_group_leave(dispatch_group); - }]; - } - - dispatch_group_notify(dispatch_group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ - if (completion) { - completion(pingTimeDictionary, errorDictionary); - } - }); - }); -} +//- (void)checkPingTimesForMasternodes:(NSArray *)masternodes +// completion:(void (^)(NSMutableDictionary *pingTimes, NSMutableDictionary *))completion { +// dispatch_async(self.platformMetadataDispatchQueue, ^{ +// HTTPLoaderFactory *loaderFactory = [DSNetworkingCoordinator sharedInstance].loaderFactory; +// __block dispatch_group_t dispatch_group = dispatch_group_create(); +// __block NSMutableDictionary *errorDictionary = [NSMutableDictionary dictionary]; +// __block NSMutableDictionary *pingTimeDictionary = [NSMutableDictionary dictionary]; +// dispatch_semaphore_t dispatchSemaphore = dispatch_semaphore_create(32); +// +// for (DSSimplifiedMasternodeEntry *masternode in masternodes) { +// if (uint128_is_zero(masternode.address)) continue; +// if (!masternode.isValid) continue; +// dispatch_semaphore_wait(dispatchSemaphore, DISPATCH_TIME_FOREVER); +// dispatch_group_enter(dispatch_group); +// DSDAPICoreNetworkService *coreNetworkService = [[DSDAPICoreNetworkService alloc] initWithDAPINodeIPAddress:masternode.ipAddressString httpLoaderFactory:loaderFactory usingGRPCDispatchQueue:self.coreNetworkingDispatchQueue onChain:self.chain]; +// __block NSDate *time = [NSDate date]; +// [coreNetworkService +// getStatusWithCompletionQueue:self.platformMetadataDispatchQueue +// success:^(NSDictionary *_Nonnull status) { +// NSTimeInterval platformPing = -[time timeIntervalSinceNow] * 1000; +// pingTimeDictionary[uint256_data(masternode.providerRegistrationTransactionHash)] = @(platformPing); +// [masternode setPlatformPing:platformPing at:[NSDate date]]; +// dispatch_semaphore_signal(dispatchSemaphore); +// dispatch_group_leave(dispatch_group); +// } +// failure:^(NSError *_Nonnull error) { +// errorDictionary[uint256_data(masternode.providerRegistrationTransactionHash)] = error; +// dispatch_semaphore_signal(dispatchSemaphore); +// dispatch_group_leave(dispatch_group); +// }]; +// } +// +// dispatch_group_notify(dispatch_group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ +// if (completion) { +// completion(pingTimeDictionary, errorDictionary); +// } +// }); +// }); +//} #pragma mark - Peers @@ -317,10 +298,8 @@ - (void)publishTransition:(DSTransition *)stateTransition completionQueue:completionQueue success:success failure:^(NSDictionary *_Nonnull errorPerAttempt) { - if (failure) { - failure(errorPerAttempt[@(4)]); - } - }]; + if (failure) failure(errorPerAttempt[@(4)]); + }]; } - (void)publishTransition:(DSTransition *)transition @@ -335,12 +314,19 @@ - (void)publishTransition:(DSTransition *)transition DSDAPIPlatformNetworkService *service = self.DAPIPlatformNetworkService; if (!service) { NSMutableDictionary *mErrorsPerAttempt = [errorPerAttempt mutableCopy]; - NSError *error = [NSError errorWithDomain:DSDAPIClientErrorDomain - code:DSDAPIClientErrorCodeNoKnownDAPINodes - userInfo:@{NSLocalizedDescriptionKey: @"No known DAPI Nodes"}]; - mErrorsPerAttempt[@(currentAttempt)] = error; + mErrorsPerAttempt[@(currentAttempt)] = [NSError errorWithDomain:DSDAPIClientErrorDomain + code:DSDAPIClientErrorCodeNoKnownDAPINodes + userInfo:@{NSLocalizedDescriptionKey: @"No known DAPI Nodes"}]; if (retryCount) { - [self publishTransition:transition retryCount:retryCount - 1 delay:delay + delayIncrease delayIncrease:delayIncrease currentAttempt:currentAttempt + 1 currentErrors:mErrorsPerAttempt completionQueue:completionQueue success:success failure:failure]; + [self publishTransition:transition + retryCount:retryCount - 1 + delay:delay + delayIncrease + delayIncrease:delayIncrease + currentAttempt:currentAttempt + 1 + currentErrors:mErrorsPerAttempt + completionQueue:completionQueue + success:success + failure:failure]; } else if (failure) { failure([mErrorsPerAttempt copy]); } @@ -351,21 +337,29 @@ - (void)publishTransition:(DSTransition *)transition completionQueue:completionQueue success:success failure:^(NSError *_Nonnull error) { - if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node - [self removeDAPINodeByAddress:service.ipAddress]; - } - NSMutableDictionary *mErrorsPerAttempt = [errorPerAttempt mutableCopy]; - if (error) { - mErrorsPerAttempt[@(currentAttempt)] = error; - } - if (retryCount) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), self.coreNetworkingDispatchQueue, ^{ - [self publishTransition:transition retryCount:retryCount - 1 delay:delay + delayIncrease delayIncrease:delayIncrease currentAttempt:currentAttempt + 1 currentErrors:mErrorsPerAttempt completionQueue:completionQueue success:success failure:failure]; - }); - } else if (failure) { - failure([mErrorsPerAttempt copy]); - } - }]; + if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node + [self removeDAPINodeByAddress:service.ipAddress]; + } + NSMutableDictionary *mErrorsPerAttempt = [errorPerAttempt mutableCopy]; + if (error) { + mErrorsPerAttempt[@(currentAttempt)] = error; + } + if (retryCount) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), self.coreNetworkingDispatchQueue, ^{ + [self publishTransition:transition + retryCount:retryCount - 1 + delay:delay + delayIncrease + delayIncrease:delayIncrease + currentAttempt:currentAttempt + 1 + currentErrors:mErrorsPerAttempt + completionQueue:completionQueue + success:success + failure:failure]; + }); + } else if (failure) { + failure([mErrorsPerAttempt copy]); + } + }]; } diff --git a/DashSync/shared/Models/DAPI/DSPlatformTreeQuery.h b/DashSync/shared/Models/DAPI/DSPlatformTreeQuery.h index a96d9d811..ca080d0b2 100644 --- a/DashSync/shared/Models/DAPI/DSPlatformTreeQuery.h +++ b/DashSync/shared/Models/DAPI/DSPlatformTreeQuery.h @@ -15,6 +15,7 @@ // limitations under the License. // +//#import #import "dash_shared_core.h" #import diff --git a/DashSync/shared/Models/DAPI/Networking/DSDAPICoreNetworkService.m b/DashSync/shared/Models/DAPI/Networking/DSDAPICoreNetworkService.m index 64bfac058..2f526a43e 100644 --- a/DashSync/shared/Models/DAPI/Networking/DSDAPICoreNetworkService.m +++ b/DashSync/shared/Models/DAPI/Networking/DSDAPICoreNetworkService.m @@ -18,6 +18,7 @@ #import "DSDAPICoreNetworkService.h" #import "DPErrors.h" #import "DSChain.h" +#import "DSChain+Params.h" #import "DSChainLock.h" #import "DSDAPIGRPCResponseHandler.h" #import "DSDashPlatform.h" diff --git a/DashSync/shared/Models/DAPI/Networking/DSDAPIGRPCResponseHandler.h b/DashSync/shared/Models/DAPI/Networking/DSDAPIGRPCResponseHandler.h index fbf7ed4bf..8032e9a77 100644 --- a/DashSync/shared/Models/DAPI/Networking/DSDAPIGRPCResponseHandler.h +++ b/DashSync/shared/Models/DAPI/Networking/DSDAPIGRPCResponseHandler.h @@ -16,6 +16,7 @@ // #import "DSChain.h" +#import "DSKeyManager.h" #import "DSPlatformDocumentsRequest.h" #import #import @@ -23,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN -@class DSQuorumEntry, DSPlatformQuery, DSTransition; +@class DSPlatformQuery, DSTransition; @interface DSDAPIGRPCResponseHandler : NSObject @@ -48,7 +49,13 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initForGetIdentityIDsByPublicKeyHashesRequest:(NSArray *)hashes withChain:(DSChain *)chain requireProof:(BOOL)requireProof; - (instancetype)initForGetIdentitiesByPublicKeyHashesRequest:(NSArray *)hashes withChain:(DSChain *)chain requireProof:(BOOL)requireProof; -+ (NSDictionary *)verifyAndExtractFromProof:(Proof *)proof withMetadata:(ResponseMetadata *)metaData query:(DSPlatformQuery *_Nullable)query forQuorumEntry:(DSQuorumEntry *)quorumEntry quorumType:(LLMQType)quorumType error:(NSError **)error; ++ (NSDictionary *)verifyAndExtractFromProof:(Proof *)proof + withMetadata:(ResponseMetadata *)metaData + query:(DSPlatformQuery *_Nullable)query + forQuorumEntry:(DLLMQEntry *)quorumEntry + quorumType:(DLLMQType *)quorumType + onChain:(DSChain *)chain + error:(NSError **)error; @end diff --git a/DashSync/shared/Models/DAPI/Networking/DSDAPIGRPCResponseHandler.m b/DashSync/shared/Models/DAPI/Networking/DSDAPIGRPCResponseHandler.m index 2bc9ef4d9..993e39d2d 100644 --- a/DashSync/shared/Models/DAPI/Networking/DSDAPIGRPCResponseHandler.m +++ b/DashSync/shared/Models/DAPI/Networking/DSDAPIGRPCResponseHandler.m @@ -18,12 +18,13 @@ #import "DSDAPIGRPCResponseHandler.h" #import "DPContract.h" #import "DSChain.h" +#import "DSChain+Params.h" #import "DSChainManager.h" #import "DSDocumentTransition.h" +#import "DSKeyManager.h" #import "DSMasternodeManager.h" #import "DSPlatformQuery.h" #import "DSPlatformRootMerkleTree.h" -#import "DSQuorumEntry.h" #import "DSTransition.h" #import "NSData+DSCborDecoding.h" #import "NSData+DSHash.h" @@ -452,7 +453,11 @@ - (NSDictionary *)verifyAndExtractFromProof:(Proof *)proof withMetadata:(Respons return [DSDAPIGRPCResponseHandler verifyAndExtractFromProof:proof withMetadata:metaData query:self.query onChain:self.chain error:error]; } -+ (NSDictionary *)verifyAndExtractFromProof:(Proof *)proof withMetadata:(ResponseMetadata *)metaData query:(DSPlatformQuery *)query onChain:(DSChain *)chain error:(NSError **)error { ++ (NSDictionary *)verifyAndExtractFromProof:(Proof *)proof + withMetadata:(ResponseMetadata *)metaData + query:(DSPlatformQuery *)query + onChain:(DSChain *)chain + error:(NSError **)error { NSData *quorumHashData = proof.signatureLlmqHash; if (!quorumHashData) { *error = [NSError errorWithCode:500 localizedDescriptionKey:@"Platform returned no quorum hash data"]; @@ -461,19 +466,29 @@ + (NSDictionary *)verifyAndExtractFromProof:(Proof *)proof withMetadata:(Respons if (uint256_is_zero(quorumHash)) { *error = [NSError errorWithCode:500 localizedDescriptionKey:@"Platform returned an empty quorum hash"]; } - DSQuorumEntry *quorumEntry = [chain.chainManager.masternodeManager quorumEntryForPlatformHavingQuorumHash:quorumHash forBlockHeight:metaData.coreChainLockedHeight]; - if (quorumEntry && quorumEntry.verified) { - return [self verifyAndExtractFromProof:proof withMetadata:metaData query:query forQuorumEntry:quorumEntry quorumType:quorum_type_for_platform(chain.chainType) error:error]; + DLLMQEntry *quorumEntry = [chain.chainManager.masternodeManager quorumEntryForPlatformHavingQuorumHash:quorumHash forBlockHeight:metaData.coreChainLockedHeight]; + + if (quorumEntry && quorumEntry->verified) { + DLLMQType *llmq_type = dash_spv_crypto_network_chain_type_ChainType_platform_type(chain.chainType); + return [self verifyAndExtractFromProof:proof withMetadata:metaData query:query forQuorumEntry:quorumEntry quorumType:llmq_type onChain:chain error:error]; } else if (quorumEntry) { *error = [NSError errorWithCode:400 descriptionKey:DSLocalizedString(@"Quorum entry %@ found but is not yet verified", uint256_hex(quorumEntry.quorumHash))]; - DSLog(@"quorum entry %@ found but is not yet verified", uint256_hex(quorumEntry.quorumHash)); + char *quorumHash = DLLMQEntryHashHex(quorumEntry); + DSLog(@"quorum entry %s found but is not yet verified", quorumHash); + str_destroy(quorumHash); } else { DSLog(@"no quorum entry found for quorum hash %@", uint256_hex(quorumHash)); } return nil; } -+ (NSDictionary *)verifyAndExtractFromProof:(Proof *)proof withMetadata:(ResponseMetadata *)metaData query:(DSPlatformQuery *)query forQuorumEntry:(DSQuorumEntry *)quorumEntry quorumType:(LLMQType)quorumType error:(NSError **)error { ++ (NSDictionary *)verifyAndExtractFromProof:(Proof *)proof + withMetadata:(ResponseMetadata *)metaData + query:(DSPlatformQuery *)query + forQuorumEntry:(DLLMQEntry *)quorumEntry + quorumType:(DLLMQType *)quorumType + onChain:(DSChain *)chain + error:(NSError **)error { NSData *signatureData = proof.signature; if (!signatureData) { *error = [NSError errorWithCode:500 localizedDescriptionKey:@"Platform returned no signature data"]; @@ -572,7 +587,7 @@ + (NSDictionary *)verifyAndExtractFromProof:(Proof *)proof withMetadata:(Respons // [stateData appendInt64:metaData.height - 1]; // [stateData appendUInt256:stateHash]; // UInt256 stateMessageHash = [stateData SHA256]; -// BOOL signatureVerified = [self verifyStateSignature:signature forStateMessageHash:stateMessageHash height:metaData.height - 1 againstQuorum:quorumEntry quorumType:quorumType]; +// BOOL signatureVerified = [self verifyStateSignature:signature forStateMessageHash:stateMessageHash height:metaData.height - 1 againstQuorum:quorumEntry quorumType:quorumType onChain:chain]; // if (!signatureVerified) { // *error = [NSError errorWithCode:500 localizedDescriptionKey:"Platform returned an empty or wrongly sized signature"]; // DSLog(@"unable to verify platform signature"); @@ -597,27 +612,34 @@ + (NSDictionary *)verifyAndExtractFromProof:(Proof *)proof withMetadata:(Respons // return elementsDictionary; } -+ (UInt256)requestIdForHeight:(int64_t)height { - NSMutableData *data = [NSMutableData data]; - [data appendBytes:@"dpsvote".UTF8String length:7]; - [data appendUInt64:height]; - return [data SHA256]; -} - -+ (UInt256)signIDForQuorumEntry:(DSQuorumEntry *)quorumEntry quorumType:(LLMQType)quorumType forStateMessageHash:(UInt256)stateMessageHash height:(int64_t)height { - UInt256 requestId = [self requestIdForHeight:height]; - NSMutableData *data = [NSMutableData data]; - [data appendUInt8:quorumType]; - [data appendUInt256:quorumEntry.quorumHash]; - [data appendUInt256:uint256_reverse(requestId)]; - [data appendUInt256:uint256_reverse(stateMessageHash)]; - return [data SHA256_2]; -} - -+ (BOOL)verifyStateSignature:(UInt768)signature forStateMessageHash:(UInt256)stateMessageHash height:(int64_t)height againstQuorum:(DSQuorumEntry *)quorumEntry quorumType:(LLMQType)quorumType { - UInt256 signId = [self signIDForQuorumEntry:quorumEntry quorumType:quorumType forStateMessageHash:stateMessageHash height:height]; - return key_bls_verify(quorumEntry.quorumPublicKey.u8, quorumEntry.useLegacyBLSScheme, signId.u8, signature.u8); +//+ (UInt256)requestIdForHeight:(int64_t)height { +// NSMutableData *data = [NSMutableData data]; +// [data appendBytes:@"dpsvote".UTF8String length:7]; +// [data appendUInt64:height]; +// return [data SHA256]; +//} +// +//+ (UInt256)signIDForQuorumEntry:(DSQuorumEntry *)quorumEntry quorumType:(DLLMQType)quorumType forStateMessageHash:(UInt256)stateMessageHash height:(int64_t)height { +// UInt256 requestId = [self requestIdForHeight:height]; +// NSMutableData *data = [NSMutableData data]; +// [data appendUInt8:quorumType]; +// [data appendUInt256:quorumEntry.quorumHash]; +// [data appendUInt256:uint256_reverse(requestId)]; +// [data appendUInt256:uint256_reverse(stateMessageHash)]; +// return [data SHA256_2]; +//} + ++ (BOOL)verifyStateSignature:(UInt768)signature + forStateMessageHash:(UInt256)stateMessageHash + height:(int64_t)height + againstQuorum:(DLLMQEntry *)quorumEntry + quorumType:(DLLMQType)quorumType + onChain:(DSChain *)chain { + u256 *state_msg_hash = Arr_u8_32_ctor(32, stateMessageHash.u8); + u768 *sig = Arr_u8_96_ctor(32, signature.u8); + u256 *sign_id = dash_spv_crypto_llmq_entry_LLMQEntry_platform_sign_id(quorumEntry, (uint32_t) height, state_msg_hash); + bool verified = DLLMQEntryVerifySignature(quorumEntry, sign_id, sig); + return verified; } - @end diff --git a/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkService.m b/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkService.m index da679a3e6..81f72891d 100644 --- a/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkService.m +++ b/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkService.m @@ -20,6 +20,7 @@ #import "DPContract.h" #import "DPErrors.h" #import "DSChain.h" +#import "DSChain+Params.h" #import "DSDAPIGRPCResponseHandler.h" #import "DSDashPlatform.h" #import "DSHTTPJSONRPCClient.h" @@ -458,57 +459,59 @@ - (void)loadBloomFilter:(NSString *)filter NSParameterAssert(completionQueue); DSPlatformRequestLog(@"fetchIdentitiesByKeyHashes %@", keyHashesArray); - NSMutableArray *identityDictionaries = [NSMutableArray array]; - __block NSUInteger remainingRequests = keyHashesArray.count; - __block NSError *lastError = nil; - - for (NSData *keyHash in keyHashesArray) { - GetIdentityByPublicKeyHashRequest *getIdentityByPublicKeyHashRequest = [[GetIdentityByPublicKeyHashRequest alloc] init]; - getIdentityByPublicKeyHashRequest.publicKeyHash = keyHash; - getIdentityByPublicKeyHashRequest.prove = DSPROVE_PLATFORM; - - DSDAPIGRPCResponseHandler *responseHandler = [[DSDAPIGRPCResponseHandler alloc] initForGetIdentitiesByPublicKeyHashesRequest:@[keyHash] withChain:self.chain requireProof:DSPROVE_PLATFORM]; - responseHandler.host = [NSString stringWithFormat:@"%@:%d", self.ipAddress, self.chain.standardDapiGRPCPort]; - responseHandler.dispatchQueue = self.grpcDispatchQueue; - responseHandler.completionQueue = completionQueue; - - responseHandler.successHandler = ^(NSDictionary *responseDictionary) { - if (responseDictionary) { - @synchronized(identityDictionaries) { - [identityDictionaries addObject:responseDictionary]; - } - } - @synchronized(self) { - remainingRequests--; - if (remainingRequests == 0) { - if (lastError) { - if (failure) { - failure(lastError); - } - } else { - if (success) { - success([identityDictionaries copy]); - } - } - } - } - }; - - responseHandler.errorHandler = ^(NSError *error) { - lastError = error; - @synchronized(self) { - remainingRequests--; - if (remainingRequests == 0) { - if (failure) { - failure(error); - } - } - } - }; - - GRPCUnaryProtoCall *call = [self.gRPCClient getIdentityByPublicKeyHashWithMessage:getIdentityByPublicKeyHashRequest responseHandler:responseHandler callOptions:nil]; - [call start]; - } +// NSMutableArray *identityDictionaries = [NSMutableArray array]; +// __block NSUInteger remainingRequests = keyHashesArray.count; +// __block NSError *lastError = nil; +// +//// for (NSData *keyHash in keyHashesArray) { +// GetIdentityRequest *request = [[GetIdentityRequest alloc] init]; +// +// GetIdentityByPublicKeyHashRequest *getIdentityByPublicKeyHashRequest = [[GetIdentityByPublicKeyHashRequest alloc] init]; +// getIdentityByPublicKeyHashRequest.publicKeyHash = keyHash; +// getIdentityByPublicKeyHashRequest.prove = DSPROVE_PLATFORM; +// +// DSDAPIGRPCResponseHandler *responseHandler = [[DSDAPIGRPCResponseHandler alloc] initForGetIdentitiesByPublicKeyHashesRequest:@[keyHash] withChain:self.chain requireProof:DSPROVE_PLATFORM]; +// responseHandler.host = [NSString stringWithFormat:@"%@:%d", self.ipAddress, self.chain.standardDapiGRPCPort]; +// responseHandler.dispatchQueue = self.grpcDispatchQueue; +// responseHandler.completionQueue = completionQueue; +// +// responseHandler.successHandler = ^(NSDictionary *responseDictionary) { +// if (responseDictionary) { +// @synchronized(identityDictionaries) { +// [identityDictionaries addObject:responseDictionary]; +// } +// } +// @synchronized(self) { +// remainingRequests--; +// if (remainingRequests == 0) { +// if (lastError) { +// if (failure) { +// failure(lastError); +// } +// } else { +// if (success) { +// success([identityDictionaries copy]); +// } +// } +// } +// } +// }; +// +// responseHandler.errorHandler = ^(NSError *error) { +// lastError = error; +// @synchronized(self) { +// remainingRequests--; +// if (remainingRequests == 0) { +// if (failure) { +// failure(error); +// } +// } +// } +// }; +// +// GRPCUnaryProtoCall *call = [self.gRPCClient getIdentityByPublicKeyHashWithMessage:getIdentityByPublicKeyHashRequest responseHandler:responseHandler callOptions:nil]; +// [call start]; +// } return nil; } @@ -520,6 +523,9 @@ - (void)loadBloomFilter:(NSString *)filter NSParameterAssert(contractId); NSParameterAssert(completionQueue); DSPlatformRequestLog(@"fetchContractForId (base58) %@", contractId.base58String); + + + GetDataContractRequest *getDataContractRequest = [[GetDataContractRequest alloc] init]; getDataContractRequest.id_p = contractId; getDataContractRequest.prove = DSPROVE_PLATFORM; @@ -621,7 +627,7 @@ - (void)loadBloomFilter:(NSString *)filter - (id)getIdentityByName:(NSString *)username inDomain:(NSString *)domain completionQueue:(dispatch_queue_t)completionQueue - success:(void (^)(NSDictionary *_Nullable blockchainIdentity))success + success:(void (^)(NSDictionary *_Nullable identity))success failure:(void (^)(NSError *error))failure { NSParameterAssert(username); NSParameterAssert(completionQueue); @@ -665,7 +671,7 @@ - (void)loadBloomFilter:(NSString *)filter - (id)getIdentityById:(NSData *)userId completionQueue:(dispatch_queue_t)completionQueue - success:(void (^)(NSDictionary *blockchainIdentity))success + success:(void (^)(NSDictionary *identity))success failure:(void (^)(NSError *error))failure { NSParameterAssert(userId); NSParameterAssert(completionQueue); diff --git a/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkServiceProtocol.h b/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkServiceProtocol.h index ad67d2e08..df13759ce 100644 --- a/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkServiceProtocol.h +++ b/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkServiceProtocol.h @@ -43,7 +43,7 @@ typedef NS_ENUM(NSUInteger, DSDAPINetworkServiceErrorCode) DSDAPINetworkServiceErrorCodeInvalidResponse = 100, }; -@class DSTransition, DSPlatformDocumentsRequest, DSBlockchainIdentity; +@class DSTransition, DSPlatformDocumentsRequest, DSIdentity; @protocol DSDAPIPlatformNetworkServiceProtocol @@ -389,7 +389,7 @@ typedef NS_ENUM(NSUInteger, DSDAPINetworkServiceErrorCode) - (id _Nullable)getIdentityByName:(NSString *)username inDomain:(NSString *)domain completionQueue:(dispatch_queue_t)completionQueue - success:(void (^)(NSDictionary *_Nullable blockchainIdentity))success + success:(void (^)(NSDictionary *_Nullable identity))success failure:(void (^)(NSError *error))failure; /** @@ -401,7 +401,7 @@ typedef NS_ENUM(NSUInteger, DSDAPINetworkServiceErrorCode) */ - (id)getIdentityById:(NSData *)userId completionQueue:(dispatch_queue_t)completionQueue - success:(void (^)(NSDictionary *_Nullable blockchainIdentity))success + success:(void (^)(NSDictionary *_Nullable identity))success failure:(void (^)(NSError *error))failure; /** diff --git a/DashSync/shared/Models/Derivation Paths/DSAssetLockDerivationPath+Protected.h b/DashSync/shared/Models/Derivation Paths/DSAssetLockDerivationPath+Protected.h new file mode 100644 index 000000000..4dcedc173 --- /dev/null +++ b/DashSync/shared/Models/Derivation Paths/DSAssetLockDerivationPath+Protected.h @@ -0,0 +1,30 @@ +// +// Created by Sam Westrich +// Copyright © 2020 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DSAssetLockDerivationPath.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSAssetLockDerivationPath () + ++ (instancetype)identityRegistrationFundingDerivationPathForChain:(DSChain *)chain; ++ (instancetype)identityTopupFundingDerivationPathForChain:(DSChain *)chain; ++ (instancetype)identityInvitationFundingDerivationPathForChain:(DSChain *)chain; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Derivation Paths/DSCreditFundingDerivationPath.h b/DashSync/shared/Models/Derivation Paths/DSAssetLockDerivationPath.h similarity index 60% rename from DashSync/shared/Models/Derivation Paths/DSCreditFundingDerivationPath.h rename to DashSync/shared/Models/Derivation Paths/DSAssetLockDerivationPath.h index cf6604efa..31ce2f6a5 100644 --- a/DashSync/shared/Models/Derivation Paths/DSCreditFundingDerivationPath.h +++ b/DashSync/shared/Models/Derivation Paths/DSAssetLockDerivationPath.h @@ -21,16 +21,14 @@ NS_ASSUME_NONNULL_BEGIN @class DSChain; -@interface DSCreditFundingDerivationPath : DSSimpleIndexedDerivationPath +@interface DSAssetLockDerivationPath : DSSimpleIndexedDerivationPath -+ (instancetype)blockchainIdentityRegistrationFundingDerivationPathForWallet:(DSWallet *)wallet; -+ (instancetype)blockchainIdentityTopupFundingDerivationPathForWallet:(DSWallet *)wallet; -+ (instancetype)blockchainIdentityInvitationFundingDerivationPathForWallet:(DSWallet *)wallet; ++ (instancetype)identityRegistrationFundingDerivationPathForWallet:(DSWallet *)wallet; ++ (instancetype)identityTopupFundingDerivationPathForWallet:(DSWallet *)wallet; ++ (instancetype)identityInvitationFundingDerivationPathForWallet:(DSWallet *)wallet; - (NSString *)receiveAddress; -- (void)signTransaction:(DSTransaction *)transaction withPrompt:(NSString *)authprompt completion:(TransactionValidityCompletionBlock)completion; - @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Derivation Paths/DSAssetLockDerivationPath.m b/DashSync/shared/Models/Derivation Paths/DSAssetLockDerivationPath.m new file mode 100644 index 000000000..9f6f3581d --- /dev/null +++ b/DashSync/shared/Models/Derivation Paths/DSAssetLockDerivationPath.m @@ -0,0 +1,100 @@ +// +// Created by Sam Westrich +// Copyright © 2019 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DSChain+Params.h" +#import "DSAssetLockDerivationPath.h" +#import "DSDerivationPath+Protected.h" +#import "DSDerivationPathFactory.h" +#import "DSMasternodeManager.h" +#import "DSSimpleIndexedDerivationPath+Protected.h" +#import "DSWallet+Protected.h" + +@implementation DSAssetLockDerivationPath + ++ (instancetype)identityRegistrationFundingDerivationPathForWallet:(DSWallet *)wallet { + return [[DSDerivationPathFactory sharedInstance] identityRegistrationFundingDerivationPathForWallet:wallet]; +} + ++ (instancetype)identityTopupFundingDerivationPathForWallet:(DSWallet *)wallet { + return [[DSDerivationPathFactory sharedInstance] identityTopupFundingDerivationPathForWallet:wallet]; +} + ++ (instancetype)identityInvitationFundingDerivationPathForWallet:(DSWallet *)wallet { + return [[DSDerivationPathFactory sharedInstance] identityInvitationFundingDerivationPathForWallet:wallet]; +} + ++ (instancetype)identityRegistrationFundingDerivationPathForChain:(DSChain *)chain { + UInt256 indexes[] = { + uint256_from_long(FEATURE_PURPOSE), + uint256_from_long(chain.coinType), + uint256_from_long(FEATURE_PURPOSE_IDENTITIES), + uint256_from_long(FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_REGISTRATION) + }; + BOOL hardenedIndexes[] = {YES, YES, YES, YES}; + return [DSAssetLockDerivationPath derivationPathWithIndexes:indexes + hardened:hardenedIndexes + length:4 + type:DSDerivationPathType_CreditFunding + signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() + reference:DSDerivationPathReference_IdentityCreditRegistrationFunding + onChain:chain]; +} + ++ (instancetype)identityTopupFundingDerivationPathForChain:(DSChain *)chain { + UInt256 indexes[] = { + uint256_from_long(FEATURE_PURPOSE), + uint256_from_long(chain.coinType), + uint256_from_long(FEATURE_PURPOSE_IDENTITIES), + uint256_from_long(FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_TOPUP) + }; + BOOL hardenedIndexes[] = {YES, YES, YES, YES}; + return [DSAssetLockDerivationPath derivationPathWithIndexes:indexes + hardened:hardenedIndexes + length:4 + type:DSDerivationPathType_CreditFunding + signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() + reference:DSDerivationPathReference_IdentityCreditTopupFunding + onChain:chain]; +} + ++ (instancetype)identityInvitationFundingDerivationPathForChain:(DSChain *)chain { + UInt256 indexes[] = { + uint256_from_long(FEATURE_PURPOSE), + uint256_from_long(chain.coinType), + uint256_from_long(FEATURE_PURPOSE_IDENTITIES), + uint256_from_long(FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_INVITATIONS) + }; + BOOL hardenedIndexes[] = {YES, YES, YES, YES}; + return [DSAssetLockDerivationPath derivationPathWithIndexes:indexes + hardened:hardenedIndexes + length:4 + type:DSDerivationPathType_CreditFunding + signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() + reference:DSDerivationPathReference_IdentityCreditInvitationFunding + onChain:chain]; +} + +- (NSString *)receiveAddress { + NSString *addr = [self registerAddressesWithGapLimit:1 error:nil].lastObject; + return addr ? addr : self.mOrderedAddresses.lastObject; +} + +- (NSUInteger)defaultGapLimit { + return 5; +} + +@end diff --git a/DashSync/shared/Models/Derivation Paths/DSAuthenticationKeysDerivationPath+Protected.h b/DashSync/shared/Models/Derivation Paths/DSAuthenticationKeysDerivationPath+Protected.h index bfce7655d..c7b1fd6f6 100644 --- a/DashSync/shared/Models/Derivation Paths/DSAuthenticationKeysDerivationPath+Protected.h +++ b/DashSync/shared/Models/Derivation Paths/DSAuthenticationKeysDerivationPath+Protected.h @@ -15,8 +15,8 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)providerOwnerKeysDerivationPathForChain:(DSChain *)chain; + (instancetype)providerOperatorKeysDerivationPathForChain:(DSChain *)chain; + (instancetype)platformNodeKeysDerivationPathForChain:(DSChain *)chain; -+ (instancetype)blockchainIdentityECDSAKeysDerivationPathForChain:(DSChain *)chain; -+ (instancetype)blockchainIdentityBLSKeysDerivationPathForChain:(DSChain *)chain; ++ (instancetype)identityECDSAKeysDerivationPathForChain:(DSChain *)chain; ++ (instancetype)identityBLSKeysDerivationPathForChain:(DSChain *)chain; @end diff --git a/DashSync/shared/Models/Derivation Paths/DSAuthenticationKeysDerivationPath.h b/DashSync/shared/Models/Derivation Paths/DSAuthenticationKeysDerivationPath.h index 7cb5c5e57..f0bdfa29d 100644 --- a/DashSync/shared/Models/Derivation Paths/DSAuthenticationKeysDerivationPath.h +++ b/DashSync/shared/Models/Derivation Paths/DSAuthenticationKeysDerivationPath.h @@ -20,16 +20,15 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)providerOwnerKeysDerivationPathForWallet:(DSWallet *)wallet; + (instancetype)providerOperatorKeysDerivationPathForWallet:(DSWallet *)wallet; + (instancetype)platformNodeKeysDerivationPathForWallet:(DSWallet *)wallet; -+ (instancetype)blockchainIdentitiesBLSKeysDerivationPathForWallet:(DSWallet *)wallet; -+ (instancetype)blockchainIdentitiesECDSAKeysDerivationPathForWallet:(DSWallet *)wallet; ++ (instancetype)identitiesBLSKeysDerivationPathForWallet:(DSWallet *)wallet; ++ (instancetype)identitiesECDSAKeysDerivationPathForWallet:(DSWallet *)wallet; - (NSData *)firstUnusedPublicKey; -- (OpaqueKey *)firstUnusedPrivateKeyFromSeed:(NSData *)seed; -- (OpaqueKey *)privateKeyForAddress:(NSString *)address fromSeed:(NSData *)seed; -- (OpaqueKey *)privateKeyForHash160:(UInt160)hash160 fromSeed:(NSData *)seed; +- (DMaybeOpaqueKey *)firstUnusedPrivateKeyFromSeed:(NSData *)seed; +- (DMaybeOpaqueKey *)privateKeyForHash160:(UInt160)hash160 fromSeed:(NSData *)seed; - (NSData *)publicKeyDataForHash160:(UInt160)hash160; -- (OpaqueKey *_Nullable)privateKeyAtIndexPath:(NSIndexPath *)indexPath; +- (DMaybeOpaqueKey *_Nullable)privateKeyAtIndexPath:(NSIndexPath *)indexPath; @end diff --git a/DashSync/shared/Models/Derivation Paths/DSAuthenticationKeysDerivationPath.m b/DashSync/shared/Models/Derivation Paths/DSAuthenticationKeysDerivationPath.m index 55b753183..8b5987f63 100644 --- a/DashSync/shared/Models/Derivation Paths/DSAuthenticationKeysDerivationPath.m +++ b/DashSync/shared/Models/Derivation Paths/DSAuthenticationKeysDerivationPath.m @@ -6,13 +6,16 @@ // #import "DSAuthenticationKeysDerivationPath.h" -#import "DSCreditFundingDerivationPath.h" +#import "DSAccount.h" +#import "DSAddressEntity+CoreDataClass.h" +#import "DSChain+Params.h" +#import "DSAssetLockDerivationPath.h" #import "DSDerivationPathFactory.h" #import "DSKeyManager.h" #import "DSSimpleIndexedDerivationPath+Protected.h" #import "NSError+Dash.h" #import "NSIndexPath+Dash.h" -#import "dash_shared_core.h" +#import "NSManagedObject+Sugar.h" @interface DSAuthenticationKeysDerivationPath () @@ -36,11 +39,11 @@ + (instancetype)providerOperatorKeysDerivationPathForWallet:(DSWallet *)wallet { + (instancetype)platformNodeKeysDerivationPathForWallet:(DSWallet *)wallet { return [[DSDerivationPathFactory sharedInstance] platformNodeKeysDerivationPathForWallet:wallet]; } -+ (instancetype)blockchainIdentitiesBLSKeysDerivationPathForWallet:(DSWallet *)wallet { - return [[DSDerivationPathFactory sharedInstance] blockchainIdentityBLSKeysDerivationPathForWallet:wallet]; ++ (instancetype)identitiesBLSKeysDerivationPathForWallet:(DSWallet *)wallet { + return [[DSDerivationPathFactory sharedInstance] identityBLSKeysDerivationPathForWallet:wallet]; } -+ (instancetype)blockchainIdentitiesECDSAKeysDerivationPathForWallet:(DSWallet *)wallet { - return [[DSDerivationPathFactory sharedInstance] blockchainIdentityECDSAKeysDerivationPathForWallet:wallet]; ++ (instancetype)identitiesECDSAKeysDerivationPathForWallet:(DSWallet *)wallet { + return [[DSDerivationPathFactory sharedInstance] identityECDSAKeysDerivationPathForWallet:wallet]; } - (NSUInteger)defaultGapLimit { @@ -49,10 +52,16 @@ - (NSUInteger)defaultGapLimit { - (instancetype)initWithIndexes:(const UInt256[_Nullable])indexes hardened:(const BOOL[_Nullable])hardenedIndexes length:(NSUInteger)length type:(DSDerivationPathType)type - signingAlgorithm:(KeyKind)signingAlgorithm + signingAlgorithm:(DKeyKind *)signingAlgorithm reference:(DSDerivationPathReference)reference onChain:(DSChain *)chain { - DSAuthenticationKeysDerivationPath *authenticationKeysDerivationPath = [super initWithIndexes:indexes hardened:hardenedIndexes length:length type:type signingAlgorithm:signingAlgorithm reference:reference onChain:chain]; + DSAuthenticationKeysDerivationPath *authenticationKeysDerivationPath = [super initWithIndexes:indexes + hardened:hardenedIndexes + length:length + type:type + signingAlgorithm:signingAlgorithm + reference:reference + onChain:chain]; authenticationKeysDerivationPath.shouldStoreExtendedPrivateKey = NO; self.addressesByIdentity = [NSMutableDictionary dictionary]; return authenticationKeysDerivationPath; @@ -60,86 +69,81 @@ - (instancetype)initWithIndexes:(const UInt256[_Nullable])indexes hardened:(cons + (instancetype _Nullable)derivationPathWithIndexes:(const UInt256[_Nullable])indexes hardened:(const BOOL[_Nullable])hardenedIndexes length:(NSUInteger)length type:(DSDerivationPathType)type - signingAlgorithm:(KeyKind)signingAlgorithm + signingAlgorithm:(DKeyKind *)signingAlgorithm reference:(DSDerivationPathReference)reference onChain:(DSChain *)chain { - DSAuthenticationKeysDerivationPath *derivationPath = [super derivationPathWithIndexes:indexes hardened:hardenedIndexes length:length type:type signingAlgorithm:signingAlgorithm reference:reference onChain:chain]; + DSAuthenticationKeysDerivationPath *derivationPath = [super derivationPathWithIndexes:indexes + hardened:hardenedIndexes + length:length + type:type + signingAlgorithm:signingAlgorithm + reference:reference + onChain:chain]; derivationPath.shouldStoreExtendedPrivateKey = NO; return derivationPath; } + (instancetype)providerVotingKeysDerivationPathForChain:(DSChain *)chain { - UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain_coin_type(chain.chainType)), uint256_from_long(3), uint256_from_long(1)}; + UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain.coinType), uint256_from_long(3), uint256_from_long(1)}; BOOL hardenedIndexes[] = {YES, YES, YES, YES}; - return [DSAuthenticationKeysDerivationPath derivationPathWithIndexes:indexes hardened:hardenedIndexes length:4 type:DSDerivationPathType_SingleUserAuthentication signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_ProviderVotingKeys onChain:chain]; + return [DSAuthenticationKeysDerivationPath derivationPathWithIndexes:indexes + hardened:hardenedIndexes + length:4 + type:DSDerivationPathType_SingleUserAuthentication + signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() + reference:DSDerivationPathReference_ProviderVotingKeys + onChain:chain]; } + (instancetype)providerOwnerKeysDerivationPathForChain:(DSChain *)chain { - UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain_coin_type(chain.chainType)), uint256_from_long(3), uint256_from_long(2)}; + UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain.coinType), uint256_from_long(3), uint256_from_long(2)}; BOOL hardenedIndexes[] = {YES, YES, YES, YES}; - return [DSAuthenticationKeysDerivationPath derivationPathWithIndexes:indexes hardened:hardenedIndexes length:4 type:DSDerivationPathType_SingleUserAuthentication signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_ProviderOwnerKeys onChain:chain]; + return [DSAuthenticationKeysDerivationPath derivationPathWithIndexes:indexes hardened:hardenedIndexes length:4 type:DSDerivationPathType_SingleUserAuthentication signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() reference:DSDerivationPathReference_ProviderOwnerKeys onChain:chain]; } + (instancetype)providerOperatorKeysDerivationPathForChain:(DSChain *)chain { - UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain_coin_type(chain.chainType)), uint256_from_long(3), uint256_from_long(3)}; + UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain.coinType), uint256_from_long(3), uint256_from_long(3)}; BOOL hardenedIndexes[] = {YES, YES, YES, YES}; - return [DSAuthenticationKeysDerivationPath derivationPathWithIndexes:indexes hardened:hardenedIndexes length:4 type:DSDerivationPathType_SingleUserAuthentication signingAlgorithm:KeyKind_BLS reference:DSDerivationPathReference_ProviderOperatorKeys onChain:chain]; + return [DSAuthenticationKeysDerivationPath derivationPathWithIndexes:indexes hardened:hardenedIndexes length:4 type:DSDerivationPathType_SingleUserAuthentication signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_BLS_ctor() reference:DSDerivationPathReference_ProviderOperatorKeys onChain:chain]; } + (instancetype)platformNodeKeysDerivationPathForChain:(DSChain *)chain { - UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain_coin_type(chain.chainType)), uint256_from_long(3), uint256_from_long(4)}; + UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain.coinType), uint256_from_long(3), uint256_from_long(4)}; BOOL hardenedIndexes[] = {YES, YES, YES, YES}; - DSAuthenticationKeysDerivationPath *path = [DSAuthenticationKeysDerivationPath derivationPathWithIndexes:indexes hardened:hardenedIndexes length:4 type:DSDerivationPathType_SingleUserAuthentication signingAlgorithm:KeyKind_ED25519 reference:DSDerivationPathReference_ProviderPlatformNodeKeys onChain:chain]; + DSAuthenticationKeysDerivationPath *path = [DSAuthenticationKeysDerivationPath derivationPathWithIndexes:indexes hardened:hardenedIndexes length:4 type:DSDerivationPathType_SingleUserAuthentication signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_ED25519_ctor() reference:DSDerivationPathReference_ProviderPlatformNodeKeys onChain:chain]; path.shouldStoreExtendedPrivateKey = YES; path.usesHardenedKeys = YES; return path; } -+ (instancetype)blockchainIdentityECDSAKeysDerivationPathForChain:(DSChain *)chain { - UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain_coin_type(chain.chainType)), uint256_from_long(FEATURE_PURPOSE_IDENTITIES), uint256_from_long(FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_AUTHENTICATION), uint256_from_long(0)}; ++ (instancetype)identityECDSAKeysDerivationPathForChain:(DSChain *)chain { + UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain.coinType), uint256_from_long(FEATURE_PURPOSE_IDENTITIES), uint256_from_long(FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_AUTHENTICATION), uint256_from_long(0)}; BOOL hardenedIndexes[] = {YES, YES, YES, YES, YES}; - DSAuthenticationKeysDerivationPath *blockchainIdentityECDSAKeysDerivationPath = [DSAuthenticationKeysDerivationPath derivationPathWithIndexes:indexes hardened:hardenedIndexes length:5 type:DSDerivationPathType_MultipleUserAuthentication signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_BlockchainIdentities onChain:chain]; - blockchainIdentityECDSAKeysDerivationPath.shouldStoreExtendedPrivateKey = YES; - blockchainIdentityECDSAKeysDerivationPath.usesHardenedKeys = YES; - return blockchainIdentityECDSAKeysDerivationPath; + DSAuthenticationKeysDerivationPath *identityECDSAKeysDerivationPath = [DSAuthenticationKeysDerivationPath derivationPathWithIndexes:indexes hardened:hardenedIndexes length:5 type:DSDerivationPathType_MultipleUserAuthentication signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() reference:DSDerivationPathReference_Identities onChain:chain]; + identityECDSAKeysDerivationPath.shouldStoreExtendedPrivateKey = YES; + identityECDSAKeysDerivationPath.usesHardenedKeys = YES; + return identityECDSAKeysDerivationPath; } -+ (instancetype)blockchainIdentityBLSKeysDerivationPathForChain:(DSChain *)chain { - UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain_coin_type(chain.chainType)), uint256_from_long(FEATURE_PURPOSE_IDENTITIES), uint256_from_long(FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_AUTHENTICATION), uint256_from_long(1)}; ++ (instancetype)identityBLSKeysDerivationPathForChain:(DSChain *)chain { + UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain.coinType), uint256_from_long(FEATURE_PURPOSE_IDENTITIES), uint256_from_long(FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_AUTHENTICATION), uint256_from_long(1)}; BOOL hardenedIndexes[] = {YES, YES, YES, YES, YES}; - DSAuthenticationKeysDerivationPath *blockchainIdentityBLSKeysDerivationPath = [DSAuthenticationKeysDerivationPath derivationPathWithIndexes:indexes hardened:hardenedIndexes length:5 type:DSDerivationPathType_MultipleUserAuthentication signingAlgorithm:KeyKind_BLS reference:DSDerivationPathReference_BlockchainIdentities onChain:chain]; - blockchainIdentityBLSKeysDerivationPath.shouldStoreExtendedPrivateKey = YES; - blockchainIdentityBLSKeysDerivationPath.usesHardenedKeys = YES; - return blockchainIdentityBLSKeysDerivationPath; + DSAuthenticationKeysDerivationPath *identityBLSKeysDerivationPath = [DSAuthenticationKeysDerivationPath derivationPathWithIndexes:indexes + hardened:hardenedIndexes + length:5 + type:DSDerivationPathType_MultipleUserAuthentication + signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_BLS_ctor() + reference:DSDerivationPathReference_Identities + onChain:chain]; + identityBLSKeysDerivationPath.shouldStoreExtendedPrivateKey = YES; + identityBLSKeysDerivationPath.usesHardenedKeys = YES; + return identityBLSKeysDerivationPath; } - (void)loadAddresses { @synchronized(self) { if (!self.addressesLoaded) { - [self.managedObjectContext performBlockAndWait:^{ - DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:self.managedObjectContext]; - self.syncBlockHeight = derivationPathEntity.syncBlockHeight; - NSArray *addresses = [derivationPathEntity.addresses sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]]]; - for (DSAddressEntity *e in addresses) { - @autoreleasepool { - while (e.index >= self.mOrderedAddresses.count) [self.mOrderedAddresses addObject:[NSNull null]]; - - if (![DSKeyManager isValidDashAddress:e.address forChain:self.wallet.chain]) { -#if DEBUG - DSLogPrivate(@"[%@] address %@ loaded but was not valid on chain", self.wallet.chain.name, e.address); -#else - DSLog(@"[%@] address %@ loaded but was not valid on chain", self.wallet.chain.name, @""); -#endif - continue; - } - self.mOrderedAddresses[e.index] = e.address; - [self.mAllAddresses addObject:e.address]; - if ([e.usedInInputs count] || [e.usedInOutputs count] || [e.usedInSpecialTransactions count] || [e.usedInSimplifiedMasternodeEntries count]) { - [self.mUsedAddresses addObject:e.address]; - } - } - } - }]; + [self loadAddressesInContext:self.managedObjectContext]; self.addressesLoaded = TRUE; if ([self type] == DSDerivationPathType_SingleUserAuthentication) { [self registerAddressesWithGapLimit:10 error:nil]; @@ -151,7 +155,9 @@ - (void)loadAddresses { } -- (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit forIdentityIndex:(uint32_t)identityIndex error:(NSError **)error { +- (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit + forIdentityIndex:(uint32_t)identityIndex + error:(NSError **)error { if (!self.account.wallet.isTransient) { NSAssert(self.addressesLoaded, @"addresses must be loaded before calling this function"); } @@ -196,7 +202,7 @@ - (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit forIdentityIndex const NSUInteger softIndexes[] = {identityIndex, n}; const NSUInteger *indexes = self.usesHardenedKeys ? hardenedIndexes : softIndexes; NSData *pubKey = [self publicKeyDataAtIndexPath:[NSIndexPath indexPathWithIndexes:indexes length:2]]; - NSString *addr = [DSKeyManager NSStringFrom:key_address_with_public_key_data(pubKey.bytes, pubKey.length, self.chain.chainType)]; + NSString *addr = [DSKeyManager NSStringFrom:dash_spv_crypto_util_address_address_with_public_key_data(slice_ctor(pubKey), self.chain.chainType)]; if (!addr) { DSLog(@"[%@] error generating keys", self.chain.name); @@ -207,16 +213,7 @@ - (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit forIdentityIndex } if (!self.account.wallet.isTransient) { - [self.managedObjectContext performBlock:^{ // store new address in core data - DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:self.managedObjectContext]; - DSAddressEntity *e = [DSAddressEntity managedObjectInContext:self.managedObjectContext]; - e.derivationPath = derivationPathEntity; - NSAssert([DSKeyManager isValidDashAddress:addr forChain:self.chain], @"the address is being saved to the wrong derivation path"); - e.address = addr; - e.index = n; - e.identityIndex = identityIndex; - e.standalone = NO; - }]; + [self storeNewAddressInContext:addr atIndex:n identityIndex:identityIndex context:self.managedObjectContext]; } [self.mAllAddresses addObject:addr]; @@ -229,36 +226,31 @@ - (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit forIdentityIndex } } + - (NSData *)firstUnusedPublicKey { return [self publicKeyDataAtIndex:(uint32_t)[self firstUnusedIndex]]; } -- (OpaqueKey *)firstUnusedPrivateKeyFromSeed:(NSData *)seed { +- (DMaybeOpaqueKey *)firstUnusedPrivateKeyFromSeed:(NSData *)seed { return [self privateKeyAtIndexPath:[NSIndexPath indexPathWithIndex:[self firstUnusedIndex]] fromSeed:seed]; } -- (OpaqueKey *)privateKeyForAddress:(NSString *)address fromSeed:(NSData *)seed { - NSUInteger index = [self indexOfKnownAddress:address]; +- (DMaybeOpaqueKey *)privateKeyForHash160:(UInt160)hash160 + fromSeed:(NSData *)seed { + NSUInteger index = [self indexOfKnownAddressHash:hash160]; return [self privateKeyAtIndexPath:[NSIndexPath indexPathWithIndex:index] fromSeed:seed]; } -- (NSData *)publicKeyDataForAddress:(NSString *)address { - uint32_t index = (uint32_t)[self indexOfKnownAddress:address]; - return [self publicKeyDataAtIndex:index]; -} - -- (OpaqueKey *)privateKeyForHash160:(UInt160)hash160 fromSeed:(NSData *)seed { - NSString *address = [DSKeyManager addressFromHash160:hash160 forChain:self.chain]; - return [self privateKeyForAddress:address fromSeed:seed]; -} - - (NSData *)publicKeyDataForHash160:(UInt160)hash160 { - NSString *address = [DSKeyManager addressFromHash160:hash160 forChain:self.chain]; - return [self publicKeyDataForAddress:address]; + uint32_t index = (uint32_t)[self indexOfKnownAddressHash:hash160]; + return [self publicKeyDataAtIndex:index]; } -- (OpaqueKey *)generateExtendedPublicKeyFromSeed:(NSData *)seed storeUnderWalletUniqueId:(NSString *)walletUniqueId { - return [super generateExtendedPublicKeyFromSeed:seed storeUnderWalletUniqueId:walletUniqueId storePrivateKey:self.shouldStoreExtendedPrivateKey]; +- (DMaybeOpaqueKey *)generateExtendedPublicKeyFromSeed:(NSData *)seed + storeUnderWalletUniqueId:(NSString *)walletUniqueId { + return [super generateExtendedPublicKeyFromSeed:seed + storeUnderWalletUniqueId:walletUniqueId + storePrivateKey:self.shouldStoreExtendedPrivateKey]; } - (BOOL)hasExtendedPrivateKey { @@ -268,14 +260,13 @@ - (BOOL)hasExtendedPrivateKey { - (NSData *)extendedPrivateKeyData { NSError *error = nil; - NSData *data = getKeychainData([self walletBasedExtendedPrivateKeyLocationString], &error); - return data; + return getKeychainData([self walletBasedExtendedPrivateKeyLocationString], &error); } -- (OpaqueKey *_Nullable)privateKeyAtIndexPath:(NSIndexPath *)indexPath { +- (DMaybeOpaqueKey *_Nullable)privateKeyAtIndexPath:(NSIndexPath *)indexPath { return [DSKeyManager deriveKeyFromExtenedPrivateKeyDataAtIndexPath:self.extendedPrivateKeyData - indexPath:indexPath - forKeyType:self.signingAlgorithm]; + indexPath:indexPath + forKeyType:self.signingAlgorithm]; } - (NSData *)publicKeyDataAtIndexPath:(NSIndexPath *)indexPath { @@ -287,7 +278,15 @@ - (NSData *)publicKeyDataAtIndexPath:(NSIndexPath *)indexPath { } if (hasHardenedDerivation || self.reference == DSDerivationPathReference_ProviderPlatformNodeKeys) { if ([self hasExtendedPrivateKey]) { - return [DSKeyManager publicKeyData:[self privateKeyAtIndexPath:indexPath]]; + DMaybeOpaqueKey *result = [self privateKeyAtIndexPath:indexPath]; + if (!result) return nil; + if (!result->ok) { + DMaybeOpaqueKeyDtor(result); + return nil; + } + NSData *data = [DSKeyManager publicKeyData:result->ok]; + DMaybeOpaqueKeyDtor(result); + return data; } else { return nil; } @@ -296,4 +295,47 @@ - (NSData *)publicKeyDataAtIndexPath:(NSIndexPath *)indexPath { } } +- (void)loadAddressesInContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:self.managedObjectContext]; + self.syncBlockHeight = derivationPathEntity.syncBlockHeight; + NSArray *addresses = [derivationPathEntity.addresses sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]]]; + for (DSAddressEntity *e in addresses) { + @autoreleasepool { + while (e.index >= self.mOrderedAddresses.count) [self.mOrderedAddresses addObject:[NSNull null]]; + + if (![DSKeyManager isValidDashAddress:e.address forChain:self.chain]) { +#if DEBUG + DSLogPrivate(@"[%@] address %@ loaded but was not valid on chain", self.chain.name, e.address); +#else + DSLog(@"[%@] address %@ loaded but was not valid on chain", self.wallet.chain.name, @""); +#endif + continue; + } + self.mOrderedAddresses[e.index] = e.address; + [self.mAllAddresses addObject:e.address]; + if ([e.usedInInputs count] || [e.usedInOutputs count] || [e.usedInSpecialTransactions count] || [e.usedInSimplifiedMasternodeEntries count]) { + [self.mUsedAddresses addObject:e.address]; + } + } + } + }]; +} + +- (void)storeNewAddressInContext:(NSString *)address + atIndex:(uint32_t)n + identityIndex:(uint32_t)identityIndex + context:(NSManagedObjectContext *)context { + [self.managedObjectContext performBlock:^{ // store new address in core data + DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:self.managedObjectContext]; + DSAddressEntity *e = [DSAddressEntity managedObjectInContext:self.managedObjectContext]; + e.derivationPath = derivationPathEntity; + NSAssert([DSKeyManager isValidDashAddress:address forChain:self.chain], @"the address is being saved to the wrong derivation path"); + e.address = address; + e.index = n; + e.identityIndex = identityIndex; + e.standalone = NO; + }]; +} + @end diff --git a/DashSync/shared/Models/Derivation Paths/DSCreditFundingDerivationPath+Protected.h b/DashSync/shared/Models/Derivation Paths/DSCreditFundingDerivationPath+Protected.h deleted file mode 100644 index 3c2b9c8e6..000000000 --- a/DashSync/shared/Models/Derivation Paths/DSCreditFundingDerivationPath+Protected.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// Created by Sam Westrich -// Copyright © 2020 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DSCreditFundingDerivationPath.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface DSCreditFundingDerivationPath () - -+ (instancetype)blockchainIdentityRegistrationFundingDerivationPathForChain:(DSChain *)chain; -+ (instancetype)blockchainIdentityTopupFundingDerivationPathForChain:(DSChain *)chain; -+ (instancetype)blockchainIdentityInvitationFundingDerivationPathForChain:(DSChain *)chain; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Derivation Paths/DSCreditFundingDerivationPath.m b/DashSync/shared/Models/Derivation Paths/DSCreditFundingDerivationPath.m deleted file mode 100644 index 1b3e212c5..000000000 --- a/DashSync/shared/Models/Derivation Paths/DSCreditFundingDerivationPath.m +++ /dev/null @@ -1,91 +0,0 @@ -// -// Created by Sam Westrich -// Copyright © 2019 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DSCreditFundingDerivationPath.h" -#import "DSDerivationPath+Protected.h" -#import "DSDerivationPathFactory.h" -#import "DSMasternodeManager.h" -#import "DSSimpleIndexedDerivationPath+Protected.h" -#import "DSWallet+Protected.h" - -@implementation DSCreditFundingDerivationPath - -+ (instancetype)blockchainIdentityRegistrationFundingDerivationPathForWallet:(DSWallet *)wallet { - return [[DSDerivationPathFactory sharedInstance] blockchainIdentityRegistrationFundingDerivationPathForWallet:wallet]; -} - -+ (instancetype)blockchainIdentityTopupFundingDerivationPathForWallet:(DSWallet *)wallet { - return [[DSDerivationPathFactory sharedInstance] blockchainIdentityTopupFundingDerivationPathForWallet:wallet]; -} - -+ (instancetype)blockchainIdentityInvitationFundingDerivationPathForWallet:(DSWallet *)wallet { - return [[DSDerivationPathFactory sharedInstance] blockchainIdentityInvitationFundingDerivationPathForWallet:wallet]; -} - -+ (instancetype)blockchainIdentityRegistrationFundingDerivationPathForChain:(DSChain *)chain { - UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain_coin_type(chain.chainType)), uint256_from_long(FEATURE_PURPOSE_IDENTITIES), uint256_from_long(FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_REGISTRATION)}; - BOOL hardenedIndexes[] = {YES, YES, YES, YES}; - return [DSCreditFundingDerivationPath derivationPathWithIndexes:indexes hardened:hardenedIndexes length:4 type:DSDerivationPathType_CreditFunding signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_BlockchainIdentityCreditRegistrationFunding onChain:chain]; -} - -+ (instancetype)blockchainIdentityTopupFundingDerivationPathForChain:(DSChain *)chain { - UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain_coin_type(chain.chainType)), uint256_from_long(FEATURE_PURPOSE_IDENTITIES), uint256_from_long(FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_TOPUP)}; - BOOL hardenedIndexes[] = {YES, YES, YES, YES}; - return [DSCreditFundingDerivationPath derivationPathWithIndexes:indexes hardened:hardenedIndexes length:4 type:DSDerivationPathType_CreditFunding signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_BlockchainIdentityCreditTopupFunding onChain:chain]; -} - -+ (instancetype)blockchainIdentityInvitationFundingDerivationPathForChain:(DSChain *)chain { - UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain_coin_type(chain.chainType)), uint256_from_long(FEATURE_PURPOSE_IDENTITIES), uint256_from_long(FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_INVITATIONS)}; - BOOL hardenedIndexes[] = {YES, YES, YES, YES}; - return [DSCreditFundingDerivationPath derivationPathWithIndexes:indexes hardened:hardenedIndexes length:4 type:DSDerivationPathType_CreditFunding signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_BlockchainIdentityCreditInvitationFunding onChain:chain]; -} - -- (NSString *)receiveAddress { - NSString *addr = [self registerAddressesWithGapLimit:1 error:nil].lastObject; - return (addr) ? addr : self.mOrderedAddresses.lastObject; -} - -- (NSUInteger)defaultGapLimit { - return 5; -} - -// sign any inputs in the given transaction that can be signed using private keys from the wallet -- (void)signTransaction:(DSTransaction *)transaction withPrompt:(NSString *)authprompt completion:(TransactionValidityCompletionBlock)completion; -{ - if ([transaction inputAddresses].count != 1) { - completion(NO, NO); - return; - } - - NSUInteger index = [self indexOfKnownAddress:[[transaction inputAddresses] firstObject]]; - - @autoreleasepool { // @autoreleasepool ensures sensitive data will be dealocated immediately - self.wallet.secureSeedRequestBlock(authprompt, MASTERNODE_COST, ^void(NSData *_Nullable seed, BOOL cancelled) { - if (!seed) { - if (completion) completion(NO, cancelled); - } else { - OpaqueKey *key = [self privateKeyAtIndex:(uint32_t)index fromSeed:seed]; - NSValue *val = [NSValue valueWithPointer:key]; - BOOL signedSuccessfully = [transaction signWithPrivateKeys:@[val]]; - processor_destroy_opaque_key(key); - if (completion) completion(signedSuccessfully, NO); - } - }); - } -} - -@end diff --git a/DashSync/shared/Models/Derivation Paths/DSDerivationPath+Protected.h b/DashSync/shared/Models/Derivation Paths/DSDerivationPath+Protected.h index 30a499839..89b447edf 100644 --- a/DashSync/shared/Models/Derivation Paths/DSDerivationPath+Protected.h +++ b/DashSync/shared/Models/Derivation Paths/DSDerivationPath+Protected.h @@ -5,25 +5,9 @@ // Created by Sam Westrich on 2/10/19. // -#import "DSAccount.h" -#import "DSAddressEntity+CoreDataClass.h" -#import "DSBlockchainIdentity.h" -#import "DSChain.h" #import "DSDerivationPath.h" #import "DSDerivationPathEntity+CoreDataClass.h" -#import "DSKeySequence.h" -#import "DSPeerManager.h" -#import "DSPriceManager.h" -#import "DSTransaction.h" -#import "DSTransactionEntity+CoreDataClass.h" -#import "DSTxInputEntity+CoreDataClass.h" -#import "DSTxOutputEntity+CoreDataClass.h" #import "DSWallet.h" -#import "NSData+Dash.h" -#import "NSManagedObject+Sugar.h" -#import "NSMutableData+Dash.h" -#import "NSString+Bitcoin.h" -#import "NSString+Dash.h" NS_ASSUME_NONNULL_BEGIN @@ -32,13 +16,15 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) BOOL addressesLoaded; @property (nonatomic, strong) NSManagedObjectContext *managedObjectContext; @property (nonatomic, strong) NSMutableSet *mAllAddresses, *mUsedAddresses; -@property (nonatomic, assign) OpaqueKey *extendedPublicKey; //master public key used to generate wallet addresses +@property (nonatomic, assign) DMaybeOpaqueKey *extendedPublicKey; //master public key used to generate wallet addresses @property (nonatomic, strong) NSString *standaloneExtendedPublicKeyUniqueID; @property (nonatomic, weak) DSWallet *wallet; @property (nonatomic, nullable, readonly) NSString *standaloneExtendedPublicKeyLocationString; -@property (nonatomic, readonly) DSDerivationPathEntity *derivationPathEntity; +//@property (nonatomic, readonly) DSDerivationPathEntity *derivationPathEntity; -- (DSDerivationPathEntity *)derivationPathEntityInContext:(NSManagedObjectContext *)context; +//- (DSDerivationPathEntity *)derivationPathEntityInContext:(NSManagedObjectContext *)context; +- (NSString *)walletBasedExtendedPublicKeyLocationStringForWalletUniqueID:(NSString *)uniqueID; +- (NSString *)createIdentifierForDerivationPath; @end diff --git a/DashSync/shared/Models/Derivation Paths/DSDerivationPath.h b/DashSync/shared/Models/Derivation Paths/DSDerivationPath.h index a0721d9d8..dbdc241df 100644 --- a/DashSync/shared/Models/Derivation Paths/DSDerivationPath.h +++ b/DashSync/shared/Models/Derivation Paths/DSDerivationPath.h @@ -23,9 +23,9 @@ // THE SOFTWARE. #import "dash_shared_core.h" +#import "DSKeyManager.h" #import "DSChain.h" #import "DSDerivationPath.h" -#import "DSKeyManager.h" #import "DSTransaction.h" #import "DSUInt256IndexPath.h" #import "NSData+Dash.h" @@ -47,7 +47,7 @@ typedef void (^TransactionValidityCompletionBlock)(BOOL signedTransaction, BOOL #define FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_INVITATIONS 3 #define FEATURE_PURPOSE_DASHPAY 15 -@class DSTransaction, DSAccount, DSDerivationPath, DSKeyManager; +@class DSTransaction, DSAccount, DSDerivationPath, DSKeyManager, DSWallet; typedef NS_ENUM(NSUInteger, DSDerivationPathType) { @@ -70,7 +70,7 @@ typedef NS_ENUM(NSUInteger, DSDerivationPathReference) DSDerivationPathReference_Unknown = 0, DSDerivationPathReference_BIP32 = 1, DSDerivationPathReference_BIP44 = 2, - DSDerivationPathReference_BlockchainIdentities = 3, + DSDerivationPathReference_Identities = 3, DSDerivationPathReference_ProviderFunds = 4, DSDerivationPathReference_ProviderVotingKeys = 5, DSDerivationPathReference_ProviderOperatorKeys = 6, @@ -78,9 +78,9 @@ typedef NS_ENUM(NSUInteger, DSDerivationPathReference) DSDerivationPathReference_ContactBasedFunds = 8, DSDerivationPathReference_ContactBasedFundsRoot = 9, DSDerivationPathReference_ContactBasedFundsExternal = 10, - DSDerivationPathReference_BlockchainIdentityCreditRegistrationFunding = 11, - DSDerivationPathReference_BlockchainIdentityCreditTopupFunding = 12, - DSDerivationPathReference_BlockchainIdentityCreditInvitationFunding = 13, + DSDerivationPathReference_IdentityCreditRegistrationFunding = 11, + DSDerivationPathReference_IdentityCreditTopupFunding = 12, + DSDerivationPathReference_IdentityCreditInvitationFunding = 13, DSDerivationPathReference_ProviderPlatformNodeKeys = 14, DSDerivationPathReference_Root = 255, }; @@ -93,7 +93,7 @@ typedef NS_ENUM(NSUInteger, DSDerivationPathReference) //is this an open account @property (nonatomic, assign, readonly) DSDerivationPathType type; -@property (nonatomic, assign, readonly) KeyKind signingAlgorithm; +@property (nonatomic, assign, readonly) dash_spv_crypto_keys_key_KeyKind *signingAlgorithm; // account for the derivation path @property (nonatomic, readonly) DSChain *chain; @@ -144,20 +144,40 @@ typedef NS_ENUM(NSUInteger, DSDerivationPathReference) // there might be times where the derivationPath is actually unknown, for example when importing from an extended public key @property (nonatomic, readonly) BOOL derivationPathIsKnown; -+ (instancetype)masterBlockchainIdentityContactsDerivationPathForAccountNumber:(uint32_t)accountNumber onChain:(DSChain *)chain; +@property (nonatomic, readonly) NSNumber *depth; + -+ (instancetype _Nullable)derivationPathWithIndexes:(const UInt256[_Nullable])indexes hardened:(const BOOL[_Nullable])hardenedIndexes length:(NSUInteger)length type:(DSDerivationPathType)type signingAlgorithm:(KeyKind)signingAlgorithm reference:(DSDerivationPathReference)reference onChain:(DSChain *)chain; ++ (instancetype)masterIdentityContactsDerivationPathForAccountNumber:(uint32_t)accountNumber onChain:(DSChain *)chain; -+ (instancetype _Nullable)derivationPathWithSerializedExtendedPrivateKey:(NSString *)serializedExtendedPrivateKey fundsType:(DSDerivationPathType)fundsType signingAlgorithm:(KeyKind)signingAlgorithm onChain:(DSChain *)chain; ++ (instancetype _Nullable)derivationPathWithIndexes:(const UInt256[_Nullable])indexes + hardened:(const BOOL[_Nullable])hardenedIndexes + length:(NSUInteger)length + type:(DSDerivationPathType)type + signingAlgorithm:(dash_spv_crypto_keys_key_KeyKind *)signingAlgorithm + reference:(DSDerivationPathReference)reference + onChain:(DSChain *)chain; -+ (instancetype _Nullable)derivationPathWithSerializedExtendedPublicKey:(NSString *)serializedExtendedPublicKey onChain:(DSChain *)chain; +//+ (instancetype _Nullable)derivationPathWithSerializedExtendedPrivateKey:(NSString *)serializedExtendedPrivateKey +// fundsType:(DSDerivationPathType)fundsType +// signingAlgorithm:(dash_spv_crypto_keys_key_KeyKind *)signingAlgorithm +// onChain:(DSChain *)chain; +// ++ (instancetype _Nullable)derivationPathWithSerializedExtendedPublicKey:(NSString *)serializedExtendedPublicKey + onChain:(DSChain *)chain; -- (instancetype _Nullable)initWithExtendedPublicKeyIdentifier:(NSString *)extendedPublicKeyIdentifier onChain:(DSChain *)chain; +- (instancetype _Nullable)initWithExtendedPublicKeyIdentifier:(NSString *)extendedPublicKeyIdentifier + onChain:(DSChain *)chain; -- (instancetype _Nullable)initWithIndexes:(const UInt256[_Nullable])indexes hardened:(const BOOL[_Nullable])hardenedIndexes length:(NSUInteger)length type:(DSDerivationPathType)type signingAlgorithm:(KeyKind)signingAlgorithm reference:(DSDerivationPathReference)reference onChain:(DSChain *)chain; +- (instancetype _Nullable)initWithIndexes:(const UInt256[_Nullable])indexes + hardened:(const BOOL[_Nullable])hardenedIndexes + length:(NSUInteger)length + type:(DSDerivationPathType)type + signingAlgorithm:(dash_spv_crypto_keys_key_KeyKind *)signingAlgorithm + reference:(DSDerivationPathReference)reference + onChain:(DSChain *)chain; -- (BOOL)isBIP32Only; -- (BOOL)isBIP43Based; +//- (BOOL)isBIP32Only; +//- (BOOL)isBIP43Based; - (NSIndexPath *)baseIndexPath; @@ -166,12 +186,13 @@ typedef NS_ENUM(NSUInteger, DSDerivationPathReference) // true if the address is controlled by the wallet - (BOOL)containsAddress:(NSString *)address; +- (BOOL)containsAddressHash:(UInt160)hash; // true if the address was previously used as an input or output in any wallet transaction - (BOOL)addressIsUsed:(NSString *)address; -// true if the address at index path was previously used as an input or output in any wallet transaction -- (BOOL)addressIsUsedAtIndexPath:(NSIndexPath *)indexPath; +//// true if the address at index path was previously used as an input or output in any wallet transaction +//- (BOOL)addressIsUsedAtIndexPath:(NSIndexPath *)indexPath; // inform the derivation path that the address has been used by a transaction, true if the derivation path contains the address - (BOOL)registerTransactionAddress:(NSString *)address; @@ -180,40 +201,29 @@ typedef NS_ENUM(NSUInteger, DSDerivationPathReference) - (NSString *)addressAtIndexPath:(NSIndexPath *)indexPath; // gets a private key at an index path -- (OpaqueKey *_Nullable)privateKeyAtIndexPath:(NSIndexPath *)indexPath fromSeed:(NSData *)seed; - -- (OpaqueKey *_Nullable)privateKeyForKnownAddress:(NSString *)address fromSeed:(NSData *)seed; +- (DMaybeOpaqueKey *_Nullable)privateKeyAtIndexPath:(NSIndexPath *)indexPath + fromSeed:(NSData *)seed; -- (OpaqueKey *_Nullable)deprecatedIncorrectExtendedPublicKeyFromSeed:(NSData *_Nullable)seed; //you can set wallet unique Id to nil if you don't wish to store the extended Public Key -- (OpaqueKey *_Nullable)generateExtendedPublicKeyFromSeed:(NSData *)seed storeUnderWalletUniqueId:(NSString *_Nullable)walletUniqueId; +- (DMaybeOpaqueKey *_Nullable)generateExtendedPublicKeyFromSeed:(NSData *)seed + storeUnderWalletUniqueId:(NSString *_Nullable)walletUniqueId; //addition option to store the private key, this should generally not be used unless the key is meant to be used without authentication -- (OpaqueKey *_Nullable)generateExtendedPublicKeyFromSeed:(NSData *)seed storeUnderWalletUniqueId:(NSString *_Nullable)walletUniqueId storePrivateKey:(BOOL)storePrivateKey; +- (DMaybeOpaqueKey *_Nullable)generateExtendedPublicKeyFromSeed:(NSData *)seed + storeUnderWalletUniqueId:(NSString *_Nullable)walletUniqueId + storePrivateKey:(BOOL)storePrivateKey; //you can set wallet unique Id to nil if you don't wish to store the extended Public Key -- (OpaqueKey *_Nullable)generateExtendedPublicKeyFromParentDerivationPath:(DSDerivationPath *)parentDerivationPath storeUnderWalletUniqueId:(NSString *_Nullable)walletUniqueId; - -//sometimes we need to store the public key but not at generation time, use this method for that -- (BOOL)storeExtendedPublicKeyUnderWalletUniqueId:(NSString *_Nonnull)walletUniqueId; +- (DMaybeOpaqueKey *_Nullable)generateExtendedPublicKeyFromParentDerivationPath:(DSDerivationPath *)parentDerivationPath + storeUnderWalletUniqueId:(NSString *_Nullable)walletUniqueId; -- (NSString *_Nullable)serializedExtendedPublicKey; -- (NSString *_Nullable)serializedExtendedPrivateKeyFromSeedAtIndexPath:(NSData *)seed indexPath:(NSIndexPath *)indexPath; -- (NSString *_Nullable)serializedExtendedPrivateKeyFromSeed:(NSData *_Nullable)seed; -+ (NSData *_Nullable)deserializedExtendedPrivateKey:(NSString *)extendedPrivateKeyString onChain:(DSChain *)chain; - -+ (NSData *)deserializedExtendedPublicKey:(NSString *)extendedPublicKeyString onChain:(DSChain *)chain; -- (NSData *_Nullable)deserializedExtendedPublicKey:(NSString *)extendedPublicKeyString; - -- (OpaqueKey *_Nullable)publicKeyAtIndexPath:(NSIndexPath *)indexPath; +- (DMaybeOpaqueKey *_Nullable)publicKeyAtIndexPath:(NSIndexPath *)indexPath; - (NSData *_Nullable)publicKeyDataAtIndexPath:(NSIndexPath *)indexPath; -- (NSArray *_Nullable)privateKeysAtIndexPaths:(NSArray *)indexPaths fromSeed:(NSData *)seed; - -- (NSArray *_Nullable)serializedPrivateKeysAtIndexPaths:(NSArray *)indexPaths fromSeed:(NSData *)seed; +//- (NSArray *_Nullable)serializedPrivateKeysAtIndexPaths:(NSArray *)indexPaths fromSeed:(NSData *)seed; //this loads the derivation path once it is set to an account that has a wallet; - (void)loadAddresses; @@ -226,4 +236,9 @@ typedef NS_ENUM(NSUInteger, DSDerivationPathReference) @end +@interface DSDerivationPath (dash_spv_crypto_keys_key_IndexPathU256) ++ (dash_spv_crypto_keys_key_IndexPathU256 *)ffi_to:(DSDerivationPath *)obj; ++ (void)ffi_destroy:(dash_spv_crypto_keys_key_IndexPathU256 *)ffi_ref; +@end + NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Derivation Paths/DSDerivationPath.m b/DashSync/shared/Models/Derivation Paths/DSDerivationPath.m index 4bf8267bf..6a7394a8b 100644 --- a/DashSync/shared/Models/Derivation Paths/DSDerivationPath.m +++ b/DashSync/shared/Models/Derivation Paths/DSDerivationPath.m @@ -22,21 +22,21 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#import "DSAccount.h" #import "DSBlockchainIdentityEntity+CoreDataClass.h" #import "DSBlockchainIdentityUsernameEntity+CoreDataClass.h" +#import "DSChain+Params.h" #import "DSChainManager.h" #import "DSDashpayUserEntity+CoreDataClass.h" +#import "DSDerivationPathFactory.h" #import "DSDerivationPath+Protected.h" #import "DSFriendRequestEntity+CoreDataClass.h" #import "DSIncomingFundsDerivationPath.h" +#import "DSWallet+Identity.h" #import "NSIndexPath+FFI.h" #import "NSManagedObject+Sugar.h" #import "NSMutableData+Dash.h" -#define DERIVATION_PATH_EXTENDED_PUBLIC_KEY_WALLET_BASED_LOCATION @"DP_EPK_WBL" -#define DERIVATION_PATH_EXTENDED_PUBLIC_KEY_STANDALONE_BASED_LOCATION @"DP_EPK_SBL" -#define DERIVATION_PATH_EXTENDED_SECRET_KEY_WALLET_BASED_LOCATION @"DP_ESK_WBL" -#define DERIVATION_PATH_STANDALONE_INFO_DICTIONARY_LOCATION @"DP_SIDL" #define DERIVATION_PATH_STANDALONE_INFO_TERMINAL_INDEX @"DP_SI_T_INDEX" #define DERIVATION_PATH_STANDALONE_INFO_TERMINAL_HARDENED @"DP_SI_T_HARDENED" #define DERIVATION_PATH_STANDALONE_INFO_DEPTH @"DP_SI_DEPTH" @@ -56,70 +56,138 @@ @implementation DSDerivationPath // MARK: - Derivation Path initialization -+ (instancetype)masterBlockchainIdentityContactsDerivationPathForAccountNumber:(uint32_t)accountNumber onChain:(DSChain *)chain { - UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long((uint64_t) chain_coin_type(chain.chainType)), uint256_from_long(FEATURE_PURPOSE_DASHPAY), uint256_from_long(accountNumber)}; ++ (instancetype)masterIdentityContactsDerivationPathForAccountNumber:(uint32_t)accountNumber + onChain:(DSChain *)chain { + UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long(chain.coinType), uint256_from_long(FEATURE_PURPOSE_DASHPAY), uint256_from_long(accountNumber)}; //todo full uint256 derivation BOOL hardenedIndexes[] = {YES, YES, YES, YES}; - return [self derivationPathWithIndexes:indexes hardened:hardenedIndexes length:4 type:DSDerivationPathType_PartialPath signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_ContactBasedFundsRoot onChain:chain]; + + dash_spv_crypto_keys_key_KeyKind *key_kind = dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor(); + return [self derivationPathWithIndexes:indexes + hardened:hardenedIndexes + length:4 + type:DSDerivationPathType_PartialPath + signingAlgorithm:key_kind + reference:DSDerivationPathReference_ContactBasedFundsRoot + onChain:chain]; } -+ (instancetype _Nullable)derivationPathWithIndexes:(const UInt256[_Nullable])indexes hardened:(const BOOL[_Nullable])hardenedIndexes length:(NSUInteger)length ++ (instancetype _Nullable)derivationPathWithIndexes:(const UInt256[_Nullable])indexes + hardened:(const BOOL[_Nullable])hardenedIndexes + length:(NSUInteger)length type:(DSDerivationPathType)type - signingAlgorithm:(KeyKind)signingAlgorithm + signingAlgorithm:(dash_spv_crypto_keys_key_KeyKind *)signingAlgorithm reference:(DSDerivationPathReference)reference onChain:(DSChain *)chain { - return [[self alloc] initWithIndexes:indexes hardened:hardenedIndexes length:length type:type signingAlgorithm:signingAlgorithm reference:reference onChain:chain]; -} - -+ (instancetype _Nullable)derivationPathWithSerializedExtendedPrivateKey:(NSString *)serializedExtendedPrivateKey fundsType:(DSDerivationPathType)fundsType signingAlgorithm:(KeyKind)signingAlgorithm onChain:(DSChain *)chain { - UInt256 indexes[] = {}; - BOOL hardenedIndexes[] = {}; - DSDerivationPath *derivationPath = [[self alloc] initWithIndexes:indexes hardened:hardenedIndexes length:0 type:fundsType signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_Unknown onChain:chain]; - NSData *extendedPrivateKey = [self deserializedExtendedPrivateKey:serializedExtendedPrivateKey onChain:chain]; - derivationPath.extendedPublicKey = key_create_ecdsa_from_secret(extendedPrivateKey.bytes, 32, true); - [derivationPath standaloneSaveExtendedPublicKeyToKeyChain]; - return derivationPath; -} + return [[self alloc] initWithIndexes:indexes + hardened:hardenedIndexes + length:length + type:type + signingAlgorithm:signingAlgorithm + reference:reference + onChain:chain]; +} + +//+ (instancetype _Nullable)derivationPathWithSerializedExtendedPrivateKey:(NSString *)serializedExtendedPrivateKey +// fundsType:(DSDerivationPathType)fundsType +// signingAlgorithm:(dash_spv_crypto_keys_key_KeyKind *)signingAlgorithm +// onChain:(DSChain *)chain { +// UInt256 indexes[] = {}; +// BOOL hardenedIndexes[] = {}; +// +// +// dash_spv_crypto_keys_key_KeyKind *key_kind = dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor(); +// DSDerivationPath *derivationPath = [[self alloc] initWithIndexes:indexes +// hardened:hardenedIndexes +// length:0 +// type:fundsType +// signingAlgorithm:key_kind +// reference:DSDerivationPathReference_Unknown +// onChain:chain]; +// @autoreleasepool { +// uint8_t depth; +// uint32_t fingerprint; +// UInt256 child; +// BOOL hardened; +// UInt256 chainHash; +// NSData *privkey = nil; +// NSMutableData *masterPrivateKey = [NSMutableData secureData]; +// BOOL valid = deserialize(serializedExtendedPrivateKey, &depth, &fingerprint, &hardened, &child, &chainHash, &privkey, [chain isMainnet]); +// if (!valid) return nil; +// [masterPrivateKey appendUInt32:fingerprint]; +// [masterPrivateKey appendBytes:&chainHash length:32]; +// [masterPrivateKey appendData:privkey]; +// SLICE *slice = slice_ctor(masterPrivateKey); +// derivationPath.extendedPublicKey = dash_spv_crypto_keys_key_KeyKind_key_with_private_key_data(key_kind, slice); +// } +// [derivationPath standaloneSaveExtendedPublicKeyToKeyChain]; +// return derivationPath; +//} -+ (instancetype _Nullable)derivationPathWithSerializedExtendedPublicKey:(NSString *)serializedExtendedPublicKey onChain:(DSChain *)chain { ++ (instancetype _Nullable)derivationPathWithSerializedExtendedPublicKey:(NSString *)serializedExtendedPublicKey + onChain:(DSChain *)chain { uint8_t depth = 0; BOOL terminalHardened; UInt256 terminalIndex = UINT256_ZERO; - NSData *extendedPublicKeyData = [self deserializedExtendedPublicKey:serializedExtendedPublicKey onChain:chain rDepth:&depth rTerminalHardened:&terminalHardened rTerminalIndex:&terminalIndex]; + NSData *extendedPublicKeyData = [DSDerivationPathFactory deserializedExtendedPublicKey:serializedExtendedPublicKey + onChain:chain + rDepth:&depth + rTerminalHardened:&terminalHardened + rTerminalIndex:&terminalIndex]; UInt256 indexes[] = {terminalIndex}; BOOL hardenedIndexes[] = {terminalHardened}; - DSDerivationPath *derivationPath = [[self alloc] initWithIndexes:indexes hardened:hardenedIndexes length:0 type:DSDerivationPathType_ViewOnlyFunds signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_Unknown onChain:chain]; //we are going to assume this is only ecdsa for now - derivationPath.extendedPublicKey = key_create_ecdsa_from_extended_public_key_data(extendedPublicKeyData.bytes, extendedPublicKeyData.length); + dash_spv_crypto_keys_key_KeyKind *key_kind = dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor(); + DSDerivationPath *derivationPath = [[self alloc] initWithIndexes:indexes + hardened:hardenedIndexes + length:0 + type:DSDerivationPathType_ViewOnlyFunds + signingAlgorithm:key_kind + reference:DSDerivationPathReference_Unknown + onChain:chain]; //we are going to assume this is only ecdsa for now + SLICE *slice = slice_ctor(extendedPublicKeyData); + DMaybeOpaqueKey *result = dash_spv_crypto_keys_key_KeyKind_key_init_with_extended_public_key_data(key_kind, slice); + derivationPath.extendedPublicKey = result; derivationPath.depth = @(depth); [derivationPath standaloneSaveExtendedPublicKeyToKeyChain]; [derivationPath loadAddresses]; return derivationPath; } -- (instancetype _Nullable)initWithExtendedPublicKeyIdentifier:(NSString *_Nonnull)extendedPublicKeyIdentifier onChain:(DSChain *_Nonnull)chain { +- (instancetype _Nullable)initWithExtendedPublicKeyIdentifier:(NSString *_Nonnull)extendedPublicKeyIdentifier + onChain:(DSChain *_Nonnull)chain { NSError *error = nil; - NSDictionary *infoDictionary = getKeychainDict([DSDerivationPath standaloneInfoDictionaryLocationStringForUniqueID:extendedPublicKeyIdentifier], @[[NSString class], [NSNumber class]], &error); + NSDictionary *infoDictionary = getKeychainDict([DSDerivationPathFactory standaloneInfoDictionaryLocationStringForUniqueID:extendedPublicKeyIdentifier], @[[NSString class], [NSNumber class]], &error); if (error) return nil; UInt256 terminalIndex = [((NSData *)infoDictionary[DERIVATION_PATH_STANDALONE_INFO_TERMINAL_INDEX]) UInt256]; BOOL terminalHardened = [((NSNumber *)infoDictionary[DERIVATION_PATH_STANDALONE_INFO_TERMINAL_HARDENED]) boolValue]; UInt256 indexes[] = {terminalIndex}; BOOL hardenedIndexes[] = {terminalHardened}; - if (!(self = [self initWithIndexes:indexes hardened:hardenedIndexes length:0 type:DSDerivationPathType_ViewOnlyFunds signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_Unknown onChain:chain])) return nil; + dash_spv_crypto_keys_key_KeyKind *key_kind = dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor(); + if (!(self = [self initWithIndexes:indexes + hardened:hardenedIndexes + length:0 + type:DSDerivationPathType_ViewOnlyFunds + signingAlgorithm:key_kind + reference:DSDerivationPathReference_Unknown + onChain:chain])) return nil; _walletBasedExtendedPublicKeyLocationString = extendedPublicKeyIdentifier; - NSData *data = getKeychainData([DSDerivationPath standaloneExtendedPublicKeyLocationStringForUniqueID:extendedPublicKeyIdentifier], &error); + NSData *data = getKeychainData([DSDerivationPathFactory standaloneExtendedPublicKeyLocationStringForUniqueID:extendedPublicKeyIdentifier], &error); if (error) return nil; - _extendedPublicKey = key_create_ecdsa_from_extended_public_key_data(data.bytes, data.length); + SLICE *slice = slice_ctor(data); + DMaybeOpaqueKey *result = dash_spv_crypto_keys_key_KeyKind_key_with_extended_public_key_data(key_kind, slice); + _extendedPublicKey = result; _depth = infoDictionary[DERIVATION_PATH_STANDALONE_INFO_DEPTH]; - [self loadAddresses]; return self; } -- (instancetype)initWithIndexes:(const UInt256[_Nullable])indexes hardened:(const BOOL[_Nullable])hardenedIndexes length:(NSUInteger)length +- (instancetype)initWithIndexes:(const UInt256[_Nullable])indexes + hardened:(const BOOL[_Nullable])hardenedIndexes + length:(NSUInteger)length type:(DSDerivationPathType)type - signingAlgorithm:(KeyKind)signingAlgorithm + signingAlgorithm:(dash_spv_crypto_keys_key_KeyKind *)signingAlgorithm reference:(DSDerivationPathReference)reference onChain:(DSChain *)chain { if (length) { @@ -157,7 +225,7 @@ - (void)dealloc { free(_hardenedIndexes); } if (_extendedPublicKey != NULL) { - processor_destroy_opaque_key(_extendedPublicKey); + DMaybeOpaqueKeyDtor(_extendedPublicKey); } } @@ -193,23 +261,6 @@ - (NSIndexPath *)baseIndexPath { return [NSIndexPath indexPathWithIndexes:indexes length:self.length]; } -// MARK: - Purpose - -- (BOOL)isBIP32Only { - if (self.length == 1) return true; - return false; -} - -- (BOOL)isBIP43Based { - if (self.length != 1) return true; - return false; -} - -- (NSUInteger)purpose { - if ([self isBIP43Based]) return [self indexAtPosition:0].u64[0]; - return 0; -} - // MARK: - Account - (NSUInteger)accountNumber { @@ -250,58 +301,22 @@ - (BOOL)hasExtendedPublicKey { } - (NSData *)extendedPublicKeyData { - if (self.extendedPublicKey != NULL) - return [DSKeyManager extendedPublicKeyData:self.extendedPublicKey]; + if (self.extendedPublicKey != NULL && self.extendedPublicKey->ok != NULL) + return [DSKeyManager extendedPublicKeyData:self.extendedPublicKey->ok]; else return nil; } -- (void)maybeRevertBLSMigration:(NSData *)extendedPublicKeyData { - // revert - // for those who already migrated from legacy to basic BLS derivation scheme - // we revert back their extended public key to legacy - BOOL isBasicBLS = self.signingAlgorithm == KeyKind_BLSBasic; - if (isBasicBLS) { - _extendedPublicKey = key_bls_migrate_from_basic_extended_public_key_data(extendedPublicKeyData.bytes, extendedPublicKeyData.length); - if (_extendedPublicKey) { - setKeychainData([DSKeyManager extendedPublicKeyData:_extendedPublicKey], [self standaloneExtendedPublicKeyLocationString], NO); - } - } -} - -- (OpaqueKey *)extendedPublicKey { +- (DMaybeOpaqueKey *)extendedPublicKey { if (!_extendedPublicKey) { if (self.wallet && (self.length || self.reference == DSDerivationPathReference_Root)) { NSData *extendedPublicKeyData = getKeychainData([self walletBasedExtendedPublicKeyLocationString], nil); if (extendedPublicKeyData) { - _extendedPublicKey = key_create_from_extended_public_key_data(extendedPublicKeyData.bytes, extendedPublicKeyData.length, (int16_t) self.signingAlgorithm); - [self maybeRevertBLSMigration:extendedPublicKeyData]; - NSAssert(_extendedPublicKey, @"extended public key not set"); + _extendedPublicKey = dash_spv_crypto_keys_key_KeyKind_key_with_extended_public_key_data(self.signingAlgorithm, slice_ctor(extendedPublicKeyData)); } } else { NSData *extendedPublicKeyData = getKeychainData([self standaloneExtendedPublicKeyLocationString], nil); -#ifdef DEBUG - if (!extendedPublicKeyData) { - if ([self isKindOfClass:[DSIncomingFundsDerivationPath class]]) { - DSFriendRequestEntity *friendRequest = [DSFriendRequestEntity anyObjectInContext:self.managedObjectContext matching:@"derivationPath.publicKeyIdentifier == %@", self.standaloneExtendedPublicKeyUniqueID]; - - NSAssert(friendRequest, @"friend request must exist"); - - DSBlockchainIdentityUsernameEntity *sourceUsernameEntity = [friendRequest.sourceContact.associatedBlockchainIdentity.usernames anyObject]; - DSBlockchainIdentityUsernameEntity *destinationUsernameEntity = [friendRequest.destinationContact.associatedBlockchainIdentity.usernames anyObject]; -#if DEBUG - DSLogPrivate(@"[%@] No extended public key set for the relationship between %@ and %@ (%@ receiving payments) ", self.chain.name, sourceUsernameEntity.stringValue, destinationUsernameEntity.stringValue, sourceUsernameEntity.stringValue); -#else - DSLog(@"[%@] No extended public key set for the relationship between %@ and %@ (%@ receiving payments) ", self.chain.name, - @"", - @"", - @""); -#endif /* DEBUG */ - } - } -#endif - _extendedPublicKey = key_create_from_extended_public_key_data(extendedPublicKeyData.bytes, extendedPublicKeyData.length, (int16_t) self.signingAlgorithm); - [self maybeRevertBLSMigration:extendedPublicKeyData]; + _extendedPublicKey = dash_spv_crypto_keys_key_KeyKind_key_with_extended_public_key_data(self.signingAlgorithm, slice_ctor(extendedPublicKeyData)); } } return _extendedPublicKey; @@ -310,7 +325,10 @@ - (OpaqueKey *)extendedPublicKey { - (void)standaloneSaveExtendedPublicKeyToKeyChain { if (!_extendedPublicKey) return; setKeychainData([self extendedPublicKeyData], [self standaloneExtendedPublicKeyLocationString], NO); - setKeychainDict(@{DERIVATION_PATH_STANDALONE_INFO_TERMINAL_INDEX: uint256_data([self terminalIndex]), DERIVATION_PATH_STANDALONE_INFO_TERMINAL_HARDENED: @([self terminalHardened]), DERIVATION_PATH_STANDALONE_INFO_DEPTH: self.depth}, [self standaloneInfoDictionaryLocationString], NO); + + NSString *dictionaryLocationString = self.standaloneExtendedPublicKeyUniqueID ? [DSDerivationPathFactory standaloneInfoDictionaryLocationStringForUniqueID:_standaloneExtendedPublicKeyUniqueID] : nil; + + setKeychainDict(@{DERIVATION_PATH_STANDALONE_INFO_TERMINAL_INDEX: uint256_data([self terminalIndex]), DERIVATION_PATH_STANDALONE_INFO_TERMINAL_HARDENED: @([self terminalHardened]), DERIVATION_PATH_STANDALONE_INFO_DEPTH: self.depth}, dictionaryLocationString, NO); [self.managedObjectContext performBlockAndWait:^{ [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:self.managedObjectContext]; }]; @@ -326,29 +344,27 @@ - (NSIndexPath *)indexPathForKnownAddress:(NSString *)address { // gets an address at an index path - (NSString *)addressAtIndexPath:(NSIndexPath *)indexPath { NSData *pubKey = [self publicKeyDataAtIndexPath:indexPath]; - return [DSKeyManager NSStringFrom:key_address_with_public_key_data(pubKey.bytes, pubKey.length, self.chain.chainType)]; + return [DSKeyManager NSStringFrom:dash_spv_crypto_util_address_address_with_public_key_data(slice_ctor(pubKey), self.chain.chainType)]; } // true if the address is controlled by the wallet - (BOOL)containsAddress:(NSString *)address { - return (address && [self.mAllAddresses containsObject:address]) ? YES : NO; + return address && [self.mAllAddresses containsObject:address]; +} +- (BOOL)containsAddressHash:(UInt160)hash { + NSString *address = [DSKeyManager addressFromHash160:hash forChain:self.chain]; + return [self containsAddress:address]; } // true if the address was previously used as an input or output in any wallet transaction - (BOOL)addressIsUsed:(NSString *)address { - return (address && [self.mUsedAddresses containsObject:address]) ? YES : NO; -} - -// true if the address at index path was previously used as an input or output in any wallet transaction -- (BOOL)addressIsUsedAtIndexPath:(NSIndexPath *)indexPath { - return [self addressIsUsed:[self addressAtIndexPath:indexPath]]; + return address && [self.mUsedAddresses containsObject:address]; } - (BOOL)registerTransactionAddress:(NSString *_Nonnull)address { if ([self containsAddress:address]) { - if (![self.mUsedAddresses containsObject:address]) { + if (![self.mUsedAddresses containsObject:address]) [self.mUsedAddresses addObject:address]; - } return TRUE; } return FALSE; @@ -369,16 +385,6 @@ - (void)loadAddresses { - (void)reloadAddresses { } -// MARK: - Derivation Path Information - -- (DSDerivationPathEntity *)derivationPathEntity { - return [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:self.managedObjectContext]; -} - -- (DSDerivationPathEntity *)derivationPathEntityInContext:(NSManagedObjectContext *)context { - return [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:context]; -} - - (NSNumber *)depth { if (_depth != nil) return _depth; @@ -398,32 +404,13 @@ - (NSUInteger)hash { return [self.standaloneExtendedPublicKeyUniqueID hash]; } -+ (NSString *)stringRepresentationOfIndex:(UInt256)index hardened:(BOOL)hardened inContext:(NSManagedObjectContext *)context { - if (uint256_is_31_bits(index)) { - return [NSString stringWithFormat:@"/%lu%@", (unsigned long)index.u64[0], hardened ? @"'" : @""]; - } else if (context) { - __block NSString *rString = nil; - [context performBlockAndWait:^{ - DSDashpayUserEntity *dashpayUserEntity = [DSDashpayUserEntity anyObjectInContext:context matching:@"associatedBlockchainIdentity.uniqueID == %@", uint256_data(index)]; - if (dashpayUserEntity) { - DSBlockchainIdentityUsernameEntity *usernameEntity = [dashpayUserEntity.associatedBlockchainIdentity.usernames anyObject]; - rString = [NSString stringWithFormat:@"/%@%@", usernameEntity.stringValue, hardened ? @"'" : @""]; - } else { - rString = [NSString stringWithFormat:@"/0x%@%@", uint256_hex(index), hardened ? @"'" : @""]; - } - }]; - return rString; - } else { - return [NSString stringWithFormat:@"/0x%@%@", uint256_hex(index), hardened ? @"'" : @""]; - } -} - (NSString *)stringRepresentation { if (_stringRepresentation) return _stringRepresentation; NSMutableString *mutableString = [NSMutableString stringWithFormat:@"m"]; if (self.length) { for (NSInteger i = 0; i < self.length; i++) { - [mutableString appendString:[DSDerivationPath stringRepresentationOfIndex:[self indexAtPosition:i] hardened:[self isHardenedAtPosition:i] inContext:self.managedObjectContext]]; + [mutableString appendString:[DSDerivationPathFactory stringRepresentationOfIndex:[self indexAtPosition:i] hardened:[self isHardenedAtPosition:i] inContext:self.managedObjectContext]]; } } else if ([self.depth integerValue]) { for (NSInteger i = 0; i < [self.depth integerValue] - 1; i++) { @@ -431,22 +418,22 @@ - (NSString *)stringRepresentation { } UInt256 terminalIndex = [self terminalIndex]; BOOL terminalHardened = [self terminalHardened]; - [mutableString appendString:[DSDerivationPath stringRepresentationOfIndex:terminalIndex hardened:terminalHardened inContext:self.managedObjectContext]]; + [mutableString appendString:[DSDerivationPathFactory stringRepresentationOfIndex:terminalIndex hardened:terminalHardened inContext:self.managedObjectContext]]; } else { if ([self isKindOfClass:[DSIncomingFundsDerivationPath class]]) { mutableString = [NSMutableString stringWithFormat:@"inc"]; DSIncomingFundsDerivationPath *incomingFundsDerivationPath = (DSIncomingFundsDerivationPath *)self; [self.managedObjectContext performBlockAndWait:^{ - DSDashpayUserEntity *sourceDashpayUserEntity = [DSDashpayUserEntity anyObjectInContext:self.managedObjectContext matching:@"associatedBlockchainIdentity.uniqueID == %@", uint256_data(incomingFundsDerivationPath.contactSourceBlockchainIdentityUniqueId)]; + DSDashpayUserEntity *sourceDashpayUserEntity = [DSDashpayUserEntity anyObjectInContext:self.managedObjectContext matching:@"associatedBlockchainIdentity.uniqueID == %@", uint256_data(incomingFundsDerivationPath.contactSourceIdentityUniqueId)]; if (sourceDashpayUserEntity) { DSBlockchainIdentityUsernameEntity *usernameEntity = [sourceDashpayUserEntity.associatedBlockchainIdentity.usernames anyObject]; [mutableString appendFormat:@"/%@", usernameEntity.stringValue]; } else { - [mutableString appendFormat:@"/0x%@", uint256_hex(incomingFundsDerivationPath.contactSourceBlockchainIdentityUniqueId)]; + [mutableString appendFormat:@"/0x%@", uint256_hex(incomingFundsDerivationPath.contactSourceIdentityUniqueId)]; } }]; - DSBlockchainIdentity *blockchainIdentity = [self.wallet blockchainIdentityForUniqueId:incomingFundsDerivationPath.contactDestinationBlockchainIdentityUniqueId]; - [mutableString appendFormat:@"/%@", blockchainIdentity.currentDashpayUsername]; + DSIdentity *identity = [self.wallet identityForUniqueId:incomingFundsDerivationPath.contactDestinationIdentityUniqueId]; + [mutableString appendFormat:@"/%@", identity.currentDashpayUsername]; } } _stringRepresentation = [mutableString copy]; @@ -476,7 +463,7 @@ - (NSString *)referenceName { case DSDerivationPathReference_ProviderVotingKeys: return @"Provider Voting Keys"; break; - case DSDerivationPathReference_BlockchainIdentities: + case DSDerivationPathReference_Identities: return @"Blockchain Identities"; break; case DSDerivationPathReference_ContactBasedFunds: @@ -488,13 +475,13 @@ - (NSString *)referenceName { case DSDerivationPathReference_ContactBasedFundsRoot: return @"Contact Funds Root"; break; - case DSDerivationPathReference_BlockchainIdentityCreditRegistrationFunding: + case DSDerivationPathReference_IdentityCreditRegistrationFunding: return @"BI Credit Registration Funding"; break; - case DSDerivationPathReference_BlockchainIdentityCreditTopupFunding: + case DSDerivationPathReference_IdentityCreditTopupFunding: return @"BI Credit Topup Funding"; break; - case DSDerivationPathReference_BlockchainIdentityCreditInvitationFunding: + case DSDerivationPathReference_IdentityCreditInvitationFunding: return @"BI Credit Invitation Funding"; break; default: @@ -512,8 +499,10 @@ - (NSString *)debugDescription { //Derivation paths can be stored based on the wallet and derivation or based solely on the public key - (NSString *)createIdentifierForDerivationPath { - // TODO: rust migration u64 - return [NSData dataWithUInt256:[[self extendedPublicKeyData] SHA256]].shortHexString; + Result_ok_u8_arr_32_err_dash_spv_crypto_keys_KeyError *result = dash_spv_crypto_keys_key_OpaqueKey_create_identifier(self.extendedPublicKey->ok); + NSData *identifier = NSDataFromPtr(result->ok); + Result_ok_u8_arr_32_err_dash_spv_crypto_keys_KeyError_destroy(result); + return identifier.shortHexString; } - (NSString *)standaloneExtendedPublicKeyUniqueID { @@ -527,26 +516,9 @@ - (NSString *)standaloneExtendedPublicKeyUniqueID { return _standaloneExtendedPublicKeyUniqueID; } -+ (NSString *)standaloneExtendedPublicKeyLocationStringForUniqueID:(NSString *)uniqueID { - return [NSString stringWithFormat:@"%@_%@", DERIVATION_PATH_EXTENDED_PUBLIC_KEY_STANDALONE_BASED_LOCATION, uniqueID]; -} - - (NSString *)standaloneExtendedPublicKeyLocationString { if (!self.standaloneExtendedPublicKeyUniqueID) return nil; - return [DSDerivationPath standaloneExtendedPublicKeyLocationStringForUniqueID:self.standaloneExtendedPublicKeyUniqueID]; -} - -+ (NSString *)standaloneInfoDictionaryLocationStringForUniqueID:(NSString *)uniqueID { - return [NSString stringWithFormat:@"%@_%@", DERIVATION_PATH_STANDALONE_INFO_DICTIONARY_LOCATION, uniqueID]; -} - -- (NSString *)standaloneInfoDictionaryLocationString { - if (!self.standaloneExtendedPublicKeyUniqueID) return nil; - return [DSDerivationPath standaloneInfoDictionaryLocationStringForUniqueID:self.standaloneExtendedPublicKeyUniqueID]; -} - -+ (NSString *)walletBasedExtendedPublicKeyLocationStringForUniqueID:(NSString *)uniqueID { - return [NSString stringWithFormat:@"%@_%@", DERIVATION_PATH_EXTENDED_PUBLIC_KEY_WALLET_BASED_LOCATION, uniqueID]; + return [DSDerivationPathFactory standaloneExtendedPublicKeyLocationStringForUniqueID:self.standaloneExtendedPublicKeyUniqueID]; } - (NSString *)walletBasedExtendedPublicKeyLocationStringForWalletUniqueID:(NSString *)uniqueID { @@ -554,9 +526,12 @@ - (NSString *)walletBasedExtendedPublicKeyLocationStringForWalletUniqueID:(NSStr for (NSInteger i = 0; i < self.length; i++) { [mutableString appendFormat:@"_%lu", (unsigned long)([self isHardenedAtPosition:i] ? [self indexAtPosition:i].u64[0] | BIP32_HARD : [self indexAtPosition:i].u64[0])]; } + char *key_storage_prefix = dash_spv_crypto_keys_key_KeyKind_key_storage_prefix(self.signingAlgorithm); + NSString *keyStoragePrefix = [NSString stringWithCString:key_storage_prefix encoding:NSUTF8StringEncoding]; + str_destroy(key_storage_prefix); return [NSString stringWithFormat:@"%@%@%@", - [DSDerivationPath walletBasedExtendedPublicKeyLocationStringForUniqueID:uniqueID], - [DSKeyManager keyStoragePrefix:self.signingAlgorithm], + [DSDerivationPathFactory walletBasedExtendedPublicKeyLocationStringForUniqueID:uniqueID], + keyStoragePrefix, mutableString]; } @@ -566,9 +541,6 @@ - (NSString *)walletBasedExtendedPublicKeyLocationString { return _walletBasedExtendedPublicKeyLocationString; } -+ (NSString *)walletBasedExtendedPrivateKeyLocationStringForUniqueID:(NSString *)uniqueID { - return [NSString stringWithFormat:@"%@_%@", DERIVATION_PATH_EXTENDED_SECRET_KEY_WALLET_BASED_LOCATION, uniqueID]; -} - (NSString *)walletBasedExtendedPrivateKeyLocationStringForWalletUniqueID:(NSString *)uniqueID { NSMutableString *mutableString = [NSMutableString string]; @@ -576,9 +548,13 @@ - (NSString *)walletBasedExtendedPrivateKeyLocationStringForWalletUniqueID:(NSSt [mutableString appendFormat:@"_%lu", (unsigned long)([self isHardenedAtPosition:i] ? [self indexAtPosition:i].u64[0] | BIP32_HARD : [self indexAtPosition:i].u64[0])]; } // TODO: ED25519 has own prefix + char *key_storage_prefix = dash_spv_crypto_keys_key_KeyKind_key_storage_prefix(self.signingAlgorithm); + NSString *keyStoragePrefix = [NSString stringWithCString:key_storage_prefix encoding:NSUTF8StringEncoding]; + str_destroy(key_storage_prefix); + return [NSString stringWithFormat:@"%@%@%@", - [DSDerivationPath walletBasedExtendedPrivateKeyLocationStringForUniqueID:uniqueID], - [DSKeyManager keyStoragePrefix:self.signingAlgorithm], + [DSDerivationPathFactory walletBasedExtendedPrivateKeyLocationStringForUniqueID:uniqueID], + keyStoragePrefix, mutableString]; } @@ -590,238 +566,114 @@ - (NSString *)walletBasedExtendedPrivateKeyLocationString { // MARK: - Key Generation -- (OpaqueKey *)generateExtendedPublicKeyFromSeed:(NSData *)seed storeUnderWalletUniqueId:(NSString *)walletUniqueId { - return [self generateExtendedPublicKeyFromSeed:seed storeUnderWalletUniqueId:walletUniqueId storePrivateKey:NO]; +- (DMaybeOpaqueKey *_Nullable)generateExtendedPublicKeyFromSeed:(NSData *)seed + storeUnderWalletUniqueId:(NSString *)walletUniqueId { + return [self generateExtendedPublicKeyFromSeed:seed + storeUnderWalletUniqueId:walletUniqueId + storePrivateKey:NO]; } -- (OpaqueKey *)generateExtendedPublicKeyFromSeed:(NSData *)seed storeUnderWalletUniqueId:(NSString *)walletUniqueId storePrivateKey:(BOOL)storePrivateKey { +- (DMaybeOpaqueKey *_Nullable)generateExtendedPublicKeyFromSeed:(NSData *)seed + storeUnderWalletUniqueId:(NSString *)walletUniqueId + storePrivateKey:(BOOL)storePrivateKey { if (!seed) return nil; if (![self length] && self.reference != DSDerivationPathReference_Root) return nil; //there needs to be at least 1 length @autoreleasepool { if (_extendedPublicKey) - processor_destroy_opaque_key(_extendedPublicKey); - _extendedPublicKey = generate_extended_public_key_from_seed(seed.bytes, seed.length, (int16_t) self.signingAlgorithm, (const uint8_t *) self->_indexes, self->_hardenedIndexes, self->_length); + DMaybeOpaqueKeyDtor(_extendedPublicKey); + + SLICE *slice = slice_ctor(seed); + dash_spv_crypto_keys_key_IndexPathU256 *path = [DSDerivationPath ffi_to:self]; + DMaybeOpaqueKey *result = dash_spv_crypto_keys_key_KeyKind_public_key_from_extended_public_key_data_at_index_path_256(self.signingAlgorithm, slice, path); + _extendedPublicKey = result; NSAssert(_extendedPublicKey, @"extendedPublicKey should be set"); if (_extendedPublicKey == NULL) { return nil; } if (walletUniqueId) { - NSData *publicKeyData = [DSKeyManager extendedPublicKeyData:_extendedPublicKey]; + NSData *publicKeyData = [DSKeyManager extendedPublicKeyData:_extendedPublicKey->ok]; setKeychainData(publicKeyData, [self walletBasedExtendedPublicKeyLocationStringForWalletUniqueID:walletUniqueId], NO); if (storePrivateKey) { - NSData *privateKeyData = [DSKeyManager extendedPrivateKeyData:_extendedPublicKey]; + NSData *privateKeyData = [DSKeyManager extendedPrivateKeyData:_extendedPublicKey->ok]; setKeychainData(privateKeyData, [self walletBasedExtendedPrivateKeyLocationStringForWalletUniqueID:walletUniqueId], YES); } } - forget_private_key(_extendedPublicKey); + dash_spv_crypto_keys_key_OpaqueKey_forget_private_key(_extendedPublicKey->ok); } return _extendedPublicKey; } -- (OpaqueKey *)generateExtendedPublicKeyFromParentDerivationPath:(DSDerivationPath *)parentDerivationPath storeUnderWalletUniqueId:(NSString *)walletUniqueId { - NSAssert(parentDerivationPath.signingAlgorithm == self.signingAlgorithm, @"The signing algorithms must be the same"); +- (DMaybeOpaqueKey *)generateExtendedPublicKeyFromParentDerivationPath:(DSDerivationPath *)parentDerivationPath + storeUnderWalletUniqueId:(NSString *)walletUniqueId { + + NSAssert(dash_spv_crypto_keys_key_KeyKind_index(parentDerivationPath.signingAlgorithm) == dash_spv_crypto_keys_key_KeyKind_index(self.signingAlgorithm), @"The signing algorithms must be the same"); NSParameterAssert(parentDerivationPath); NSAssert(self.length > parentDerivationPath.length, @"length must be inferior to the parent derivation path length"); NSAssert(parentDerivationPath.extendedPublicKey, @"the parent derivation path must have an extended public key"); if (![self length]) return nil; //there needs to be at least 1 length if (self.length <= parentDerivationPath.length) return nil; // we need to be longer if (!parentDerivationPath.extendedPublicKey) return nil; //parent derivation path - if (parentDerivationPath.signingAlgorithm != self.signingAlgorithm) return nil; + if (dash_spv_crypto_keys_key_KeyKind_index(parentDerivationPath.signingAlgorithm) != dash_spv_crypto_keys_key_KeyKind_index(self.signingAlgorithm)) return nil; for (NSInteger i = 0; i < [parentDerivationPath length] - 1; i++) { NSAssert(uint256_eq([parentDerivationPath indexAtPosition:i], [self indexAtPosition:i]), @"This derivation path must start with elements of the parent derivation path"); if (!uint256_eq([parentDerivationPath indexAtPosition:i], [self indexAtPosition:i])) return nil; } if (_extendedPublicKey) - processor_destroy_opaque_key(_extendedPublicKey); - _extendedPublicKey = [DSKeyManager keyPublicDeriveTo256Bit:parentDerivationPath childIndexes:self->_indexes childHardened:self->_hardenedIndexes length:self.length]; + DMaybeOpaqueKeyDtor(_extendedPublicKey); + + DIndexPathU256 *path = [DSDerivationPath ffi_to:self]; + _extendedPublicKey = dash_spv_crypto_keys_key_OpaqueKey_public_derive_to_256_path_with_offset(parentDerivationPath.extendedPublicKey->ok, path, parentDerivationPath.length); NSAssert(_extendedPublicKey, @"extendedPublicKey should be set"); if (walletUniqueId) { - NSData *publicKeyData = [DSKeyManager extendedPublicKeyData:_extendedPublicKey]; + NSData *publicKeyData = [DSKeyManager extendedPublicKeyData:_extendedPublicKey->ok]; setKeychainData(publicKeyData, [self walletBasedExtendedPublicKeyLocationStringForWalletUniqueID:walletUniqueId], NO); } return _extendedPublicKey; } -- (OpaqueKey *)privateKeyForKnownAddress:(NSString *)address fromSeed:(NSData *)seed { - NSIndexPath *indexPathForAddress = [self indexPathForKnownAddress:address]; - return [self privateKeyAtIndexPath:indexPathForAddress fromSeed:seed]; +- (DMaybeOpaqueKey *_Nullable)privateKeyAtIndexPath:(NSIndexPath *)indexPath + fromSeed:(NSData *)seed { + NSParameterAssert(indexPath); + NSParameterAssert(seed); + if (!seed || !indexPath) return nil; + if (!self->_length) return nil; //there needs to be at least 1 length + SLICE *seed_slice = slice_ctor(seed); + Vec_u32 *index_path = [NSIndexPath ffi_to:indexPath]; + DIndexPathU256 *path = [DSDerivationPath ffi_to:self]; + DMaybeOpaqueKey *result = dash_spv_crypto_keys_key_KeyKind_private_key_at_index_path_wrapped(self.signingAlgorithm, seed_slice, index_path, path); + return result; } -- (OpaqueKey *_Nullable)privateKeyAtIndexPath:(NSIndexPath *)indexPath fromSeed:(NSData *)seed { - return [DSKeyManager privateKeyAtIndexPath:self.signingAlgorithm indexes:self->_indexes hardened:self->_hardenedIndexes length:self.length indexPath:indexPath fromSeed:seed]; -} +- (DMaybeOpaqueKey *_Nullable)publicKeyAtIndexPath:(NSIndexPath *)indexPath { + NSData *publicKeyData = [self publicKeyDataAtIndexPath:indexPath]; + NSLog(@"publicKeyDataAtIndexPath: %@: %@", indexPath, publicKeyData.hexString); -- (OpaqueKey *)publicKeyAtIndexPath:(NSIndexPath *)indexPath { - return [DSKeyManager publicKeyAtIndexPath:self.extendedPublicKey indexPath:indexPath]; + return dash_spv_crypto_keys_key_KeyKind_key_with_public_key_data(self.signingAlgorithm, slice_ctor(publicKeyData)); } - (NSData *)publicKeyDataAtIndexPath:(NSIndexPath *)indexPath { - return [DSKeyManager publicKeyDataAtIndexPath:self.extendedPublicKey indexPath:indexPath]; -} - -- (NSArray *)privateKeysAtIndexPaths:(NSArray *)indexPaths fromSeed:(NSData *)seed { - if (!seed || !indexPaths) return nil; - if (indexPaths.count == 0) return @[]; - - NSUInteger count = indexPaths.count; - IndexPathData *data = malloc(sizeof(IndexPathData) * count); - for (NSUInteger i = 0; i < count; i++) { - NSIndexPath *indexPath = indexPaths[i]; - NSUInteger length = indexPath.length; - NSUInteger *indexes = malloc(sizeof(NSUInteger) * length); - [indexPath getIndexes:indexes]; - data[i].indexes = indexes; - data[i].len = length; - } - OpaqueKeys *keys = key_private_keys_at_index_paths(seed.bytes, seed.length, (int16_t) self.signingAlgorithm, data, count, (const uint8_t *) self->_indexes, self->_hardenedIndexes, self->_length); - for (NSUInteger i = 0; i < count; i++) { - free((void *)data[i].indexes); - } - free(data); - NSMutableArray *privateKeys = [NSMutableArray arrayWithCapacity:keys->len]; - for (NSUInteger i = 0; i < keys->len; i++) { - [privateKeys addObject:[NSValue valueWithPointer:keys->keys[i]]]; - } - // TODO: destroy when keys don't need anymore - // processor_destroy_opaque_keys(keys); - -// NSMutableArray *privateKeys = [NSMutableArray arrayWithCapacity:indexPaths.count]; -// DSKey *topKey = [DSKey keyWithSeedData:seed forKeyType:self.signingAlgorithm]; -// DSKey *derivationPathExtendedKey = [topKey privateDeriveTo256BitDerivationPath:self]; -// -//#if DEBUG -// if (_extendedPublicKey) { -// NSData *publicKey = _extendedPublicKey.extendedPublicKeyData; -// NSAssert([publicKey isEqualToData:derivationPathExtendedKey.extendedPublicKeyData], @"The derivation doesn't match the public key"); -// } -//#endif - - return privateKeys; -} - - -- (NSArray *)serializedPrivateKeysAtIndexPaths:(NSArray *)indexPaths fromSeed:(NSData *)seed { - if (!seed || !indexPaths) return nil; - if (indexPaths.count == 0) return @[]; - - NSUInteger count = indexPaths.count; - IndexPathData *data = malloc(sizeof(IndexPathData) * count); - for (NSUInteger i = 0; i < count; i++) { - NSIndexPath *indexPath = indexPaths[i]; - NSUInteger length = indexPath.length; - NSUInteger *indexes = malloc(sizeof(NSUInteger) * length); - [indexPath getIndexes:indexes]; - data[i].indexes = indexes; - data[i].len = length; - } - OpaqueSerializedKeys *keys = serialized_key_private_keys_at_index_paths(seed.bytes, seed.length, (int16_t) self.signingAlgorithm, data, count, (const uint8_t *) self->_indexes, self->_hardenedIndexes, self.length, self.chain.chainType); - for (NSUInteger i = 0; i < count; i++) { - free((void *)data[i].indexes); - } - free(data); - NSMutableArray *privateKeys = [NSMutableArray arrayWithCapacity:keys->len]; - for (NSUInteger i = 0; i < keys->len; i++) { - [privateKeys addObject:[NSString stringWithUTF8String:keys->keys[i]]]; - } - processor_destroy_serialized_opaque_keys(keys); - return privateKeys; -} - - -// MARK: - Deprecated - -//this is for upgrade purposes only -// TODO: check if this needed -- (OpaqueKey *)deprecatedIncorrectExtendedPublicKeyFromSeed:(NSData *)seed { - if (!seed) return nil; - if (![self length]) return nil; //there needs to be at least 1 length - return [DSKeyManager keyDeprecatedExtendedPublicKeyFromSeed:seed indexes:self->_indexes hardened:self->_hardenedIndexes length:self.length]; -} - -// MARK: - Storage - -- (BOOL)storeExtendedPublicKeyUnderWalletUniqueId:(NSString *)walletUniqueId { - if (!_extendedPublicKey) return FALSE; - NSParameterAssert(walletUniqueId); - NSData *data = [DSKeyManager extendedPublicKeyData:_extendedPublicKey]; - setKeychainData(data, [self walletBasedExtendedPublicKeyLocationStringForWalletUniqueID:walletUniqueId], NO); - return TRUE; + return [DSKeyManager publicKeyDataAtIndexPath:self.extendedPublicKey->ok indexPath:indexPath]; } -// MARK: - Serializations +@end -- (NSString *_Nullable)serializedExtendedPrivateKeyFromSeedAtIndexPath:(NSData *)seed indexPath:(NSIndexPath *)indexPath { - OpaqueKey *key = [self privateKeyAtIndexPath:indexPath fromSeed:seed]; - NSString *pk = [DSKeyManager NSStringFrom:key_serialized_private_key_for_chain(key, self.chain.chainType)]; - processor_destroy_opaque_key(key); - return pk; -} -- (NSString *)serializedExtendedPrivateKeyFromSeed:(NSData *)seed { - @autoreleasepool { - if (!seed) return nil; - return [DSKeyManager NSStringFrom:key_serialized_extended_private_key_from_seed(seed.bytes, seed.length, (const uint8_t *) self->_indexes, self->_hardenedIndexes, self->_length, self.chain.chainType)]; - } -} -+ (NSData *)deserializedExtendedPrivateKey:(NSString *)extendedPrivateKeyString onChain:(DSChain *)chain { - @autoreleasepool { - uint8_t depth; - uint32_t fingerprint; - UInt256 child; - BOOL hardened; - UInt256 chainHash; - NSData *privkey = nil; - NSMutableData *masterPrivateKey = [NSMutableData secureData]; - BOOL valid = deserialize(extendedPrivateKeyString, &depth, &fingerprint, &hardened, &child, &chainHash, &privkey, [chain isMainnet]); - if (!valid) return nil; - [masterPrivateKey appendUInt32:fingerprint]; - [masterPrivateKey appendBytes:&chainHash length:32]; - [masterPrivateKey appendData:privkey]; - return [masterPrivateKey copy]; - } -} +@implementation DSDerivationPath (dash_spv_crypto_keys_key_IndexPathU256) -- (NSString *)serializedExtendedPublicKey { - //todo make sure this works with BLS keys - NSData *extPubKeyData = self.extendedPublicKeyData; - if (extPubKeyData.length < 36) return nil; - uint32_t fingerprint = [extPubKeyData UInt32AtOffset:0]; - UInt256 chain = [extPubKeyData UInt256AtOffset:4]; - DSECPoint pubKey = [extPubKeyData ECPointAtOffset:36]; - UInt256 child = UINT256_ZERO; - BOOL isHardened = NO; - if (self.length) { - child = [self indexAtPosition:[self length] - 1]; - isHardened = [self isHardenedAtPosition:[self length] - 1]; ++ (dash_spv_crypto_keys_key_IndexPathU256 *)ffi_to:(DSDerivationPath *)obj { + uintptr_t length = obj.length; + u256 **indexes = malloc(length * sizeof(u256 *)); + bool *hardened = malloc(length * sizeof(bool)); + for (NSUInteger i = 0; i < length; i++) { + indexes[i] = u256_ctor_u(obj->_indexes[i]); + hardened[i] = obj->_hardenedIndexes[i]; } - - return serialize([self.depth unsignedCharValue], fingerprint, isHardened, child, chain, [NSData dataWithBytes:&pubKey length:sizeof(pubKey)], [self.chain isMainnet]); + Vec_u8_32 *i = Vec_u8_32_ctor(length, indexes); + Vec_bool *h = Vec_bool_ctor(length, hardened); + return dash_spv_crypto_keys_key_IndexPathU256_ctor(i, h); } - -+ (NSData *)deserializedExtendedPublicKey:(NSString *)extendedPublicKeyString onChain:(DSChain *)chain rDepth:(uint8_t *)depth rTerminalHardened:(BOOL *)terminalHardened rTerminalIndex:(UInt256 *)terminalIndex { - uint32_t fingerprint; - UInt256 chainHash; - NSData *pubkey = nil; - NSMutableData *masterPublicKey = [NSMutableData secureData]; - BOOL valid = deserialize(extendedPublicKeyString, depth, &fingerprint, terminalHardened, terminalIndex, &chainHash, &pubkey, [chain isMainnet]); - if (!valid) return nil; - [masterPublicKey appendUInt32:fingerprint]; - [masterPublicKey appendBytes:&chainHash length:32]; - [masterPublicKey appendData:pubkey]; - return [masterPublicKey copy]; ++ (void)ffi_destroy:(dash_spv_crypto_keys_key_IndexPathU256 *)ffi_ref { + dash_spv_crypto_keys_key_IndexPathU256_destroy(ffi_ref); } - -+ (NSData *)deserializedExtendedPublicKey:(NSString *)extendedPublicKeyString onChain:(DSChain *)chain { - __unused uint8_t depth = 0; - __unused BOOL terminalHardened = 0; - __unused UInt256 terminalIndex = UINT256_ZERO; - NSData *extendedPublicKey = [self deserializedExtendedPublicKey:extendedPublicKeyString onChain:chain rDepth:&depth rTerminalHardened:&terminalHardened rTerminalIndex:&terminalIndex]; - return extendedPublicKey; -} - -- (NSData *)deserializedExtendedPublicKey:(NSString *)extendedPublicKeyString { - return [DSDerivationPath deserializedExtendedPublicKey:extendedPublicKeyString onChain:self.chain]; -} - @end diff --git a/DashSync/shared/Models/Derivation Paths/DSDerivationPathFactory.h b/DashSync/shared/Models/Derivation Paths/DSDerivationPathFactory.h index 0a06858fe..fa5465d0b 100644 --- a/DashSync/shared/Models/Derivation Paths/DSDerivationPathFactory.h +++ b/DashSync/shared/Models/Derivation Paths/DSDerivationPathFactory.h @@ -6,10 +6,12 @@ // #import +#import "DSKeyManager.h" + NS_ASSUME_NONNULL_BEGIN -@class DSAuthenticationKeysDerivationPath, DSWallet, DSMasternodeHoldingsDerivationPath, DSDerivationPath, DSCreditFundingDerivationPath; +@class DSAuthenticationKeysDerivationPath, DSWallet, DSMasternodeHoldingsDerivationPath, DSDerivationPath, DSAssetLockDerivationPath; @interface DSDerivationPathFactory : NSObject @@ -21,11 +23,11 @@ NS_ASSUME_NONNULL_BEGIN - (DSAuthenticationKeysDerivationPath *)platformNodeKeysDerivationPathForWallet:(DSWallet *)wallet; - (DSMasternodeHoldingsDerivationPath *)providerFundsDerivationPathForWallet:(DSWallet *)wallet; -- (DSCreditFundingDerivationPath *)blockchainIdentityRegistrationFundingDerivationPathForWallet:(DSWallet *)wallet; -- (DSCreditFundingDerivationPath *)blockchainIdentityTopupFundingDerivationPathForWallet:(DSWallet *)wallet; -- (DSCreditFundingDerivationPath *)blockchainIdentityInvitationFundingDerivationPathForWallet:(DSWallet *)wallet; -- (DSAuthenticationKeysDerivationPath *)blockchainIdentityBLSKeysDerivationPathForWallet:(DSWallet *)wallet; -- (DSAuthenticationKeysDerivationPath *)blockchainIdentityECDSAKeysDerivationPathForWallet:(DSWallet *)wallet; +- (DSAssetLockDerivationPath *)identityRegistrationFundingDerivationPathForWallet:(DSWallet *)wallet; +- (DSAssetLockDerivationPath *)identityTopupFundingDerivationPathForWallet:(DSWallet *)wallet; +- (DSAssetLockDerivationPath *)identityInvitationFundingDerivationPathForWallet:(DSWallet *)wallet; +- (DSAuthenticationKeysDerivationPath *)identityBLSKeysDerivationPathForWallet:(DSWallet *)wallet; +- (DSAuthenticationKeysDerivationPath *)identityECDSAKeysDerivationPathForWallet:(DSWallet *)wallet; - (NSArray *)loadedSpecializedDerivationPathsForWallet:(DSWallet *)wallet; - (NSArray *)unloadedSpecializedDerivationPathsForWallet:(DSWallet *)wallet; @@ -33,6 +35,36 @@ NS_ASSUME_NONNULL_BEGIN - (NSArray *)fundDerivationPathsNeedingExtendedPublicKeyForWallet:(DSWallet *)wallet; + + + + ++ (DMaybeOpaqueKeys *)privateKeysAtIndexPaths:(NSArray *)indexPaths + fromSeed:(NSData *)seed + derivationPath:(DSDerivationPath *)derivationPath; ++ (NSArray *)serializedPrivateKeysAtIndexPaths:(NSArray *)indexPaths + fromSeed:(NSData *)seed + derivationPath:(DSDerivationPath *)derivationPath; ++ (NSString *_Nullable)serializedExtendedPublicKey:(DSDerivationPath *)derivationPath; + ++ (NSString *)serializedExtendedPrivateKeyFromSeed:(NSData *)seed + derivationPath:(DSDerivationPath *)derivationPath; ++ (NSData *)deserializedExtendedPublicKey:(NSString *)extendedPublicKeyString onChain:(DSChain *)chain; ++ (NSData *)deserializedExtendedPublicKey:(NSString *)extendedPublicKeyString + onChain:(DSChain *)chain + rDepth:(uint8_t *)depth + rTerminalHardened:(BOOL *)terminalHardened + rTerminalIndex:(UInt256 *)terminalIndex; ++ (NSData *)deserializedExtendedPublicKey:(DSDerivationPath *)derivationPath extendedPublicKeyString:(NSString *)extendedPublicKeyString; + ++ (NSString *)standaloneExtendedPublicKeyLocationStringForUniqueID:(NSString *)uniqueID; ++ (NSString *)standaloneInfoDictionaryLocationStringForUniqueID:(NSString *)uniqueID; ++ (NSString *)walletBasedExtendedPublicKeyLocationStringForUniqueID:(NSString *)uniqueID; ++ (NSString *)walletBasedExtendedPrivateKeyLocationStringForUniqueID:(NSString *)uniqueID; + + ++ (NSString *)stringRepresentationOfIndex:(UInt256)index hardened:(BOOL)hardened inContext:(NSManagedObjectContext *)context; + @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Derivation Paths/DSDerivationPathFactory.m b/DashSync/shared/Models/Derivation Paths/DSDerivationPathFactory.m index caebe861c..63ec7cdd9 100644 --- a/DashSync/shared/Models/Derivation Paths/DSDerivationPathFactory.m +++ b/DashSync/shared/Models/Derivation Paths/DSDerivationPathFactory.m @@ -6,9 +6,22 @@ // #import "DSDerivationPathFactory.h" +#import "DSAccount.h" #import "DSAuthenticationKeysDerivationPath+Protected.h" -#import "DSCreditFundingDerivationPath+Protected.h" +#import "DSChain+Params.h" +#import "DSAssetLockDerivationPath+Protected.h" #import "DSMasternodeHoldingsDerivationPath+Protected.h" +#import "DSBlockchainIdentityEntity+CoreDataClass.h" +#import "DSBlockchainIdentityUsernameEntity+CoreDataClass.h" +#import "DSDashpayUserEntity+CoreDataClass.h" +#import "NSManagedObject+Sugar.h" +#import "NSMutableData+Dash.h" + +#define DERIVATION_PATH_EXTENDED_PUBLIC_KEY_WALLET_BASED_LOCATION @"DP_EPK_WBL" +#define DERIVATION_PATH_EXTENDED_PUBLIC_KEY_STANDALONE_BASED_LOCATION @"DP_EPK_SBL" +#define DERIVATION_PATH_STANDALONE_INFO_DICTIONARY_LOCATION @"DP_SIDL" +#define DERIVATION_PATH_EXTENDED_SECRET_KEY_WALLET_BASED_LOCATION @"DP_ESK_WBL" + @interface DSDerivationPathFactory () @@ -17,11 +30,11 @@ @interface DSDerivationPathFactory () @property (nonatomic, strong) NSMutableDictionary *operatorKeysDerivationPathByWallet; @property (nonatomic, strong) NSMutableDictionary *platformNodeKeysDerivationPathByWallet; @property (nonatomic, strong) NSMutableDictionary *providerFundsDerivationPathByWallet; -@property (nonatomic, strong) NSMutableDictionary *blockchainIdentityRegistrationFundingDerivationPathByWallet; -@property (nonatomic, strong) NSMutableDictionary *blockchainIdentityTopupFundingDerivationPathByWallet; -@property (nonatomic, strong) NSMutableDictionary *blockchainIdentityInvitationFundingDerivationPathByWallet; -@property (nonatomic, strong) NSMutableDictionary *blockchainIdentityBLSDerivationPathByWallet; -@property (nonatomic, strong) NSMutableDictionary *blockchainIdentityECDSADerivationPathByWallet; +@property (nonatomic, strong) NSMutableDictionary *identityRegistrationFundingDerivationPathByWallet; +@property (nonatomic, strong) NSMutableDictionary *identityTopupFundingDerivationPathByWallet; +@property (nonatomic, strong) NSMutableDictionary *identityInvitationFundingDerivationPathByWallet; +@property (nonatomic, strong) NSMutableDictionary *identityBLSDerivationPathByWallet; +@property (nonatomic, strong) NSMutableDictionary *identityECDSADerivationPathByWallet; @end @@ -134,104 +147,104 @@ - (DSMasternodeHoldingsDerivationPath *)providerFundsDerivationPathForWallet:(DS return [self.providerFundsDerivationPathByWallet objectForKey:wallet.uniqueIDString]; } -// MARK: - Blockchain Identity Funding +// MARK: - Identity Funding -- (DSCreditFundingDerivationPath *)blockchainIdentityRegistrationFundingDerivationPathForWallet:(DSWallet *)wallet { - static dispatch_once_t blockchainIdentityRegistrationFundingDerivationPathByWalletToken = 0; - dispatch_once(&blockchainIdentityRegistrationFundingDerivationPathByWalletToken, ^{ - self.blockchainIdentityRegistrationFundingDerivationPathByWallet = [NSMutableDictionary dictionary]; +- (DSAssetLockDerivationPath *)identityRegistrationFundingDerivationPathForWallet:(DSWallet *)wallet { + static dispatch_once_t identityRegistrationFundingDerivationPathByWalletToken = 0; + dispatch_once(&identityRegistrationFundingDerivationPathByWalletToken, ^{ + self.identityRegistrationFundingDerivationPathByWallet = [NSMutableDictionary dictionary]; }); @synchronized(self) { - if (![self.blockchainIdentityRegistrationFundingDerivationPathByWallet objectForKey:wallet.uniqueIDString]) { - DSCreditFundingDerivationPath *derivationPath = [DSCreditFundingDerivationPath blockchainIdentityRegistrationFundingDerivationPathForChain:wallet.chain]; + if (![self.identityRegistrationFundingDerivationPathByWallet objectForKey:wallet.uniqueIDString]) { + DSAssetLockDerivationPath *derivationPath = [DSAssetLockDerivationPath identityRegistrationFundingDerivationPathForChain:wallet.chain]; derivationPath.wallet = wallet; if (derivationPath.hasExtendedPublicKey) { [derivationPath loadAddresses]; } - [self.blockchainIdentityRegistrationFundingDerivationPathByWallet setObject:derivationPath - forKey:wallet.uniqueIDString]; + [self.identityRegistrationFundingDerivationPathByWallet setObject:derivationPath + forKey:wallet.uniqueIDString]; } } - return [self.blockchainIdentityRegistrationFundingDerivationPathByWallet objectForKey:wallet.uniqueIDString]; + return [self.identityRegistrationFundingDerivationPathByWallet objectForKey:wallet.uniqueIDString]; } -- (DSCreditFundingDerivationPath *)blockchainIdentityTopupFundingDerivationPathForWallet:(DSWallet *)wallet { - static dispatch_once_t blockchainIdentityTopupFundingDerivationPathByWalletToken = 0; - dispatch_once(&blockchainIdentityTopupFundingDerivationPathByWalletToken, ^{ - self.blockchainIdentityTopupFundingDerivationPathByWallet = [NSMutableDictionary dictionary]; +- (DSAssetLockDerivationPath *)identityTopupFundingDerivationPathForWallet:(DSWallet *)wallet { + static dispatch_once_t identityTopupFundingDerivationPathByWalletToken = 0; + dispatch_once(&identityTopupFundingDerivationPathByWalletToken, ^{ + self.identityTopupFundingDerivationPathByWallet = [NSMutableDictionary dictionary]; }); @synchronized(self) { - if (![self.blockchainIdentityTopupFundingDerivationPathByWallet objectForKey:wallet.uniqueIDString]) { - DSCreditFundingDerivationPath *derivationPath = [DSCreditFundingDerivationPath blockchainIdentityTopupFundingDerivationPathForChain:wallet.chain]; + if (![self.identityTopupFundingDerivationPathByWallet objectForKey:wallet.uniqueIDString]) { + DSAssetLockDerivationPath *derivationPath = [DSAssetLockDerivationPath identityTopupFundingDerivationPathForChain:wallet.chain]; derivationPath.wallet = wallet; if (derivationPath.hasExtendedPublicKey) { [derivationPath loadAddresses]; } - [self.blockchainIdentityTopupFundingDerivationPathByWallet setObject:derivationPath - forKey:wallet.uniqueIDString]; + [self.identityTopupFundingDerivationPathByWallet setObject:derivationPath + forKey:wallet.uniqueIDString]; } } - return [self.blockchainIdentityTopupFundingDerivationPathByWallet objectForKey:wallet.uniqueIDString]; + return [self.identityTopupFundingDerivationPathByWallet objectForKey:wallet.uniqueIDString]; } -- (DSCreditFundingDerivationPath *)blockchainIdentityInvitationFundingDerivationPathForWallet:(DSWallet *)wallet { - static dispatch_once_t blockchainIdentityInvitationFundingDerivationPathByWalletToken = 0; - dispatch_once(&blockchainIdentityInvitationFundingDerivationPathByWalletToken, ^{ - self.blockchainIdentityInvitationFundingDerivationPathByWallet = [NSMutableDictionary dictionary]; +- (DSAssetLockDerivationPath *)identityInvitationFundingDerivationPathForWallet:(DSWallet *)wallet { + static dispatch_once_t identityInvitationFundingDerivationPathByWalletToken = 0; + dispatch_once(&identityInvitationFundingDerivationPathByWalletToken, ^{ + self.identityInvitationFundingDerivationPathByWallet = [NSMutableDictionary dictionary]; }); @synchronized(self) { - if (![self.blockchainIdentityInvitationFundingDerivationPathByWallet objectForKey:wallet.uniqueIDString]) { - DSCreditFundingDerivationPath *derivationPath = [DSCreditFundingDerivationPath blockchainIdentityInvitationFundingDerivationPathForChain:wallet.chain]; + if (![self.identityInvitationFundingDerivationPathByWallet objectForKey:wallet.uniqueIDString]) { + DSAssetLockDerivationPath *derivationPath = [DSAssetLockDerivationPath identityInvitationFundingDerivationPathForChain:wallet.chain]; derivationPath.wallet = wallet; if (derivationPath.hasExtendedPublicKey) { [derivationPath loadAddresses]; } - [self.blockchainIdentityInvitationFundingDerivationPathByWallet setObject:derivationPath + [self.identityInvitationFundingDerivationPathByWallet setObject:derivationPath forKey:wallet.uniqueIDString]; } } - return [self.blockchainIdentityInvitationFundingDerivationPathByWallet objectForKey:wallet.uniqueIDString]; + return [self.identityInvitationFundingDerivationPathByWallet objectForKey:wallet.uniqueIDString]; } -// MARK: - Blockchain Identity Authentication +// MARK: - Identity Authentication -- (DSAuthenticationKeysDerivationPath *)blockchainIdentityBLSKeysDerivationPathForWallet:(DSWallet *)wallet { - static dispatch_once_t blockchainIdentityBLSDerivationPathByWalletToken = 0; - dispatch_once(&blockchainIdentityBLSDerivationPathByWalletToken, ^{ - self.blockchainIdentityBLSDerivationPathByWallet = [NSMutableDictionary dictionary]; +- (DSAuthenticationKeysDerivationPath *)identityBLSKeysDerivationPathForWallet:(DSWallet *)wallet { + static dispatch_once_t identityBLSDerivationPathByWalletToken = 0; + dispatch_once(&identityBLSDerivationPathByWalletToken, ^{ + self.identityBLSDerivationPathByWallet = [NSMutableDictionary dictionary]; }); @synchronized(self) { - if (![self.blockchainIdentityBLSDerivationPathByWallet objectForKey:wallet.uniqueIDString]) { - DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath blockchainIdentityBLSKeysDerivationPathForChain:wallet.chain]; + if (![self.identityBLSDerivationPathByWallet objectForKey:wallet.uniqueIDString]) { + DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath identityBLSKeysDerivationPathForChain:wallet.chain]; derivationPath.wallet = wallet; if (derivationPath.hasExtendedPrivateKey || (derivationPath.hasExtendedPublicKey && !derivationPath.usesHardenedKeys)) { [derivationPath loadAddresses]; } - [self.blockchainIdentityBLSDerivationPathByWallet setObject:derivationPath - forKey:wallet.uniqueIDString]; + [self.identityBLSDerivationPathByWallet setObject:derivationPath + forKey:wallet.uniqueIDString]; } } - return [self.blockchainIdentityBLSDerivationPathByWallet objectForKey:wallet.uniqueIDString]; + return [self.identityBLSDerivationPathByWallet objectForKey:wallet.uniqueIDString]; } -- (DSAuthenticationKeysDerivationPath *)blockchainIdentityECDSAKeysDerivationPathForWallet:(DSWallet *)wallet { +- (DSAuthenticationKeysDerivationPath *)identityECDSAKeysDerivationPathForWallet:(DSWallet *)wallet { NSParameterAssert(wallet); - static dispatch_once_t blockchainIdentityECDSADerivationPathByWalletToken = 0; - dispatch_once(&blockchainIdentityECDSADerivationPathByWalletToken, ^{ - self.blockchainIdentityECDSADerivationPathByWallet = [NSMutableDictionary dictionary]; + static dispatch_once_t identityECDSADerivationPathByWalletToken = 0; + dispatch_once(&identityECDSADerivationPathByWalletToken, ^{ + self.identityECDSADerivationPathByWallet = [NSMutableDictionary dictionary]; }); @synchronized(self) { - if (![self.blockchainIdentityECDSADerivationPathByWallet objectForKey:wallet.uniqueIDString]) { - DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath blockchainIdentityECDSAKeysDerivationPathForChain:wallet.chain]; + if (![self.identityECDSADerivationPathByWallet objectForKey:wallet.uniqueIDString]) { + DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath identityECDSAKeysDerivationPathForChain:wallet.chain]; derivationPath.wallet = wallet; if (derivationPath.hasExtendedPrivateKey || (derivationPath.hasExtendedPublicKey && !derivationPath.usesHardenedKeys)) { [derivationPath loadAddresses]; } - [self.blockchainIdentityECDSADerivationPathByWallet setObject:derivationPath - forKey:wallet.uniqueIDString]; + [self.identityECDSADerivationPathByWallet setObject:derivationPath + forKey:wallet.uniqueIDString]; } } - return [self.blockchainIdentityECDSADerivationPathByWallet objectForKey:wallet.uniqueIDString]; + return [self.identityECDSADerivationPathByWallet objectForKey:wallet.uniqueIDString]; } - (NSArray *)loadedSpecializedDerivationPathsForWallet:(DSWallet *)wallet { @@ -242,10 +255,10 @@ - (DSAuthenticationKeysDerivationPath *)blockchainIdentityECDSAKeysDerivationPat [mArray addObject:[[DSDerivationPathFactory sharedInstance] platformNodeKeysDerivationPathForWallet:wallet]]; [mArray addObject:[[DSDerivationPathFactory sharedInstance] providerFundsDerivationPathForWallet:wallet]]; if (wallet.chain.isEvolutionEnabled) { - [mArray addObject:[[DSDerivationPathFactory sharedInstance] blockchainIdentityECDSAKeysDerivationPathForWallet:wallet]]; - [mArray addObject:[[DSDerivationPathFactory sharedInstance] blockchainIdentityBLSKeysDerivationPathForWallet:wallet]]; - [mArray addObject:[[DSDerivationPathFactory sharedInstance] blockchainIdentityRegistrationFundingDerivationPathForWallet:wallet]]; - [mArray addObject:[[DSDerivationPathFactory sharedInstance] blockchainIdentityTopupFundingDerivationPathForWallet:wallet]]; + [mArray addObject:[[DSDerivationPathFactory sharedInstance] identityECDSAKeysDerivationPathForWallet:wallet]]; + [mArray addObject:[[DSDerivationPathFactory sharedInstance] identityBLSKeysDerivationPathForWallet:wallet]]; + [mArray addObject:[[DSDerivationPathFactory sharedInstance] identityRegistrationFundingDerivationPathForWallet:wallet]]; + [mArray addObject:[[DSDerivationPathFactory sharedInstance] identityTopupFundingDerivationPathForWallet:wallet]]; } return mArray; } @@ -281,10 +294,10 @@ - (DSAuthenticationKeysDerivationPath *)blockchainIdentityECDSAKeysDerivationPat } if (wallet.chain.isEvolutionEnabled) { for (DSAccount *account in wallet.accounts) { - DSDerivationPath *masterBlockchainIdentityContactsDerivationPath = [DSDerivationPath masterBlockchainIdentityContactsDerivationPathForAccountNumber:account.accountNumber onChain:wallet.chain]; - masterBlockchainIdentityContactsDerivationPath.wallet = wallet; - if (![masterBlockchainIdentityContactsDerivationPath hasExtendedPublicKey]) { - [mArray addObject:masterBlockchainIdentityContactsDerivationPath]; + DSDerivationPath *masterIdentityContactsDerivationPath = [DSDerivationPath masterIdentityContactsDerivationPathForAccountNumber:account.accountNumber onChain:wallet.chain]; + masterIdentityContactsDerivationPath.wallet = wallet; + if (![masterIdentityContactsDerivationPath hasExtendedPublicKey]) { + [mArray addObject:masterIdentityContactsDerivationPath]; } } } @@ -319,29 +332,176 @@ - (DSAuthenticationKeysDerivationPath *)blockchainIdentityECDSAKeysDerivationPat [mArray addObject:providerPlatformNodeKeysDerivationPath]; if (wallet.chain.isEvolutionEnabled) { - // Blockchain Identities - DSAuthenticationKeysDerivationPath *blockchainIdentitiesECDSADerivationPath = [DSAuthenticationKeysDerivationPath blockchainIdentityECDSAKeysDerivationPathForChain:wallet.chain]; - blockchainIdentitiesECDSADerivationPath.wallet = wallet; - [mArray addObject:blockchainIdentitiesECDSADerivationPath]; - - DSAuthenticationKeysDerivationPath *blockchainIdentitiesBLSDerivationPath = [DSAuthenticationKeysDerivationPath blockchainIdentityBLSKeysDerivationPathForChain:wallet.chain]; - blockchainIdentitiesBLSDerivationPath.wallet = wallet; - [mArray addObject:blockchainIdentitiesBLSDerivationPath]; - - DSCreditFundingDerivationPath *blockchainIdentitiesRegistrationDerivationPath = [DSCreditFundingDerivationPath blockchainIdentityRegistrationFundingDerivationPathForChain:wallet.chain]; - blockchainIdentitiesRegistrationDerivationPath.wallet = wallet; - [mArray addObject:blockchainIdentitiesRegistrationDerivationPath]; - - DSCreditFundingDerivationPath *blockchainIdentitiesTopupDerivationPath = [DSCreditFundingDerivationPath blockchainIdentityTopupFundingDerivationPathForChain:wallet.chain]; - blockchainIdentitiesTopupDerivationPath.wallet = wallet; - [mArray addObject:blockchainIdentitiesTopupDerivationPath]; - - DSCreditFundingDerivationPath *blockchainIdentitiesInvitationsDerivationPath = [DSCreditFundingDerivationPath blockchainIdentityInvitationFundingDerivationPathForChain:wallet.chain]; - blockchainIdentitiesInvitationsDerivationPath.wallet = wallet; - [mArray addObject:blockchainIdentitiesInvitationsDerivationPath]; + // Identities + DSAuthenticationKeysDerivationPath *identitiesECDSADerivationPath = [DSAuthenticationKeysDerivationPath identityECDSAKeysDerivationPathForChain:wallet.chain]; + identitiesECDSADerivationPath.wallet = wallet; + [mArray addObject:identitiesECDSADerivationPath]; + + DSAuthenticationKeysDerivationPath *identitiesBLSDerivationPath = [DSAuthenticationKeysDerivationPath identityBLSKeysDerivationPathForChain:wallet.chain]; + identitiesBLSDerivationPath.wallet = wallet; + [mArray addObject:identitiesBLSDerivationPath]; + + DSAssetLockDerivationPath *identitiesRegistrationDerivationPath = [DSAssetLockDerivationPath identityRegistrationFundingDerivationPathForChain:wallet.chain]; + identitiesRegistrationDerivationPath.wallet = wallet; + [mArray addObject:identitiesRegistrationDerivationPath]; + + DSAssetLockDerivationPath *identitiesTopupDerivationPath = [DSAssetLockDerivationPath identityTopupFundingDerivationPathForChain:wallet.chain]; + identitiesTopupDerivationPath.wallet = wallet; + [mArray addObject:identitiesTopupDerivationPath]; + + DSAssetLockDerivationPath *identitiesInvitationsDerivationPath = [DSAssetLockDerivationPath identityInvitationFundingDerivationPathForChain:wallet.chain]; + identitiesInvitationsDerivationPath.wallet = wallet; + [mArray addObject:identitiesInvitationsDerivationPath]; } return [mArray copy]; } + ++ (DMaybeOpaqueKeys *)privateKeysAtIndexPaths:(NSArray *)indexPaths + fromSeed:(NSData *)seed + derivationPath:(DSDerivationPath *)derivationPath { + if (!seed || !indexPaths || !derivationPath) + return nil; + if (indexPaths.count == 0) + return Result_ok_Vec_dash_spv_crypto_keys_key_OpaqueKey_err_dash_spv_crypto_keys_KeyError_ctor(Vec_dash_spv_crypto_keys_key_OpaqueKey_ctor(0, NULL), NULL); + NSUInteger count = indexPaths.count; + SLICE *seed_slice = slice_ctor(seed); + Vec_u32 **values = malloc(count * sizeof(Vec_u32 *)); + for (NSUInteger i = 0; i < count; i++) { + values[i] = [NSIndexPath ffi_to:indexPaths[i]]; + } + Vec_Vec_u32 *index_paths = Vec_Vec_u32_ctor(count, values); + dash_spv_crypto_keys_key_IndexPathU256 *path = [DSDerivationPath ffi_to:derivationPath]; + return dash_spv_crypto_keys_key_KeyKind_private_keys_at_index_paths_wrapped(derivationPath.signingAlgorithm, seed_slice, index_paths, path); +} + ++ (NSString *)serializedExtendedPrivateKeyFromSeed:(NSData *)seed + derivationPath:(DSDerivationPath *)derivationPath { + @autoreleasepool { + if (!seed) return nil; + SLICE *slice = slice_ctor(seed); + DIndexPathU256 *path = [DSDerivationPath ffi_to:derivationPath]; + DMaybeKeyString *result = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_serialized_extended_private_key_from_seed_at_u256_path(slice, path, derivationPath.chain.chainType); + NSString *serializedKey = nil; + if (result->ok) { + serializedKey = [NSString stringWithUTF8String:result->ok]; + } + DMaybeKeyStringDtor(result); + return serializedKey; + } +} + ++ (NSArray *)serializedPrivateKeysAtIndexPaths:(NSArray *)indexPaths + fromSeed:(NSData *)seed + derivationPath:(DSDerivationPath *)derivationPath { + if (!seed || !indexPaths) return nil; + if (indexPaths.count == 0) return @[]; + + NSUInteger count = indexPaths.count; + Vec_u32 **values = malloc(count * sizeof(Vec_u32 *)); + for (NSUInteger i = 0; i < count; i++) { + values[i] = [NSIndexPath ffi_to:indexPaths[i]]; + } + Vec_Vec_u32 *index_paths = Vec_Vec_u32_ctor(count, values); + DIndexPathU256 *path = [DSDerivationPath ffi_to:derivationPath]; + SLICE *seed_slice = slice_ctor(seed); + Result_ok_Vec_String_err_dash_spv_crypto_keys_KeyError *result = dash_spv_crypto_keys_key_KeyKind_serialized_private_keys_at_index_paths_wrapper(derivationPath.signingAlgorithm, seed_slice, index_paths, path, derivationPath.chain.chainType); + Vec_String *keys = result->ok; + NSMutableArray *privateKeys = [NSMutableArray arrayWithCapacity:keys->count]; + for (NSUInteger i = 0; i < keys->count; i++) { + [privateKeys addObject:[NSString stringWithUTF8String:keys->values[i]]]; + } + Result_ok_Vec_String_err_dash_spv_crypto_keys_KeyError_destroy(result); + return privateKeys; +} + ++ (NSData *)deserializedExtendedPublicKey:(NSString *)extendedPublicKeyString + onChain:(DSChain *)chain + rDepth:(uint8_t *)depth + rTerminalHardened:(BOOL *)terminalHardened + rTerminalIndex:(UInt256 *)terminalIndex { + uint32_t fingerprint; + UInt256 chainHash; + NSData *pubkey = nil; + NSMutableData *masterPublicKey = [NSMutableData secureData]; + BOOL valid = deserialize(extendedPublicKeyString, depth, &fingerprint, terminalHardened, terminalIndex, &chainHash, &pubkey, [chain isMainnet]); + if (!valid) return nil; + [masterPublicKey appendUInt32:fingerprint]; + [masterPublicKey appendBytes:&chainHash length:32]; + [masterPublicKey appendData:pubkey]; + return [masterPublicKey copy]; +} ++ (NSData *)deserializedExtendedPublicKey:(NSString *)extendedPublicKeyString onChain:(DSChain *)chain { + __unused uint8_t depth = 0; + __unused BOOL terminalHardened = 0; + __unused UInt256 terminalIndex = UINT256_ZERO; + NSData *extendedPublicKey = [self deserializedExtendedPublicKey:extendedPublicKeyString onChain:chain rDepth:&depth rTerminalHardened:&terminalHardened rTerminalIndex:&terminalIndex]; + return extendedPublicKey; +} + ++ (NSString *)serializedExtendedPublicKey:(DSDerivationPath *)derivationPath { + //todo make sure this works with BLS keys + NSData *extPubKeyData = derivationPath.extendedPublicKeyData; + if (extPubKeyData.length < 36) return nil; + uint32_t fingerprint = [extPubKeyData UInt32AtOffset:0]; + UInt256 chain = [extPubKeyData UInt256AtOffset:4]; + DSECPoint pubKey = [extPubKeyData ECPointAtOffset:36]; + UInt256 child = UINT256_ZERO; + BOOL isHardened = NO; + if (derivationPath.length) { + child = [derivationPath indexAtPosition:[derivationPath length] - 1]; + isHardened = [derivationPath isHardenedAtPosition:[derivationPath length] - 1]; + } + + return serialize([derivationPath.depth unsignedCharValue], fingerprint, isHardened, child, chain, [NSData dataWithBytes:&pubKey length:sizeof(pubKey)], [derivationPath.chain isMainnet]); +} + + + ++ (NSData *)deserializedExtendedPublicKey:(DSDerivationPath *)derivationPath extendedPublicKeyString:(NSString *)extendedPublicKeyString { + return [DSDerivationPathFactory deserializedExtendedPublicKey:extendedPublicKeyString onChain:derivationPath.chain]; +} + + + ++ (NSString *)standaloneExtendedPublicKeyLocationStringForUniqueID:(NSString *)uniqueID { + return [NSString stringWithFormat:@"%@_%@", DERIVATION_PATH_EXTENDED_PUBLIC_KEY_STANDALONE_BASED_LOCATION, uniqueID]; +} + ++ (NSString *)standaloneInfoDictionaryLocationStringForUniqueID:(NSString *)uniqueID { + return [NSString stringWithFormat:@"%@_%@", DERIVATION_PATH_STANDALONE_INFO_DICTIONARY_LOCATION, uniqueID]; +} + ++ (NSString *)walletBasedExtendedPublicKeyLocationStringForUniqueID:(NSString *)uniqueID { + return [NSString stringWithFormat:@"%@_%@", DERIVATION_PATH_EXTENDED_PUBLIC_KEY_WALLET_BASED_LOCATION, uniqueID]; +} + ++ (NSString *)walletBasedExtendedPrivateKeyLocationStringForUniqueID:(NSString *)uniqueID { + return [NSString stringWithFormat:@"%@_%@", DERIVATION_PATH_EXTENDED_SECRET_KEY_WALLET_BASED_LOCATION, uniqueID]; +} + + ++ (NSString *)stringRepresentationOfIndex:(UInt256)index + hardened:(BOOL)hardened + inContext:(NSManagedObjectContext *)context { + if (uint256_is_31_bits(index)) { + return [NSString stringWithFormat:@"/%lu%@", (unsigned long)index.u64[0], hardened ? @"'" : @""]; + } else if (context) { + __block NSString *rString = nil; + [context performBlockAndWait:^{ + DSDashpayUserEntity *dashpayUserEntity = [DSDashpayUserEntity anyObjectInContext:context matching:@"associatedBlockchainIdentity.uniqueID == %@", uint256_data(index)]; + if (dashpayUserEntity) { + DSBlockchainIdentityUsernameEntity *usernameEntity = [dashpayUserEntity.associatedBlockchainIdentity.usernames anyObject]; + rString = [NSString stringWithFormat:@"/%@%@", usernameEntity.stringValue, hardened ? @"'" : @""]; + } else { + rString = [NSString stringWithFormat:@"/0x%@%@", uint256_hex(index), hardened ? @"'" : @""]; + } + }]; + return rString; + } else { + return [NSString stringWithFormat:@"/0x%@%@", uint256_hex(index), hardened ? @"'" : @""]; + } +} + @end diff --git a/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.h b/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.h index 6a134286b..3db1d2cd4 100644 --- a/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.h +++ b/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.h @@ -59,10 +59,6 @@ NS_ASSUME_NONNULL_BEGIN // for receive addresses. These have a hardened purpose scheme depending on the derivation path - (NSArray *_Nullable)registerAddressesWithGapLimit:(NSUInteger)gapLimit internal:(BOOL)internal error:(NSError **)error; -- (NSString *_Nullable)privateKeyStringAtIndex:(uint32_t)n internal:(BOOL)internal fromSeed:(NSData *)seed; -- (NSArray *_Nullable)serializedPrivateKeys:(NSArray *)n internal:(BOOL)internal fromSeed:(NSData *)seed; -- (NSArray *_Nullable)privateKeys:(NSArray *)n internal:(BOOL)internal fromSeed:(NSData *)seed; - - (NSData *_Nullable)publicKeyDataAtIndex:(uint32_t)n internal:(BOOL)internal; // gets an addess at an index one level down based on bip32 diff --git a/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m b/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m index 1e8493331..76d4c6436 100644 --- a/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m +++ b/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m @@ -6,13 +6,16 @@ // #import "DSFundsDerivationPath.h" +#import "DSAddressEntity+CoreDataClass.h" #import "DSAccount.h" -#import "DSBlockchainIdentity.h" +#import "DSIdentity.h" +#import "DSChain+Params.h" #import "DSDashpayUserEntity+CoreDataClass.h" #import "DSDerivationPath+Protected.h" #import "DSKeyManager.h" #import "DSLogger.h" #import "NSError+Dash.h" +#import "NSManagedObject+Sugar.h" #define DERIVATION_PATH_IS_USED_KEY @"DERIVATION_PATH_IS_USED_KEY" @@ -30,16 +33,40 @@ @implementation DSFundsDerivationPath + (instancetype _Nonnull)bip32DerivationPathForAccountNumber:(uint32_t)accountNumber onChain:(DSChain *)chain { UInt256 indexes[] = {uint256_from_long(accountNumber)}; BOOL hardenedIndexes[] = {YES}; - return [self derivationPathWithIndexes:indexes hardened:hardenedIndexes length:1 type:DSDerivationPathType_ClearFunds signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_BIP32 onChain:chain]; + return [self derivationPathWithIndexes:indexes + hardened:hardenedIndexes + length:1 + type:DSDerivationPathType_ClearFunds + signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() + reference:DSDerivationPathReference_BIP32 + onChain:chain]; } + (instancetype _Nonnull)bip44DerivationPathForAccountNumber:(uint32_t)accountNumber onChain:(DSChain *)chain { - UInt256 indexes[] = {uint256_from_long(44), uint256_from_long(chain_coin_type(chain.chainType)), uint256_from_long(accountNumber)}; + UInt256 indexes[] = {uint256_from_long(44), uint256_from_long(chain.coinType), uint256_from_long(accountNumber)}; BOOL hardenedIndexes[] = {YES, YES, YES}; - return [self derivationPathWithIndexes:indexes hardened:hardenedIndexes length:3 type:DSDerivationPathType_ClearFunds signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_BIP44 onChain:chain]; + return [self derivationPathWithIndexes:indexes + hardened:hardenedIndexes + length:3 + type:DSDerivationPathType_ClearFunds + signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() + reference:DSDerivationPathReference_BIP44 + onChain:chain]; } -- (instancetype)initWithIndexes:(const UInt256[])indexes hardened:(const BOOL[])hardenedIndexes length:(NSUInteger)length type:(DSDerivationPathType)type signingAlgorithm:(KeyKind)signingAlgorithm reference:(DSDerivationPathReference)reference onChain:(DSChain *)chain { - if (!(self = [super initWithIndexes:indexes hardened:hardenedIndexes length:length type:type signingAlgorithm:signingAlgorithm reference:reference onChain:chain])) return nil; +- (instancetype)initWithIndexes:(const UInt256[])indexes + hardened:(const BOOL[])hardenedIndexes + length:(NSUInteger)length + type:(DSDerivationPathType)type + signingAlgorithm:(DKeyKind *)signingAlgorithm + reference:(DSDerivationPathReference)reference + onChain:(DSChain *)chain { + if (!(self = [super initWithIndexes:indexes + hardened:hardenedIndexes + length:length + type:type + signingAlgorithm:signingAlgorithm + reference:reference + onChain:chain])) return nil; UInt256 lastIndex = indexes[length - 1]; self.isForFirstAccount = uint256_is_zero(lastIndex); @@ -54,7 +81,7 @@ - (BOOL)shouldUseReducedGapLimit { NSError *error = nil; uint64_t hasKnownBalance = getKeychainInt([self hasKnownBalanceUniqueIDString], &error); if (!error) { - self.hasKnownBalanceInternal = hasKnownBalance ? TRUE : FALSE; + self.hasKnownBalanceInternal = hasKnownBalance; self.checkedInitialHasKnownBalance = TRUE; } } @@ -82,30 +109,7 @@ - (void)reloadAddresses { - (void)loadAddresses { if (!self.addressesLoaded) { - [self.managedObjectContext performBlockAndWait:^{ - DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:self.managedObjectContext]; - self.syncBlockHeight = derivationPathEntity.syncBlockHeight; - for (DSAddressEntity *e in derivationPathEntity.addresses) { - @autoreleasepool { - NSMutableArray *a = (e.internal) ? self.internalAddresses : self.externalAddresses; - - while (e.index >= a.count) [a addObject:[NSNull null]]; - if (![DSKeyManager isValidDashAddress:e.address forChain:self.account.wallet.chain]) { -#if DEBUG - DSLogPrivate(@"[%@] address %@ loaded but was not valid on chain", self.account.wallet.chain.name, e.address); -#else - DSLog(@"[%@] address %@ loaded but was not valid on chain %@", self.account.wallet.chain.name, @""); -#endif /* DEBUG */ - continue; - } - a[e.index] = e.address; - [self.mAllAddresses addObject:e.address]; - if ([e.usedInInputs count] || [e.usedInOutputs count]) { - [self.mUsedAddresses addObject:e.address]; - } - } - } - }]; + [self loadAddressesInContext:self.managedObjectContext]; self.addressesLoaded = TRUE; [self registerAddressesWithGapLimit:(self.shouldUseReducedGapLimit ? SEQUENCE_UNUSED_GAP_LIMIT_INITIAL : SEQUENCE_GAP_LIMIT_INITIAL) internal:YES error:nil]; [self registerAddressesWithGapLimit:(self.shouldUseReducedGapLimit ? SEQUENCE_UNUSED_GAP_LIMIT_INITIAL : SEQUENCE_GAP_LIMIT_INITIAL) internal:NO error:nil]; @@ -189,20 +193,7 @@ - (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit internal:(BOOL)i } if (!self.account.wallet.isTransient) { - [self.managedObjectContext performBlock:^{ // store new address in core data - DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:self.managedObjectContext]; - for (NSNumber *number in addAddresses) { - NSString *address = [addAddresses objectForKey:number]; - NSAssert([DSKeyManager isValidDashAddress:address forChain:self.chain], @"the address is being saved to the wrong derivation path"); - DSAddressEntity *e = [DSAddressEntity managedObjectInContext:self.managedObjectContext]; - e.derivationPath = derivationPathEntity; - e.address = address; - e.index = [number intValue]; - e.internal = internal; - e.standalone = NO; - } - [self.managedObjectContext ds_save]; - }]; + [self storeNewAddressesInContext:addAddresses internal:internal context:self.managedObjectContext]; } return a; @@ -289,30 +280,6 @@ - (NSData *)publicKeyDataAtIndex:(uint32_t)n internal:(BOOL)internal { return [self publicKeyDataAtIndexPath:[NSIndexPath indexPathWithIndexes:indexes length:2]]; } -- (NSString *)privateKeyStringAtIndex:(uint32_t)n internal:(BOOL)internal fromSeed:(NSData *)seed { - return seed ? [self serializedPrivateKeys:@[@(n)] internal:internal fromSeed:seed].lastObject : nil; -} - -- (NSArray *)privateKeys:(NSArray *)n internal:(BOOL)internal fromSeed:(NSData *)seed { - NSMutableArray *mArray = [NSMutableArray array]; - for (NSNumber *index in n) { - NSUInteger indexes[] = {(internal ? 1 : 0), index.unsignedIntValue}; - [mArray addObject:[NSIndexPath indexPathWithIndexes:indexes length:2]]; - } - - return [self privateKeysAtIndexPaths:mArray fromSeed:seed]; -} - -- (NSArray *)serializedPrivateKeys:(NSArray *)n internal:(BOOL)internal fromSeed:(NSData *)seed { - NSMutableArray *mArray = [NSMutableArray array]; - for (NSNumber *index in n) { - NSUInteger indexes[] = {(internal ? 1 : 0), index.unsignedIntValue}; - [mArray addObject:[NSIndexPath indexPathWithIndexes:indexes length:2]]; - } - - return [self serializedPrivateKeysAtIndexPaths:mArray fromSeed:seed]; -} - - (NSIndexPath *)indexPathForKnownAddress:(NSString *)address { if ([self.allChangeAddresses containsObject:address]) { NSUInteger indexes[] = {1, [self.allChangeAddresses indexOfObject:address]}; @@ -324,4 +291,53 @@ - (NSIndexPath *)indexPathForKnownAddress:(NSString *)address { return nil; } + +// CoreData +- (void)loadAddressesInContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:self.managedObjectContext]; + self.syncBlockHeight = derivationPathEntity.syncBlockHeight; + for (DSAddressEntity *e in derivationPathEntity.addresses) { + @autoreleasepool { + NSMutableArray *a = (e.internal) ? self.internalAddresses : self.externalAddresses; + + while (e.index >= a.count) [a addObject:[NSNull null]]; + if (![DSKeyManager isValidDashAddress:e.address forChain:self.account.wallet.chain]) { +#if DEBUG + DSLogPrivate(@"[%@] address %@ loaded but was not valid on chain", self.account.wallet.chain.name, e.address); +#else + DSLog(@"[%@] address %@ loaded but was not valid on chain %@", self.account.wallet.chain.name, @""); +#endif /* DEBUG */ + continue; + } + a[e.index] = e.address; + [self.mAllAddresses addObject:e.address]; + if ([e.usedInInputs count] || [e.usedInOutputs count]) { + [self.mUsedAddresses addObject:e.address]; + } + } + } + }]; + +} +- (void)storeNewAddressesInContext:(NSDictionary *)addAddresses + internal:(BOOL)internal + context:(NSManagedObjectContext *)context { + [context performBlock:^{ // store new address in core data + DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:self.managedObjectContext]; + for (NSNumber *number in addAddresses) { + NSString *address = [addAddresses objectForKey:number]; + NSAssert([DSKeyManager isValidDashAddress:address forChain:self.chain], @"the address is being saved to the wrong derivation path"); + DSAddressEntity *e = [DSAddressEntity managedObjectInContext:self.managedObjectContext]; + e.derivationPath = derivationPathEntity; + e.address = address; + e.index = [number intValue]; + e.internal = internal; + e.standalone = NO; + } + [self.managedObjectContext ds_save]; + }]; + +} + @end diff --git a/DashSync/shared/Models/Derivation Paths/DSIncomingFundsDerivationPath.h b/DashSync/shared/Models/Derivation Paths/DSIncomingFundsDerivationPath.h index 72f788ab6..0dbf1274e 100644 --- a/DashSync/shared/Models/Derivation Paths/DSIncomingFundsDerivationPath.h +++ b/DashSync/shared/Models/Derivation Paths/DSIncomingFundsDerivationPath.h @@ -21,20 +21,25 @@ NS_ASSUME_NONNULL_BEGIN @interface DSIncomingFundsDerivationPath : DSDerivationPath -@property (nonatomic, readonly) UInt256 contactSourceBlockchainIdentityUniqueId; -@property (nonatomic, readonly) UInt256 contactDestinationBlockchainIdentityUniqueId; -@property (nonatomic, readonly) DSBlockchainIdentity *contactSourceBlockchainIdentity; -@property (nonatomic, readonly) DSBlockchainIdentity *contactDestinationBlockchainIdentity; -@property (nonatomic, readonly) BOOL sourceIsLocal; -@property (nonatomic, readonly) BOOL destinationIsLocal; +@property (nonatomic, readonly) UInt256 contactSourceIdentityUniqueId; +@property (nonatomic, readonly) UInt256 contactDestinationIdentityUniqueId; -+ (instancetype)contactBasedDerivationPathWithDestinationBlockchainIdentityUniqueId:(UInt256)destinationBlockchainIdentityUniqueId sourceBlockchainIdentityUniqueId:(UInt256)sourceBlockchainIdentityUniqueId forAccountNumber:(uint32_t)accountNumber onChain:(DSChain *)chain; ++ (instancetype)contactBasedDerivationPathWithDestinationIdentityUniqueId:(UInt256)destinationIdentityUniqueId + sourceIdentityUniqueId:(UInt256)sourceIdentityUniqueId + forAccount:(DSAccount *)account + onChain:(DSChain *)chain; //The extended public key will be saved to disk (storeExternalDerivationPathExtendedPublicKeyToKeyChain call needed) -+ (instancetype)externalDerivationPathWithExtendedPublicKey:(OpaqueKey *)extendedPublicKey withDestinationBlockchainIdentityUniqueId:(UInt256)destinationBlockchainIdentityUniqueId sourceBlockchainIdentityUniqueId:(UInt256)sourceBlockchainIdentityUniqueId onChain:(DSChain *)chain; ++ (instancetype)externalDerivationPathWithExtendedPublicKey:(DMaybeOpaqueKey *)extendedPublicKey + withDestinationIdentityUniqueId:(UInt256)destinationIdentityUniqueId + sourceIdentityUniqueId:(UInt256)sourceIdentityUniqueId + onChain:(DSChain *)chain; //The extended public key will be loaded from disk -+ (instancetype)externalDerivationPathWithExtendedPublicKeyUniqueID:(NSString *)extendedPublicKeyUniqueId withDestinationBlockchainIdentityUniqueId:(UInt256)destinationBlockchainIdentityUniqueId sourceBlockchainIdentityUniqueId:(UInt256)sourceBlockchainIdentityUniqueId onChain:(DSChain *)chain; ++ (instancetype)externalDerivationPathWithExtendedPublicKeyUniqueID:(NSString *)extendedPublicKeyUniqueId + withDestinationIdentityUniqueId:(UInt256)destinationIdentityUniqueId + sourceIdentityUniqueId:(UInt256)sourceIdentityUniqueId + onChain:(DSChain *)chain; // returns the first unused external address @property (nonatomic, readonly, nullable) NSString *receiveAddress; @@ -47,14 +52,10 @@ NS_ASSUME_NONNULL_BEGIN - (NSArray *_Nullable)registerAddressesWithGapLimit:(NSUInteger)gapLimit error:(NSError *_Nullable *_Nullable)error; -- (NSString *_Nullable)privateKeyStringAtIndex:(uint32_t)n fromSeed:(NSData *)seed; -- (NSArray *_Nullable)serializedPrivateKeys:(NSArray *)n fromSeed:(NSData *)seed; -- (NSArray *_Nullable)privateKeys:(NSArray *)n fromSeed:(NSData *)seed; - - (NSData *_Nullable)publicKeyDataAtIndex:(uint32_t)n; -// gets an addess at an index one level down based on bip32 -- (NSString *)addressAtIndex:(uint32_t)index; +//// gets an addess at an index one level down based on bip32 +//- (NSString *)addressAtIndex:(uint32_t)index; - (NSString *)receiveAddressAtOffset:(NSUInteger)offset; diff --git a/DashSync/shared/Models/Derivation Paths/DSIncomingFundsDerivationPath.m b/DashSync/shared/Models/Derivation Paths/DSIncomingFundsDerivationPath.m index 7bbc16d2f..1608a0495 100644 --- a/DashSync/shared/Models/Derivation Paths/DSIncomingFundsDerivationPath.m +++ b/DashSync/shared/Models/Derivation Paths/DSIncomingFundsDerivationPath.m @@ -17,67 +17,104 @@ #import "DSIncomingFundsDerivationPath.h" #import "DSAccount.h" -#import "DSBlockchainIdentity.h" +#import "DSAddressEntity+CoreDataClass.h" +#import "DSIdentity.h" #import "DSChainManager.h" +#import "DSChain+Params.h" #import "DSDashpayUserEntity+CoreDataClass.h" #import "DSDerivationPath+Protected.h" +#import "DSTxOutputEntity+CoreDataClass.h" #import "NSError+Dash.h" +#import "NSManagedObject+Sugar.h" #import "dash_shared_core.h" @interface DSIncomingFundsDerivationPath () @property (atomic, strong) NSMutableArray *externalAddresses; -@property (nonatomic, assign) UInt256 contactSourceBlockchainIdentityUniqueId; -@property (nonatomic, assign) UInt256 contactDestinationBlockchainIdentityUniqueId; -@property (nonatomic, assign) BOOL externalDerivationPath; +@property (nonatomic, assign) UInt256 contactSourceIdentityUniqueId; +@property (nonatomic, assign) UInt256 contactDestinationIdentityUniqueId; @end @implementation DSIncomingFundsDerivationPath -+ (instancetype)contactBasedDerivationPathWithDestinationBlockchainIdentityUniqueId:(UInt256)destinationBlockchainIdentityUniqueId sourceBlockchainIdentityUniqueId:(UInt256)sourceBlockchainIdentityUniqueId forAccountNumber:(uint32_t)accountNumber onChain:(DSChain *)chain { - NSAssert(!uint256_eq(sourceBlockchainIdentityUniqueId, destinationBlockchainIdentityUniqueId), @"source and destination must be different"); - UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long((uint64_t) chain_coin_type(chain.chainType)), uint256_from_long(FEATURE_PURPOSE_DASHPAY), uint256_from_long(accountNumber), sourceBlockchainIdentityUniqueId, destinationBlockchainIdentityUniqueId}; ++ (instancetype)contactBasedDerivationPathWithDestinationIdentityUniqueId:(UInt256)destinationIdentityUniqueId + sourceIdentityUniqueId:(UInt256)sourceIdentityUniqueId + forAccount:(DSAccount *)account + onChain:(DSChain *)chain { + NSAssert(!uint256_eq(sourceIdentityUniqueId, destinationIdentityUniqueId), @"source and destination must be different"); + UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long((uint64_t) chain.coinType), uint256_from_long(FEATURE_PURPOSE_DASHPAY), uint256_from_long(account.accountNumber), sourceIdentityUniqueId, destinationIdentityUniqueId}; BOOL hardenedIndexes[] = {YES, YES, YES, YES, NO, NO}; //todo full uint256 derivation - DSIncomingFundsDerivationPath *derivationPath = [self derivationPathWithIndexes:indexes hardened:hardenedIndexes length:6 type:DSDerivationPathType_ClearFunds signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_ContactBasedFunds onChain:chain]; - - derivationPath.contactSourceBlockchainIdentityUniqueId = sourceBlockchainIdentityUniqueId; - derivationPath.contactDestinationBlockchainIdentityUniqueId = destinationBlockchainIdentityUniqueId; - + DSIncomingFundsDerivationPath *derivationPath = [self derivationPathWithIndexes:indexes + hardened:hardenedIndexes + length:6 + type:DSDerivationPathType_ClearFunds + signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() + reference:DSDerivationPathReference_ContactBasedFunds + onChain:chain]; + + derivationPath.contactSourceIdentityUniqueId = sourceIdentityUniqueId; + derivationPath.contactDestinationIdentityUniqueId = destinationIdentityUniqueId; + derivationPath.account = account; return derivationPath; } -+ (instancetype)externalDerivationPathWithExtendedPublicKey:(OpaqueKey *)extendedPublicKey - withDestinationBlockchainIdentityUniqueId:(UInt256)destinationBlockchainIdentityUniqueId - sourceBlockchainIdentityUniqueId:(UInt256)sourceBlockchainIdentityUniqueId ++ (instancetype)externalDerivationPathWithExtendedPublicKey:(DMaybeOpaqueKey *)extendedPublicKey + withDestinationIdentityUniqueId:(UInt256)destinationIdentityUniqueId + sourceIdentityUniqueId:(UInt256)sourceIdentityUniqueId onChain:(DSChain *)chain { UInt256 indexes[] = {}; BOOL hardenedIndexes[] = {}; - DSIncomingFundsDerivationPath *derivationPath = [[self alloc] initWithIndexes:indexes hardened:hardenedIndexes length:0 type:DSDerivationPathType_ViewOnlyFunds signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_ContactBasedFundsExternal onChain:chain]; //we are going to assume this is only ecdsa for now + DSIncomingFundsDerivationPath *derivationPath = [[self alloc] initWithIndexes:indexes + hardened:hardenedIndexes + length:0 + type:DSDerivationPathType_ViewOnlyFunds + signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() + reference:DSDerivationPathReference_ContactBasedFundsExternal + onChain:chain]; //we are going to assume this is only ecdsa for now derivationPath.extendedPublicKey = extendedPublicKey; - - derivationPath.contactSourceBlockchainIdentityUniqueId = sourceBlockchainIdentityUniqueId; - derivationPath.contactDestinationBlockchainIdentityUniqueId = destinationBlockchainIdentityUniqueId; - derivationPath.externalDerivationPath = TRUE; + derivationPath.contactSourceIdentityUniqueId = sourceIdentityUniqueId; + derivationPath.contactDestinationIdentityUniqueId = destinationIdentityUniqueId; return derivationPath; } -+ (instancetype)externalDerivationPathWithExtendedPublicKeyUniqueID:(NSString *)extendedPublicKeyUniqueId withDestinationBlockchainIdentityUniqueId:(UInt256)destinationBlockchainIdentityUniqueId sourceBlockchainIdentityUniqueId:(UInt256)sourceBlockchainIdentityUniqueId onChain:(DSChain *)chain { + ++ (instancetype)externalDerivationPathWithExtendedPublicKeyUniqueID:(NSString *)extendedPublicKeyUniqueId + withDestinationIdentityUniqueId:(UInt256)destinationIdentityUniqueId + sourceIdentityUniqueId:(UInt256)sourceIdentityUniqueId + onChain:(DSChain *)chain { UInt256 indexes[] = {}; BOOL hardenedIndexes[] = {}; - DSIncomingFundsDerivationPath *derivationPath = [[self alloc] initWithIndexes:indexes hardened:hardenedIndexes length:0 type:DSDerivationPathType_ViewOnlyFunds signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_ContactBasedFundsExternal onChain:chain]; //we are going to assume this is only ecdsa for now + DSIncomingFundsDerivationPath *derivationPath = [[self alloc] initWithIndexes:indexes + hardened:hardenedIndexes + length:0 + type:DSDerivationPathType_ViewOnlyFunds + signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() + reference:DSDerivationPathReference_ContactBasedFundsExternal + onChain:chain]; //we are going to assume this is only ecdsa for now derivationPath.standaloneExtendedPublicKeyUniqueID = extendedPublicKeyUniqueId; - derivationPath.contactSourceBlockchainIdentityUniqueId = sourceBlockchainIdentityUniqueId; - derivationPath.contactDestinationBlockchainIdentityUniqueId = destinationBlockchainIdentityUniqueId; - derivationPath.externalDerivationPath = TRUE; + derivationPath.contactSourceIdentityUniqueId = sourceIdentityUniqueId; + derivationPath.contactDestinationIdentityUniqueId = destinationIdentityUniqueId; return derivationPath; } -- (instancetype)initWithIndexes:(const UInt256[])indexes hardened:(const BOOL[])hardenedIndexes length:(NSUInteger)length type:(DSDerivationPathType)type signingAlgorithm:(KeyKind)signingAlgorithm reference:(DSDerivationPathReference)reference onChain:(DSChain *)chain { - if (!(self = [super initWithIndexes:indexes hardened:hardenedIndexes length:length type:type signingAlgorithm:signingAlgorithm reference:reference onChain:chain])) return nil; +- (instancetype)initWithIndexes:(const UInt256[])indexes + hardened:(const BOOL[])hardenedIndexes + length:(NSUInteger)length + type:(DSDerivationPathType)type + signingAlgorithm:(DKeyKind *)signingAlgorithm + reference:(DSDerivationPathReference)reference + onChain:(DSChain *)chain { + if (!(self = [super initWithIndexes:indexes + hardened:hardenedIndexes + length:length + type:type + signingAlgorithm:signingAlgorithm + reference:reference + onChain:chain])) return nil; self.externalAddresses = [NSMutableArray array]; @@ -102,30 +139,7 @@ - (void)loadAddresses { - (void)loadAddressesInContext:(NSManagedObjectContext *)context { if (!self.addressesLoaded) { - [context performBlockAndWait:^{ - DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:context]; - self.syncBlockHeight = derivationPathEntity.syncBlockHeight; - for (DSAddressEntity *e in derivationPathEntity.addresses) { - @autoreleasepool { - NSMutableArray *a = self.externalAddresses; - - while (e.index >= a.count) [a addObject:[NSNull null]]; - if (![DSKeyManager isValidDashAddress:e.address forChain:self.account.wallet.chain]) { -#if DEBUG - DSLogPrivate(@"[%@] address %@ loaded but was not valid on chain", self.account.wallet.chain.name, e.address); -#else - DSLog(@"[%@] address %@ loaded but was not valid on chain", self.account.wallet.chain.name, @""); -#endif /* DEBUG */ - continue; - } - a[e.index] = e.address; - [self.mAllAddresses addObject:e.address]; - if ([e.usedInInputs count] || [e.usedInOutputs count]) { - [self.mUsedAddresses addObject:e.address]; - } - } - } - }]; + [self _loadAddressesInContext:context]; self.addressesLoaded = TRUE; [self registerAddressesWithGapLimit:SEQUENCE_DASHPAY_GAP_LIMIT_INITIAL inContext:context error:nil]; } @@ -135,14 +149,6 @@ - (NSUInteger)accountNumber { return [self indexAtPosition:[self length] - 3].u64[0] & ~BIP32_HARD; } -- (BOOL)sourceIsLocal { - return !![self.chain blockchainIdentityForUniqueId:self.contactSourceBlockchainIdentityUniqueId]; -} - -- (BOOL)destinationIsLocal { - return !![self.chain blockchainIdentityForUniqueId:self.contactDestinationBlockchainIdentityUniqueId]; -} - // MARK: - Derivation Path Addresses - (BOOL)registerTransactionAddress:(NSString *_Nonnull)address { @@ -158,7 +164,11 @@ - (BOOL)registerTransactionAddress:(NSString *_Nonnull)address { - (NSString *)createIdentifierForDerivationPath { - return [NSString stringWithFormat:@"%@-%@-%@", [NSData dataWithUInt256:_contactSourceBlockchainIdentityUniqueId].shortHexString, [NSData dataWithUInt256:_contactDestinationBlockchainIdentityUniqueId].shortHexString, [NSData dataWithUInt256:[[self extendedPublicKeyData] SHA256]].shortHexString]; + return [NSString stringWithFormat:@"%@-%@-%@", + uint256_data(_contactSourceIdentityUniqueId).shortHexString, + uint256_data(_contactDestinationIdentityUniqueId).shortHexString, + [super createIdentifierForDerivationPath] + ]; } - (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit error:(NSError **)error { @@ -169,7 +179,9 @@ - (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit error:(NSError * // found that haven't been used in any transactions. This method returns an array of unused addresses // following the last used address in the chain. The internal chain is used for change addresses and the external chain // for receive addresses. -- (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit inContext:(NSManagedObjectContext *)context error:(NSError **)error { +- (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit + inContext:(NSManagedObjectContext *)context + error:(NSError **)error { NSAssert(self.account, @"Account must be set"); if (!self.account.wallet.isTransient) { if (!self.addressesLoaded) { @@ -210,7 +222,8 @@ - (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit inContext:(NSMan NSUInteger upperLimit = gapLimit; while (a.count < upperLimit) { // generate new addresses up to gapLimit - NSString *address = [self addressAtIndex:n]; + NSData *pubKey = [self publicKeyDataAtIndex:n]; + NSString *address = [DSKeyManager ecdsaKeyAddressFromPublicKeyData:pubKey forChainType:self.chain.chainType]; if (!address) { DSLog(@"[%@] error generating keys", self.chain.name); if (error) { @@ -219,26 +232,12 @@ - (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit inContext:(NSMan return nil; } - __block BOOL isUsed = FALSE; - if (!self.account.wallet.isTransient) { - [context performBlockAndWait:^{ // store new address in core data - DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:context]; - DSAddressEntity *e = [DSAddressEntity managedObjectInContext:context]; - e.derivationPath = derivationPathEntity; - NSAssert([DSKeyManager isValidDashAddress:address forChain:self.chain], @"the address is being saved to the wrong derivation path"); - e.address = address; - e.index = n; - e.internal = NO; - e.standalone = NO; - NSArray *outputs = [DSTxOutputEntity objectsInContext:context matching:@"address == %@", address]; - [e addUsedInOutputs:[NSSet setWithArray:outputs]]; - if (outputs.count) isUsed = TRUE; - }]; - } - if (isUsed) { - [self.mUsedAddresses addObject:address]; - upperLimit++; + BOOL isUsed = [self storeNewAddressInContext:address atIndex:n context:context]; + if (isUsed) { + [self.mUsedAddresses addObject:address]; + upperLimit++; + } } [self.mAllAddresses addObject:address]; [self.externalAddresses addObject:address]; @@ -250,12 +249,6 @@ - (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit inContext:(NSMan } } -// gets an address at an index path -- (NSString *)addressAtIndex:(uint32_t)index { - NSData *pubKey = [self publicKeyDataAtIndex:index]; - return [DSKeyManager ecdsaKeyAddressFromPublicKeyData:pubKey forChainType:self.chain.chainType]; -} - // returns the first unused external address - (NSString *)receiveAddress { return [self receiveAddressInContext:self.managedObjectContext]; @@ -291,30 +284,6 @@ - (NSData *)publicKeyDataAtIndex:(uint32_t)n { return [self publicKeyDataAtIndexPath:[NSIndexPath indexPathWithIndexes:indexes length:1]]; } -- (NSString *)privateKeyStringAtIndex:(uint32_t)n fromSeed:(NSData *)seed { - return seed ? [self serializedPrivateKeys:@[@(n)] fromSeed:seed].lastObject : nil; -} - -- (NSArray *)privateKeys:(NSArray *)n fromSeed:(NSData *)seed { - NSMutableArray *mArray = [NSMutableArray array]; - for (NSNumber *index in n) { - NSUInteger indexes[] = {index.unsignedIntValue}; - [mArray addObject:[NSIndexPath indexPathWithIndexes:indexes length:1]]; - } - - return [self privateKeysAtIndexPaths:mArray fromSeed:seed]; -} - -- (NSArray *)serializedPrivateKeys:(NSArray *)n fromSeed:(NSData *)seed { - NSMutableArray *mArray = [NSMutableArray array]; - for (NSNumber *index in n) { - NSUInteger indexes[] = {index.unsignedIntValue}; - [mArray addObject:[NSIndexPath indexPathWithIndexes:indexes length:1]]; - } - - return [self serializedPrivateKeysAtIndexPaths:mArray fromSeed:seed]; -} - - (NSIndexPath *)indexPathForKnownAddress:(NSString *)address { if ([self.allReceiveAddresses containsObject:address]) { NSUInteger indexes[] = {[self.allReceiveAddresses indexOfObject:address]}; @@ -324,12 +293,52 @@ - (NSIndexPath *)indexPathForKnownAddress:(NSString *)address { } -- (DSBlockchainIdentity *)contactSourceBlockchainIdentity { - return [self.chain blockchainIdentityForUniqueId:self.contactSourceBlockchainIdentityUniqueId foundInWallet:nil includeForeignBlockchainIdentities:YES]; +- (void)_loadAddressesInContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:context]; + self.syncBlockHeight = derivationPathEntity.syncBlockHeight; + for (DSAddressEntity *e in derivationPathEntity.addresses) { + @autoreleasepool { + NSMutableArray *a = self.externalAddresses; + + while (e.index >= a.count) [a addObject:[NSNull null]]; + if (![DSKeyManager isValidDashAddress:e.address forChain:self.account.wallet.chain]) { +#if DEBUG + DSLogPrivate(@"[%@] address %@ loaded but was not valid on chain", self.account.wallet.chain.name, e.address); +#else + DSLog(@"[%@] address %@ loaded but was not valid on chain", self.account.wallet.chain.name, @""); +#endif /* DEBUG */ + continue; + } + a[e.index] = e.address; + [self.mAllAddresses addObject:e.address]; + if ([e.usedInInputs count] || [e.usedInOutputs count]) { + [self.mUsedAddresses addObject:e.address]; + } + } + } + }]; } -- (DSBlockchainIdentity *)contactDestinationBlockchainIdentity { - return [self.chain blockchainIdentityForUniqueId:self.contactDestinationBlockchainIdentityUniqueId foundInWallet:nil includeForeignBlockchainIdentities:YES]; +- (BOOL)storeNewAddressInContext:(NSString *)address + atIndex:(uint32_t)n + context:(NSManagedObjectContext *)context { + __block BOOL isUsed = FALSE; + [context performBlockAndWait:^{ // store new address in core data + DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:context]; + DSAddressEntity *e = [DSAddressEntity managedObjectInContext:context]; + e.derivationPath = derivationPathEntity; + NSAssert([DSKeyManager isValidDashAddress:address forChain:self.chain], @"the address is being saved to the wrong derivation path"); + e.address = address; + e.index = n; + e.internal = NO; + e.standalone = NO; + NSArray *outputs = [DSTxOutputEntity objectsInContext:context matching:@"address == %@", address]; + [e addUsedInOutputs:[NSSet setWithArray:outputs]]; + if (outputs.count) isUsed = TRUE; + }]; + return isUsed; } + @end diff --git a/DashSync/shared/Models/Derivation Paths/DSMasternodeHoldingsDerivationPath.h b/DashSync/shared/Models/Derivation Paths/DSMasternodeHoldingsDerivationPath.h index ee41d035a..c081cfe5c 100644 --- a/DashSync/shared/Models/Derivation Paths/DSMasternodeHoldingsDerivationPath.h +++ b/DashSync/shared/Models/Derivation Paths/DSMasternodeHoldingsDerivationPath.h @@ -13,10 +13,7 @@ NS_ASSUME_NONNULL_BEGIN @interface DSMasternodeHoldingsDerivationPath : DSSimpleIndexedDerivationPath + (instancetype)providerFundsDerivationPathForWallet:(DSWallet *)wallet; - -- (NSString *)receiveAddress; - -- (void)signTransaction:(DSTransaction *)transaction withPrompt:(NSString *)authprompt completion:(TransactionValidityCompletionBlock)completion; +//- (NSString *)receiveAddress; @end diff --git a/DashSync/shared/Models/Derivation Paths/DSMasternodeHoldingsDerivationPath.m b/DashSync/shared/Models/Derivation Paths/DSMasternodeHoldingsDerivationPath.m index efb570a29..450a2208f 100644 --- a/DashSync/shared/Models/Derivation Paths/DSMasternodeHoldingsDerivationPath.m +++ b/DashSync/shared/Models/Derivation Paths/DSMasternodeHoldingsDerivationPath.m @@ -5,6 +5,7 @@ // Created by Sam Westrich on 2/10/19. // +#import "DSChain+Params.h" #import "DSMasternodeHoldingsDerivationPath.h" #import "DSDerivationPath+Protected.h" #import "DSDerivationPathFactory.h" @@ -23,41 +24,19 @@ + (instancetype _Nonnull)providerFundsDerivationPathForWallet:(DSWallet *)wallet } + (instancetype _Nonnull)providerFundsDerivationPathForChain:(DSChain *)chain { - UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long((uint64_t) chain_coin_type(chain.chainType)), uint256_from_long(3), uint256_from_long(0)}; + UInt256 indexes[] = {uint256_from_long(FEATURE_PURPOSE), uint256_from_long((uint64_t) chain.coinType), uint256_from_long(3), uint256_from_long(0)}; BOOL hardenedIndexes[] = {YES, YES, YES, YES}; - return [self derivationPathWithIndexes:indexes hardened:hardenedIndexes length:4 type:DSDerivationPathType_ProtectedFunds signingAlgorithm:KeyKind_ECDSA reference:DSDerivationPathReference_ProviderFunds onChain:chain]; -} - -- (NSString *)receiveAddress { - NSString *addr = [self registerAddressesWithGapLimit:1 error:nil].lastObject; - return (addr) ? addr : self.mOrderedAddresses.lastObject; + return [self derivationPathWithIndexes:indexes + hardened:hardenedIndexes + length:4 + type:DSDerivationPathType_ProtectedFunds + signingAlgorithm:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() + reference:DSDerivationPathReference_ProviderFunds + onChain:chain]; } - (NSUInteger)defaultGapLimit { return 5; } -// sign any inputs in the given transaction that can be signed using private keys from the wallet -- (void)signTransaction:(DSTransaction *)transaction withPrompt:(NSString *)authprompt completion:(TransactionValidityCompletionBlock)completion; -{ - if ([transaction inputAddresses].count != 1) { - completion(NO, NO); - return; - } - - NSUInteger index = [self indexOfKnownAddress:[[transaction inputAddresses] firstObject]]; - - @autoreleasepool { // @autoreleasepool ensures sensitive data will be dealocated immediately - self.wallet.secureSeedRequestBlock(authprompt, MASTERNODE_COST, ^void(NSData *_Nullable seed, BOOL cancelled) { - if (!seed) { - if (completion) completion(NO, cancelled); - } else { - OpaqueKey *key = [self privateKeyAtIndex:(uint32_t)index fromSeed:seed]; - BOOL signedSuccessfully = [transaction signWithPrivateKeys:@[[NSValue valueWithPointer:key]]]; - if (completion) completion(signedSuccessfully, NO); - } - }); - } -} - @end diff --git a/DashSync/shared/Models/Derivation Paths/DSSimpleIndexedDerivationPath.h b/DashSync/shared/Models/Derivation Paths/DSSimpleIndexedDerivationPath.h index 4410191a5..3312133d3 100644 --- a/DashSync/shared/Models/Derivation Paths/DSSimpleIndexedDerivationPath.h +++ b/DashSync/shared/Models/Derivation Paths/DSSimpleIndexedDerivationPath.h @@ -13,6 +13,7 @@ NS_ASSUME_NONNULL_BEGIN // returns the index of an address in the derivation path as long as it is within the gap limit - (NSUInteger)indexOfKnownAddress:(NSString *)address; +- (NSUInteger)indexOfKnownAddressHash:(UInt160)hash; // returns the index of the first unused Address; - (NSUInteger)firstUnusedIndex; @@ -27,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN - (NSString *)addressAtIndex:(uint32_t)index; // true if the address at the index was previously used as an input or output in any wallet transaction -- (BOOL)addressIsUsedAtIndex:(uint32_t)index; +//- (BOOL)addressIsUsedAtIndex:(uint32_t)index; // gets addresses to an index, does not use cache and does not add to cache - (NSArray *)addressesToIndex:(NSUInteger)index; @@ -36,7 +37,7 @@ NS_ASSUME_NONNULL_BEGIN - (NSArray *)addressesToIndex:(NSUInteger)index useCache:(BOOL)useCache addToCache:(BOOL)addToCache; // gets a private key at an index -- (OpaqueKey *_Nullable)privateKeyAtIndex:(uint32_t)index fromSeed:(NSData *)seed; +- (DMaybeOpaqueKey *_Nullable)privateKeyAtIndex:(uint32_t)index fromSeed:(NSData *)seed; // get private keys for a range or to an index - (NSArray *)privateKeysToIndex:(NSUInteger)index fromSeed:(NSData *)seed; diff --git a/DashSync/shared/Models/Derivation Paths/DSSimpleIndexedDerivationPath.m b/DashSync/shared/Models/Derivation Paths/DSSimpleIndexedDerivationPath.m index 41254a03c..e1025ed36 100644 --- a/DashSync/shared/Models/Derivation Paths/DSSimpleIndexedDerivationPath.m +++ b/DashSync/shared/Models/Derivation Paths/DSSimpleIndexedDerivationPath.m @@ -5,15 +5,30 @@ // Created by Sam Westrich on 2/20/19. // +#import "DSAddressEntity+CoreDataClass.h" +#import "DSChain+Params.h" #import "DSDerivationPath+Protected.h" #import "DSKeyManager.h" #import "DSSimpleIndexedDerivationPath+Protected.h" #import "NSError+Dash.h" +#import "NSManagedObject+Sugar.h" @implementation DSSimpleIndexedDerivationPath -- (instancetype _Nullable)initWithIndexes:(const UInt256[_Nullable])indexes hardened:(const BOOL[_Nullable])hardenedIndexes length:(NSUInteger)length type:(DSDerivationPathType)type signingAlgorithm:(KeyKind)signingAlgorithm reference:(DSDerivationPathReference)reference onChain:(DSChain *)chain { - if (!(self = [super initWithIndexes:indexes hardened:hardenedIndexes length:length type:type signingAlgorithm:signingAlgorithm reference:reference onChain:chain])) return nil; +- (instancetype _Nullable)initWithIndexes:(const UInt256[_Nullable])indexes + hardened:(const BOOL[_Nullable])hardenedIndexes + length:(NSUInteger)length + type:(DSDerivationPathType)type + signingAlgorithm:(DKeyKind *)signingAlgorithm + reference:(DSDerivationPathReference)reference + onChain:(DSChain *)chain { + if (!(self = [super initWithIndexes:indexes + hardened:hardenedIndexes + length:length + type:type + signingAlgorithm:signingAlgorithm + reference:reference + onChain:chain])) return nil; self.mOrderedAddresses = [NSMutableArray array]; @@ -23,29 +38,7 @@ - (instancetype _Nullable)initWithIndexes:(const UInt256[_Nullable])indexes hard - (void)loadAddresses { @synchronized(self) { if (!self.addressesLoaded) { - [self.managedObjectContext performBlockAndWait:^{ - DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:self.managedObjectContext]; - self.syncBlockHeight = derivationPathEntity.syncBlockHeight; - NSArray *addresses = [derivationPathEntity.addresses sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]]]; - for (DSAddressEntity *e in addresses) { - @autoreleasepool { - while (e.index >= self.mOrderedAddresses.count) [self.mOrderedAddresses addObject:[NSNull null]]; - if (![DSKeyManager isValidDashAddress:e.address forChain:self.wallet.chain]) { -#if DEBUG - DSLogPrivate(@"[%@] address %@ loaded but was not valid on chain", self.account.wallet.chain.name, e.address); -#else - DSLog(@"[%@] address %@ loaded but was not valid on chain", self.account.wallet.chain.name, @""); -#endif /* DEBUG */ - continue; - } - self.mOrderedAddresses[e.index] = e.address; - [self.mAllAddresses addObject:e.address]; - if ([e.usedInInputs count] || [e.usedInOutputs count] || [e.usedInSpecialTransactions count] || [e.usedInSimplifiedMasternodeEntries count]) { - [self.mUsedAddresses addObject:e.address]; - } - } - } - }]; + [self loadAddressesInContext:self.managedObjectContext]; self.addressesLoaded = TRUE; [self registerAddressesWithGapLimit:10 error:nil]; } @@ -129,15 +122,7 @@ - (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit error:(NSError * } if (!self.wallet.isTransient) { - [self.managedObjectContext performBlock:^{ // store new address in core data - DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:self.managedObjectContext]; - DSAddressEntity *e = [DSAddressEntity managedObjectInContext:self.managedObjectContext]; - e.derivationPath = derivationPathEntity; - NSAssert([DSKeyManager isValidDashAddress:addr forChain:self.chain], @"the address is being saved to the wrong derivation path"); - e.address = addr; - e.index = n; - e.standalone = NO; - }]; + [self storeNewAddressInContext:addr atIndex:n context:self.managedObjectContext]; } [self.mAllAddresses addObject:addr]; @@ -166,9 +151,9 @@ - (NSString *)addressAtIndex:(uint32_t)index { return [self addressAtIndexPath:[NSIndexPath indexPathWithIndex:index]]; } -- (BOOL)addressIsUsedAtIndex:(uint32_t)index { - return [self addressIsUsedAtIndexPath:[NSIndexPath indexPathWithIndex:index]]; -} +//- (BOOL)addressIsUsedAtIndex:(uint32_t)index { +// return [self addressIsUsedAtIndexPath:[NSIndexPath indexPathWithIndex:index]]; +//} - (NSIndexPath *)indexPathForKnownAddress:(NSString *)address { return [NSIndexPath indexPathWithIndex:[self indexOfKnownAddress:address]]; @@ -178,12 +163,17 @@ - (NSUInteger)indexOfKnownAddress:(NSString *)address { return [self.mOrderedAddresses indexOfObject:address]; } +- (NSUInteger)indexOfKnownAddressHash:(UInt160)hash { + NSString *address = [DSKeyManager addressFromHash160:hash forChain:self.chain]; + return [self.mOrderedAddresses indexOfObject:address]; +} + // gets a public key at an index - (NSData *)publicKeyDataAtIndex:(uint32_t)index { return [self publicKeyDataAtIndexPath:[NSIndexPath indexPathWithIndex:index]]; } -- (OpaqueKey *)privateKeyAtIndex:(uint32_t)index fromSeed:(NSData *)seed { +- (DMaybeOpaqueKey *)privateKeyAtIndex:(uint32_t)index fromSeed:(NSData *)seed { return [self privateKeyAtIndexPath:[NSIndexPath indexPathWithIndex:index] fromSeed:seed]; } @@ -220,7 +210,7 @@ - (NSArray *)addressesToIndex:(NSUInteger)index useCache:(BOOL)useCache addToCac - (NSArray *)privateKeysForRange:(NSRange)range fromSeed:(NSData *)seed { NSMutableArray *mArray = [NSMutableArray array]; for (NSUInteger i = range.location; i < (range.location + range.length); i++) { - OpaqueKey *privateKey = [self privateKeyAtIndex:(uint32_t)i fromSeed:seed]; + DMaybeOpaqueKey *privateKey = [self privateKeyAtIndex:(uint32_t)i fromSeed:seed]; NSValue *privateKeyValue = [NSValue valueWithPointer:privateKey]; [mArray addObject:privateKeyValue]; } @@ -231,4 +221,45 @@ - (NSArray *)privateKeysToIndex:(NSUInteger)index fromSeed:(NSData *)seed { return [self privateKeysForRange:NSMakeRange(0, index) fromSeed:seed]; } + +- (void)loadAddressesInContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:self.managedObjectContext]; + self.syncBlockHeight = derivationPathEntity.syncBlockHeight; + NSArray *addresses = [derivationPathEntity.addresses sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]]]; + for (DSAddressEntity *e in addresses) { + @autoreleasepool { + while (e.index >= self.mOrderedAddresses.count) [self.mOrderedAddresses addObject:[NSNull null]]; + if (![DSKeyManager isValidDashAddress:e.address forChain:self.wallet.chain]) { +#if DEBUG + DSLogPrivate(@"[%@] address %@ loaded but was not valid on chain", self.chain.name, e.address); +#else + DSLog(@"[%@] address %@ loaded but was not valid on chain", self.account.wallet.chain.name, @""); +#endif /* DEBUG */ + continue; + } + self.mOrderedAddresses[e.index] = e.address; + [self.mAllAddresses addObject:e.address]; + if ([e.usedInInputs count] || [e.usedInOutputs count] || [e.usedInSpecialTransactions count] || [e.usedInSimplifiedMasternodeEntries count]) { + [self.mUsedAddresses addObject:e.address]; + } + } + } + }]; + +} +- (void)storeNewAddressInContext:(NSString *)address + atIndex:(uint32_t)n + context:(NSManagedObjectContext *)context { + [context performBlock:^{ // store new address in core data + DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self inContext:self.managedObjectContext]; + DSAddressEntity *e = [DSAddressEntity managedObjectInContext:self.managedObjectContext]; + e.derivationPath = derivationPathEntity; + NSAssert([DSKeyManager isValidDashAddress:address forChain:self.chain], @"the address is being saved to the wrong derivation path"); + e.address = address; + e.index = n; + e.standalone = NO; + }]; + +} @end diff --git a/DashSync/shared/Models/Entities/DSAccountEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSAccountEntity+CoreDataClass.m index 9d5ee4f16..474faa315 100644 --- a/DashSync/shared/Models/Entities/DSAccountEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSAccountEntity+CoreDataClass.m @@ -12,7 +12,10 @@ @implementation DSAccountEntity -+ (DSAccountEntity *_Nonnull)accountEntityForWalletUniqueID:(NSString *)walletUniqueID index:(uint32_t)index onChain:(DSChain *)chain inContext:(NSManagedObjectContext *)context { ++ (DSAccountEntity *_Nonnull)accountEntityForWalletUniqueID:(NSString *)walletUniqueID + index:(uint32_t)index + onChain:(DSChain *)chain + inContext:(NSManagedObjectContext *)context { NSParameterAssert(walletUniqueID); NSParameterAssert(chain); NSParameterAssert(context); diff --git a/DashSync/shared/Models/Entities/DSAssetLockTransactionEntity+CoreDataProperties.h b/DashSync/shared/Models/Entities/DSAssetLockTransactionEntity+CoreDataProperties.h index fafef85b5..6c7118238 100644 --- a/DashSync/shared/Models/Entities/DSAssetLockTransactionEntity+CoreDataProperties.h +++ b/DashSync/shared/Models/Entities/DSAssetLockTransactionEntity+CoreDataProperties.h @@ -16,6 +16,7 @@ // #import "DSAssetLockTransactionEntity+CoreDataClass.h" +#import "DSBlockchainIdentityEntity+CoreDataClass.h" NS_ASSUME_NONNULL_BEGIN @@ -24,6 +25,8 @@ NS_ASSUME_NONNULL_BEGIN + (NSFetchRequest *)fetchRequest; +@property (nullable, nonatomic, retain) DSBlockchainIdentityEntity *identity; + @property (nonatomic, retain) NSOrderedSet *creditOutputs; @end diff --git a/DashSync/shared/Models/Entities/DSAssetLockTransactionEntity+CoreDataProperties.m b/DashSync/shared/Models/Entities/DSAssetLockTransactionEntity+CoreDataProperties.m index 3b8e838d9..5f7e9278f 100644 --- a/DashSync/shared/Models/Entities/DSAssetLockTransactionEntity+CoreDataProperties.m +++ b/DashSync/shared/Models/Entities/DSAssetLockTransactionEntity+CoreDataProperties.m @@ -23,6 +23,7 @@ @implementation DSAssetLockTransactionEntity (CoreDataProperties) return [NSFetchRequest fetchRequestWithEntityName:@"DSAssetLockTransactionEntity"]; } +@dynamic identity; @dynamic creditOutputs; @end diff --git a/DashSync/shared/Models/Entities/DSBlockchainIdentityEntity+CoreDataClass.h b/DashSync/shared/Models/Entities/DSBlockchainIdentityEntity+CoreDataClass.h index f9ce47430..8eb48ce28 100644 --- a/DashSync/shared/Models/Entities/DSBlockchainIdentityEntity+CoreDataClass.h +++ b/DashSync/shared/Models/Entities/DSBlockchainIdentityEntity+CoreDataClass.h @@ -9,13 +9,13 @@ #import #import -@class DSBlockchainIdentityKeyPathEntity, DSChainEntity, DSDashpayUserEntity, DSCreditFundingTransactionEntity, DSTransitionEntity, DSBlockchainIdentityUsernameEntity, DSBlockchainIdentity, DSBlockchainInvitationEntity; +@class DSBlockchainIdentityKeyPathEntity, DSChainEntity, DSDashpayUserEntity, DSTransitionEntity, DSBlockchainIdentityUsernameEntity, DSIdentity, DSBlockchainInvitationEntity; NS_ASSUME_NONNULL_BEGIN @interface DSBlockchainIdentityEntity : NSManagedObject -- (DSBlockchainIdentity *)blockchainIdentity; +- (DSIdentity *)identity; + (void)deleteBlockchainIdentitiesOnChainEntity:(DSChainEntity *)chainEntity; diff --git a/DashSync/shared/Models/Entities/DSBlockchainIdentityEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSBlockchainIdentityEntity+CoreDataClass.m index 0816ad7c6..8db507374 100644 --- a/DashSync/shared/Models/Entities/DSBlockchainIdentityEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSBlockchainIdentityEntity+CoreDataClass.m @@ -6,7 +6,7 @@ // // -#import "DSBlockchainIdentity+Protected.h" +#import "DSIdentity+Protected.h" #import "DSBlockchainIdentityEntity+CoreDataClass.h" #import "DSChainEntity+CoreDataClass.h" #import "NSManagedObject+Sugar.h" @@ -15,15 +15,15 @@ @implementation DSBlockchainIdentityEntity + (void)deleteBlockchainIdentitiesOnChainEntity:(DSChainEntity *)chainEntity { [chainEntity.managedObjectContext performBlockAndWait:^{ - NSArray *blockchainIdentitiesToDelete = [self objectsInContext:chainEntity.managedObjectContext matching:@"(chain == %@)", chainEntity]; - for (DSBlockchainIdentityEntity *blockchainIdentity in blockchainIdentitiesToDelete) { - [chainEntity.managedObjectContext deleteObject:blockchainIdentity]; + NSArray *identitiesToDelete = [self objectsInContext:chainEntity.managedObjectContext matching:@"(chain == %@)", chainEntity]; + for (DSBlockchainIdentityEntity *identity in identitiesToDelete) { + [chainEntity.managedObjectContext deleteObject:identity]; } }]; } -- (DSBlockchainIdentity *)blockchainIdentity { - return [[DSBlockchainIdentity alloc] initWithBlockchainIdentityEntity:self]; +- (DSIdentity *)identity { + return [[DSIdentity alloc] initWithIdentityEntity:self]; } @end diff --git a/DashSync/shared/Models/Entities/DSBlockchainIdentityEntity+CoreDataProperties.h b/DashSync/shared/Models/Entities/DSBlockchainIdentityEntity+CoreDataProperties.h index 15af858a6..e1cfce493 100644 --- a/DashSync/shared/Models/Entities/DSBlockchainIdentityEntity+CoreDataProperties.h +++ b/DashSync/shared/Models/Entities/DSBlockchainIdentityEntity+CoreDataProperties.h @@ -7,7 +7,7 @@ // #import "DSBlockchainIdentityEntity+CoreDataClass.h" - +#import "DSAssetLockTransactionEntity+CoreDataClass.h" NS_ASSUME_NONNULL_BEGIN @@ -20,8 +20,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) uint16_t registrationStatus; @property (nonatomic, assign) uint64_t creditBalance; @property (nullable, nonatomic, retain) NSData *dashpaySyncronizationBlockHash; -@property (nullable, nonatomic, retain) NSSet *topUpFundingTransactions; -@property (nullable, nonatomic, retain) DSCreditFundingTransactionEntity *registrationFundingTransaction; +@property (nullable, nonatomic, retain) NSSet *topUpFundingTransactions; +@property (nullable, nonatomic, retain) DSAssetLockTransactionEntity *registrationFundingTransaction; @property (nullable, nonatomic, retain) NSSet *keyPaths; @property (nullable, nonatomic, retain) DSDashpayUserEntity *matchingDashpayUser; @property (nullable, nonatomic, retain) DSBlockchainInvitationEntity *associatedInvitation; @@ -37,10 +37,10 @@ NS_ASSUME_NONNULL_BEGIN @interface DSBlockchainIdentityEntity (CoreDataGeneratedAccessors) -- (void)addTopUpFundingTransactionsObject:(DSCreditFundingTransactionEntity *)value; -- (void)removeTopUpFundingTransactionsObject:(DSCreditFundingTransactionEntity *)value; -- (void)addTopUpFundingTransactions:(NSSet *)values; -- (void)removeTopUpFundingTransactions:(NSSet *)values; +- (void)addTopUpFundingTransactionsObject:(DSAssetLockTransactionEntity *)value; +- (void)removeTopUpFundingTransactionsObject:(DSAssetLockTransactionEntity *)value; +- (void)addTopUpFundingTransactions:(NSSet *)values; +- (void)removeTopUpFundingTransactions:(NSSet *)values; - (void)addKeyPathsObject:(DSBlockchainIdentityKeyPathEntity *)value; - (void)removeKeyPathsObject:(DSBlockchainIdentityKeyPathEntity *)value; diff --git a/DashSync/shared/Models/Entities/DSBlockchainIdentityKeyPathEntity+CoreDataProperties.h b/DashSync/shared/Models/Entities/DSBlockchainIdentityKeyPathEntity+CoreDataProperties.h index b7df527df..35bf6f7c3 100644 --- a/DashSync/shared/Models/Entities/DSBlockchainIdentityKeyPathEntity+CoreDataProperties.h +++ b/DashSync/shared/Models/Entities/DSBlockchainIdentityKeyPathEntity+CoreDataProperties.h @@ -6,7 +6,7 @@ // // -#import "DSBlockchainIdentity.h" +#import "DSIdentity.h" #import "DSBlockchainIdentityKeyPathEntity+CoreDataClass.h" NS_ASSUME_NONNULL_BEGIN diff --git a/DashSync/shared/Models/Entities/DSChainEntity+CoreDataClass.h b/DashSync/shared/Models/Entities/DSChainEntity+CoreDataClass.h index c09518fa4..7f6f9123a 100644 --- a/DashSync/shared/Models/Entities/DSChainEntity+CoreDataClass.h +++ b/DashSync/shared/Models/Entities/DSChainEntity+CoreDataClass.h @@ -23,6 +23,7 @@ // THE SOFTWARE. #import "DSChain.h" +#import "DSKeyManager.h" #import #import @@ -32,7 +33,7 @@ NS_ASSUME_NONNULL_BEGIN @interface DSChainEntity : NSManagedObject -+ (DSChainEntity *_Nonnull)chainEntityForType:(ChainType)type checkpoints:(NSArray *_Nullable)checkpoints inContext:(NSManagedObjectContext *)context; ++ (DSChainEntity *_Nonnull)chainEntityForType:(DChainType *)type checkpoints:(NSArray *_Nullable)checkpoints inContext:(NSManagedObjectContext *)context; - (instancetype)setAttributesFromChain:(DSChain *_Nonnull)chain; - (DSChain *_Nonnull)chain; diff --git a/DashSync/shared/Models/Entities/DSChainEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSChainEntity+CoreDataClass.m index d99f69eca..fc21c8f69 100644 --- a/DashSync/shared/Models/Entities/DSChainEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSChainEntity+CoreDataClass.m @@ -23,6 +23,7 @@ // THE SOFTWARE. #import "dash_shared_core.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" #import "DSChainEntity+CoreDataProperties.h" #import "DSChainLockEntity+CoreDataProperties.h" @@ -45,7 +46,7 @@ @implementation DSChainEntity @synthesize cachedChain; - (instancetype)setAttributesFromChain:(DSChain *)chain { - self.type = chain_type_index(chain.chainType); + self.type = dash_spv_crypto_network_chain_type_ChainType_index(chain.chainType); self.totalGovernanceObjectsCount = chain.totalGovernanceObjectsCount; return self; } @@ -54,7 +55,7 @@ - (DSChain *)chain { if (self.cachedChain) { return self.cachedChain; } - __block ChainType type; + __block dash_spv_crypto_network_chain_type_ChainType *type; __block NSString *devnetIdentifier; __block uint16_t devnetVersion; __block NSData *data; @@ -68,7 +69,7 @@ - (DSChain *)chain { __block NSArray *lastPersistedChainSyncLocators; [self.managedObjectContext performBlockAndWait:^{ - type = chain_type_from_index(self.type); + type = dash_spv_crypto_network_chain_type_chain_type_from_index(self.type); devnetIdentifier = self.devnetIdentifier; devnetVersion = self.devnetVersion; data = self.checkpoints; @@ -81,17 +82,18 @@ - (DSChain *)chain { lastPersistedChainSyncBlockTimestamp = self.syncBlockTimestamp; }]; DSChain *chain = nil; - if (type.tag == ChainType_MainNet) { + + if (type->tag == dash_spv_crypto_network_chain_type_ChainType_MainNet) { chain = [DSChain mainnet]; - } else if (type.tag == ChainType_TestNet) { + } else if (type->tag == dash_spv_crypto_network_chain_type_ChainType_TestNet) { chain = [DSChain testnet]; - } else if (type.tag == ChainType_DevNet) { + } else if (type->tag == dash_spv_crypto_network_chain_type_ChainType_DevNet) { if ([DSChain devnetWithIdentifier:devnetIdentifier]) { chain = [DSChain devnetWithIdentifier:devnetIdentifier]; } else { NSError *checkpointRetrievalError = nil; NSArray *checkpointArray = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:data error:&checkpointRetrievalError]; - chain = [DSChain recoverKnownDevnetWithIdentifier:devnet_type_for_chain_type(type) withCheckpoints:checkpointRetrievalError ? @[] : checkpointArray performSetup:YES]; + chain = [DSChain recoverKnownDevnetWithIdentifier:dash_spv_crypto_network_chain_type_ChainType_devnet_type(type) withCheckpoints:checkpointRetrievalError ? @[] : checkpointArray performSetup:YES]; } } else { NSAssert(FALSE, @"Unknown ChainType"); @@ -123,10 +125,12 @@ - (DSChain *)chain { return chain; } -+ (DSChainEntity *)chainEntityForType:(ChainType)type checkpoints:(NSArray *)checkpoints inContext:(NSManagedObjectContext *)context { ++ (DSChainEntity *)chainEntityForType:(dash_spv_crypto_network_chain_type_ChainType *)type + checkpoints:(NSArray *)checkpoints + inContext:(NSManagedObjectContext *)context { NSString *devnetIdentifier = [DSKeyManager devnetIdentifierFor:type]; - int16_t devnetVersion = devnet_version_for_chain_type(type); - NSArray *objects = [DSChainEntity objectsForPredicate:[NSPredicate predicateWithFormat:@"type = %d && ((type != %d) || devnetIdentifier = %@)", type, ChainType_DevNet, devnetIdentifier] inContext:context]; + int16_t *devnetVersion = dash_spv_crypto_network_chain_type_ChainType_devnet_version(type); + NSArray *objects = [DSChainEntity objectsForPredicate:[NSPredicate predicateWithFormat:@"type = %d && ((type != %d) || devnetIdentifier = %@)", type->tag, dash_spv_crypto_network_chain_type_ChainType_DevNet, devnetIdentifier] inContext:context]; if (objects.count) { NSAssert(objects.count == 1, @"There should only ever be 1 chain for either mainnet, testnet, or a devnet Identifier"); if (objects.count > 1) { @@ -135,7 +139,7 @@ + (DSChainEntity *)chainEntityForType:(ChainType)type checkpoints:(NSArray *)che DSChainEntity *chainEntityToRemove = objects[i]; [context deleteObject:chainEntityToRemove]; [context ds_save]; - DSLog(@"Removing extra chain entity of type %d", type.tag); + DSLog(@"Removing extra chain entity of type %d", type->tag); } } DSChainEntity *chainEntity = [objects objectAtIndex:0]; @@ -156,9 +160,9 @@ + (DSChainEntity *)chainEntityForType:(ChainType)type checkpoints:(NSArray *)che } DSChainEntity *chainEntity = [self managedObjectInBlockedContext:context]; - chainEntity.type = (uint16_t) type.tag; + chainEntity.type = (uint16_t) type->tag; chainEntity.devnetIdentifier = devnetIdentifier; - chainEntity.devnetVersion = devnetVersion; + chainEntity.devnetVersion = devnetVersion ? *devnetVersion : 0; if (checkpoints && devnetIdentifier) { NSError *error = nil; NSData *archivedCheckpoints = [NSKeyedArchiver archivedDataWithRootObject:checkpoints requiringSecureCoding:NO error:&error]; diff --git a/DashSync/shared/Models/Entities/DSChainLockEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSChainLockEntity+CoreDataClass.m index 50e4e1a91..5814c0d5e 100644 --- a/DashSync/shared/Models/Entities/DSChainLockEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSChainLockEntity+CoreDataClass.m @@ -13,16 +13,15 @@ #import "DSChainLockEntity+CoreDataClass.h" #import "DSMerkleBlock.h" #import "DSMerkleBlockEntity+CoreDataClass.h" -#import "DSQuorumEntry.h" #import "DSQuorumEntryEntity+CoreDataClass.h" -#import "DSSimplifiedMasternodeEntry.h" #import "NSData+Dash.h" #import "NSManagedObject+Sugar.h" @implementation DSChainLockEntity -+ (instancetype)chainLockEntityForChainLock:(DSChainLock *)chainLock inContext:(NSManagedObjectContext *)context { - DSMerkleBlockEntity *merkleBlockEntity = [DSMerkleBlockEntity merkleBlockEntityForBlockHash:chainLock.blockHash inContext:context]; ++ (instancetype)chainLockEntityForChainLock:(DSChainLock *)chainLock + inContext:(NSManagedObjectContext *)context { + DSMerkleBlockEntity *merkleBlockEntity = [DSMerkleBlockEntity merkleBlockEntityForBlockHash:uint256_data(chainLock.blockHash) inContext:context]; if (!merkleBlockEntity) { return nil; } @@ -30,9 +29,12 @@ + (instancetype)chainLockEntityForChainLock:(DSChainLock *)chainLock inContext:( chainLockEntity.validSignature = chainLock.signatureVerified; chainLockEntity.signature = [NSData dataWithUInt768:chainLock.signature]; chainLockEntity.merkleBlock = merkleBlockEntity; - chainLockEntity.quorum = [chainLock.intendedQuorum matchingQuorumEntryEntityInContext:context]; //the quorum might not yet + chainLockEntity.quorum = [DSQuorumEntryEntity anyObjectInContext:context matching:@"quorumPublicKeyData == %@", NSDataFromPtr(chainLock.intendedQuorumPublicKey)]; +// chainLockEntity.quorum = [chainLock.intendedQuorum matchingQuorumEntryEntityInContext:context]; //the quorum might not yet if (chainLock.signatureVerified) { - DSChainEntity *chainEntity = [chainLock.intendedQuorum.chain chainEntityInContext:context]; + +// DSChainEntity *chainEntity = [chainLock.intendedQuorum.chain chainEntityInContext:context]; + DSChainEntity *chainEntity = [chainLock.chain chainEntityInContext:context]; if (!chainEntity.lastChainLock || chainEntity.lastChainLock.merkleBlock.height < chainLock.height) { chainEntity.lastChainLock = chainLockEntity; } diff --git a/DashSync/shared/Models/Entities/DSContactRequest.h b/DashSync/shared/Models/Entities/DSContactRequest.h index eb0c65be5..b407be734 100644 --- a/DashSync/shared/Models/Entities/DSContactRequest.h +++ b/DashSync/shared/Models/Entities/DSContactRequest.h @@ -17,17 +17,18 @@ #import "BigIntTypes.h" #import "dash_shared_core.h" +#import "DSKeyManager.h" #import "DPTypes.h" #import -@class DSBlockchainIdentity; +@class DSIdentity; NS_ASSUME_NONNULL_BEGIN @interface DSContactRequest : NSObject -@property (nonatomic, readonly) UInt256 recipientBlockchainIdentityUniqueId; -@property (nonatomic, readonly) UInt256 senderBlockchainIdentityUniqueId; +@property (nonatomic, readonly) UInt256 recipientIdentityUniqueId; +@property (nonatomic, readonly) UInt256 senderIdentityUniqueId; @property (nonatomic, readonly) uint32_t recipientKeyIndex; @property (nonatomic, readonly) uint32_t senderKeyIndex; @property (nonatomic, readonly) uint32_t accountReference; @@ -37,9 +38,10 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) NSData *encryptedPublicKeyData; -+ (instancetype)contactRequestFromDictionary:(DSStringValueDictionary *)serverDictionary onBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity; ++ (instancetype)contactRequestFromDictionary:(DSStringValueDictionary *)serverDictionary + onIdentity:(DSIdentity *)identity; -- (NSData *)decryptedPublicKeyDataWithKey:(OpaqueKey *)key; +- (NSData *)decryptedPublicKeyDataWithKey:(DOpaqueKey *)key; @end diff --git a/DashSync/shared/Models/Entities/DSContactRequest.m b/DashSync/shared/Models/Entities/DSContactRequest.m index da7aabb5c..62a5bde15 100644 --- a/DashSync/shared/Models/Entities/DSContactRequest.m +++ b/DashSync/shared/Models/Entities/DSContactRequest.m @@ -16,15 +16,15 @@ // #import "DSContactRequest.h" -#import "DSBlockchainIdentity+Protected.h" +#import "DSIdentity+Protected.h" #import "NSData+Dash.h" #import "NSData+Encryption.h" #import "NSString+Bitcoin.h" @interface DSContactRequest () -@property (nonatomic, assign) UInt256 recipientBlockchainIdentityUniqueId; -@property (nonatomic, assign) UInt256 senderBlockchainIdentityUniqueId; +@property (nonatomic, assign) UInt256 recipientIdentityUniqueId; +@property (nonatomic, assign) UInt256 senderIdentityUniqueId; @property (nonatomic, assign) uint32_t recipientKeyIndex; @property (nonatomic, assign) uint32_t senderKeyIndex; @property (nonatomic, assign) uint32_t accountReference; @@ -33,15 +33,15 @@ @interface DSContactRequest () @property (nonatomic, assign) NSTimeInterval createdAt; @property (nonatomic, strong) NSData *encryptedPublicKeyData; -@property (nonatomic, strong) DSBlockchainIdentity *blockchainIdentity; +@property (nonatomic, strong) DSIdentity *identity; @end @implementation DSContactRequest -- (instancetype)initWithDictionary:(DSStringValueDictionary *)rawContact onBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity { +- (instancetype)initWithDictionary:(DSStringValueDictionary *)rawContact onIdentity:(DSIdentity *)identity { NSParameterAssert(rawContact); - NSParameterAssert(blockchainIdentity); + NSParameterAssert(identity); if (!(self = [super init])) return nil; NSData *recipientData = rawContact[@"toUserId"]; @@ -56,27 +56,28 @@ - (instancetype)initWithDictionary:(DSStringValueDictionary *)rawContact onBlock NSAssert(FALSE, @"malformed server response"); return nil; } - self.recipientBlockchainIdentityUniqueId = recipientData.UInt256; - self.senderBlockchainIdentityUniqueId = senderData.UInt256; + self.recipientIdentityUniqueId = recipientData.UInt256; + self.senderIdentityUniqueId = senderData.UInt256; self.encryptedPublicKeyData = encryptedPublicKeyData; self.encryptedAccountLabel = encryptedAccountLabel; self.accountReference = [accountReference unsignedIntValue]; self.createdAt = [createdAt doubleValue] / 1000.0; self.recipientKeyIndex = [recipientKeyIndex unsignedIntValue]; self.senderKeyIndex = [senderKeyIndex unsignedIntValue]; - self.blockchainIdentity = blockchainIdentity; + self.identity = identity; return self; } -+ (instancetype)contactRequestFromDictionary:(DSStringValueDictionary *)serverDictionary onBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity { - return [[self alloc] initWithDictionary:serverDictionary onBlockchainIdentity:blockchainIdentity]; ++ (instancetype)contactRequestFromDictionary:(DSStringValueDictionary *)serverDictionary + onIdentity:(DSIdentity *)identity { + return [[self alloc] initWithDictionary:serverDictionary onIdentity:identity]; } -- (BOOL)blockchainIdentityIsRecipient { - if (uint256_eq(self.blockchainIdentity.uniqueID, self.recipientBlockchainIdentityUniqueId)) { +- (BOOL)identityIsRecipient { + if (uint256_eq(self.identity.uniqueID, self.recipientIdentityUniqueId)) { //we are the recipient of the friend request return YES; - } else if (uint256_eq(self.blockchainIdentity.uniqueID, self.senderBlockchainIdentityUniqueId)) { + } else if (uint256_eq(self.identity.uniqueID, self.senderIdentityUniqueId)) { //we are the sender of the friend request return NO; } @@ -84,20 +85,24 @@ - (BOOL)blockchainIdentityIsRecipient { return NO; } -- (OpaqueKey *)secretKeyForDecryptionOfType:(KeyKind)type { - uint32_t index = [self blockchainIdentityIsRecipient] ? self.recipientKeyIndex : self.senderKeyIndex; - OpaqueKey *key = [self.blockchainIdentity privateKeyAtIndex:index ofType:type]; +- (DMaybeOpaqueKey *)secretKeyForDecryptionOfType:(DKeyKind *)type { + uint32_t index = [self identityIsRecipient] ? self.recipientKeyIndex : self.senderKeyIndex; + DMaybeOpaqueKey *key = [self.identity privateKeyAtIndex:index ofType:type]; NSAssert(key, @"Key should exist"); return key; } -- (NSData *)decryptedPublicKeyDataWithKey:(OpaqueKey *)key { +- (NSData *)decryptedPublicKeyDataWithKey:(DOpaqueKey *)key { NSParameterAssert(key); - return [self.encryptedPublicKeyData decryptWithSecretKey:[self secretKeyForDecryptionOfType:(int16_t) key->tag] fromPublicKey:key]; + DKeyKind *kind = dash_spv_crypto_keys_key_OpaqueKey_kind(key); + DMaybeOpaqueKey *maybe_key = [self secretKeyForDecryptionOfType:kind]; + NSData *data = [self.encryptedPublicKeyData decryptWithSecretKey:maybe_key->ok fromPublicKey:key]; + DMaybeOpaqueKeyDtor(maybe_key); + return data; } - (NSString *)debugDescription { - return [NSString stringWithFormat:@"%@ - from %@/%d to %@/%d", [super debugDescription], uint256_base58(self.senderBlockchainIdentityUniqueId), self.senderKeyIndex, uint256_base58(self.recipientBlockchainIdentityUniqueId), self.recipientKeyIndex]; + return [NSString stringWithFormat:@"%@ - from %@/%d to %@/%d", [super debugDescription], uint256_base58(self.senderIdentityUniqueId), self.senderKeyIndex, uint256_base58(self.recipientIdentityUniqueId), self.recipientKeyIndex]; } @end diff --git a/DashSync/shared/Models/Entities/DSContractEntity+CoreDataClass.h b/DashSync/shared/Models/Entities/DSContractEntity+CoreDataClass.h index 8b98e38b5..e3fbdf9f9 100644 --- a/DashSync/shared/Models/Entities/DSContractEntity+CoreDataClass.h +++ b/DashSync/shared/Models/Entities/DSContractEntity+CoreDataClass.h @@ -8,13 +8,18 @@ #import #import +#import "NSManagedObject+Sugar.h" -@class DSBlockchainIdentityEntity, DSChainEntity; +@class DSBlockchainIdentityEntity, DSChainEntity, DSChain; NS_ASSUME_NONNULL_BEGIN @interface DSContractEntity : NSManagedObject ++ (instancetype)entityWithLocalContractIdentifier:(NSString *)identifier + onChain:(DSChain *)chain + inContext:(NSManagedObjectContext *)context; + @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Entities/DSContractEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSContractEntity+CoreDataClass.m index 67144ea83..a028ab500 100644 --- a/DashSync/shared/Models/Entities/DSContractEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSContractEntity+CoreDataClass.m @@ -7,7 +7,14 @@ // #import "DSContractEntity+CoreDataClass.h" +#import "DSChain.h" @implementation DSContractEntity ++ (instancetype)entityWithLocalContractIdentifier:(NSString *)identifier + onChain:(DSChain *)chain + inContext:(NSManagedObjectContext *)context { + return [DSContractEntity anyObjectInContext:context matching:@"localContractIdentifier == %@ && chain == %@", identifier, [chain chainEntityInContext:context]]; +} + @end diff --git a/DashSync/shared/Models/Entities/DSCreditFundingTransactionEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSCreditFundingTransactionEntity+CoreDataClass.m index 1a382dbce..74a69d4e0 100644 --- a/DashSync/shared/Models/Entities/DSCreditFundingTransactionEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSCreditFundingTransactionEntity+CoreDataClass.m @@ -7,7 +7,7 @@ // #import "DSAccount.h" -#import "DSBlockchainIdentity+Protected.h" +#import "DSIdentity+Protected.h" #import "DSBlockchainIdentityEntity+CoreDataClass.h" #import "DSCreditFundingTransaction.h" #import "DSCreditFundingTransactionEntity+CoreDataClass.h" @@ -38,8 +38,8 @@ - (DSTransaction *)transactionForChain:(DSChain *)chain { // [super setAttributesFromTransaction:tx]; // DSCreditFundingTransaction * creditFundingTransaction = (DSCreditFundingTransaction *)tx; // DSWallet * wallet = tx.account.wallet; -// DSBlockchainIdentity * identity = [wallet blockchainIdentityForUniqueId:creditFundingTransaction.creditBurnIdentityIdentifier]; -// self.blockchainIdentity = identity.blockchainIdentityEntity; +// DSIdentity * identity = [wallet identityForUniqueId:creditFundingTransaction.creditBurnIdentityIdentifier]; +// self.identity = identity.identityEntity; // }]; // // return self; diff --git a/DashSync/shared/Models/Entities/DSDashpayUserEntity+CoreDataClass.h b/DashSync/shared/Models/Entities/DSDashpayUserEntity+CoreDataClass.h index afe661408..375d2f044 100644 --- a/DashSync/shared/Models/Entities/DSDashpayUserEntity+CoreDataClass.h +++ b/DashSync/shared/Models/Entities/DSDashpayUserEntity+CoreDataClass.h @@ -27,7 +27,7 @@ typedef NS_ENUM(NSUInteger, DSDashpayUserEntityFriendActivityType) DSDashpayUserEntityFriendActivityType_OutgoingTransactions }; -@class DSAccountEntity, DSFriendRequestEntity, DSTransitionEntity, DSTransientDashpayUser, DSBlockchainIdentity, DSPotentialOneWayFriendship, DSWallet, DSIncomingFundsDerivationPath, DSChainEntity, DSBlockchainIdentityEntity, DPDocument; +@class DSAccountEntity, DSFriendRequestEntity, DSTransitionEntity, DSTransientDashpayUser, DSIdentity, DSPotentialOneWayFriendship, DSWallet, DSIncomingFundsDerivationPath, DSChainEntity, DSBlockchainIdentityEntity, DPDocument; NS_ASSUME_NONNULL_BEGIN diff --git a/DashSync/shared/Models/Entities/DSDerivationPathEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSDerivationPathEntity+CoreDataClass.m index a56c2580b..fee5dd003 100644 --- a/DashSync/shared/Models/Entities/DSDerivationPathEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSDerivationPathEntity+CoreDataClass.m @@ -36,7 +36,8 @@ @implementation DSDerivationPathEntity -+ (DSDerivationPathEntity *_Nonnull)derivationPathEntityMatchingDerivationPath:(DSDerivationPath *)derivationPath inContext:(NSManagedObjectContext *)context { ++ (DSDerivationPathEntity *_Nonnull)derivationPathEntityMatchingDerivationPath:(DSDerivationPath *)derivationPath + inContext:(NSManagedObjectContext *)context { NSAssert(derivationPath.standaloneExtendedPublicKeyUniqueID, @"standaloneExtendedPublicKeyUniqueID must be set"); //DSChain * chain = derivationPath.chain; NSArray *derivationPathEntities; @@ -59,12 +60,15 @@ + (DSDerivationPathEntity *_Nonnull)derivationPathEntityMatchingDerivationPath:( derivationPathEntity.publicKeyIdentifier = derivationPath.standaloneExtendedPublicKeyUniqueID; derivationPathEntity.syncBlockHeight = BIP39_CREATION_TIME; if (derivationPath.account) { - derivationPathEntity.account = [DSAccountEntity accountEntityForWalletUniqueID:derivationPath.account.wallet.uniqueIDString index:derivationPath.account.accountNumber onChain:derivationPath.chain inContext:context]; + derivationPathEntity.account = [DSAccountEntity accountEntityForWalletUniqueID:derivationPath.account.wallet.uniqueIDString + index:derivationPath.account.accountNumber + onChain:derivationPath.chain + inContext:context]; } if ([derivationPath isKindOfClass:[DSIncomingFundsDerivationPath class]]) { //NSLog(@"--->creating derivation path entity on path %@ (%@) with no friendship identifier %@", derivationPath, derivationPath.stringRepresentation, [NSThread callStackSymbols]); DSIncomingFundsDerivationPath *incomingFundsDerivationPath = (DSIncomingFundsDerivationPath *)derivationPath; - NSPredicate *predicatee = [NSPredicate predicateWithFormat:@"sourceContact.associatedBlockchainIdentity.uniqueID == %@ && destinationContact.associatedBlockchainIdentity.uniqueID == %@", uint256_data(incomingFundsDerivationPath.contactSourceBlockchainIdentityUniqueId), uint256_data(incomingFundsDerivationPath.contactDestinationBlockchainIdentityUniqueId)]; + NSPredicate *predicatee = [NSPredicate predicateWithFormat:@"sourceContact.associatedBlockchainIdentity.uniqueID == %@ && destinationContact.associatedBlockchainIdentity.uniqueID == %@", uint256_data(incomingFundsDerivationPath.contactSourceIdentityUniqueId), uint256_data(incomingFundsDerivationPath.contactDestinationIdentityUniqueId)]; DSFriendRequestEntity *friendRequest = [DSFriendRequestEntity anyObjectForPredicate:predicatee inContext:context]; if (friendRequest) { derivationPathEntity.friendRequest = friendRequest; @@ -75,7 +79,9 @@ + (DSDerivationPathEntity *_Nonnull)derivationPathEntityMatchingDerivationPath:( } } -+ (DSDerivationPathEntity *_Nonnull)derivationPathEntityMatchingDerivationPath:(DSIncomingFundsDerivationPath *)derivationPath associateWithFriendRequest:(DSFriendRequestEntity *)friendRequest inContext:(NSManagedObjectContext *)context { ++ (DSDerivationPathEntity *_Nonnull)derivationPathEntityMatchingDerivationPath:(DSIncomingFundsDerivationPath *)derivationPath + associateWithFriendRequest:(DSFriendRequestEntity *)friendRequest + inContext:(NSManagedObjectContext *)context { NSAssert(derivationPath.standaloneExtendedPublicKeyUniqueID, @"standaloneExtendedPublicKeyUniqueID must be set"); NSParameterAssert(friendRequest); @@ -96,7 +102,10 @@ + (DSDerivationPathEntity *_Nonnull)derivationPathEntityMatchingDerivationPath:( derivationPathEntity.publicKeyIdentifier = derivationPath.standaloneExtendedPublicKeyUniqueID; derivationPathEntity.syncBlockHeight = BIP39_CREATION_TIME; if (derivationPath.account) { - derivationPathEntity.account = [DSAccountEntity accountEntityForWalletUniqueID:derivationPath.account.wallet.uniqueIDString index:derivationPath.account.accountNumber onChain:derivationPath.chain inContext:context]; + derivationPathEntity.account = [DSAccountEntity accountEntityForWalletUniqueID:derivationPath.account.wallet.uniqueIDString + index:derivationPath.account.accountNumber + onChain:derivationPath.chain + inContext:context]; } derivationPathEntity.friendRequest = friendRequest; diff --git a/DashSync/shared/Models/Entities/DSFriendRequestEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSFriendRequestEntity+CoreDataClass.m index 0975c5589..944685dd5 100644 --- a/DashSync/shared/Models/Entities/DSFriendRequestEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSFriendRequestEntity+CoreDataClass.m @@ -89,7 +89,7 @@ - (void)sendAmount:(uint64_t)amount fromAccount:(DSAccount *)account requestingA NSAssert([paymentRequest isValidAsNonDashpayPaymentRequest], @"Payment request must be valid"); [account.wallet.chain.chainManager.transactionManager confirmPaymentRequest:paymentRequest - usingUserBlockchainIdentity:nil + usingUserIdentity:nil fromAccount:account acceptInternalAddress:NO acceptReusingAddress:YES diff --git a/DashSync/shared/Models/Entities/DSInstantSendLockEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSInstantSendLockEntity+CoreDataClass.m index 14de18afa..6e0b2dc78 100644 --- a/DashSync/shared/Models/Entities/DSInstantSendLockEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSInstantSendLockEntity+CoreDataClass.m @@ -10,7 +10,6 @@ #import "DSChainEntity+CoreDataClass.h" #import "DSInstantSendLockEntity+CoreDataClass.h" #import "DSInstantSendTransactionLock.h" -#import "DSQuorumEntry.h" #import "DSQuorumEntryEntity+CoreDataClass.h" #import "DSSimplifiedMasternodeEntry.h" #import "DSTransactionEntity+CoreDataClass.h" @@ -30,7 +29,9 @@ + (DSInstantSendLockEntity *)instantSendLockEntityFromInstantSendLock:(DSInstant NSAssert(transactionEntity, @"transaction must exist"); entity.transaction = transactionEntity; - entity.quorum = [instantSendTransactionLock.intendedQuorum matchingQuorumEntryEntityInContext:context]; //the quorum might not yet + + entity.quorum = [DSQuorumEntryEntity anyObjectInContext:context matching:@"quorumPublicKeyData == %@", NSDataFromPtr(instantSendTransactionLock.intendedQuorumPublicKey)]; +// entity.quorum = [instantSendTransactionLock.intendedQuorum matchingQuorumEntryEntityInContext:context]; //the quorum might not yet } return nil; @@ -43,7 +44,8 @@ - (instancetype)setAttributesFromInstantSendTransactionLock:(DSInstantSendTransa DSTransactionEntity *transactionEntity = [DSTransactionEntity anyObjectInContext:self.managedObjectContext matching:@"transactionHash.txHash == %@", uint256_data(instantSendTransactionLock.transactionHash)]; NSAssert(transactionEntity, @"transaction must exist"); self.transaction = transactionEntity; - self.quorum = [instantSendTransactionLock.intendedQuorum matchingQuorumEntryEntityInContext:self.managedObjectContext]; //the quorum might not yet + self.quorum = [DSQuorumEntryEntity anyObjectInContext:self.managedObjectContext matching:@"quorumPublicKeyData == %@", NSDataFromPtr(instantSendTransactionLock.intendedQuorumPublicKey)]; +// self.quorum = [instantSendTransactionLock.intendedQuorum matchingQuorumEntryEntityInContext:self.managedObjectContext]; //the quorum might not yet }]; return self; diff --git a/DashSync/shared/Models/Entities/DSMasternodeListEntity+CoreDataClass.h b/DashSync/shared/Models/Entities/DSMasternodeListEntity+CoreDataClass.h index ea4882528..7c216ea54 100644 --- a/DashSync/shared/Models/Entities/DSMasternodeListEntity+CoreDataClass.h +++ b/DashSync/shared/Models/Entities/DSMasternodeListEntity+CoreDataClass.h @@ -7,18 +7,22 @@ // #import "BigIntTypes.h" +#import "dash_shared_core.h" +#import "DSKeyManager.h" #import #import -@class DSMerkleBlockEntity, DSQuorumEntryEntity, DSSimplifiedMasternodeEntryEntity, DSMasternodeList, DSSimplifiedMasternodeEntry, DSQuorumEntry, DSChainEntity; +@class DSMerkleBlockEntity, DSQuorumEntryEntity, DSSimplifiedMasternodeEntryEntity, DSChainEntity; NS_ASSUME_NONNULL_BEGIN @interface DSMasternodeListEntity : NSManagedObject -- (DSMasternodeList *)masternodeListWithSimplifiedMasternodeEntryPool:(NSDictionary *)simplifiedMasternodeEntries quorumEntryPool:(NSDictionary *)quorumEntries; +- (DArcMasternodeList *)masternodeListWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; -- (DSMasternodeList *)masternodeListWithSimplifiedMasternodeEntryPool:(NSDictionary *)simplifiedMasternodeEntries quorumEntryPool:(NSDictionary *)quorumEntries withBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; +//- (DArcMasternodeList *)masternodeListWithSimplifiedMasternodeEntryPool:(DMasternodeEntryMap *)simplifiedMasternodeEntries +// quorumEntryPool:(DLLMQMap *)quorumEntries +// withBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; + (void)deleteAllOnChainEntity:(DSChainEntity *)chainEntity; diff --git a/DashSync/shared/Models/Entities/DSMasternodeListEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSMasternodeListEntity+CoreDataClass.m index c9dcadd50..021a1a790 100644 --- a/DashSync/shared/Models/Entities/DSMasternodeListEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSMasternodeListEntity+CoreDataClass.m @@ -6,9 +6,7 @@ // // -#import "BigIntTypes.h" #import "DSChainEntity+CoreDataClass.h" -#import "DSMasternodeList.h" #import "DSMasternodeListEntity+CoreDataClass.h" #import "DSMerkleBlockEntity+CoreDataClass.h" #import "DSQuorumEntryEntity+CoreDataClass.h" @@ -16,33 +14,50 @@ #import "NSData+Dash.h" #import "NSManagedObject+Sugar.h" -@implementation DSMasternodeListEntity -- (DSMasternodeList *)masternodeListWithSimplifiedMasternodeEntryPool:(NSDictionary *)simplifiedMasternodeEntries quorumEntryPool:(NSDictionary *)quorumEntries { - return [self masternodeListWithSimplifiedMasternodeEntryPool:simplifiedMasternodeEntries quorumEntryPool:quorumEntries withBlockHeightLookup:nil]; -} +@implementation DSMasternodeListEntity -- (DSMasternodeList *)masternodeListWithSimplifiedMasternodeEntryPool:(NSDictionary *)simplifiedMasternodeEntries quorumEntryPool:(NSDictionary *)quorumEntries withBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { - - /// TODO: it's a BS to collect this stuff into arrays and then to recollect it into dictionaries in the next step... - NSMutableArray *masternodeEntriesArray = [NSMutableArray array]; - +- (DArcMasternodeList *)masternodeListWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { + DMasternodeEntry **masternodes = malloc(self.masternodes.count * sizeof(DMasternodeEntry *)); + DLLMQEntry **quorums = malloc(self.quorums.count * sizeof(DLLMQEntry *)); + uintptr_t masternodes_count = 0; for (DSSimplifiedMasternodeEntryEntity *masternodeEntity in self.masternodes) { - DSSimplifiedMasternodeEntry *masternodeEntry = [simplifiedMasternodeEntries objectForKey:masternodeEntity.providerRegistrationTransactionHash]; - if (!masternodeEntry) { - masternodeEntry = [masternodeEntity simplifiedMasternodeEntryWithBlockHeightLookup:blockHeightLookup]; - } - [masternodeEntriesArray addObject:masternodeEntry]; + DMasternodeEntry *entry = [masternodeEntity simplifiedMasternodeEntryWithBlockHeightLookup:blockHeightLookup]; + masternodes[masternodes_count] = entry; + masternodes_count++; } - NSMutableArray *quorumEntriesArray = [NSMutableArray array]; + uintptr_t quorums_count = 0; for (DSQuorumEntryEntity *quorumEntity in self.quorums) { - DSQuorumEntry *quorumEntry = [[quorumEntries objectForKey:@(quorumEntity.llmqType)] objectForKey:quorumEntity.quorumHashData]; - if (!quorumEntry) { - quorumEntry = quorumEntity.quorumEntry; - } - [quorumEntriesArray addObject:quorumEntry]; + uint16_t version = quorumEntity.version; + int16_t llmq_type = quorumEntity.llmqType; + int32_t llmq_index = quorumEntity.quorumIndex; + BOOL verified = quorumEntity.verified; + u256 *llmq_hash = u256_ctor(quorumEntity.quorumHashData); + BYTES *signers = bytes_ctor(quorumEntity.signersBitset); + int32_t signers_count = quorumEntity.signersCount; + BYTES *valid_members = bytes_ctor(quorumEntity.validMembersBitset); + int32_t valid_members_count = quorumEntity.validMembersCount; + u384 *public_key = u384_ctor(quorumEntity.quorumPublicKeyData); + u256 *verification_vector_hash = u256_ctor(quorumEntity.quorumVerificationVectorHashData); + u768 *threshold_signature = u768_ctor(quorumEntity.quorumThresholdSignatureData); + u768 *all_commitment_aggregated_signature = u768_ctor(quorumEntity.allCommitmentAggregatedSignatureData); + // yes this is crazy but this is correct (legacy) + u256 *entry_hash = u256_ctor(quorumEntity.commitmentHashData); + DLLMQEntry *entry = dash_spv_crypto_llmq_entry_from_entity(version, llmq_type, llmq_hash, llmq_index, signers, signers_count, valid_members, valid_members_count, public_key, verification_vector_hash, threshold_signature, all_commitment_aggregated_signature, verified, entry_hash); + quorums[quorums_count] = entry; + quorums_count++; } - return [DSMasternodeList masternodeListWithSimplifiedMasternodeEntries:masternodeEntriesArray quorumEntries:quorumEntriesArray atBlockHash:self.block.blockHash.UInt256 atBlockHeight:self.block.height withMasternodeMerkleRootHash:self.masternodeListMerkleRoot.UInt256 withQuorumMerkleRootHash:self.quorumListMerkleRoot.UInt256 onChain:self.block.chain.chain]; + uint32_t block_height = self.block.height; + u256 *block_hash = u256_ctor(self.block.blockHash); + u256 *mn_merkle_root = u256_ctor(self.masternodeListMerkleRoot); + u256 *llmq_merkle_root = u256_ctor(self.quorumListMerkleRoot); + DMasternodeEntryList *masternodes_vec = DMasternodeEntryListCtor(masternodes_count, masternodes); + DLLMQEntryList *quorums_vec = DLLMQEntryListCtor(quorums_count, quorums); +// DSLog(@"•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••"); + DMasternodeList *list = dash_spv_masternode_processor_models_masternode_list_from_entry_pool(block_hash, block_height, mn_merkle_root, llmq_merkle_root, masternodes_vec, quorums_vec); +// DSLog(@"•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••"); + + return std_sync_Arc_dash_spv_masternode_processor_models_masternode_list_MasternodeList_ctor(list); } + (void)deleteAllOnChainEntity:(DSChainEntity *)chainEntity { diff --git a/DashSync/shared/Models/Entities/DSMerkleBlockEntity+CoreDataClass.h b/DashSync/shared/Models/Entities/DSMerkleBlockEntity+CoreDataClass.h index f82e7854a..c53e43800 100644 --- a/DashSync/shared/Models/Entities/DSMerkleBlockEntity+CoreDataClass.h +++ b/DashSync/shared/Models/Entities/DSMerkleBlockEntity+CoreDataClass.h @@ -40,9 +40,11 @@ NS_ASSUME_NONNULL_BEGIN + (DSMerkleBlockEntity *)blockWithHash:(UInt256)hash onChainEntity:(DSChainEntity *)chainEntity; + (void)deleteBlocksOnChainEntity:(DSChainEntity *)chainEntity; -+ (instancetype)merkleBlockEntityForBlockHash:(UInt256)blockHash inContext:(NSManagedObjectContext *)context; -+ (instancetype)merkleBlockEntityForBlockHashFromCheckpoint:(UInt256)blockHash chain:(DSChain *)chain inContext:(NSManagedObjectContext *)context; -+ (instancetype)createMerkleBlockEntityForBlockHash:(UInt256)blockHash ++ (instancetype)merkleBlockEntityForBlockHash:(NSData *)blockHash inContext:(NSManagedObjectContext *)context; ++ (instancetype)merkleBlockEntityForBlockHashFromCheckpoint:(UInt256)blockHash + chain:(DSChain *)chain + inContext:(NSManagedObjectContext *)context; ++ (instancetype)createMerkleBlockEntityForBlockHash:(NSData *)blockHash blockHeight:(uint32_t)blockHeight chainEntity:(DSChainEntity *)chainEntity inContext:(NSManagedObjectContext *)context; diff --git a/DashSync/shared/Models/Entities/DSMerkleBlockEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSMerkleBlockEntity+CoreDataClass.m index 80e367643..a760d3969 100644 --- a/DashSync/shared/Models/Entities/DSMerkleBlockEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSMerkleBlockEntity+CoreDataClass.m @@ -21,6 +21,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#import "DSChain+Checkpoint.h" #import "DSChain+Protected.h" #import "DSChainEntity+CoreDataClass.h" #import "DSChainLockEntity+CoreDataClass.h" @@ -147,8 +148,8 @@ + (void)deleteBlocksOnChainEntity:(DSChainEntity *)chainEntity { }]; } -+ (instancetype)merkleBlockEntityForBlockHash:(UInt256)blockHash inContext:(NSManagedObjectContext *)context { - DSMerkleBlockEntity *merkleBlockEntity = [DSMerkleBlockEntity anyObjectInContext:context matching:@"blockHash == %@", uint256_data(blockHash)]; ++ (instancetype)merkleBlockEntityForBlockHash:(NSData *)blockHash inContext:(NSManagedObjectContext *)context { + DSMerkleBlockEntity *merkleBlockEntity = [DSMerkleBlockEntity anyObjectInContext:context matching:@"blockHash == %@", blockHash]; return merkleBlockEntity; } @@ -162,12 +163,12 @@ + (instancetype)merkleBlockEntityForBlockHashFromCheckpoint:(UInt256)blockHash c return nil; } -+ (instancetype)createMerkleBlockEntityForBlockHash:(UInt256)blockHash ++ (instancetype)createMerkleBlockEntityForBlockHash:(NSData *)blockHash blockHeight:(uint32_t)blockHeight chainEntity:(DSChainEntity *)chainEntity inContext:(NSManagedObjectContext *)context { DSMerkleBlockEntity *merkleBlockEntity = [DSMerkleBlockEntity managedObjectInBlockedContext:context]; - merkleBlockEntity.blockHash = uint256_data(blockHash); + merkleBlockEntity.blockHash = blockHash; merkleBlockEntity.height = blockHeight; merkleBlockEntity.chain = chainEntity; return merkleBlockEntity; diff --git a/DashSync/shared/Models/Entities/DSPeerEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSPeerEntity+CoreDataClass.m index d9d23deff..f0a07ae26 100644 --- a/DashSync/shared/Models/Entities/DSPeerEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSPeerEntity+CoreDataClass.m @@ -23,6 +23,7 @@ // THE SOFTWARE. #import "DSChain.h" +#import "DSChain+Params.h" #import "DSChainEntity+CoreDataClass.h" #import "DSPeer.h" #import "DSPeerEntity+CoreDataClass.h" diff --git a/DashSync/shared/Models/Entities/DSQuorumEntryEntity+CoreDataClass.h b/DashSync/shared/Models/Entities/DSQuorumEntryEntity+CoreDataClass.h index fb3c6084e..503341bf9 100644 --- a/DashSync/shared/Models/Entities/DSQuorumEntryEntity+CoreDataClass.h +++ b/DashSync/shared/Models/Entities/DSQuorumEntryEntity+CoreDataClass.h @@ -7,10 +7,12 @@ // #import "BigIntTypes.h" +#import "dash_shared_core.h" +#import "DSKeyManager.h" #import #import -@class DSChainEntity, DSInstantSendLockEntity, DSMasternodeListEntity, DSMerkleBlockEntity, DSQuorumCommitmentTransactionEntity, DSChain, DSQuorumEntry, DSChainLockEntity; +@class DSChainEntity, DSInstantSendLockEntity, DSMasternodeListEntity, DSMerkleBlockEntity, DSQuorumCommitmentTransactionEntity, DSChain, DSChainLockEntity; NS_ASSUME_NONNULL_BEGIN @@ -22,12 +24,18 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) UInt768 quorumThresholdSignature; @property (nonatomic, assign) UInt256 quorumVerificationVectorHash; @property (nonatomic, assign) UInt768 allCommitmentAggregatedSignature; -@property (nonatomic, readonly) DSQuorumEntry *quorumEntry; - -+ (instancetype _Nullable)quorumEntryEntityFromPotentialQuorumEntry:(DSQuorumEntry *)potentialQuorumEntry inContext:(NSManagedObjectContext *)context; -+ (instancetype _Nullable)quorumEntryEntityFromPotentialQuorumEntryForMerging:(DSQuorumEntry *)potentialQuorumEntry inContext:(NSManagedObjectContext *)context; - -- (void)setAttributesFromPotentialQuorumEntry:(DSQuorumEntry *)potentialQuorumEntry onBlock:(DSMerkleBlockEntity *_Nullable)block; +//@property (nonatomic, readonly) DSQuorumEntry *quorumEntry; + ++ (instancetype _Nullable)quorumEntryEntityFromPotentialQuorumEntry:(DLLMQEntry *)potentialQuorumEntry + inContext:(NSManagedObjectContext *)context + onChain:(DSChain *)chain; ++ (instancetype _Nullable)quorumEntryEntityFromPotentialQuorumEntryForMerging:(DLLMQEntry *)potentialQuorumEntry + inContext:(NSManagedObjectContext *)context + onChain:(DSChain *)chain; + +- (void)setAttributesFromPotentialQuorumEntry:(DLLMQEntry *)potentialQuorumEntry + onBlock:(DSMerkleBlockEntity *_Nullable)block + onChain:(DSChain *)chain; + (void)deleteHavingQuorumHashes:(NSArray *)quorumHashes onChainEntity:(DSChainEntity *)chainEntity; + (DSQuorumEntryEntity *_Nullable)quorumEntryForHash:(NSData *)quorumEntryHash onChainEntity:(DSChainEntity *)chainEntity; diff --git a/DashSync/shared/Models/Entities/DSQuorumEntryEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSQuorumEntryEntity+CoreDataClass.m index 2d3e88129..b3adc5673 100644 --- a/DashSync/shared/Models/Entities/DSQuorumEntryEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSQuorumEntryEntity+CoreDataClass.m @@ -8,8 +8,9 @@ #import "DSChain+Protected.h" #import "DSChainEntity+CoreDataClass.h" +#import "DSKeyManager.h" #import "DSMerkleBlockEntity+CoreDataClass.h" -#import "DSQuorumEntry.h" +//#import "DSQuorumEntry.h" #import "DSQuorumEntryEntity+CoreDataClass.h" #import "NSData+Dash.h" #import "NSManagedObject+Sugar.h" @@ -18,20 +19,24 @@ @implementation DSQuorumEntryEntity -+ (instancetype)quorumEntryEntityFromPotentialQuorumEntry:(DSQuorumEntry *)potentialQuorumEntry inContext:(NSManagedObjectContext *)context { - DSMerkleBlockEntity *block = [DSMerkleBlockEntity merkleBlockEntityForBlockHash:potentialQuorumEntry.quorumHash inContext:context]; ++ (instancetype)quorumEntryEntityFromPotentialQuorumEntry:(DLLMQEntry *)potentialQuorumEntry + inContext:(NSManagedObjectContext *)context + onChain:(DSChain *)chain { + NSData *quorumHash = NSDataFromPtr(potentialQuorumEntry->llmq_hash); + int16_t llmqType = dash_spv_crypto_network_llmq_type_LLMQType_index(potentialQuorumEntry->llmq_type); + DSMerkleBlockEntity *block = [DSMerkleBlockEntity merkleBlockEntityForBlockHash:quorumHash inContext:context]; DSQuorumEntryEntity *quorumEntryEntity = nil; if (block) { - quorumEntryEntity = [[block.usedByQuorums filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"quorumHashData == %@ && llmqType == %@ ", uint256_data(potentialQuorumEntry.quorumHash), @((int16_t)potentialQuorumEntry.llmqType)]] anyObject]; + quorumEntryEntity = [[block.usedByQuorums filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"quorumHashData == %@ && llmqType == %@ ", quorumHash, @(llmqType)]] anyObject]; } else { - quorumEntryEntity = [DSQuorumEntryEntity anyObjectInContext:context matching:@"quorumHashData == %@ && llmqType == %@ ", uint256_data(potentialQuorumEntry.quorumHash), @((int16_t)potentialQuorumEntry.llmqType)]; + quorumEntryEntity = [DSQuorumEntryEntity anyObjectInContext:context matching:@"quorumHashData == %@ && llmqType == %@ ", quorumHash, @(llmqType)]; } if (!quorumEntryEntity) { - if (potentialQuorumEntry.saved) { //it was deleted in the meantime, and should be ignored + if (potentialQuorumEntry->saved) { //it was deleted in the meantime, and should be ignored return nil; } else { quorumEntryEntity = [DSQuorumEntryEntity managedObjectInBlockedContext:context]; - [quorumEntryEntity setAttributesFromPotentialQuorumEntry:potentialQuorumEntry onBlock:block]; + [quorumEntryEntity setAttributesFromPotentialQuorumEntry:potentialQuorumEntry onBlock:block onChain:chain]; } } else { [quorumEntryEntity updateAttributesFromPotentialQuorumEntry:potentialQuorumEntry onBlock:block]; @@ -39,20 +44,24 @@ + (instancetype)quorumEntryEntityFromPotentialQuorumEntry:(DSQuorumEntry *)poten return quorumEntryEntity; } -+ (instancetype)quorumEntryEntityFromPotentialQuorumEntryForMerging:(DSQuorumEntry *)potentialQuorumEntry inContext:(NSManagedObjectContext *)context { - DSMerkleBlockEntity *block = [DSMerkleBlockEntity merkleBlockEntityForBlockHash:potentialQuorumEntry.quorumHash inContext:context]; ++ (instancetype)quorumEntryEntityFromPotentialQuorumEntryForMerging:(DLLMQEntry *)potentialQuorumEntry + inContext:(NSManagedObjectContext *)context + onChain:(DSChain *)chain { + NSData *quorumHash = NSDataFromPtr(potentialQuorumEntry->llmq_hash); + int16_t llmqType = dash_spv_crypto_network_llmq_type_LLMQType_index(potentialQuorumEntry->llmq_type); + DSMerkleBlockEntity *block = [DSMerkleBlockEntity merkleBlockEntityForBlockHash:quorumHash inContext:context]; DSQuorumEntryEntity *quorumEntryEntity = nil; if (block) { - quorumEntryEntity = [[block.usedByQuorums filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"quorumHashData == %@ && llmqType == %@ ", uint256_data(potentialQuorumEntry.quorumHash), @((int16_t)potentialQuorumEntry.llmqType)]] anyObject]; + quorumEntryEntity = [[block.usedByQuorums filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"quorumHashData == %@ && llmqType == %@ ", quorumHash, @(llmqType)]] anyObject]; } else { - quorumEntryEntity = [DSQuorumEntryEntity anyObjectInContext:context matching:@"quorumHashData == %@ && llmqType == %@ ", uint256_data(potentialQuorumEntry.quorumHash), @((int16_t)potentialQuorumEntry.llmqType)]; + quorumEntryEntity = [DSQuorumEntryEntity anyObjectInContext:context matching:@"quorumHashData == %@ && llmqType == %@ ", quorumHash, @(llmqType)]; } if (!quorumEntryEntity) { - if (potentialQuorumEntry.saved && potentialQuorumEntry.verified) { + if (potentialQuorumEntry->saved && potentialQuorumEntry->verified) { return nil; } else { quorumEntryEntity = [DSQuorumEntryEntity managedObjectInBlockedContext:context]; - [quorumEntryEntity setAttributesFromPotentialQuorumEntry:potentialQuorumEntry onBlock:block]; + [quorumEntryEntity setAttributesFromPotentialQuorumEntry:potentialQuorumEntry onBlock:block onChain:chain]; } } else { [quorumEntryEntity updateAttributesFromPotentialQuorumEntry:potentialQuorumEntry onBlock:block]; @@ -60,29 +69,33 @@ + (instancetype)quorumEntryEntityFromPotentialQuorumEntryForMerging:(DSQuorumEnt return quorumEntryEntity; } -- (void)setAttributesFromPotentialQuorumEntry:(DSQuorumEntry *)potentialQuorumEntry onBlock:(DSMerkleBlockEntity *)block { - self.verified = (block != nil) && potentialQuorumEntry.verified; +- (void)setAttributesFromPotentialQuorumEntry:(DLLMQEntry *)potentialQuorumEntry + onBlock:(DSMerkleBlockEntity *)block + onChain:(DSChain *)chain { + self.verified = (block != nil) && potentialQuorumEntry->verified; self.block = block; - self.quorumHash = potentialQuorumEntry.quorumHash; - self.quorumPublicKey = potentialQuorumEntry.quorumPublicKey; - self.quorumThresholdSignature = potentialQuorumEntry.quorumThresholdSignature; - self.quorumVerificationVectorHash = potentialQuorumEntry.quorumVerificationVectorHash; - self.signersCount = potentialQuorumEntry.signersCount; - self.signersBitset = potentialQuorumEntry.signersBitset; - self.validMembersCount = potentialQuorumEntry.validMembersCount; - self.validMembersBitset = potentialQuorumEntry.validMembersBitset; - self.llmqType = (int16_t)potentialQuorumEntry.llmqType; - self.version = potentialQuorumEntry.version; - self.allCommitmentAggregatedSignature = potentialQuorumEntry.allCommitmentAggregatedSignature; - self.commitmentHash = potentialQuorumEntry.quorumEntryHash; - self.quorumIndex = potentialQuorumEntry.quorumIndex; - self.chain = [potentialQuorumEntry.chain chainEntityInContext:self.managedObjectContext]; - potentialQuorumEntry.saved = TRUE; -} - -- (void)updateAttributesFromPotentialQuorumEntry:(DSQuorumEntry *)potentialQuorumEntry onBlock:(DSMerkleBlockEntity *)block { + self.quorumHash = u256_cast(potentialQuorumEntry->llmq_hash); + self.quorumPublicKey = u384_cast(potentialQuorumEntry->public_key); + self.quorumThresholdSignature = u768_cast(potentialQuorumEntry->threshold_signature); + self.quorumVerificationVectorHash = u256_cast(potentialQuorumEntry->verification_vector_hash); + self.signersCount = (int32_t) potentialQuorumEntry->signers->count; + self.signersBitset = NSDataFromPtr(potentialQuorumEntry->signers->bitset); + self.validMembersCount = (int32_t) potentialQuorumEntry->valid_members->count; + self.validMembersBitset = NSDataFromPtr(potentialQuorumEntry->valid_members->bitset); + self.llmqType = (int16_t)dash_spv_crypto_network_llmq_type_LLMQType_index(potentialQuorumEntry->llmq_type); + self.version = dash_spv_crypto_llmq_version_LLMQVersion_index(potentialQuorumEntry->version); + self.allCommitmentAggregatedSignature = u768_cast(potentialQuorumEntry->all_commitment_aggregated_signature); + self.commitmentHashData = NSDataFromPtr(potentialQuorumEntry->entry_hash); + self.quorumIndex = potentialQuorumEntry->index; + self.chain = [chain chainEntityInContext:self.managedObjectContext]; + potentialQuorumEntry->saved = TRUE; + // TODO: make sure the cache is updated with "saved" flag +} + +- (void)updateAttributesFromPotentialQuorumEntry:(DLLMQEntry *)potentialQuorumEntry + onBlock:(DSMerkleBlockEntity *)block { if (!self.verified) { - self.verified = (block != nil) && potentialQuorumEntry.verified; + self.verified = (block != nil) && potentialQuorumEntry->verified; } if (!self.block) { self.block = block; @@ -165,8 +178,8 @@ - (UInt256)orderingHashForRequestID:(UInt256)requestID { return [data SHA256_2]; } -- (DSQuorumEntry *)quorumEntry { - return [[DSQuorumEntry alloc] initWithVersion:self.version type:self.llmqType quorumHash:self.quorumHash quorumIndex:self.quorumIndex signersCount:self.signersCount signersBitset:self.signersBitset validMembersCount:self.validMembersCount validMembersBitset:self.validMembersBitset quorumPublicKey:self.quorumPublicKey quorumVerificationVectorHash:self.quorumVerificationVectorHash quorumThresholdSignature:self.quorumThresholdSignature allCommitmentAggregatedSignature:self.allCommitmentAggregatedSignature quorumEntryHash:self.commitmentHash onChain:self.chain.chain]; -} - +//- (DSQuorumEntry *)quorumEntry { +// return [[DSQuorumEntry alloc] initWithVersion:self.version type:self.llmqType quorumHash:self.quorumHash quorumIndex:self.quorumIndex signersCount:self.signersCount signersBitset:self.signersBitset validMembersCount:self.validMembersCount validMembersBitset:self.validMembersBitset quorumPublicKey:self.quorumPublicKey quorumVerificationVectorHash:self.quorumVerificationVectorHash quorumThresholdSignature:self.quorumThresholdSignature allCommitmentAggregatedSignature:self.allCommitmentAggregatedSignature quorumEntryHash:self.commitmentHash onChain:self.chain.chain]; +//} +// @end diff --git a/DashSync/shared/Models/Entities/DSQuorumEntryEntity+CoreDataProperties.h b/DashSync/shared/Models/Entities/DSQuorumEntryEntity+CoreDataProperties.h index 1d1c08ae0..95760ec1a 100644 --- a/DashSync/shared/Models/Entities/DSQuorumEntryEntity+CoreDataProperties.h +++ b/DashSync/shared/Models/Entities/DSQuorumEntryEntity+CoreDataProperties.h @@ -6,7 +6,7 @@ // // -#import "DSQuorumEntry.h" +//#import "DSQuorumEntry.h" #import "DSQuorumEntryEntity+CoreDataClass.h" NS_ASSUME_NONNULL_BEGIN diff --git a/DashSync/shared/Models/Entities/DSQuorumSnapshotEntity+CoreDataClass.h b/DashSync/shared/Models/Entities/DSQuorumSnapshotEntity+CoreDataClass.h index 48b3cf581..61428182c 100644 --- a/DashSync/shared/Models/Entities/DSQuorumSnapshotEntity+CoreDataClass.h +++ b/DashSync/shared/Models/Entities/DSQuorumSnapshotEntity+CoreDataClass.h @@ -19,7 +19,7 @@ #import #import "DSChainEntity+CoreDataProperties.h" #import "DSMerkleBlockEntity+CoreDataProperties.h" -#import "DSQuorumSnapshot.h" +//#import "DSQuorumSnapshot.h" #import "dash_shared_core.h" @class DSChainEntity, DSMerkleBlockEntity; @@ -28,11 +28,15 @@ NS_ASSUME_NONNULL_BEGIN @interface DSQuorumSnapshotEntity : NSManagedObject -+ (instancetype)quorumSnapshotEntityFromPotentialQuorumSnapshot:(DSQuorumSnapshot *)potentialQuorumSnapshot inContext:(NSManagedObjectContext *)context; -+ (instancetype)quorumSnapshotEntityForMerkleBlockEntity:(DSMerkleBlockEntity *)blockEntity quorumSnapshot:(DSQuorumSnapshot *)quorumSnapshot inContext:(NSManagedObjectContext *)context; +//+ (instancetype)quorumSnapshotEntityFromPotentialQuorumSnapshot:(DLLMQSnapshot *)potentialQuorumSnapshot +// inContext:(NSManagedObjectContext *)context; +//+ (instancetype)quorumSnapshotEntityForMerkleBlockEntity:(DSMerkleBlockEntity *)blockEntity +// quorumSnapshot:(DLLMQSnapshot *)quorumSnapshot +// inContext:(NSManagedObjectContext *)context; + (void)deleteAllOnChainEntity:(DSChainEntity *)chainEntity; -- (void)updateAttributesFromPotentialQuorumSnapshot:(DSQuorumSnapshot *)quorumSnapshot onBlock:(DSMerkleBlockEntity *) block; +- (void)updateAttributesFromPotentialQuorumSnapshot:(DLLMQSnapshot *)quorumSnapshot + onBlock:(DSMerkleBlockEntity *) block; @end diff --git a/DashSync/shared/Models/Entities/DSQuorumSnapshotEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSQuorumSnapshotEntity+CoreDataClass.m index 69b06285b..d3c9f7322 100644 --- a/DashSync/shared/Models/Entities/DSQuorumSnapshotEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSQuorumSnapshotEntity+CoreDataClass.m @@ -15,53 +15,64 @@ // limitations under the License. // +#import "DSKeyManager.h" #import "DSQuorumSnapshotEntity+CoreDataClass.h" #import "NSManagedObject+Sugar.h" #import "NSData+Dash.h" @implementation DSQuorumSnapshotEntity -+ (instancetype)quorumSnapshotEntityFromPotentialQuorumSnapshot:(DSQuorumSnapshot *)potentialQuorumSnapshot inContext:(NSManagedObjectContext *)context { - UInt256 quorumSnapshotBlockHash = potentialQuorumSnapshot.blockHash; - DSMerkleBlockEntity *block = [DSMerkleBlockEntity merkleBlockEntityForBlockHash:quorumSnapshotBlockHash inContext:context]; - DSQuorumSnapshotEntity *quorumSnapshotEntity = nil; - if (block) { - quorumSnapshotEntity = block.quorumSnapshot; - } - if (!quorumSnapshotEntity) { - quorumSnapshotEntity = [DSQuorumSnapshotEntity managedObjectInBlockedContext:context]; - [quorumSnapshotEntity updateAttributesFromPotentialQuorumSnapshot:potentialQuorumSnapshot onBlock:block]; - } else { - [quorumSnapshotEntity updateAttributesFromPotentialQuorumSnapshot:potentialQuorumSnapshot onBlock:block]; - } - - return quorumSnapshotEntity; -} - -+ (instancetype)quorumSnapshotEntityForMerkleBlockEntity:(DSMerkleBlockEntity *)blockEntity quorumSnapshot:(DSQuorumSnapshot *)quorumSnapshot inContext:(NSManagedObjectContext *)context { - NSArray *objects = [DSQuorumSnapshotEntity objectsForPredicate:[NSPredicate predicateWithFormat:@"block = %@", blockEntity] inContext:context]; - DSQuorumSnapshotEntity *entity = NULL; - if (objects.count) { - NSAssert(objects.count == 1, @"There should only ever be 1 quorum snapshot for either mainnet, testnet, or a devnet Identifier"); - entity = objects[0]; +//+ (instancetype)quorumSnapshotEntityFromPotentialQuorumSnapshot:(DLLMQSnapshot *)potentialQuorumSnapshot +// inContext:(NSManagedObjectContext *)context { +// UInt256 quorumSnapshotBlockHash = *((UInt256 *)potentialQuorumSnapshot->block_hash->values); +// UInt256 quorumSnapshotBlockHash = potentialQuorumSnapshot.blockHash; +// DSMerkleBlockEntity *block = [DSMerkleBlockEntity merkleBlockEntityForBlockHash:quorumSnapshotBlockHash inContext:context]; +// DSQuorumSnapshotEntity *quorumSnapshotEntity = nil; +// if (block) { +// quorumSnapshotEntity = block.quorumSnapshot; +// } +// if (!quorumSnapshotEntity) { +// quorumSnapshotEntity = [DSQuorumSnapshotEntity managedObjectInBlockedContext:context]; +// [quorumSnapshotEntity updateAttributesFromPotentialQuorumSnapshot:potentialQuorumSnapshot onBlock:block]; +// } else { +// [quorumSnapshotEntity updateAttributesFromPotentialQuorumSnapshot:potentialQuorumSnapshot onBlock:block]; +// } +// +// return quorumSnapshotEntity; +//} - } else { - entity = [self managedObjectInBlockedContext:context]; - } - [entity updateAttributesFromPotentialQuorumSnapshot:quorumSnapshot onBlock:blockEntity]; - return entity; -} +//+ (instancetype)quorumSnapshotEntityForMerkleBlockEntity:(DSMerkleBlockEntity *)blockEntity +// quorumSnapshot:(DLLMQSnapshot *)quorumSnapshot +// inContext:(NSManagedObjectContext *)context { +// NSArray *objects = [DSQuorumSnapshotEntity objectsForPredicate:[NSPredicate predicateWithFormat:@"block = %@", blockEntity] inContext:context]; +// DSQuorumSnapshotEntity *entity = NULL; +// if (objects.count) { +// NSAssert(objects.count == 1, @"There should only ever be 1 quorum snapshot for either mainnet, testnet, or a devnet Identifier"); +// entity = objects[0]; +// +// } else { +// entity = [self managedObjectInBlockedContext:context]; +// } +// [entity updateAttributesFromPotentialQuorumSnapshot:quorumSnapshot onBlock:blockEntity]; +// return entity; +//} -- (void)updateAttributesFromPotentialQuorumSnapshot:(DSQuorumSnapshot *)quorumSnapshot onBlock:(DSMerkleBlockEntity *) block { +- (void)updateAttributesFromPotentialQuorumSnapshot:(DLLMQSnapshot *)quorumSnapshot + onBlock:(DSMerkleBlockEntity *) block { self.block = block; NSError *error = nil; - NSData *archivedSkipList = [NSKeyedArchiver archivedDataWithRootObject:quorumSnapshot.skipList requiringSecureCoding:YES error:&error]; + NSMutableArray *skipList = [NSMutableArray arrayWithCapacity:quorumSnapshot->skip_list->count]; + for (int i = 0; i < quorumSnapshot->skip_list->count; i++) { + [skipList addObject:@(quorumSnapshot->skip_list->values[i])]; + } + + NSData *archivedSkipList = [NSKeyedArchiver archivedDataWithRootObject:skipList requiringSecureCoding:YES error:&error]; NSAssert(error == nil, @"There should not be an error when decrypting skipList"); if (!error) { self.skipList = archivedSkipList; } - self.memberList = quorumSnapshot.memberList; - self.skipListMode = quorumSnapshot.skipListMode; + self.memberList = NSDataFromPtr(quorumSnapshot->member_list); + self.skipListMode = dash_spv_masternode_processor_common_llmq_snapshot_skip_mode_LLMQSnapshotSkipMode_index(quorumSnapshot->skip_list_mode); } + (void)deleteAllOnChainEntity:(DSChainEntity *)chainEntity { diff --git a/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataClass.h b/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataClass.h index c1c080717..2f0ca81ff 100644 --- a/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataClass.h +++ b/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataClass.h @@ -7,25 +7,48 @@ // #import "BigIntTypes.h" +#import "dash_shared_core.h" +#import "DSChain.h" #import #import -@class DSAddressEntity, DSChainEntity, DSGovernanceVoteEntity, DSLocalMasternodeEntity, DSMasternodeListEntity, DSTransactionLockVoteEntity, DSSimplifiedMasternodeEntry, DSMasternodeList; +@class DSAddressEntity, DSChainEntity, DSGovernanceVoteEntity, DSLocalMasternodeEntity, DSMasternodeListEntity, DSTransactionLockVoteEntity; NS_ASSUME_NONNULL_BEGIN @interface DSSimplifiedMasternodeEntryEntity : NSManagedObject -- (void)updateAttributesFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *_Nonnull)simplifiedMasternodeEntry atBlockHeight:(uint32_t)blockHeight; -- (void)updateAttributesFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *_Nonnull)simplifiedMasternodeEntry atBlockHeight:(uint32_t)blockHeight knownOperatorAddresses:(NSDictionary *_Nullable)knownOperatorAddresses knownVotingAddresses:(NSDictionary *_Nullable)knownVotingAddresses localMasternodes:(NSDictionary *_Nullable)localMasternodes; -- (void)setAttributesFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry atBlockHeight:(uint32_t)blockHeight knownOperatorAddresses:(NSDictionary *_Nullable)knownOperatorAddresses knownVotingAddresses:(NSDictionary *_Nullable)knownVotingAddresses localMasternodes:(NSDictionary *_Nullable)localMasternodes onChainEntity:(DSChainEntity *_Nullable)chainEntity; -- (void)setAttributesFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *_Nonnull)simplifiedMasternodeEntry atBlockHeight:(uint32_t)blockHeight onChainEntity:(DSChainEntity *_Nullable)chainEntity; -+ (void)deleteHavingProviderTransactionHashes:(NSArray *)providerTransactionHashes onChainEntity:(DSChainEntity *_Nonnull)chainEntity; -+ (DSSimplifiedMasternodeEntryEntity *_Nullable)simplifiedMasternodeEntryForHash:(NSData *)simplifiedMasternodeEntryHash onChainEntity:(DSChainEntity *_Nonnull)chainEntity; -+ (DSSimplifiedMasternodeEntryEntity *)simplifiedMasternodeEntryForProviderRegistrationTransactionHash:(NSData *)providerRegistrationTransactionHash onChainEntity:(DSChainEntity *_Nonnull)chainEntity; - -- (DSSimplifiedMasternodeEntry *_Nullable)simplifiedMasternodeEntry; -- (DSSimplifiedMasternodeEntry *_Nullable)simplifiedMasternodeEntryWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; +- (void)updateAttributesFromSimplifiedMasternodeEntry:(DMasternodeEntry *_Nonnull)simplifiedMasternodeEntry + atBlockHeight:(uint32_t)blockHeight + onChain:(DSChain *)chain; +- (void)updateAttributesFromSimplifiedMasternodeEntry:(DMasternodeEntry *_Nonnull)simplifiedMasternodeEntry + atBlockHeight:(uint32_t)blockHeight + knownOperatorAddresses:(NSDictionary *_Nullable)knownOperatorAddresses + knownVotingAddresses:(NSDictionary *_Nullable)knownVotingAddresses + platformNodeAddresses:(NSDictionary *_Nullable)platformNodeAddresses + localMasternodes:(NSDictionary *_Nullable)localMasternodes + onChain:(DSChain *)chain; +- (void)setAttributesFromSimplifiedMasternodeEntry:(DMasternodeEntry *)simplifiedMasternodeEntry + atBlockHeight:(uint32_t)blockHeight + knownOperatorAddresses:(NSDictionary *_Nullable)knownOperatorAddresses + knownVotingAddresses:(NSDictionary *_Nullable)knownVotingAddresses + platformNodeAddresses:(NSDictionary *_Nullable)platformNodeAddresses + localMasternodes:(NSDictionary *_Nullable)localMasternodes + onChain:(DSChain *)chain + onChainEntity:(DSChainEntity *_Nullable)chainEntity; +- (void)setAttributesFromSimplifiedMasternodeEntry:(DMasternodeEntry *_Nonnull)simplifiedMasternodeEntry + atBlockHeight:(uint32_t)blockHeight + onChain:(DSChain *)chain + onChainEntity:(DSChainEntity *_Nullable)chainEntity; ++ (void)deleteHavingProviderTransactionHashes:(NSArray *)providerTransactionHashes + onChainEntity:(DSChainEntity *_Nonnull)chainEntity; ++ (DSSimplifiedMasternodeEntryEntity *_Nullable)simplifiedMasternodeEntryForHash:(NSData *)simplifiedMasternodeEntryHash + onChainEntity:(DSChainEntity *_Nonnull)chainEntity; ++ (DSSimplifiedMasternodeEntryEntity *)simplifiedMasternodeEntryForProviderRegistrationTransactionHash:(NSData *)providerRegistrationTransactionHash + onChainEntity:(DSChainEntity *_Nonnull)chainEntity; + +//- (DMasternodeEntry *_Nullable)simplifiedMasternodeEntry; +- (DMasternodeEntry *_Nullable)simplifiedMasternodeEntryWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; + (void)deleteAllOnChainEntity:(DSChainEntity *)chainEntity; @end diff --git a/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataClass.m index 2ffc58237..bc99d6904 100644 --- a/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataClass.m @@ -7,6 +7,7 @@ // #import "DSAddressEntity+CoreDataClass.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" #import "DSChainEntity+CoreDataProperties.h" #import "NSDictionary+Dash.h" @@ -16,7 +17,9 @@ #import "DSSimplifiedMasternodeEntry.h" #import "DSSimplifiedMasternodeEntryEntity+CoreDataClass.h" #import "NSData+Dash.h" +#import "NSDictionary+Dash.h" #import "NSManagedObject+Sugar.h" +#import "NSMutableData+Dash.h" #include #define LOG_SMNE_CHANGES 0 @@ -31,18 +34,30 @@ @implementation DSSimplifiedMasternodeEntryEntity -- (void)updateAttributesFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry atBlockHeight:(uint32_t)blockHeight { - [self updateAttributesFromSimplifiedMasternodeEntry:simplifiedMasternodeEntry atBlockHeight:(uint32_t)blockHeight knownOperatorAddresses:nil knownVotingAddresses:nil localMasternodes:nil]; +- (void)updateAttributesFromSimplifiedMasternodeEntry:(DMasternodeEntry *)simplifiedMasternodeEntry + atBlockHeight:(uint32_t)blockHeight + onChain:(DSChain *)chain { + [self updateAttributesFromSimplifiedMasternodeEntry:simplifiedMasternodeEntry atBlockHeight:(uint32_t)blockHeight knownOperatorAddresses:nil knownVotingAddresses:nil platformNodeAddresses:nil localMasternodes:nil onChain:chain]; } -- (void)updateAttributesFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry atBlockHeight:(uint32_t)blockHeight knownOperatorAddresses:(NSDictionary *)knownOperatorAddresses knownVotingAddresses:(NSDictionary *)knownVotingAddresses localMasternodes:(NSDictionary *)localMasternodes { +- (void)updateAttributesFromSimplifiedMasternodeEntry:(DMasternodeEntry *)simplifiedMasternodeEntry + atBlockHeight:(uint32_t)blockHeight + knownOperatorAddresses:(NSDictionary *)knownOperatorAddresses + knownVotingAddresses:(NSDictionary *)knownVotingAddresses + platformNodeAddresses:(NSDictionary *)platformNodeAddresses + localMasternodes:(NSDictionary *)localMasternodes + onChain:(DSChain *)chain { if (self.updateHeight < blockHeight) { //NSAssert(simplifiedMasternodeEntry.updateHeight == blockHeight, @"the block height should be the same as the entry update height"); self.updateHeight = blockHeight; //we should only update if the data received is the most recent - if (!uint128_eq(self.ipv6Address.UInt128, simplifiedMasternodeEntry.address)) { - self.ipv6Address = uint128_data(simplifiedMasternodeEntry.address); - uint32_t address32 = CFSwapInt32BigToHost(simplifiedMasternodeEntry.address.u32[3]); + bool same_addr = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_address_is_equal_to(simplifiedMasternodeEntry, Arr_u8_16_ctor(16, (uint8_t *) self.ipv6Address.bytes)); +// if (!uint128_eq(self.ipv6Address.UInt128, simplifiedMasternodeEntry.address)) { + if (!same_addr) { + self.ipv6Address = uint128_data(u128_cast(simplifiedMasternodeEntry->socket_address->ip_address)); + uint32_t address32 = dash_spv_masternode_processor_common_socket_address_SocketAddress_ipv4(simplifiedMasternodeEntry->socket_address); +// uint32_t address32 = CFSwapInt32BigToHost(simplifiedMasternodeEntry.address.u32[3]); +// uint32_t address32 = CFSwapInt32BigToHost(simplifiedMasternodeEntry.address.u32[3]); if (self.address != address32) { self.address = address32; #if LOG_SMNE_CHANGES @@ -51,158 +66,351 @@ - (void)updateAttributesFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEnt DSDSMNELog(@"changing address to %@", @(inet_ntop(AF_INET, &address32, s, sizeof(s)))); } } - NSData *confirmedHashData = uint256_data(simplifiedMasternodeEntry.confirmedHash); - if (![self.confirmedHash isEqualToData:confirmedHashData]) { + + NSData *confirmedHashData = [NSData dataWithBytes:simplifiedMasternodeEntry->confirmed_hash->values length:32]; +// NSData *confirmedHashData = uint256_data(simplifiedMasternodeEntry.confirmedHash); + bool same_confirmed_hash = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_confirmed_hash_is_equal_to(simplifiedMasternodeEntry, Arr_u8_32_ctor(32, (uint8_t *) self.confirmedHash.bytes)); + if (!same_confirmed_hash) { +// if (![self.confirmedHash isEqualToData:confirmedHashData]) { NSAssert(self.confirmedHash == nil || uint256_is_zero(self.confirmedHash.UInt256), @"If this changes the previous should be empty"); //this should only happen once at confirmation self.confirmedHash = confirmedHashData; self.knownConfirmedAtHeight = blockHeight; DSDSMNELog(@"changing confirmedHashData to %@", confirmedHashData.hexString); } - if (self.port != simplifiedMasternodeEntry.port) { - self.port = simplifiedMasternodeEntry.port; + if (self.port != simplifiedMasternodeEntry->socket_address->port) { + self.port = simplifiedMasternodeEntry->socket_address->port; DSDSMNELog(@"changing port to %u", simplifiedMasternodeEntry.port); } - NSData *keyIDVotingData = [NSData dataWithUInt160:simplifiedMasternodeEntry.keyIDVoting]; - if (![self.keyIDVoting isEqualToData:keyIDVotingData]) { - self.keyIDVoting = keyIDVotingData; - DSDSMNELog(@"changing keyIDVotingData to %@", keyIDVotingData.hexString); + + bool same_key_id = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_key_id_is_equal_to(simplifiedMasternodeEntry, Arr_u8_20_ctor(20, (uint8_t *) self.keyIDVoting.bytes)); + + +// NSData *keyIDVotingData = [NSData dataWithUInt160:simplifiedMasternodeEntry.keyIDVoting]; + if (!same_key_id) { +// if (![self.keyIDVoting isEqualToData:keyIDVotingData]) { + self.keyIDVoting = [NSData dataWithBytes:simplifiedMasternodeEntry->key_id_voting->values length:20]; +// DSDSMNELog(@"changing keyIDVotingData to %@", keyIDVotingData.hexString); } - NSData *operatorPublicKeyData = [NSData dataWithUInt384:simplifiedMasternodeEntry.operatorPublicKey]; - if (![self.operatorBLSPublicKey isEqualToData:operatorPublicKeyData]) { - self.operatorBLSPublicKey = operatorPublicKeyData; - self.operatorPublicKeyVersion = simplifiedMasternodeEntry.operatorPublicKeyVersion; - DSDSMNELog(@"changing operatorBLSPublicKey to %@", operatorPublicKeyData.hexString); + bool same_operator_public_key = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_operator_pub_key_is_equal_to(simplifiedMasternodeEntry, Arr_u8_48_ctor(48, (uint8_t *) self.operatorBLSPublicKey.bytes)); + +// NSData *operatorPublicKeyData = [NSData dataWithUInt384:simplifiedMasternodeEntry.operatorPublicKey]; + if (!same_operator_public_key) { +// if (![self.operatorBLSPublicKey isEqualToData:operatorPublicKeyData]) { +// self.operatorBLSPublicKey = operatorPublicKeyData; +// self.operatorPublicKeyVersion = simplifiedMasternodeEntry.operatorPublicKeyVersion; + self.operatorBLSPublicKey = [NSData dataWithBytes:simplifiedMasternodeEntry->operator_public_key->data->values length:48]; + self.operatorPublicKeyVersion = simplifiedMasternodeEntry->operator_public_key->version; +// DSDSMNELog(@"changing operatorBLSPublicKey to %@", operatorPublicKeyData.hexString); } + bool same_type = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_type_is_equal_to(simplifiedMasternodeEntry, self.type); - if (self.type != simplifiedMasternodeEntry.type) { - self.type = simplifiedMasternodeEntry.type; - DSDSMNELog(@"changing type to %d", simplifiedMasternodeEntry.type); + if (!same_type) { + self.type = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_type_uint(simplifiedMasternodeEntry); + DSDSMNELog(@"changing type to %d", self.type); } - NSData *platformNodeIDData = uint160_data(simplifiedMasternodeEntry.platformNodeID); - if (![self.platformNodeID isEqualToData:platformNodeIDData]) { - self.platformNodeID = platformNodeIDData; +// if (self.type != simplifiedMasternodeEntry.type) { +// self.type = simplifiedMasternodeEntry.type; +// DSDSMNELog(@"changing type to %d", simplifiedMasternodeEntry.type); +// } + u160 *platform_node_id = u160_ctor(self.platformNodeID); + bool same_evonode_id = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_platform_node_id_is_equal_to(simplifiedMasternodeEntry, platform_node_id); +// NSData *platformNodeIDData = uint160_data(simplifiedMasternodeEntry.platformNodeID); +// if (![self.platformNodeID isEqualToData:platformNodeIDData]) { +// self.platformNodeID = platformNodeIDData; +// DSDSMNELog(@"changing platformNodeID to %d", platformNodeIDData.hexString); +// } + if (!same_evonode_id) { + self.platformNodeID = [NSData dataWithBytes:simplifiedMasternodeEntry->platform_node_id length:20]; DSDSMNELog(@"changing platformNodeID to %d", platformNodeIDData.hexString); } - if (self.platformHTTPPort != simplifiedMasternodeEntry.platformHTTPPort) { - self.platformHTTPPort = simplifiedMasternodeEntry.platformHTTPPort; - DSDSMNELog(@"changing platformHTTPPort to %d", simplifiedMasternodeEntry.platformHTTPPort); + if (self.platformHTTPPort != simplifiedMasternodeEntry->platform_http_port) { + self.platformHTTPPort = simplifiedMasternodeEntry->platform_http_port; + DSDSMNELog(@"changing platformHTTPPort to %d", simplifiedMasternodeEntry->platformHTTPPort); } - if (self.isValid != simplifiedMasternodeEntry.isValid) { - self.isValid = simplifiedMasternodeEntry.isValid; - DSDSMNELog(@"changing isValid to %@", simplifiedMasternodeEntry.isValid ? @"TRUE" : @"FALSE"); + if (self.isValid != simplifiedMasternodeEntry->is_valid) { + self.isValid = simplifiedMasternodeEntry->is_valid; + DSDSMNELog(@"changing isValid to %@", simplifiedMasternodeEntry->isValid ? @"TRUE" : @"FALSE"); } - self.simplifiedMasternodeEntryHash = [NSData dataWithUInt256:simplifiedMasternodeEntry.simplifiedMasternodeEntryHash]; +// TODO: +// self.version = simplifiedMasternodeEntry-> + self.simplifiedMasternodeEntryHash = [NSData dataWithBytes:simplifiedMasternodeEntry->entry_hash->values length:32]; + [self mergePreviousFieldsUsingSimplifiedMasternodeEntrysPreviousFields:simplifiedMasternodeEntry atBlockHeight:blockHeight]; + NSData *localNodeHash = [NSData dataWithBytes:simplifiedMasternodeEntry->provider_registration_transaction_hash->values length:32]; DSLocalMasternodeEntity *localMasternode = localMasternodes - ? [localMasternodes objectForKey:uint256_data(simplifiedMasternodeEntry.providerRegistrationTransactionHash)] - : [DSLocalMasternodeEntity anyObjectInContext:self.managedObjectContext matching:@"providerRegistrationTransaction.transactionHash.txHash == %@", uint256_data(simplifiedMasternodeEntry.providerRegistrationTransactionHash)]; + ? [localMasternodes objectForKey:localNodeHash] + : [DSLocalMasternodeEntity anyObjectInContext:self.managedObjectContext matching:@"providerRegistrationTransaction.transactionHash.txHash == %@", localNodeHash]; self.localMasternode = localMasternode; - NSString *operatorAddress = [DSKeyManager addressWithPublicKeyData:self.operatorBLSPublicKey forChain:simplifiedMasternodeEntry.chain]; - NSString *votingAddress = [DSKeyManager addressFromHash160:self.keyIDVoting.UInt160 forChain:simplifiedMasternodeEntry.chain]; + char *operator_address = DMasternodeEntryOperatorPublicKeyAddress(simplifiedMasternodeEntry, chain.chainType); + char *voting_address = DMasternodeEntryVotingAddress(simplifiedMasternodeEntry, chain.chainType); + char *platform_node_address = DMasternodeEntryEvoNodeAddress(simplifiedMasternodeEntry, chain.chainType); + + NSString *operatorAddress = [NSString stringWithCString:operator_address encoding:NSUTF8StringEncoding]; + NSString *votingAddress = [NSString stringWithCString:voting_address encoding:NSUTF8StringEncoding]; + NSString *platformNodeAddress = [NSString stringWithCString:platform_node_address encoding:NSUTF8StringEncoding]; + str_destroy(operator_address); + str_destroy(voting_address); + str_destroy(platform_node_address); DSAddressEntity *operatorAddressEntity = knownOperatorAddresses ? [knownOperatorAddresses objectForKey:operatorAddress] - : [DSAddressEntity findAddressMatching:operatorAddress onChain:simplifiedMasternodeEntry.chain inContext:self.managedObjectContext]; + : [DSAddressEntity findAddressMatching:operatorAddress onChain:chain inContext:self.managedObjectContext]; if (operatorAddressEntity) { [self addAddressesObject:operatorAddressEntity]; } DSAddressEntity *votingAddressEntity = knownVotingAddresses ? [knownVotingAddresses objectForKey:operatorAddress] - : [DSAddressEntity findAddressMatching:votingAddress onChain:simplifiedMasternodeEntry.chain inContext:self.managedObjectContext]; + : [DSAddressEntity findAddressMatching:votingAddress onChain:chain inContext:self.managedObjectContext]; if (votingAddressEntity) { [self addAddressesObject:votingAddressEntity]; } + DSAddressEntity *platformNodeAddressEntity = platformNodeAddresses + ? [platformNodeAddresses objectForKey:platformNodeAddress] + : [DSAddressEntity findAddressMatching:platformNodeAddress onChain:chain inContext:self.managedObjectContext]; + if (platformNodeAddressEntity) { + [self addAddressesObject:platformNodeAddressEntity]; + } } else if (blockHeight < self.updateHeight) { [self mergePreviousFieldsUsingSimplifiedMasternodeEntrysPreviousFields:simplifiedMasternodeEntry atBlockHeight:blockHeight]; } } -- (void)mergePreviousFieldsUsingSimplifiedMasternodeEntrysPreviousFields:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry atBlockHeight:(uint32_t)blockHeight { +- (void)mergePreviousFieldsUsingSimplifiedMasternodeEntrysPreviousFields:(DMasternodeEntry *)entry + atBlockHeight:(uint32_t)blockHeight { //we should not update current values but we should merge some fields //currentPrevious means the current set of previous values //oldPrevious means the old set of previous values - //SimplifiedMasternodeEntryHashes - NSDictionary *oldPreviousSimplifiedMasternodeEntryHashesDictionary = [self blockHashDictionaryFromBlockDictionary:simplifiedMasternodeEntry.previousSimplifiedMasternodeEntryHashes]; - if (oldPreviousSimplifiedMasternodeEntryHashesDictionary && oldPreviousSimplifiedMasternodeEntryHashesDictionary.count) { - self.previousSimplifiedMasternodeEntryHashes = [NSDictionary mergeDictionary:self.previousSimplifiedMasternodeEntryHashes withDictionary:oldPreviousSimplifiedMasternodeEntryHashesDictionary]; + std_collections_Map_keys_dash_spv_masternode_processor_common_block_Block_values_u8_arr_32 *prev_entry_hashes = entry->previous_entry_hashes; + NSMutableDictionary *prevEntryHashes = [NSMutableDictionary dictionaryWithCapacity:prev_entry_hashes->count]; + for (int i = 0; i < prev_entry_hashes->count; i++) { + DBlock *key = prev_entry_hashes->keys[i]; + NSData *k = NSDataFromPtr(key->hash); +// NSMutableData *d = [NSMutableData dataWithBytes:key->hash->values length:32]; +// [d appendUInt32:key->height]; + u256 *value = prev_entry_hashes->values[i]; + [prevEntryHashes setObject:NSDataFromPtr(value) forKey:k]; } - - //OperatorBLSPublicKeys - NSDictionary *oldPreviousOperatorBLSPublicKeysDictionary = [self blockHashDictionaryFromBlockDictionary:simplifiedMasternodeEntry.previousOperatorPublicKeys]; - if (oldPreviousOperatorBLSPublicKeysDictionary && oldPreviousOperatorBLSPublicKeysDictionary.count) { - self.previousOperatorBLSPublicKeys = [NSDictionary mergeDictionary:self.previousOperatorBLSPublicKeys withDictionary:oldPreviousOperatorBLSPublicKeysDictionary]; + if (!self.previousSimplifiedMasternodeEntryHashes || [self.previousSimplifiedMasternodeEntryHashes count] == 0) { + self.previousSimplifiedMasternodeEntryHashes = prevEntryHashes; + } else { + NSMutableDictionary *mergedDictionary = [self.previousSimplifiedMasternodeEntryHashes mutableCopy]; + [mergedDictionary addEntriesFromDictionary:prevEntryHashes]; + self.previousSimplifiedMasternodeEntryHashes = mergedDictionary; } + std_collections_Map_keys_dash_spv_masternode_processor_common_block_Block_values_dash_spv_crypto_keys_operator_public_key_OperatorPublicKey *prev_operator_keys = entry->previous_operator_public_keys; + NSMutableDictionary *prevOperatorKeys = [NSMutableDictionary dictionaryWithCapacity:prev_operator_keys->count]; - //MasternodeValidity - NSDictionary *oldPreviousValidityDictionary = [self blockHashDictionaryFromBlockDictionary:simplifiedMasternodeEntry.previousValidity]; - if (oldPreviousValidityDictionary && oldPreviousValidityDictionary.count) { - self.previousValidity = [NSDictionary mergeDictionary:self.previousValidity withDictionary:oldPreviousValidityDictionary]; + for (int i = 0; i < prev_operator_keys->count; i++) { + DBlock *key = prev_operator_keys->keys[i]; + NSData *k = NSDataFromPtr(key->hash); +// NSMutableData *k = [NSMutableData dataWithBytes:key->hash->values length:32]; +// [k appendUInt32:key->height]; + dash_spv_crypto_keys_operator_public_key_OperatorPublicKey *value = prev_operator_keys->values[i]; + NSMutableData *v = [NSMutableData dataWithBytes:value->data->values length:48]; + [v appendUInt16:value->version]; + [prevOperatorKeys setObject:v forKey:k]; + } + if (!self.previousOperatorBLSPublicKeys || [self.previousOperatorBLSPublicKeys count] == 0) { + self.previousOperatorBLSPublicKeys = prevOperatorKeys; + } else { + NSMutableDictionary *mergedDictionary = [self.previousOperatorBLSPublicKeys mutableCopy]; + [mergedDictionary addEntriesFromDictionary:prevOperatorKeys]; + self.previousOperatorBLSPublicKeys = mergedDictionary; + } + std_collections_Map_keys_dash_spv_masternode_processor_common_block_Block_values_bool *prev_validity = entry->previous_validity; + NSMutableDictionary *prevValidity = [NSMutableDictionary dictionaryWithCapacity:prev_validity->count]; + for (int i = 0; i < prev_validity->count; i++) { + DBlock *key = prev_validity->keys[i]; + NSData *k = NSDataFromPtr(key->hash); +// NSMutableData *d = [NSMutableData dataWithBytes:key->hash->values length:32]; +// [d appendUInt32:key->height]; + bool value = prev_validity->values[i]; + [prevValidity setObject:@(value) forKey:k]; + } + if (!self.previousValidity || [self.previousValidity count] == 0) { + self.previousValidity = prevValidity; + } else { + NSMutableDictionary *mergedDictionary = [self.previousValidity mutableCopy]; + [mergedDictionary addEntriesFromDictionary:prevValidity]; + self.previousValidity = mergedDictionary; } - if (uint256_is_not_zero(self.confirmedHash.UInt256) && uint256_is_not_zero(simplifiedMasternodeEntry.confirmedHash) && (self.knownConfirmedAtHeight > blockHeight)) { + if (uint256_is_not_zero(self.confirmedHash.UInt256) && !u_is_zero(entry->confirmed_hash) && (self.knownConfirmedAtHeight > blockHeight)) { //we now know it was confirmed earlier so update to earlier self.knownConfirmedAtHeight = blockHeight; } + + + + + + + +// //SimplifiedMasternodeEntryHashes +// NSDictionary *oldPreviousSimplifiedMasternodeEntryHashesDictionary = [self blockHashDictionaryFromBlockDictionary:simplifiedMasternodeEntry.previousSimplifiedMasternodeEntryHashes]; +// if (oldPreviousSimplifiedMasternodeEntryHashesDictionary && oldPreviousSimplifiedMasternodeEntryHashesDictionary.count) { +// self.previousSimplifiedMasternodeEntryHashes = [NSDictionary mergeDictionary:self.previousSimplifiedMasternodeEntryHashes withDictionary:oldPreviousSimplifiedMasternodeEntryHashesDictionary]; +// } +// +// //OperatorBLSPublicKeys +// NSDictionary *oldPreviousOperatorBLSPublicKeysDictionary = [self blockHashDictionaryFromBlockDictionary:simplifiedMasternodeEntry.previousOperatorPublicKeys]; +// if (oldPreviousOperatorBLSPublicKeysDictionary && oldPreviousOperatorBLSPublicKeysDictionary.count) { +// self.previousOperatorBLSPublicKeys = [NSDictionary mergeDictionary:self.previousOperatorBLSPublicKeys withDictionary:oldPreviousOperatorBLSPublicKeysDictionary]; +// } +// +// //MasternodeValidity +// NSDictionary *oldPreviousValidityDictionary = [self blockHashDictionaryFromBlockDictionary:simplifiedMasternodeEntry.previousValidity]; +// if (oldPreviousValidityDictionary && oldPreviousValidityDictionary.count) { +// self.previousValidity = [NSDictionary mergeDictionary:self.previousValidity withDictionary:oldPreviousValidityDictionary]; +// } +// +// if (uint256_is_not_zero(self.confirmedHash.UInt256) && uint256_is_not_zero(simplifiedMasternodeEntry.confirmedHash) && (self.knownConfirmedAtHeight > blockHeight)) { +// //we now know it was confirmed earlier so update to earlier +// self.knownConfirmedAtHeight = blockHeight; +// } } -- (void)setAttributesFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry atBlockHeight:(uint32_t)blockHeight onChainEntity:(DSChainEntity *)chainEntity { - [self setAttributesFromSimplifiedMasternodeEntry:simplifiedMasternodeEntry atBlockHeight:blockHeight knownOperatorAddresses:nil knownVotingAddresses:nil localMasternodes:nil onChainEntity:chainEntity]; +- (void)setAttributesFromSimplifiedMasternodeEntry:(DMasternodeEntry *)entry + atBlockHeight:(uint32_t)blockHeight + onChain:(DSChain *)chain + onChainEntity:(DSChainEntity *)chainEntity { + [self setAttributesFromSimplifiedMasternodeEntry:entry + atBlockHeight:blockHeight + knownOperatorAddresses:nil + knownVotingAddresses:nil + platformNodeAddresses:nil + localMasternodes:nil + onChain:chain + onChainEntity:chainEntity]; } -- (void)setAttributesFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry atBlockHeight:(uint32_t)blockHeight knownOperatorAddresses:(NSDictionary *)knownOperatorAddresses knownVotingAddresses:(NSDictionary *)knownVotingAddresses localMasternodes:(NSDictionary *)localMasternodes onChainEntity:(DSChainEntity *)chainEntity { - NSParameterAssert(simplifiedMasternodeEntry); - self.providerRegistrationTransactionHash = [NSData dataWithUInt256:simplifiedMasternodeEntry.providerRegistrationTransactionHash]; - self.confirmedHash = [NSData dataWithUInt256:simplifiedMasternodeEntry.confirmedHash]; - if (uint256_is_not_zero(simplifiedMasternodeEntry.confirmedHash)) { +- (void)setAttributesFromSimplifiedMasternodeEntry:(DMasternodeEntry *)entry + atBlockHeight:(uint32_t)blockHeight + knownOperatorAddresses:(NSDictionary *)knownOperatorAddresses + knownVotingAddresses:(NSDictionary *)knownVotingAddresses + platformNodeAddresses:(NSDictionary *)platformNodeAddresses + localMasternodes:(NSDictionary *)localMasternodes + onChain:(DSChain *)chain + onChainEntity:(DSChainEntity *)chainEntity { + NSParameterAssert(entry); + NSData *providerRegistrationTransactionHash = NSDataFromPtr(entry->provider_registration_transaction_hash); + self.providerRegistrationTransactionHash = providerRegistrationTransactionHash; + self.confirmedHash = NSDataFromPtr(entry->confirmed_hash); + if (!u_is_zero(entry->confirmed_hash)) self.knownConfirmedAtHeight = blockHeight; - } - self.ipv6Address = uint128_data(simplifiedMasternodeEntry.address); - self.address = CFSwapInt32BigToHost(simplifiedMasternodeEntry.address.u32[3]); - self.port = simplifiedMasternodeEntry.port; - self.keyIDVoting = [NSData dataWithUInt160:simplifiedMasternodeEntry.keyIDVoting]; - self.operatorBLSPublicKey = [NSData dataWithUInt384:simplifiedMasternodeEntry.operatorPublicKey]; - self.operatorPublicKeyVersion = simplifiedMasternodeEntry.operatorPublicKeyVersion; - self.type = simplifiedMasternodeEntry.type; - self.platformNodeID = [NSData dataWithUInt160:simplifiedMasternodeEntry.platformNodeID]; - self.platformHTTPPort = simplifiedMasternodeEntry.platformHTTPPort; - self.isValid = simplifiedMasternodeEntry.isValid; - self.simplifiedMasternodeEntryHash = [NSData dataWithUInt256:simplifiedMasternodeEntry.simplifiedMasternodeEntryHash]; + + self.ipv6Address = NSDataFromPtr(entry->socket_address->ip_address); + self.address = dash_spv_masternode_processor_common_socket_address_SocketAddress_ipv4(entry->socket_address); + self.port = entry->socket_address->port; + self.keyIDVoting = NSDataFromPtr(entry->key_id_voting); + self.operatorBLSPublicKey = NSDataFromPtr(entry->operator_public_key->data); + self.operatorPublicKeyVersion = entry->operator_public_key->version; + self.type = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_type_uint(entry); + self.platformNodeID = NSDataFromPtr(entry->platform_node_id); + self.platformHTTPPort = entry->platform_http_port; + self.isValid = entry->is_valid; + self.simplifiedMasternodeEntryHash = NSDataFromPtr(entry->entry_hash); self.updateHeight = blockHeight; +// if (entry->update_height != blockHeight) +// DSLog(@"• setAttributesFromSimplifiedMasternodeEntry: list.height %u != entry.height %u", blockHeight, entry->update_height); + - if (simplifiedMasternodeEntry.updateHeight != blockHeight) { - DSLog(@"• setAttributesFromSimplifiedMasternodeEntry: list.height %u != entry.height %u", blockHeight, simplifiedMasternodeEntry.updateHeight); - } - // TODO: make sure we're doing -// NSAssert(simplifiedMasternodeEntry.updateHeight == blockHeight, ([NSString stringWithFormat:@"the block height (%i) for %@ should be the same as the entry update height (%i)", blockHeight, uint256_hex(simplifiedMasternodeEntry.providerRegistrationTransactionHash), simplifiedMasternodeEntry.updateHeight])); if (!chainEntity) { - self.chain = [simplifiedMasternodeEntry.chain chainEntityInContext:self.managedObjectContext]; + self.chain = [chain chainEntityInContext:self.managedObjectContext]; } else { self.chain = chainEntity; } + DSLocalMasternodeEntity *localMasternode = localMasternodes - ? [localMasternodes objectForKey:uint256_data(simplifiedMasternodeEntry.providerRegistrationTransactionHash)] - : [DSLocalMasternodeEntity anyObjectInContext:chainEntity.managedObjectContext matching:@"providerRegistrationTransaction.transactionHash.txHash == %@", uint256_data(simplifiedMasternodeEntry.providerRegistrationTransactionHash)]; + ? [localMasternodes objectForKey:providerRegistrationTransactionHash] + : [DSLocalMasternodeEntity anyObjectInContext:chainEntity.managedObjectContext matching:@"providerRegistrationTransaction.transactionHash.txHash == %@", providerRegistrationTransactionHash]; self.localMasternode = localMasternode; - NSString *operatorAddress = [DSKeyManager addressWithPublicKeyData:self.operatorBLSPublicKey forChain:simplifiedMasternodeEntry.chain]; - NSString *votingAddress = [DSKeyManager addressFromHash160:self.keyIDVoting.UInt160 forChain:simplifiedMasternodeEntry.chain]; - // TODO: check do we have to do the same for platform node addresses + NSString *operatorAddress = [DSKeyManager addressWithPublicKeyData:self.operatorBLSPublicKey forChain:chain]; + NSString *votingAddress = [DSKeyManager addressFromHash160:self.keyIDVoting.UInt160 forChain:chain]; + NSString *platformNodeAddress = [DSKeyManager addressFromHash160:self.platformNodeID.UInt160 forChain:chain]; + DSAddressEntity *operatorAddressEntity = knownOperatorAddresses ? [knownOperatorAddresses objectForKey:operatorAddress] - : [DSAddressEntity findAddressMatching:operatorAddress onChain:simplifiedMasternodeEntry.chain inContext:self.managedObjectContext]; + : [DSAddressEntity findAddressMatching:operatorAddress onChain:chain inContext:self.managedObjectContext]; if (operatorAddressEntity) { [self addAddressesObject:operatorAddressEntity]; } DSAddressEntity *votingAddressEntity = knownVotingAddresses ? [knownVotingAddresses objectForKey:votingAddress] - : [DSAddressEntity findAddressMatching:votingAddress onChain:simplifiedMasternodeEntry.chain inContext:self.managedObjectContext]; + : [DSAddressEntity findAddressMatching:votingAddress onChain:chain inContext:self.managedObjectContext]; if (votingAddressEntity) { [self addAddressesObject:votingAddressEntity]; } + DSAddressEntity *platformNodeAddressEntity = platformNodeAddresses + ? [platformNodeAddresses objectForKey:platformNodeAddress] + : [DSAddressEntity findAddressMatching:platformNodeAddress onChain:chain inContext:self.managedObjectContext]; + if (platformNodeAddressEntity) { + [self addAddressesObject:platformNodeAddressEntity]; + } + + + + +// self.providerRegistrationTransactionHash = [NSData dataWithUInt256:simplifiedMasternodeEntry.providerRegistrationTransactionHash]; +// self.confirmedHash = [NSData dataWithUInt256:simplifiedMasternodeEntry.confirmedHash]; +// if (uint256_is_not_zero(simplifiedMasternodeEntry.confirmedHash)) { +// self.knownConfirmedAtHeight = blockHeight; +// } +// self.ipv6Address = uint128_data(simplifiedMasternodeEntry.address); +// self.address = CFSwapInt32BigToHost(simplifiedMasternodeEntry.address.u32[3]); +// self.port = simplifiedMasternodeEntry.port; +// self.keyIDVoting = [NSData dataWithUInt160:simplifiedMasternodeEntry.keyIDVoting]; +// self.operatorBLSPublicKey = [NSData dataWithUInt384:simplifiedMasternodeEntry.operatorPublicKey]; +// self.operatorPublicKeyVersion = simplifiedMasternodeEntry.operatorPublicKeyVersion; +// self.type = simplifiedMasternodeEntry.type; +// self.platformNodeID = [NSData dataWithUInt160:simplifiedMasternodeEntry.platformNodeID]; +// self.platformHTTPPort = simplifiedMasternodeEntry.platformHTTPPort; +// self.isValid = simplifiedMasternodeEntry.isValid; +// self.simplifiedMasternodeEntryHash = [NSData dataWithUInt256:simplifiedMasternodeEntry.simplifiedMasternodeEntryHash]; +// self.updateHeight = blockHeight; +// +// if (simplifiedMasternodeEntry.updateHeight != blockHeight) { +// DSLog(@"• setAttributesFromSimplifiedMasternodeEntry: list.height %u != entry.height %u", blockHeight, simplifiedMasternodeEntry.updateHeight); +// } +// // TODO: make sure we're doing +//// NSAssert(simplifiedMasternodeEntry.updateHeight == blockHeight, ([NSString stringWithFormat:@"the block height (%i) for %@ should be the same as the entry update height (%i)", blockHeight, uint256_hex(simplifiedMasternodeEntry.providerRegistrationTransactionHash), simplifiedMasternodeEntry.updateHeight])); +// if (!chainEntity) { +// self.chain = [simplifiedMasternodeEntry.chain chainEntityInContext:self.managedObjectContext]; +// } else { +// self.chain = chainEntity; +// } +// DSLocalMasternodeEntity *localMasternode = localMasternodes +// ? [localMasternodes objectForKey:uint256_data(simplifiedMasternodeEntry.providerRegistrationTransactionHash)] +// : [DSLocalMasternodeEntity anyObjectInContext:chainEntity.managedObjectContext matching:@"providerRegistrationTransaction.transactionHash.txHash == %@", uint256_data(simplifiedMasternodeEntry.providerRegistrationTransactionHash)]; +// self.localMasternode = localMasternode; +// NSString *operatorAddress = [DSKeyManager addressWithPublicKeyData:self.operatorBLSPublicKey forChain:simplifiedMasternodeEntry.chain]; +// NSString *votingAddress = [DSKeyManager addressFromHash160:self.keyIDVoting.UInt160 forChain:simplifiedMasternodeEntry.chain]; +// NSString *platformNodeAddress = [DSKeyManager addressFromHash160:self.platformNodeID.UInt160 forChain:simplifiedMasternodeEntry.chain]; +// // TODO: check do we have to do the same for platform node addresses +// DSAddressEntity *operatorAddressEntity = knownOperatorAddresses +// ? [knownOperatorAddresses objectForKey:operatorAddress] +// : [DSAddressEntity findAddressMatching:operatorAddress onChain:simplifiedMasternodeEntry.chain inContext:self.managedObjectContext]; +// if (operatorAddressEntity) { +// [self addAddressesObject:operatorAddressEntity]; +// } +// DSAddressEntity *votingAddressEntity = knownVotingAddresses +// ? [knownVotingAddresses objectForKey:votingAddress] +// : [DSAddressEntity findAddressMatching:votingAddress onChain:simplifiedMasternodeEntry.chain inContext:self.managedObjectContext]; +// if (votingAddressEntity) { +// [self addAddressesObject:votingAddressEntity]; +// } +// DSAddressEntity *platformNodeAddressEntity = platformNodeAddresses +// ? [platformNodeAddresses objectForKey:platformNodeAddress] +// : [DSAddressEntity findAddressMatching:platformNodeAddress onChain:simplifiedMasternodeEntry.chain inContext:self.managedObjectContext]; +// if (platformNodeAddressEntity) { +// [self addAddressesObject:platformNodeAddressEntity]; +// } } -+ (void)deleteHavingProviderTransactionHashes:(NSArray *)providerTransactionHashes onChainEntity:(DSChainEntity *)chainEntity { ++ (void)deleteHavingProviderTransactionHashes:(NSArray *)providerTransactionHashes + onChainEntity:(DSChainEntity *)chainEntity { NSArray *hashesToDelete = [self objectsInContext:chainEntity.managedObjectContext matching:@"(chain == %@) && (providerRegistrationTransactionHash IN %@)", chainEntity, providerTransactionHashes]; for (DSSimplifiedMasternodeEntryEntity *simplifiedMasternodeEntryEntity in hashesToDelete) { DSLog(@"deleteHavingProviderTransactionHashes: %@", simplifiedMasternodeEntryEntity.providerRegistrationTransactionHash.hexString); @@ -217,11 +425,13 @@ + (void)deleteAllOnChainEntity:(DSChainEntity *)chainEntity { } } -+ (DSSimplifiedMasternodeEntryEntity *)simplifiedMasternodeEntryForProviderRegistrationTransactionHash:(NSData *)providerRegistrationTransactionHash onChainEntity:(DSChainEntity *)chainEntity { ++ (DSSimplifiedMasternodeEntryEntity *)simplifiedMasternodeEntryForProviderRegistrationTransactionHash:(NSData *)providerRegistrationTransactionHash + onChainEntity:(DSChainEntity *)chainEntity { return [self anyObjectInContext:chainEntity.managedObjectContext matching:@"(providerRegistrationTransactionHash == %@) && (chain == %@)", providerRegistrationTransactionHash, chainEntity]; } -+ (DSSimplifiedMasternodeEntryEntity *)simplifiedMasternodeEntryForHash:(NSData *)simplifiedMasternodeEntryHash onChainEntity:(DSChainEntity *)chainEntity { ++ (DSSimplifiedMasternodeEntryEntity *)simplifiedMasternodeEntryForHash:(NSData *)simplifiedMasternodeEntryHash + onChainEntity:(DSChainEntity *)chainEntity { return [self anyObjectInContext:chainEntity.managedObjectContext matching:@"(simplifiedMasternodeEntryHash == %@) && (chain == %@)", simplifiedMasternodeEntryHash, chainEntity]; } @@ -229,7 +439,8 @@ + (DSSimplifiedMasternodeEntryEntity *)simplifiedMasternodeEntryForHash:(NSData return [self blockDictionaryFromBlockHashDictionary:blockHashDictionary blockHeightLookup:nil]; } -- (NSDictionary *)blockDictionaryFromBlockHashDictionary:(NSDictionary *)blockHashDictionary blockHeightLookup:(BlockHeightFinder)blockHeightLookup { +- (NSDictionary *)blockDictionaryFromBlockHashDictionary:(NSDictionary *)blockHashDictionary + blockHeightLookup:(BlockHeightFinder)blockHeightLookup { NSMutableDictionary *rDictionary = [NSMutableDictionary dictionary]; DSChain *chain = self.chain.chain; for (NSData *blockHash in blockHashDictionary) { @@ -259,13 +470,83 @@ + (DSSimplifiedMasternodeEntryEntity *)simplifiedMasternodeEntryForHash:(NSData return rDictionary; } -- (DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry { - return [self simplifiedMasternodeEntryWithBlockHeightLookup:nil]; -} +//- (DMasternodeEntry *)simplifiedMasternodeEntry { +// return [self simplifiedMasternodeEntryWithBlockHeightLookup:nil]; +//} + +- (DMasternodeEntry *)simplifiedMasternodeEntryWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { + u256 *provider_registration_transaction_hash = u256_ctor(self.providerRegistrationTransactionHash); + u256 *confirmed_hash = u256_ctor(self.confirmedHash); + u128 *ip_address = u128_ctor_u(self.ipv6Address.UInt128); + uint16_t port = self.port; + u160 *key_id_voting = u160_ctor(self.keyIDVoting); + u384 *operator_public_key_data = u384_ctor(self.operatorBLSPublicKey); + uint16_t operator_public_key_version = self.operatorPublicKeyVersion; + uint16_t mn_type = self.type; + uint16_t platform_http_port = self.platformHTTPPort; + u160 *platform_node_id = u160_ctor(self.platformNodeID); + uint32_t update_height = self.updateHeight; + u256 *entry_hash = u256_ctor(self.simplifiedMasternodeEntryHash); + + u256 *hash_confirmed_hash = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_hash_confirmed_hash(confirmed_hash, provider_registration_transaction_hash); + uintptr_t prev_entry_hashes_count = self.previousSimplifiedMasternodeEntryHashes.count; + Arr_u8_68 **prev_entry_hashes_values = malloc(prev_entry_hashes_count * sizeof(Arr_u8_68 *)); + uintptr_t index = 0; + for (NSData *key in self.previousSimplifiedMasternodeEntryHashes) { + NSData *value = self.previousSimplifiedMasternodeEntryHashes[key]; + NSMutableData *blob = [key mutableCopy]; + uint32_t height = blockHeightLookup(key.UInt256); + [blob appendUInt32:height]; + [blob appendUInt256:value.UInt256]; + //DSLog(@"prev_entry_hash: %@ --> %@ (%u) -> %@", self.providerRegistrationTransactionHash.hexString, value.hexString, height, value.hexString); + + prev_entry_hashes_values[index] = Arr_u8_68_ctor(blob.length, (uint8_t *) blob.bytes); + index++; + } -- (DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntryWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { - DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry = [DSSimplifiedMasternodeEntry simplifiedMasternodeEntryWithProviderRegistrationTransactionHash:[self.providerRegistrationTransactionHash UInt256] confirmedHash:[self.confirmedHash UInt256] address:self.ipv6Address.UInt128 port:self.port operatorBLSPublicKey:[self.operatorBLSPublicKey UInt384] operatorPublicKeyVersion:self.operatorPublicKeyVersion previousOperatorBLSPublicKeys:[self blockDictionaryFromBlockHashDictionary:self.previousOperatorBLSPublicKeys blockHeightLookup:blockHeightLookup] keyIDVoting:[self.keyIDVoting UInt160] isValid:self.isValid type:self.type platformHTTPPort:self.platformHTTPPort platformNodeID:[self.platformNodeID UInt160] previousValidity:[self blockDictionaryFromBlockHashDictionary:self.previousValidity blockHeightLookup:blockHeightLookup] knownConfirmedAtHeight:self.knownConfirmedAtHeight updateHeight:self.updateHeight simplifiedMasternodeEntryHash:[self.simplifiedMasternodeEntryHash UInt256] previousSimplifiedMasternodeEntryHashes:[self blockDictionaryFromBlockHashDictionary:self.previousSimplifiedMasternodeEntryHashes blockHeightLookup:blockHeightLookup] onChain:self.chain.chain]; - return simplifiedMasternodeEntry; +// for (uintptr_t i = 0; i < index; i++) { +// free(prev_entry_hashes_values[i]); +// } +// free(prev_entry_hashes_values); + + uintptr_t prev_operator_keys_count = self.previousOperatorBLSPublicKeys.count; + Arr_u8_86 **prev_operator_keys_values = malloc(prev_operator_keys_count * sizeof(Arr_u8_86 *)); + index = 0; + for (NSData *key in self.previousOperatorBLSPublicKeys) { + NSData *value = self.previousOperatorBLSPublicKeys[key]; + NSMutableData *blob = [key mutableCopy]; + [blob appendUInt32:blockHeightLookup(key.UInt256)]; + [blob appendData:value]; + prev_operator_keys_values[index] = Arr_u8_86_ctor(blob.length, (uint8_t *) blob.bytes); + index++; + } +// for (uintptr_t i = 0; i < index; i++) { +// free(prev_operator_keys_values[i]); +// } +// free(prev_operator_keys_values); + + uintptr_t prev_validity_count = self.previousValidity.count; + Arr_u8_37 **prev_validity_values = malloc(prev_validity_count * sizeof(Arr_u8_37 *)); + index = 0; + for (NSData *key in self.previousValidity) { + NSNumber *value = self.previousValidity[key]; + NSMutableData *blob = [key mutableCopy]; + [blob appendUInt32:blockHeightLookup(key.UInt256)]; + [blob appendUInt8:(uint8_t) value.boolValue]; + prev_validity_values[index] = Arr_u8_37_ctor(blob.length, (uint8_t *) blob.bytes); + index++; + } +// for (uintptr_t i = 0; i < index; i++) { +// free(prev_validity_values[i]); +// } +// free(prev_validity_values); + + Vec_u8_68 *prev_entry_hashes = Vec_u8_68_ctor(prev_entry_hashes_count, prev_entry_hashes_values); + Vec_u8_86 *previous_operator_public_keys = Vec_u8_86_ctor(prev_operator_keys_count, prev_operator_keys_values); + Vec_u8_37 *previous_validity = Vec_u8_37_ctor(prev_validity_count, prev_validity_values); + DMasternodeEntry *entry = dash_spv_masternode_processor_models_masternode_entry_from_entity(self.version, provider_registration_transaction_hash, confirmed_hash, ip_address, port, key_id_voting, operator_public_key_data, operator_public_key_version, self.isValid, mn_type, platform_http_port, platform_node_id, update_height, hash_confirmed_hash, self.knownConfirmedAtHeight, entry_hash, prev_entry_hashes, previous_operator_public_keys, previous_validity); + // TODO: free mem + return entry; } @@ -301,3 +582,4 @@ - (NSString *)debugDescription { @end + diff --git a/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataProperties.h b/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataProperties.h index 64ed9c1a8..e9b6ddbe4 100644 --- a/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataProperties.h +++ b/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataProperties.h @@ -44,6 +44,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nullable, nonatomic, retain) NSSet *governanceVotes; @property (nullable, nonatomic, retain) DSLocalMasternodeEntity *localMasternode; @property (nullable, nonatomic, retain) NSSet *masternodeLists; +@property (nonatomic, assign) uint16_t version; @end diff --git a/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataProperties.m b/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataProperties.m index 2958437fb..18030966c 100644 --- a/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataProperties.m +++ b/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataProperties.m @@ -43,5 +43,6 @@ @implementation DSSimplifiedMasternodeEntryEntity (CoreDataProperties) @dynamic type; @dynamic platformHTTPPort; @dynamic platformNodeID; +@dynamic version; @end diff --git a/DashSync/shared/Models/Entities/DSTransactionEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSTransactionEntity+CoreDataClass.m index b65805d62..80ce83953 100644 --- a/DashSync/shared/Models/Entities/DSTransactionEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSTransactionEntity+CoreDataClass.m @@ -24,6 +24,7 @@ #import "DSAddressEntity+CoreDataClass.h" #import "DSChain+Protected.h" +#import "DSChain+Transaction.h" #import "DSChainEntity+CoreDataClass.h" #import "DSInstantSendLockEntity+CoreDataClass.h" #import "DSInstantSendTransactionLock.h" diff --git a/DashSync/shared/Models/Governance/DSGovernanceObject.m b/DashSync/shared/Models/Governance/DSGovernanceObject.m index 0d0968390..74e370ae3 100644 --- a/DashSync/shared/Models/Governance/DSGovernanceObject.m +++ b/DashSync/shared/Models/Governance/DSGovernanceObject.m @@ -7,6 +7,7 @@ #import "DSGovernanceObject.h" #import "DSAccount.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" #import "DSChainEntity+CoreDataProperties.h" #import "DSChainManager.h" @@ -299,7 +300,9 @@ - (void)loadGovernanceVotes:(NSUInteger)count { if (!_knownGovernanceVoteHashesForExistingGovernanceVotes) _knownGovernanceVoteHashesForExistingGovernanceVotes = [NSMutableOrderedSet orderedSet]; for (DSGovernanceVoteEntity *governanceVoteEntity in governanceVoteEntities) { DSGovernanceVote *governanceVote = [governanceVoteEntity governanceVote]; - DSLog(@"%@ : %@ -> %d/%d", self.identifier, [NSData dataWithUInt256:governanceVote.masternode.simplifiedMasternodeEntryHash].shortHexString, governanceVote.outcome, governanceVote.signal); + + UInt256 entryHash = u256_cast(governanceVote.masternode->entry_hash); + DSLog(@"%@ : %@ -> %d/%d", self.identifier, [NSData dataWithUInt256:entryHash].shortHexString, governanceVote.outcome, governanceVote.signal); [_knownGovernanceVoteHashesForExistingGovernanceVotes addObject:[NSData dataWithUInt256:governanceVote.governanceVoteHash]]; [_governanceVotes addObject:governanceVote]; } diff --git a/DashSync/shared/Models/Governance/DSGovernanceVote.h b/DashSync/shared/Models/Governance/DSGovernanceVote.h index 275039c80..265fe8783 100644 --- a/DashSync/shared/Models/Governance/DSGovernanceVote.h +++ b/DashSync/shared/Models/Governance/DSGovernanceVote.h @@ -7,6 +7,7 @@ #import "dash_shared_core.h" #import "DSChain.h" +#import "DSKeyManager.h" #import NS_ASSUME_NONNULL_BEGIN @@ -33,7 +34,7 @@ typedef NS_ENUM(uint32_t, DSGovernanceVoteOutcome) @interface DSGovernanceVote : NSObject @property (nullable, nonatomic, strong) DSGovernanceObject *governanceObject; -@property (nonatomic, readonly) DSSimplifiedMasternodeEntry *masternode; +@property (nonatomic, readonly) DMasternodeEntry *masternode; @property (nonatomic, readonly) DSGovernanceVoteOutcome outcome; @property (nonatomic, readonly) DSGovernanceVoteSignal signal; @property (nonatomic, readonly) NSTimeInterval createdAt; @@ -45,7 +46,7 @@ typedef NS_ENUM(uint32_t, DSGovernanceVoteOutcome) + (DSGovernanceVote *_Nullable)governanceVoteFromMessage:(NSData *)message onChain:(DSChain *)chain; - (instancetype)initWithParentHash:(UInt256)parentHash forMasternodeUTXO:(DSUTXO)masternodeUTXO voteOutcome:(DSGovernanceVoteOutcome)voteOutcome voteSignal:(DSGovernanceVoteSignal)voteSignal createdAt:(NSTimeInterval)createdAt signature:(NSData *_Nullable)signature onChain:(DSChain *)chain; -- (void)signWithKey:(OpaqueKey *)key; +- (void)signWithKey:(DOpaqueKey *)key; - (NSData *)dataMessage; - (BOOL)isValid; diff --git a/DashSync/shared/Models/Governance/DSGovernanceVote.m b/DashSync/shared/Models/Governance/DSGovernanceVote.m index 2ba347b9c..d32ab9724 100644 --- a/DashSync/shared/Models/Governance/DSGovernanceVote.m +++ b/DashSync/shared/Models/Governance/DSGovernanceVote.m @@ -7,6 +7,7 @@ #import "DSGovernanceVote.h" #import "DSChain.h" +#import "DSChain+Params.h" #import "DSChainsManager.h" #import "DSKeyManager.h" #import "DSMasternodeManager.h" @@ -20,7 +21,8 @@ @interface DSGovernanceVote () -@property (nonatomic, strong) DSSimplifiedMasternodeEntry *masternode; +@property (nonatomic, assign) DMasternodeEntry *masternode; +//@property (nonatomic, strong) DSSimplifiedMasternodeEntry *masternode; @property (nonatomic, assign) DSGovernanceVoteOutcome outcome; @property (nonatomic, assign) DSGovernanceVoteSignal signal; @property (nonatomic, assign) NSTimeInterval createdAt; @@ -138,18 +140,20 @@ - (instancetype)initWithParentHash:(UInt256)parentHash forMasternodeUTXO:(DSUTXO return self; } -- (DSSimplifiedMasternodeEntry *)masternode { - if (!_masternode) { - /// WTF? old fields ? - self.masternode = [DSSimplifiedMasternodeEntryEntity anyObjectInContext:[NSManagedObjectContext chainContext] matching:@"utxoHash = %@ && utxoIndex = %@", [NSData dataWithUInt256:(UInt256)self.masternodeUTXO.hash], @(self.masternodeUTXO.n)].simplifiedMasternodeEntry; - } - return _masternode; -} - -- (void)signWithKey:(OpaqueKey *)key { +//- (DMasternodeEntry *)masternode { +// if (!_masternode) { +// /// WTF? old fields ? +// self.masternode = [DSSimplifiedMasternodeEntryEntity anyObjectInContext:[NSManagedObjectContext chainContext] matching:@"utxoHash = %@ && utxoIndex = %@", [NSData dataWithUInt256:(UInt256)self.masternodeUTXO.hash], @(self.masternodeUTXO.n)].simplifiedMasternodeEntry; +// } +// return _masternode; +//} +// +- (void)signWithKey:(DOpaqueKey *)key { NSParameterAssert(key); // ECDSA - self.signature = [DSKeyManager NSDataFrom:key_ecdsa_sign(key->ecdsa, self.governanceVoteHash.u8, 32)]; + SLICE *slice = slice_u256_ctor_u(self.governanceVoteHash); + BYTES *result = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_sign(key->ecdsa, slice); + self.signature = [DSKeyManager NSDataFrom:result]; } - (BOOL)isValid { diff --git a/DashSync/shared/Models/Identity/DSBlockchainIdentity+Protected.h b/DashSync/shared/Models/Identity/DSBlockchainIdentity+Protected.h deleted file mode 100644 index d77540f5a..000000000 --- a/DashSync/shared/Models/Identity/DSBlockchainIdentity+Protected.h +++ /dev/null @@ -1,91 +0,0 @@ -// -// Created by Sam Westrich -// Copyright © 2020 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DSBlockchainIdentity.h" - -NS_ASSUME_NONNULL_BEGIN - -@class DSBlockchainIdentityEntity; - -@interface DSBlockchainIdentity () - -@property (nonatomic, readonly) DSBlockchainIdentityEntity *blockchainIdentityEntity; -@property (nullable, nonatomic, strong) DSTransientDashpayUser *transientDashpayUser; -@property (nonatomic, weak) DSBlockchainInvitation *associatedInvitation; -@property (nonatomic, assign) OpaqueKey *registrationFundingPrivateKey; -@property (nonatomic, assign) BOOL isLocal; -@property (nonatomic, assign) UInt256 registrationCreditFundingTransactionHash; - - -- (DSBlockchainIdentityEntity *)blockchainIdentityEntityInContext:(NSManagedObjectContext *)context; - -- (instancetype)initWithBlockchainIdentityEntity:(DSBlockchainIdentityEntity *)blockchainIdentityEntity; - -//This one is called for a local identity that is being recreated from the network -- (instancetype)initAtIndex:(uint32_t)index withUniqueId:(UInt256)uniqueId inWallet:(DSWallet *)wallet withBlockchainIdentityEntity:(DSBlockchainIdentityEntity *)blockchainIdentityEntity; - -//This one is called from an identity that was created locally by creating a credit funding transaction -- (instancetype)initAtIndex:(uint32_t)index withLockedOutpoint:(DSUTXO)lockedOutpoint inWallet:(DSWallet *)wallet withBlockchainIdentityEntity:(DSBlockchainIdentityEntity *)blockchainIdentityEntity; - -- (instancetype)initAtIndex:(uint32_t)index withLockedOutpoint:(DSUTXO)lockedOutpoint inWallet:(DSWallet *)wallet withBlockchainIdentityEntity:(DSBlockchainIdentityEntity *)blockchainIdentityEntity associatedToInvitation:(DSBlockchainInvitation *)invitation; - -- (instancetype)initWithUniqueId:(UInt256)uniqueId isTransient:(BOOL)isTransient onChain:(DSChain *)chain; - -- (instancetype)initAtIndex:(uint32_t)index inWallet:(DSWallet *)wallet; - -- (instancetype)initAtIndex:(uint32_t)index withLockedOutpoint:(DSUTXO)lockedOutpoint inWallet:(DSWallet *)wallet; - -- (instancetype)initAtIndex:(uint32_t)index withUniqueId:(UInt256)uniqueId inWallet:(DSWallet *)wallet; - -- (instancetype)initAtIndex:(uint32_t)index withIdentityDictionary:(NSDictionary *)identityDictionary version:(uint32_t)version inWallet:(DSWallet *)wallet; - -- (instancetype)initAtIndex:(uint32_t)index withFundingTransaction:(DSCreditFundingTransaction *)transaction withUsernameDictionary:(NSDictionary *_Nullable)usernameDictionary inWallet:(DSWallet *)wallet; - -- (instancetype)initAtIndex:(uint32_t)index withFundingTransaction:(DSCreditFundingTransaction *)transaction withUsernameDictionary:(NSDictionary *_Nullable)usernameDictionary havingCredits:(uint64_t)credits registrationStatus:(DSBlockchainIdentityRegistrationStatus)registrationStatus inWallet:(DSWallet *)wallet; - -- (void)addUsername:(NSString *)username inDomain:(NSString *)domain status:(DSBlockchainIdentityUsernameStatus)status save:(BOOL)save registerOnNetwork:(BOOL)registerOnNetwork; - -- (void)addKey:(OpaqueKey *)key atIndex:(uint32_t)index ofType:(KeyKind)type withStatus:(DSBlockchainIdentityKeyStatus)status save:(BOOL)save; -- (void)addKey:(OpaqueKey *)key atIndexPath:(NSIndexPath *)indexPath ofType:(KeyKind)type withStatus:(DSBlockchainIdentityKeyStatus)status save:(BOOL)save; -- (BOOL)registerKeyWithStatus:(DSBlockchainIdentityKeyStatus)status atIndexPath:(NSIndexPath *)indexPath ofType:(KeyKind)type; -- (OpaqueKey *_Nullable)privateKeyAtIndex:(uint32_t)index ofType:(KeyKind)type; -- (OpaqueKey *_Nullable)privateKeyAtIndex:(uint32_t)index ofType:(KeyKind)type forSeed:(NSData *)seed; -- (void)deletePersistentObjectAndSave:(BOOL)save inContext:(NSManagedObjectContext *)context; - -- (void)saveInitial; - -- (void)saveInitialInContext:(NSManagedObjectContext *)context; - -- (void)registerInWalletForBlockchainIdentityUniqueId:(UInt256)blockchainIdentityUniqueId; - -- (void)registrationTransitionWithCompletion:(void (^_Nullable)(DSBlockchainIdentityRegistrationTransition *_Nullable blockchainIdentityRegistrationTransition, NSError *_Nullable error))completion; - -- (void)createFundingPrivateKeyWithSeed:(NSData *)seed isForInvitation:(BOOL)isForInvitation completion:(void (^_Nullable)(BOOL success))completion; - -- (void)applyProfileChanges:(DSTransientDashpayUser *)transientDashpayUser inContext:(NSManagedObjectContext *)context saveContext:(BOOL)saveContext completion:(void (^_Nullable)(BOOL success, NSError *_Nullable error))completion onCompletionQueue:(dispatch_queue_t)completionQueue; - -- (void)setInvitationUniqueId:(UInt256)uniqueId; - -- (void)setInvitationRegistrationCreditFundingTransaction:(DSCreditFundingTransaction *)creditFundingTransaction; - -//-(void)topupTransitionForForFundingTransaction:(DSTransaction*)fundingTransaction completion:(void (^ _Nullable)(DSBlockchainIdentityTopupTransition * blockchainIdentityTopupTransition))completion; -// -//-(void)updateTransitionUsingNewIndex:(uint32_t)index completion:(void (^ _Nullable)(DSBlockchainIdentityUpdateTransition * blockchainIdentityUpdateTransition))completion; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Identity/DSBlockchainIdentity.h b/DashSync/shared/Models/Identity/DSBlockchainIdentity.h deleted file mode 100644 index 6f62e921c..000000000 --- a/DashSync/shared/Models/Identity/DSBlockchainIdentity.h +++ /dev/null @@ -1,409 +0,0 @@ -// -// DSBlockchainIdentity.h -// DashSync -// -// Created by Sam Westrich on 7/26/18. -// - -#import "BigIntTypes.h" -#import "DSDAPIClient.h" -#import "DSDerivationPath.h" -#import "DSKeyManager.h" -#import - -NS_ASSUME_NONNULL_BEGIN -@class DSWallet, DSBlockchainIdentityRegistrationTransition, DSBlockchainIdentityTopupTransition, DSBlockchainIdentityUpdateTransition, DSBlockchainIdentityCloseTransition, DSAccount, DSChain, DSTransition, DSDashpayUserEntity, DSPotentialOneWayFriendship, DSTransaction, DSFriendRequestEntity, DSPotentialContact, DSCreditFundingTransaction, DSDocumentTransition, DPDocumentFactory, DSTransientDashpayUser, DSBlockchainInvitation, DSAuthenticationKeysDerivationPath, UIImage; - -typedef NS_ENUM(NSUInteger, DSBlockchainIdentityRegistrationStep) -{ - DSBlockchainIdentityRegistrationStep_None = 0, - DSBlockchainIdentityRegistrationStep_FundingTransactionCreation = 1, - DSBlockchainIdentityRegistrationStep_FundingTransactionAccepted = 2, - DSBlockchainIdentityRegistrationStep_LocalInWalletPersistence = 4, - DSBlockchainIdentityRegistrationStep_ProofAvailable = 8, - DSBlockchainIdentityRegistrationStep_L1Steps = DSBlockchainIdentityRegistrationStep_FundingTransactionCreation | DSBlockchainIdentityRegistrationStep_FundingTransactionAccepted | DSBlockchainIdentityRegistrationStep_LocalInWalletPersistence | DSBlockchainIdentityRegistrationStep_ProofAvailable, - DSBlockchainIdentityRegistrationStep_Identity = 16, - DSBlockchainIdentityRegistrationStep_RegistrationSteps = DSBlockchainIdentityRegistrationStep_L1Steps | DSBlockchainIdentityRegistrationStep_Identity, - DSBlockchainIdentityRegistrationStep_Username = 32, - DSBlockchainIdentityRegistrationStep_RegistrationStepsWithUsername = DSBlockchainIdentityRegistrationStep_RegistrationSteps | DSBlockchainIdentityRegistrationStep_Username, - DSBlockchainIdentityRegistrationStep_Profile = 64, - DSBlockchainIdentityRegistrationStep_RegistrationStepsWithUsernameAndDashpayProfile = DSBlockchainIdentityRegistrationStep_RegistrationStepsWithUsername | DSBlockchainIdentityRegistrationStep_Profile, - DSBlockchainIdentityRegistrationStep_All = DSBlockchainIdentityRegistrationStep_RegistrationStepsWithUsernameAndDashpayProfile, - DSBlockchainIdentityRegistrationStep_Cancelled = 1 << 30 -}; - -typedef NS_ENUM(NSUInteger, DSBlockchainIdentityMonitorOptions) -{ - DSBlockchainIdentityMonitorOptions_None = 0, - DSBlockchainIdentityMonitorOptions_AcceptNotFoundAsNotAnError = 1, -}; - -typedef NS_ENUM(NSUInteger, DSBlockchainIdentityQueryStep) -{ - DSBlockchainIdentityQueryStep_None = DSBlockchainIdentityRegistrationStep_None, //0 - DSBlockchainIdentityQueryStep_Identity = DSBlockchainIdentityRegistrationStep_Identity, //16 - DSBlockchainIdentityQueryStep_Username = DSBlockchainIdentityRegistrationStep_Username, //32 - DSBlockchainIdentityQueryStep_Profile = DSBlockchainIdentityRegistrationStep_Profile, //64 - DSBlockchainIdentityQueryStep_IncomingContactRequests = 128, - DSBlockchainIdentityQueryStep_OutgoingContactRequests = 256, - DSBlockchainIdentityQueryStep_ContactRequests = DSBlockchainIdentityQueryStep_IncomingContactRequests | DSBlockchainIdentityQueryStep_OutgoingContactRequests, - DSBlockchainIdentityQueryStep_AllForForeignBlockchainIdentity = DSBlockchainIdentityQueryStep_Identity | DSBlockchainIdentityQueryStep_Username | DSBlockchainIdentityQueryStep_Profile, - DSBlockchainIdentityQueryStep_AllForLocalBlockchainIdentity = DSBlockchainIdentityQueryStep_Identity | DSBlockchainIdentityQueryStep_Username | DSBlockchainIdentityQueryStep_Profile | DSBlockchainIdentityQueryStep_ContactRequests, - DSBlockchainIdentityQueryStep_NoIdentity = 1 << 28, - DSBlockchainIdentityQueryStep_BadQuery = 1 << 29, - DSBlockchainIdentityQueryStep_Cancelled = 1 << 30 -}; - -typedef NS_ENUM(NSUInteger, DSBlockchainIdentityRegistrationStatus) -{ - DSBlockchainIdentityRegistrationStatus_Unknown = 0, - DSBlockchainIdentityRegistrationStatus_Registered = 1, - DSBlockchainIdentityRegistrationStatus_Registering = 2, - DSBlockchainIdentityRegistrationStatus_NotRegistered = 3, //sent to DAPI, not yet confirmed -}; - -typedef NS_ENUM(NSUInteger, DSBlockchainIdentityUsernameStatus) -{ - DSBlockchainIdentityUsernameStatus_NotPresent = 0, - DSBlockchainIdentityUsernameStatus_Initial = 1, - DSBlockchainIdentityUsernameStatus_PreorderRegistrationPending = 2, - DSBlockchainIdentityUsernameStatus_Preordered = 3, - DSBlockchainIdentityUsernameStatus_RegistrationPending = 4, //sent to DAPI, not yet confirmed - DSBlockchainIdentityUsernameStatus_Confirmed = 5, - DSBlockchainIdentityUsernameStatus_TakenOnNetwork = 6, -}; - -typedef NS_ENUM(NSUInteger, DSBlockchainIdentityFriendshipStatus) -{ - DSBlockchainIdentityFriendshipStatus_Unknown = NSUIntegerMax, - DSBlockchainIdentityFriendshipStatus_None = 0, - DSBlockchainIdentityFriendshipStatus_Outgoing = 1, - DSBlockchainIdentityFriendshipStatus_Incoming = 2, - DSBlockchainIdentityFriendshipStatus_Friends = DSBlockchainIdentityFriendshipStatus_Outgoing | DSBlockchainIdentityFriendshipStatus_Incoming, -}; - -typedef NS_ENUM(NSUInteger, DSBlockchainIdentityRetryDelayType) -{ - DSBlockchainIdentityRetryDelayType_Linear = 0, - DSBlockchainIdentityRetryDelayType_SlowingDown20Percent = 1, - DSBlockchainIdentityRetryDelayType_SlowingDown50Percent = 2, -}; - -typedef NS_ENUM(NSUInteger, DSBlockchainIdentityKeyStatus) -{ - DSBlockchainIdentityKeyStatus_Unknown = 0, - DSBlockchainIdentityKeyStatus_Registered = 1, - DSBlockchainIdentityKeyStatus_Registering = 2, - DSBlockchainIdentityKeyStatus_NotRegistered = 3, - DSBlockchainIdentityKeyStatus_Revoked = 4, -}; - -#define BLOCKCHAIN_USERNAME_STATUS @"BLOCKCHAIN_USERNAME_STATUS" -#define BLOCKCHAIN_USERNAME_PROPER @"BLOCKCHAIN_USERNAME_PROPER" -#define BLOCKCHAIN_USERNAME_DOMAIN @"BLOCKCHAIN_USERNAME_DOMAIN" -#define BLOCKCHAIN_USERNAME_SALT @"BLOCKCHAIN_USERNAME_SALT" - -FOUNDATION_EXPORT NSString *const DSBlockchainIdentityDidUpdateNotification; -FOUNDATION_EXPORT NSString *const DSBlockchainIdentityDidUpdateUsernameStatusNotification; -FOUNDATION_EXPORT NSString *const DSBlockchainIdentityKey; -FOUNDATION_EXPORT NSString *const DSBlockchainIdentityUsernameKey; -FOUNDATION_EXPORT NSString *const DSBlockchainIdentityUsernameDomainKey; - -FOUNDATION_EXPORT NSString *const DSBlockchainIdentityUpdateEvents; -FOUNDATION_EXPORT NSString *const DSBlockchainIdentityUpdateEventKeyUpdate; -FOUNDATION_EXPORT NSString *const DSBlockchainIdentityUpdateEventRegistration; -FOUNDATION_EXPORT NSString *const DSBlockchainIdentityUpdateEventCreditBalance; -FOUNDATION_EXPORT NSString *const DSBlockchainIdentityUpdateEventType; -FOUNDATION_EXPORT NSString *const DSBlockchainIdentityUpdateEventDashpaySyncronizationBlockHash; - -@interface DSBlockchainIdentity : NSObject - -/*! @brief This is the unique identifier representing the blockchain identity. It is derived from the credit funding transaction credit burn UTXO (as of dpp v10). Returned as a 256 bit number */ -@property (nonatomic, readonly) UInt256 uniqueID; - -/*! @brief This is the unique identifier representing the blockchain identity. It is derived from the credit funding transaction credit burn UTXO (as of dpp v10). Returned as a base 58 string of a 256 bit number */ -@property (nonatomic, readonly) NSString *uniqueIdString; - -/*! @brief This is the unique identifier representing the blockchain identity. It is derived from the credit funding transaction credit burn UTXO (as of dpp v10). Returned as a NSData of a 256 bit number */ -@property (nonatomic, readonly) NSData *uniqueIDData; - -/*! @brief This is the outpoint of the registration credit funding transaction. It is used to determine the unique ID by double SHA256 its value. Returned as a UTXO { .hash , .n } */ -@property (nonatomic, readonly) DSUTXO lockedOutpoint; - -/*! @brief This is the outpoint of the registration credit funding transaction. It is used to determine the unique ID by double SHA256 its value. Returned as an NSData of a UTXO { .hash , .n } */ -@property (nonatomic, readonly) NSData *lockedOutpointData; - -/*! @brief This is if the blockchain identity is present in wallets or not. If this is false then the blockchain identity is known for example from being a dashpay friend. */ -@property (nonatomic, readonly) BOOL isLocal; - -/*! @brief This is if the blockchain identity is made for being an invitation. All invitations should be marked as non local as well. */ -@property (nonatomic, readonly) BOOL isOutgoingInvitation; - -/*! @brief This is if the blockchain identity is made from an invitation we received. */ -@property (nonatomic, readonly) BOOL isFromIncomingInvitation; - -/*! @brief This is TRUE if the blockchain identity is an effemeral identity returned when searching. */ -@property (nonatomic, readonly) BOOL isTransient; - -/*! @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; - -/*! @brief This is the bitwise steps that the identity has already performed in registration. */ -@property (nonatomic, readonly) DSBlockchainIdentityRegistrationStep 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. */ -@property (nonatomic, weak, readonly) DSWallet *wallet; - -/*! @brief This is invitation that is identity originated from. */ -@property (nonatomic, weak, readonly) DSBlockchainInvitation *associatedInvitation; - -/*! @brief This is the index of the blockchain identity in the wallet. The index is the top derivation used to derive an extended set of keys for the identity. No two local blockchain identities should be allowed to have the same index in a wallet. For example m/.../.../.../index/key */ -@property (nonatomic, readonly) uint32_t index; - -/*! @brief Related to DPNS. This is the list of usernames that are associated to the identity in the domain "dash". These usernames however might not yet be registered or might be invalid. This can be used in tandem with the statusOfUsername: method */ -@property (nonatomic, readonly) NSArray *dashpayUsernames; - -/*! @brief Related to DPNS. This is the list of usernames with their .dash domain that are associated to the identity in the domain "dash". These usernames however might not yet be registered or might be invalid. This can be used in tandem with the statusOfUsername: method */ -@property (nonatomic, readonly) NSArray *dashpayUsernameFullPaths; - -/*! @brief Related to DPNS. This is current and most likely username associated to the identity. It is not necessarily registered yet on L2 however so its state should be determined with the statusOfUsername: method - @discussion There are situations where this is nil as it is not yet known or if no username has yet been set. */ -@property (nullable, nonatomic, readonly) NSString *currentDashpayUsername; - -/*! @brief Related to registering the identity. This is the address used to fund the registration of the identity. Dash sent to this address in the special credit funding transaction will be converted to L2 credits */ -@property (nonatomic, readonly) NSString *registrationFundingAddress; - -/*! @brief The known balance in credits of the identity */ -@property (nonatomic, readonly) uint64_t creditBalance; - -/*! @brief The number of registered active keys that the blockchain identity has */ -@property (nonatomic, readonly) uint32_t activeKeyCount; - -/*! @brief The number of all keys that the blockchain identity has, registered, in registration, or inactive */ -@property (nonatomic, readonly) uint32_t totalKeyCount; - -/*! @brief This is the transaction on L1 that has an output that is used to fund the creation of this blockchain identity. - @discussion There are situations where this is nil as it is not yet known ; if the blockchain identity is being retrieved from L2 or if we are resyncing the chain. */ -@property (nullable, nonatomic, readonly) DSCreditFundingTransaction *registrationCreditFundingTransaction; - -/*! @brief This is the hash of the transaction on L1 that has an output that is used to fund the creation of this blockchain identity. - @discussion There are situations where this is nil as it is not yet known ; if the blockchain identity is being retrieved from L2 or if we are resyncing the chain. */ -@property (nonatomic, readonly) UInt256 registrationCreditFundingTransactionHash; - -/*! @brief In our system a contact is a vue on a blockchain identity for Dashpay. A blockchain identity is therefore represented by a contact that will have relationships in the system. This is in the default backgroundContext. */ -@property (nonatomic, readonly) DSDashpayUserEntity *matchingDashpayUserInViewContext; - -/*! @brief This is the status of the registration of the identity. It starts off in an initial status, and ends in a confirmed status */ -@property (nonatomic, readonly) DSBlockchainIdentityRegistrationStatus registrationStatus; - -/*! @brief This is the localized status of the registration of the identity returned as a string. It starts off in an initial status, and ends in a confirmed status */ -@property (nonatomic, readonly) NSString *localizedRegistrationStatusString; - -/*! @brief This is a convenience method that checks to see if registrationStatus is confirmed */ -@property (nonatomic, readonly, getter=isRegistered) BOOL registered; - -/*! @brief This is a convenience factory to quickly make dashpay documents */ -@property (nonatomic, readonly) DPDocumentFactory *dashpayDocumentFactory; - -/*! @brief This is a convenience factory to quickly make dpns documents */ -@property (nonatomic, readonly) DPDocumentFactory *dpnsDocumentFactory; - -/*! @brief DashpaySyncronizationBlock represents the last L1 block height for which Dashpay would be synchronized, if this isn't at the end of the chain then we need to query L2 to make sure we don't need to update our bloom filter */ -@property (nonatomic, readonly) uint32_t dashpaySyncronizationBlockHeight; - -/*! @brief DashpaySyncronizationBlock represents the last L1 block hash for which Dashpay would be synchronized */ -@property (nonatomic, readonly) UInt256 dashpaySyncronizationBlockHash; - -// MARK: - Contracts - -- (void)fetchAndUpdateContract:(DPContract *)contract; - -// MARK: - Helpers - -- (DSDashpayUserEntity *)matchingDashpayUserInContext:(NSManagedObjectContext *)context; - -// MARK: - Identity - -- (void)registerOnNetwork:(DSBlockchainIdentityRegistrationStep)steps withFundingAccount:(DSAccount *)account forTopupAmount:(uint64_t)topupDuffAmount pinPrompt:(NSString *)prompt stepCompletion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepCompleted))stepCompletion completion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepsCompleted, NSError *error))completion; - -- (void)continueRegisteringOnNetwork:(DSBlockchainIdentityRegistrationStep)steps withFundingAccount:(DSAccount *)fundingAccount forTopupAmount:(uint64_t)topupDuffAmount pinPrompt:(NSString *)prompt stepCompletion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepCompleted))stepCompletion completion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepsCompleted, NSError *error))completion; - -- (void)continueRegisteringIdentityOnNetwork:(DSBlockchainIdentityRegistrationStep)steps stepsCompleted:(DSBlockchainIdentityRegistrationStep)stepsAlreadyCompleted stepCompletion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepCompleted))stepCompletion completion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepsCompleted, NSError *error))completion; - -- (void)fundingTransactionForTopupAmount:(uint64_t)topupAmount toAddress:(NSString *)address fundedByAccount:(DSAccount *)fundingAccount completion:(void (^_Nullable)(DSCreditFundingTransaction *fundingTransaction))completion; - -- (void)fetchIdentityNetworkStateInformationWithCompletion:(void (^)(BOOL success, BOOL found, NSError *error))completion; - -- (void)fetchAllNetworkStateInformationWithCompletion:(void (^)(DSBlockchainIdentityQueryStep failureStep, NSArray *errors))completion; - -- (void)fetchNeededNetworkStateInformationWithCompletion:(void (^)(DSBlockchainIdentityQueryStep failureStep, NSArray *errors))completion; - -- (void)signStateTransition:(DSTransition *)transition completion:(void (^_Nullable)(BOOL success))completion; - -- (void)signStateTransition:(DSTransition *)transition forKeyIndex:(uint32_t)keyIndex ofType:(KeyKind)signingAlgorithm completion:(void (^_Nullable)(BOOL success))completion; - -- (void)signMessageDigest:(UInt256)digest forKeyIndex:(uint32_t)keyIndex ofType:(KeyKind)signingAlgorithm completion:(void (^_Nullable)(BOOL success, NSData *signature))completion; - -- (BOOL)verifySignature:(NSData *)signature forKeyIndex:(uint32_t)keyIndex ofType:(KeyKind)signingAlgorithm forMessageDigest:(UInt256)messageDigest; - -- (BOOL)verifySignature:(NSData *)signature ofType:(KeyKind)signingAlgorithm forMessageDigest:(UInt256)messageDigest; - -- (void)createFundingPrivateKeyWithPrompt:(NSString *)prompt completion:(void (^_Nullable)(BOOL success, BOOL cancelled))completion; - -- (void)createFundingPrivateKeyForInvitationWithPrompt:(NSString *)prompt completion:(void (^_Nullable)(BOOL success, BOOL cancelled))completion; - -- (void)createAndPublishRegistrationTransitionWithCompletion:(void (^_Nullable)(NSDictionary *_Nullable successInfo, NSError *_Nullable error))completion; - - -- (void)encryptData:(NSData *)data withKeyAtIndex:(uint32_t)index forRecipientKey:(OpaqueKey *)recipientKey completion:(void (^_Nullable)(NSData *encryptedData))completion; - -/*! @brief Register the blockchain identity to its wallet. This should only be done once on the creation of the blockchain identity. -*/ -- (void)registerInWallet; - -/*! @brief Unregister the blockchain identity from the wallet. This should only be used if the blockchain identity is not yet registered or if a progressive wallet wipe is happening. - @discussion When a blockchain identity is registered on the network it is automatically retrieved from the L1 chain on resync. If a client wallet wishes to change their default blockchain identity in a wallet it should be done by marking the default blockchain identity index in the wallet. Clients should not try to delete a registered blockchain identity from a wallet. - */ -- (BOOL)unregisterLocally; - -/*! @brief Register the blockchain identity to its wallet from a credit funding registration transaction. This should only be done once on the creation of the blockchain identity. - @param fundingTransaction The funding transaction used to initially fund the blockchain identity. -*/ -- (void)registerInWalletForRegistrationFundingTransaction:(DSCreditFundingTransaction *)fundingTransaction; - -// MARK: - Keys - -/*! @brief Register the blockchain identity to its wallet from a credit funding registration transaction. This should only be done once on the creation of the blockchain identity. -*/ - -- (void)generateBlockchainIdentityExtendedPublicKeysWithPrompt:(NSString *)prompt completion:(void (^_Nullable)(BOOL registered))completion; - -- (BOOL)setExternalFundingPrivateKey:(OpaqueKey *)privateKey; - -- (BOOL)hasBlockchainIdentityExtendedPublicKeys; - -- (DSBlockchainIdentityKeyStatus)statusOfKeyAtIndex:(NSUInteger)index; - -- (KeyKind)typeOfKeyAtIndex:(NSUInteger)index; - -- (OpaqueKey *_Nullable)keyAtIndex:(NSUInteger)index; - -- (uint32_t)keyCountForKeyType:(KeyKind)keyType; - -+ (NSString *)localizedStatusOfKeyForBlockchainIdentityKeyStatus:(DSBlockchainIdentityKeyStatus)status; - -- (NSString *)localizedStatusOfKeyAtIndex:(NSUInteger)index; - -- (OpaqueKey *_Nullable)createNewKeyOfType:(KeyKind)type saveKey:(BOOL)saveKey returnIndex:(uint32_t *)rIndex; - -- (OpaqueKey *_Nullable)keyOfType:(KeyKind)type atIndex:(uint32_t)rIndex; - -+ (DSAuthenticationKeysDerivationPath *_Nullable)derivationPathForType:(KeyKind)type forWallet:(DSWallet *)wallet; - -+ (OpaqueKey *_Nullable)keyFromKeyDictionary:(NSDictionary *)dictionary rType:(uint32_t *)rType rIndex:(uint32_t *)rIndex; - -+ (OpaqueKey *_Nullable)firstKeyInIdentityDictionary:(NSDictionary *)identityDictionary; - -// MARK: - Dashpay - -/*! @brief This is a helper to easily get the avatar path of the matching dashpay user. */ -@property (nonatomic, readonly, nullable) NSString *avatarPath; - -/*! @brief This is a helper to easily get the avatar fingerprint of the matching dashpay user. */ -@property (nonatomic, readonly) NSData *avatarFingerprint; - -/*! @brief This is a helper to easily get the avatar hash of the matching dashpay user. */ -@property (nonatomic, readonly, nullable) NSData *avatarHash; - -/*! @brief This is a helper to easily get the display name of the matching dashpay user. */ -@property (nonatomic, readonly, nullable) NSString *displayName; - -/*! @brief This is a helper to easily get the public message of the matching dashpay user. */ -@property (nonatomic, readonly, nullable) NSString *publicMessage; - -/*! @brief This is a helper to easily get the last time the profile was updated of the matching dashpay user. */ -@property (nonatomic, assign) uint64_t dashpayProfileUpdatedAt; - -/*! @brief This is a helper to easily get the creation time of the profile of the matching dashpay user. */ -@property (nonatomic, assign) uint64_t dashpayProfileCreatedAt; - -- (void)sendNewFriendRequestToBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity completion:(void (^)(BOOL success, NSArray *_Nullable errors))completion; - -- (void)sendNewFriendRequestToPotentialContact:(DSPotentialContact *)potentialContact completion:(void (^_Nullable)(BOOL success, NSArray *errors))completion; - -- (void)sendNewFriendRequestMatchingPotentialFriendship:(DSPotentialOneWayFriendship *)potentialFriendship completion:(void (^_Nullable)(BOOL success, NSArray *errors))completion; - - -- (void)acceptFriendRequestFromBlockchainIdentity:(DSBlockchainIdentity *)otherBlockchainIdentity completion:(void (^)(BOOL success, NSArray *errors))completion; - -- (void)acceptFriendRequest:(DSFriendRequestEntity *)friendRequest completion:(void (^_Nullable)(BOOL success, NSArray *errors))completion; - -- (BOOL)activePrivateKeysAreLoadedWithFetchingError:(NSError **)error; - -- (void)fetchContactRequests:(void (^_Nullable)(BOOL success, NSArray *errors))completion; - -- (void)fetchOutgoingContactRequests:(void (^_Nullable)(BOOL success, NSArray *errors))completion; - -- (void)fetchIncomingContactRequests:(void (^_Nullable)(BOOL success, NSArray *errors))completion; - -- (void)fetchProfileWithCompletion:(void (^_Nullable)(BOOL success, NSError *error))completion; - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName; - -- (void)updateDashpayProfileWithPublicMessage:(NSString *)publicMessage; - -- (void)updateDashpayProfileWithAvatarURLString:(NSString *)avatarURLString; - -- (void)updateDashpayProfileWithAvatarURLString:(NSString *)avatarURLString avatarHash:(NSData *)avatarHash avatarFingerprint:(NSData *)avatarFingerprint; - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage; - -#if TARGET_OS_IOS - -- (void)updateDashpayProfileWithAvatarImage:(UIImage *)avatarImage avatarData:(NSData *)data avatarURLString:(NSString *)avatarURLString; - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage avatarImage:(UIImage *)avatarImage avatarData:(NSData *)data avatarURLString:(NSString *)avatarURLString; - -#else - -- (void)updateDashpayProfileWithAvatarImage:(NSImage *)avatarImage avatarData:(NSData *)data avatarURLString:(NSString *)avatarURLString; - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage avatarImage:(NSImage *)avatarImage avatarData:(NSData *)data avatarURLString:(NSString *)avatarURLString; - - -#endif - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage avatarURLString:(NSString *)avatarURLString; - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage avatarURLString:(NSString *)avatarURLString avatarHash:(NSData *)avatarHash avatarFingerprint:(NSData *)avatarFingerprint; - -- (void)signedProfileDocumentTransitionInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(DSTransition *transition, BOOL cancelled, NSError *error))completion; - -- (void)signAndPublishProfileWithCompletion:(void (^)(BOOL success, BOOL cancelled, NSError *error))completion; - -- (BOOL)verifyKeysForWallet:(DSWallet *)wallet; - -// MARK: - Dashpay Friendship Helpers - -- (DSBlockchainIdentityFriendshipStatus)friendshipStatusForRelationshipWithBlockchainIdentity:(DSBlockchainIdentity *)otherBlockchainIdentity; - -// MARK: - DPNS - -- (void)addDashpayUsername:(NSString *)username save:(BOOL)save; - -- (void)addUsername:(NSString *)username inDomain:(NSString *)domain save:(BOOL)save; - -- (DSBlockchainIdentityUsernameStatus)statusOfUsername:(NSString *)username inDomain:(NSString *)domain; - -- (DSBlockchainIdentityUsernameStatus)statusOfDashpayUsername:(NSString *)username; - -- (void)registerUsernamesWithCompletion:(void (^_Nullable)(BOOL success, NSError *error))completion; - -- (void)fetchUsernamesWithCompletion:(void (^_Nullable)(BOOL success, NSError *error))completion; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Identity/DSBlockchainIdentity.m b/DashSync/shared/Models/Identity/DSBlockchainIdentity.m deleted file mode 100644 index bfc7b862f..000000000 --- a/DashSync/shared/Models/Identity/DSBlockchainIdentity.m +++ /dev/null @@ -1,4849 +0,0 @@ -// -// DSBlockchainIdentity.m -// DashSync -// -// Created by Sam Westrich on 7/26/18. -// - -#import "BigIntTypes.h" -#import "dash_shared_core.h" -#import "DPContract+Protected.h" -#import "DPDocumentFactory.h" -#import "DSAccount.h" -#import "DSAccountEntity+CoreDataClass.h" -#import "DSAuthenticationKeysDerivationPath.h" -#import "DSAuthenticationManager.h" -#import "DSBlockchainIdentity+Protected.h" -#import "DSBlockchainIdentityCloseTransition.h" -#import "DSBlockchainIdentityEntity+CoreDataClass.h" -#import "DSBlockchainIdentityKeyPathEntity+CoreDataClass.h" -#import "DSBlockchainIdentityRegistrationTransition.h" -#import "DSBlockchainIdentityTopupTransition.h" -#import "DSBlockchainIdentityUpdateTransition.h" -#import "DSBlockchainIdentityUsernameEntity+CoreDataClass.h" -#import "DSBlockchainInvitation+Protected.h" -#import "DSBlockchainInvitationEntity+CoreDataClass.h" -#import "DSChain+Protected.h" -#import "DSChainEntity+CoreDataClass.h" -#import "DSChainManager.h" -#import "DSContactRequest.h" -#import "DSContractTransition.h" -#import "DSCreditFundingDerivationPath.h" -#import "DSCreditFundingTransaction.h" -#import "DSCreditFundingTransactionEntity+CoreDataClass.h" -#import "DSDAPIPlatformNetworkService.h" -#import "DSDashPlatform.h" -#import "DSDashpayUserEntity+CoreDataClass.h" -#import "DSDerivationPath.h" -#import "DSDerivationPathFactory.h" -#import "DSDocumentTransition.h" -#import "DSFriendRequestEntity+CoreDataClass.h" -#import "DSIdentitiesManager+Protected.h" -#import "DSIncomingFundsDerivationPath.h" -#import "DSMerkleBlock.h" -#import "DSOptionsManager.h" -#import "DSPeerManager.h" -#import "DSPotentialContact.h" -#import "DSPotentialOneWayFriendship.h" -#import "DSPriceManager.h" -#import "DSSpecialTransactionsWalletHolder.h" -#import "DSTransaction+Protected.h" -#import "DSTransactionHashEntity+CoreDataClass.h" -#import "DSTransactionManager+Protected.h" -#import "DSTransientDashpayUser.h" -#import "DSTransition+Protected.h" -#import "DSWallet.h" -#import "NSCoder+Dash.h" -#import "NSData+Dash.h" -#import "NSData+Encryption.h" -#import "NSError+Dash.h" -#import "NSIndexPath+Dash.h" -#import "NSManagedObject+Sugar.h" -#import "NSMutableData+Dash.h" -#import -#import -#import "dash_shared_core.h" - -#define BLOCKCHAIN_USER_UNIQUE_IDENTIFIER_KEY @"BLOCKCHAIN_USER_UNIQUE_IDENTIFIER_KEY" -#define DEFAULT_SIGNING_ALGORITHM KeyKind_ECDSA -#define DEFAULT_FETCH_IDENTITY_RETRY_COUNT 5 -#define DEFAULT_FETCH_USERNAMES_RETRY_COUNT 5 -#define DEFAULT_FETCH_PROFILE_RETRY_COUNT 5 - -typedef NS_ENUM(NSUInteger, DSBlockchainIdentityKeyDictionary) -{ - DSBlockchainIdentityKeyDictionary_Key = 0, - DSBlockchainIdentityKeyDictionary_KeyType = 1, - DSBlockchainIdentityKeyDictionary_KeyStatus = 2, -}; - -@interface DSBlockchainIdentity () - -@property (nonatomic, weak) DSWallet *wallet; -@property (nonatomic, strong) NSMutableDictionary *usernameStatuses; -@property (nonatomic, assign) UInt256 uniqueID; -@property (nonatomic, assign) BOOL isOutgoingInvitation; -@property (nonatomic, assign) BOOL isFromIncomingInvitation; -@property (nonatomic, assign) BOOL isTransient; -@property (nonatomic, assign) DSUTXO lockedOutpoint; -@property (nonatomic, assign) uint32_t index; -@property (nonatomic, assign) DSBlockchainIdentityRegistrationStatus registrationStatus; -@property (nonatomic, assign) uint64_t creditBalance; - -@property (nonatomic, assign) uint32_t keysCreated; -@property (nonatomic, strong) NSMutableDictionary *keyInfoDictionaries; -@property (nonatomic, assign) uint32_t currentMainKeyIndex; -@property (nonatomic, assign) KeyKind currentMainKeyType; - -@property (nonatomic, strong) NSMutableDictionary *usernameSalts; -@property (nonatomic, strong) NSMutableDictionary *usernameDomains; - -@property (nonatomic, readonly) DSDAPIClient *DAPIClient; -@property (nonatomic, readonly) DSDAPIPlatformNetworkService *DAPINetworkService; - -@property (nonatomic, strong) DPDocumentFactory *dashpayDocumentFactory; -@property (nonatomic, strong) DPDocumentFactory *dpnsDocumentFactory; - -@property (nonatomic, strong) DSDashpayUserEntity *matchingDashpayUserInViewContext; - -@property (nonatomic, strong) DSDashpayUserEntity *matchingDashpayUserInPlatformContext; - -@property (nonatomic, strong) DSChain *chain; - -@property (nonatomic, assign) OpaqueKey *internalRegistrationFundingPrivateKey; - -@property (nonatomic, assign) UInt256 dashpaySyncronizationBlockHash; - -@property (nonatomic, readonly) DSIdentitiesManager *identitiesManager; - -@property (nonatomic, readonly) NSManagedObjectContext *platformContext; - -@property (nonatomic, strong) DSCreditFundingTransaction *registrationCreditFundingTransaction; - -@property (nonatomic, strong) dispatch_queue_t identityQueue; - - -@property (nonatomic, assign) uint64_t lastCheckedUsernamesTimestamp; -@property (nonatomic, assign) uint64_t lastCheckedProfileTimestamp; -@property (nonatomic, assign) uint64_t lastCheckedIncomingContactsTimestamp; -@property (nonatomic, assign) uint64_t lastCheckedOutgoingContactsTimestamp; - -@end - -@implementation DSBlockchainIdentity - -- (void)dealloc { - if (_internalRegistrationFundingPrivateKey != NULL) { - processor_destroy_opaque_key(_internalRegistrationFundingPrivateKey); - } -} -// MARK: - Initialization - -- (instancetype)initWithUniqueId:(UInt256)uniqueId isTransient:(BOOL)isTransient onChain:(DSChain *)chain { - //this is the initialization of a non local blockchain 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 = KeyKind_ECDSA; - self.usernameStatuses = [NSMutableDictionary dictionary]; - self.usernameDomains = [NSMutableDictionary dictionary]; - self.keyInfoDictionaries = [NSMutableDictionary dictionary]; - _registrationStatus = DSBlockchainIdentityRegistrationStatus_Registered; - self.chain = chain; - return self; -} - -- (instancetype)initWithUniqueId:(UInt256)uniqueId isTransient:(BOOL)isTransient withCredits:(uint32_t)credits onChain:(DSChain *)chain { - //this is the initialization of a non local blockchain identity - if (!(self = [self initWithUniqueId:uniqueId isTransient:isTransient onChain:chain])) return nil; - _creditBalance = credits; - return self; -} - -- (void)applyIdentityEntity:(DSBlockchainIdentityEntity *)blockchainIdentityEntity { - for (DSBlockchainIdentityUsernameEntity *usernameEntity in blockchainIdentityEntity.usernames) { - NSData *salt = usernameEntity.salt; - if (salt) { - [self.usernameStatuses setObject:@{BLOCKCHAIN_USERNAME_PROPER: usernameEntity.stringValue, BLOCKCHAIN_USERNAME_DOMAIN: usernameEntity.domain ? usernameEntity.domain : @"", BLOCKCHAIN_USERNAME_STATUS: @(usernameEntity.status), BLOCKCHAIN_USERNAME_SALT: usernameEntity.salt} forKey:[self fullPathForUsername:usernameEntity.stringValue inDomain:usernameEntity.domain]]; - [self.usernameSalts setObject:usernameEntity.salt forKey:usernameEntity.stringValue]; - } else { - [self.usernameStatuses setObject:@{BLOCKCHAIN_USERNAME_PROPER: usernameEntity.stringValue, BLOCKCHAIN_USERNAME_DOMAIN: usernameEntity.domain ? usernameEntity.domain : @"", BLOCKCHAIN_USERNAME_STATUS: @(usernameEntity.status)} forKey:[self fullPathForUsername:usernameEntity.stringValue inDomain:usernameEntity.domain]]; - } - } - _creditBalance = blockchainIdentityEntity.creditBalance; - _registrationStatus = blockchainIdentityEntity.registrationStatus; - - _lastCheckedProfileTimestamp = blockchainIdentityEntity.lastCheckedProfileTimestamp; - _lastCheckedUsernamesTimestamp = blockchainIdentityEntity.lastCheckedUsernamesTimestamp; - _lastCheckedIncomingContactsTimestamp = blockchainIdentityEntity.lastCheckedIncomingContactsTimestamp; - _lastCheckedOutgoingContactsTimestamp = blockchainIdentityEntity.lastCheckedOutgoingContactsTimestamp; - - self.dashpaySyncronizationBlockHash = blockchainIdentityEntity.dashpaySyncronizationBlockHash.UInt256; - for (DSBlockchainIdentityKeyPathEntity *keyPath in blockchainIdentityEntity.keyPaths) { - KeyKind keyType = (KeyKind) keyPath.keyType; - NSIndexPath *keyIndexPath = (NSIndexPath *)[keyPath path]; - if (keyIndexPath) { - NSIndexPath *nonHardenedKeyIndexPath = [keyIndexPath softenAllItems]; - BOOL success = [self registerKeyWithStatus:keyPath.keyStatus atIndexPath:nonHardenedKeyIndexPath ofType:keyType]; - if (!success) { - OpaqueKey *key = [DSKeyManager keyWithPublicKeyData:keyPath.publicKeyData ofType:keyType]; - [self registerKey:key withStatus:keyPath.keyStatus atIndex:keyPath.keyID ofType:keyType]; - } - } else { - OpaqueKey *key = [DSKeyManager keyWithPublicKeyData:keyPath.publicKeyData ofType:keyType]; - [self registerKey:key withStatus:keyPath.keyStatus atIndex:keyPath.keyID ofType:keyType]; - } - } - if (self.isLocal || self.isOutgoingInvitation) { - if (blockchainIdentityEntity.registrationFundingTransaction) { - self.registrationCreditFundingTransactionHash = blockchainIdentityEntity.registrationFundingTransaction.transactionHash.txHash.UInt256; - } else { - NSData *transactionHashData = uint256_data(uint256_reverse(self.lockedOutpoint.hash)); - DSTransactionEntity *creditRegitrationTransactionEntity = [DSTransactionEntity anyObjectInContext:blockchainIdentityEntity.managedObjectContext matching:@"transactionHash.txHash == %@", transactionHashData]; - if (creditRegitrationTransactionEntity) { - self.registrationCreditFundingTransactionHash = creditRegitrationTransactionEntity.transactionHash.txHash.UInt256; - DSCreditFundingTransaction *registrationCreditFundingTransaction = (DSCreditFundingTransaction *)[creditRegitrationTransactionEntity transactionForChain:self.chain]; - BOOL correctIndex; - if (self.isOutgoingInvitation) { - correctIndex = [registrationCreditFundingTransaction checkInvitationDerivationPathIndexForWallet:self.wallet isIndex:self.index]; - } else { - correctIndex = [registrationCreditFundingTransaction checkDerivationPathIndexForWallet:self.wallet isIndex:self.index]; - } - if (!correctIndex) { - NSAssert(FALSE, @"We should implement this"); - } - } - } - } -} - -- (instancetype)initWithBlockchainIdentityEntity:(DSBlockchainIdentityEntity *)blockchainIdentityEntity { - if (!(self = [self initWithUniqueId:blockchainIdentityEntity.uniqueID.UInt256 isTransient:FALSE onChain:blockchainIdentityEntity.chain.chain])) return nil; - [self applyIdentityEntity:blockchainIdentityEntity]; - - return self; -} - -- (instancetype)initAtIndex:(uint32_t)index withLockedOutpoint:(DSUTXO)lockedOutpoint inWallet:(DSWallet *)wallet withBlockchainIdentityEntity:(DSBlockchainIdentityEntity *)blockchainIdentityEntity { - if (!(self = [self initAtIndex:index withLockedOutpoint:lockedOutpoint inWallet:wallet])) return nil; - [self applyIdentityEntity:blockchainIdentityEntity]; - return self; -} - -- (instancetype)initAtIndex:(uint32_t)index withUniqueId:(UInt256)uniqueId inWallet:(DSWallet *)wallet withBlockchainIdentityEntity:(DSBlockchainIdentityEntity *)blockchainIdentityEntity { - if (!(self = [self initAtIndex:index withUniqueId:uniqueId inWallet:wallet])) return nil; - [self applyIdentityEntity:blockchainIdentityEntity]; - return self; -} - -- (instancetype)initAtIndex:(uint32_t)index withLockedOutpoint:(DSUTXO)lockedOutpoint inWallet:(DSWallet *)wallet withBlockchainIdentityEntity:(DSBlockchainIdentityEntity *)blockchainIdentityEntity associatedToInvitation:(DSBlockchainInvitation *)invitation { - if (!(self = [self initAtIndex:index withLockedOutpoint:lockedOutpoint inWallet:wallet])) return nil; - [self setAssociatedInvitation:invitation]; - [self applyIdentityEntity:blockchainIdentityEntity]; - 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.isLocal = YES; - self.isOutgoingInvitation = NO; - self.isTransient = FALSE; - self.keysCreated = 0; - self.currentMainKeyIndex = 0; - self.currentMainKeyType = KeyKind_ECDSA; - self.index = index; - self.usernameStatuses = [NSMutableDictionary dictionary]; - self.keyInfoDictionaries = [NSMutableDictionary dictionary]; - self.registrationStatus = DSBlockchainIdentityRegistrationStatus_Unknown; - self.usernameSalts = [NSMutableDictionary dictionary]; - self.chain = wallet.chain; - return self; -} - -- (void)setAssociatedInvitation:(DSBlockchainInvitation *)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; - } -} - -- (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; -} - -- (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"); - self.lockedOutpoint = lockedOutpoint; - self.uniqueID = [dsutxo_data(lockedOutpoint) SHA256_2]; - return self; -} - -- (instancetype)initAtIndex:(uint32_t)index withFundingTransaction:(DSCreditFundingTransaction *)transaction inWallet:(DSWallet *)wallet { - NSParameterAssert(wallet); - if (![transaction isCreditFundingTransaction]) return nil; - NSAssert(index != UINT32_MAX, @"index must be found"); - if (!(self = [self initAtIndex:index withLockedOutpoint:transaction.lockedOutpoint inWallet:wallet])) return nil; - - self.registrationCreditFundingTransaction = transaction; - - return self; -} - -- (instancetype)initAtIndex:(uint32_t)index withFundingTransaction:(DSCreditFundingTransaction *)transaction withUsernameDictionary:(NSDictionary *)usernameDictionary inWallet:(DSWallet *)wallet { - NSAssert(index != UINT32_MAX, @"index must be found"); - if (!(self = [self initAtIndex:index withFundingTransaction:transaction inWallet:wallet])) return nil; - - if (usernameDictionary) { - NSMutableDictionary *usernameSalts = [NSMutableDictionary dictionary]; - for (NSString *username in usernameDictionary) { - NSDictionary *subDictionary = usernameDictionary[username]; - NSData *salt = subDictionary[BLOCKCHAIN_USERNAME_SALT]; - if (salt) { - usernameSalts[username] = salt; - } - } - self.usernameStatuses = [usernameDictionary mutableCopy]; - self.usernameSalts = usernameSalts; - } - return self; -} - -- (instancetype)initAtIndex:(uint32_t)index withFundingTransaction:(DSCreditFundingTransaction *)transaction withUsernameDictionary:(NSDictionary *_Nullable)usernameDictionary havingCredits:(uint64_t)credits registrationStatus:(DSBlockchainIdentityRegistrationStatus)registrationStatus inWallet:(DSWallet *)wallet { - if (!(self = [self initAtIndex:index withFundingTransaction:transaction withUsernameDictionary:usernameDictionary inWallet:wallet])) return nil; - - self.creditBalance = credits; - self.registrationStatus = registrationStatus; - - return self; -} - -- (instancetype)initAtIndex:(uint32_t)index withIdentityDictionary:(NSDictionary *)identityDictionary version:(uint32_t)version inWallet:(DSWallet *)wallet { - NSParameterAssert(wallet); - - if (!(self = [super init])) return nil; - self.wallet = wallet; - self.isLocal = YES; - self.isOutgoingInvitation = NO; - self.isTransient = FALSE; - self.keysCreated = 0; - self.currentMainKeyIndex = 0; - self.currentMainKeyType = KeyKind_ECDSA; - NSData *identityIdData = [identityDictionary objectForKey:@"id"]; - self.uniqueID = identityIdData.UInt256; - self.usernameStatuses = [NSMutableDictionary dictionary]; - self.keyInfoDictionaries = [NSMutableDictionary dictionary]; - self.registrationStatus = DSBlockchainIdentityRegistrationStatus_Registered; - self.usernameSalts = [NSMutableDictionary dictionary]; - self.chain = wallet.chain; - self.index = index; - - [self applyIdentityDictionary:identityDictionary version:version save:NO inContext:nil]; - - return self; -} - -- (dispatch_queue_t)identityQueue { - if (_identityQueue) return _identityQueue; - _identityQueue = self.chain.chainManager.identitiesManager.identityQueue; - return _identityQueue; -} - -// MARK: - Full Registration agglomerate - -- (DSBlockchainIdentityRegistrationStep)stepsCompleted { - DSBlockchainIdentityRegistrationStep stepsCompleted = DSBlockchainIdentityRegistrationStep_None; - if (self.isRegistered) { - stepsCompleted = DSBlockchainIdentityRegistrationStep_RegistrationSteps; - if ([self usernameFullPathsWithStatus:DSBlockchainIdentityUsernameStatus_Confirmed].count) { - stepsCompleted |= DSBlockchainIdentityRegistrationStep_Username; - } - } else if (self.registrationCreditFundingTransaction) { - stepsCompleted |= DSBlockchainIdentityRegistrationStep_FundingTransactionCreation; - DSAccount *account = [self.chain firstAccountThatCanContainTransaction:self.registrationCreditFundingTransaction]; - if (self.registrationCreditFundingTransaction.blockHeight != TX_UNCONFIRMED || [account transactionIsVerified:self.registrationCreditFundingTransaction]) { - stepsCompleted |= DSBlockchainIdentityRegistrationStep_FundingTransactionAccepted; - } - if ([self isRegisteredInWallet]) { - stepsCompleted |= DSBlockchainIdentityRegistrationStep_LocalInWalletPersistence; - } - if (self.registrationCreditFundingTransaction.instantSendLockAwaitingProcessing) { - stepsCompleted |= DSBlockchainIdentityRegistrationStep_ProofAvailable; - } - } - - return stepsCompleted; -} - -- (void)continueRegisteringProfileOnNetwork:(DSBlockchainIdentityRegistrationStep)steps stepsCompleted:(DSBlockchainIdentityRegistrationStep)stepsAlreadyCompleted stepCompletion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepCompleted))stepCompletion completion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepsCompleted, NSError *error))completion { - __block DSBlockchainIdentityRegistrationStep stepsCompleted = stepsAlreadyCompleted; - - if (!(steps & DSBlockchainIdentityRegistrationStep_Profile)) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(stepsCompleted, nil); - }); - } - return; - } - //todo:we need to still do profile - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(stepsCompleted, nil); - }); - } -} - - -- (void)continueRegisteringUsernamesOnNetwork:(DSBlockchainIdentityRegistrationStep)steps stepsCompleted:(DSBlockchainIdentityRegistrationStep)stepsAlreadyCompleted stepCompletion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepCompleted))stepCompletion completion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepsCompleted, NSError *error))completion { - __block DSBlockchainIdentityRegistrationStep stepsCompleted = stepsAlreadyCompleted; - - if (!(steps & DSBlockchainIdentityRegistrationStep_Username)) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(stepsCompleted, nil); - }); - } - return; - } - - [self registerUsernamesWithCompletion:^(BOOL success, NSError *_Nonnull error) { - if (!success) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(stepsCompleted, error); - }); - } - return; - } - if (stepCompletion) { - dispatch_async(dispatch_get_main_queue(), ^{ - stepCompletion(DSBlockchainIdentityRegistrationStep_Username); - }); - } - stepsCompleted |= DSBlockchainIdentityRegistrationStep_Username; - - [self continueRegisteringProfileOnNetwork:steps stepsCompleted:stepsCompleted stepCompletion:stepCompletion completion:completion]; - }]; -} - -- (void)continueRegisteringIdentityOnNetwork:(DSBlockchainIdentityRegistrationStep)steps stepsCompleted:(DSBlockchainIdentityRegistrationStep)stepsAlreadyCompleted stepCompletion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepCompleted))stepCompletion completion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepsCompleted, NSError *error))completion { - __block DSBlockchainIdentityRegistrationStep stepsCompleted = stepsAlreadyCompleted; - if (!(steps & DSBlockchainIdentityRegistrationStep_Identity)) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(stepsCompleted, nil); - }); - } - return; - } - - - [self createAndPublishRegistrationTransitionWithCompletion:^(NSDictionary *_Nullable successInfo, NSError *_Nullable error) { - if (error) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(stepsCompleted, error); - }); - } - return; - } - if (stepCompletion) { - dispatch_async(dispatch_get_main_queue(), ^{ - stepCompletion(DSBlockchainIdentityRegistrationStep_Identity); - }); - } - stepsCompleted |= DSBlockchainIdentityRegistrationStep_Identity; - - [self continueRegisteringUsernamesOnNetwork:steps stepsCompleted:stepsCompleted stepCompletion:stepCompletion completion:completion]; - }]; -} - -- (void)continueRegisteringOnNetwork:(DSBlockchainIdentityRegistrationStep)steps withFundingAccount:(DSAccount *)fundingAccount forTopupAmount:(uint64_t)topupDuffAmount pinPrompt:(NSString *)prompt stepCompletion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepCompleted))stepCompletion completion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepsCompleted, NSError *error))completion { - [self continueRegisteringOnNetwork:steps withFundingAccount:fundingAccount forTopupAmount:topupDuffAmount pinPrompt:prompt inContext:self.platformContext stepCompletion:stepCompletion completion:completion]; -} - -- (void)continueRegisteringOnNetwork:(DSBlockchainIdentityRegistrationStep)steps withFundingAccount:(DSAccount *)fundingAccount forTopupAmount:(uint64_t)topupDuffAmount pinPrompt:(NSString *)prompt inContext:(NSManagedObjectContext *)context stepCompletion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepCompleted))stepCompletion completion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepsCompleted, NSError *error))completion { - if (!self.registrationCreditFundingTransaction) { - [self registerOnNetwork:steps withFundingAccount:fundingAccount forTopupAmount:topupDuffAmount pinPrompt:prompt stepCompletion:stepCompletion completion:completion]; - } else if (self.registrationStatus != DSBlockchainIdentityRegistrationStatus_Registered) { - [self continueRegisteringIdentityOnNetwork:steps stepsCompleted:DSBlockchainIdentityRegistrationStep_L1Steps stepCompletion:stepCompletion completion:completion]; - } else if ([self.unregisteredUsernameFullPaths count]) { - [self continueRegisteringUsernamesOnNetwork:steps stepsCompleted:DSBlockchainIdentityRegistrationStep_L1Steps | DSBlockchainIdentityRegistrationStep_Identity stepCompletion:stepCompletion completion:completion]; - } else if ([self matchingDashpayUserInContext:context].remoteProfileDocumentRevision < 1) { - [self continueRegisteringProfileOnNetwork:steps stepsCompleted:DSBlockchainIdentityRegistrationStep_L1Steps | DSBlockchainIdentityRegistrationStep_Identity stepCompletion:stepCompletion completion:completion]; - } -} - - -- (void)registerOnNetwork:(DSBlockchainIdentityRegistrationStep)steps withFundingAccount:(DSAccount *)fundingAccount forTopupAmount:(uint64_t)topupDuffAmount pinPrompt:(NSString *)prompt stepCompletion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepCompleted))stepCompletion completion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepsCompleted, NSError *error))completion { - __block DSBlockchainIdentityRegistrationStep stepsCompleted = DSBlockchainIdentityRegistrationStep_None; - if (![self hasBlockchainIdentityExtendedPublicKeys]) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(stepsCompleted, [NSError errorWithCode:500 localizedDescriptionKey:@"The blockchain identity extended public keys need to be registered before you can register a blockchain identity."]); - }); - } - return; - } - if (!(steps & DSBlockchainIdentityRegistrationStep_FundingTransactionCreation)) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(stepsCompleted, nil); - }); - } - return; - } - NSString *creditFundingRegistrationAddress = [self registrationFundingAddress]; - [self fundingTransactionForTopupAmount:topupDuffAmount - toAddress:creditFundingRegistrationAddress - fundedByAccount:fundingAccount - completion:^(DSCreditFundingTransaction *_Nonnull fundingTransaction) { - if (!fundingTransaction) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(stepsCompleted, [NSError errorWithCode:500 localizedDescriptionKey:@"Funding transaction could not be created"]); - }); - } - return; - } - [fundingAccount signTransaction:fundingTransaction - withPrompt:prompt - completion:^(BOOL signedTransaction, BOOL cancelled) { - if (!signedTransaction) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - if (cancelled) { - stepsCompleted |= DSBlockchainIdentityRegistrationStep_Cancelled; - } - completion(stepsCompleted, cancelled ? nil : [NSError errorWithCode:500 localizedDescriptionKey:@"Transaction could not be signed"]); - }); - } - return; - } - if (stepCompletion) { - dispatch_async(dispatch_get_main_queue(), ^{ - stepCompletion(DSBlockchainIdentityRegistrationStep_FundingTransactionCreation); - }); - } - stepsCompleted |= DSBlockchainIdentityRegistrationStep_FundingTransactionCreation; - - //In wallet registration occurs now - - if (!(steps & DSBlockchainIdentityRegistrationStep_LocalInWalletPersistence)) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(stepsCompleted, nil); - }); - } - return; - } - if (self.isOutgoingInvitation) { - [self.associatedInvitation registerInWalletForRegistrationFundingTransaction:fundingTransaction]; - } else { - [self registerInWalletForRegistrationFundingTransaction:fundingTransaction]; - } - - if (stepCompletion) { - dispatch_async(dispatch_get_main_queue(), ^{ - stepCompletion(DSBlockchainIdentityRegistrationStep_LocalInWalletPersistence); - }); - } - stepsCompleted |= DSBlockchainIdentityRegistrationStep_LocalInWalletPersistence; - - if (!(steps & DSBlockchainIdentityRegistrationStep_FundingTransactionAccepted)) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(stepsCompleted, nil); - }); - } - return; - } - - dispatch_semaphore_t sem = dispatch_semaphore_create(0); - __block BOOL transactionSuccessfullyPublished = FALSE; - __block DSInstantSendTransactionLock *instantSendLock = nil; - - __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:DSTransactionManagerTransactionStatusDidChangeNotification - object:nil - queue:nil - usingBlock:^(NSNotification *note) { - DSTransaction *tx = [note.userInfo objectForKey:DSTransactionManagerNotificationTransactionKey]; - if ([tx isEqual:fundingTransaction]) { - NSDictionary *changes = [note.userInfo objectForKey:DSTransactionManagerNotificationTransactionChangesKey]; - if (changes) { - NSNumber *accepted = changes[DSTransactionManagerNotificationTransactionAcceptedStatusKey]; - NSNumber *lockVerified = changes[DSTransactionManagerNotificationInstantSendTransactionLockVerifiedKey]; - DSInstantSendTransactionLock *lock = changes[DSTransactionManagerNotificationInstantSendTransactionLockKey]; - if ([lockVerified boolValue] && lock != nil) { - instantSendLock = lock; - transactionSuccessfullyPublished = TRUE; - dispatch_semaphore_signal(sem); - } else if ([accepted boolValue]) { - transactionSuccessfullyPublished = TRUE; - } - } - } - }]; - - - [self.chain.chainManager.transactionManager publishTransaction:fundingTransaction - completion:^(NSError *_Nullable error) { - if (error) { - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(stepsCompleted, error); - }); - } - return; - } - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ - dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, 25 * NSEC_PER_SEC)); - - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - - if (!transactionSuccessfullyPublished) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(stepsCompleted, [NSError errorWithCode:500 localizedDescriptionKey:@"Timeout while waiting for funding transaction to be accepted by network"]); - }); - } - return; - } - - if (stepCompletion) { - dispatch_async(dispatch_get_main_queue(), ^{ - stepCompletion(DSBlockchainIdentityRegistrationStep_FundingTransactionAccepted); - }); - } - stepsCompleted |= DSBlockchainIdentityRegistrationStep_FundingTransactionAccepted; - - if (!instantSendLock) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(stepsCompleted, [NSError errorWithCode:500 localizedDescriptionKey:@"Timeout while waiting for funding transaction to aquire an instant send lock"]); - }); - } - return; - } - - - if (stepCompletion) { - dispatch_async(dispatch_get_main_queue(), ^{ - stepCompletion(DSBlockchainIdentityRegistrationStep_ProofAvailable); - }); - } - stepsCompleted |= DSBlockchainIdentityRegistrationStep_ProofAvailable; - - - [self continueRegisteringIdentityOnNetwork:steps stepsCompleted:stepsCompleted stepCompletion:stepCompletion completion:completion]; - }); - }]; - }]; - }]; -} - -// MARK: - Local Registration and Generation - -- (BOOL)hasBlockchainIdentityExtendedPublicKeys { - NSAssert(_isLocal || _isOutgoingInvitation, @"This should not be performed on a non local blockchain identity (but can be done for an invitation)"); - if (!_isLocal && !_isOutgoingInvitation) return FALSE; - if (_isLocal) { - DSAuthenticationKeysDerivationPath *derivationPathBLS = [[DSDerivationPathFactory sharedInstance] blockchainIdentityBLSKeysDerivationPathForWallet:self.wallet]; - DSAuthenticationKeysDerivationPath *derivationPathECDSA = [[DSDerivationPathFactory sharedInstance] blockchainIdentityECDSAKeysDerivationPathForWallet:self.wallet]; - DSCreditFundingDerivationPath *derivationPathRegistrationFunding = [[DSDerivationPathFactory sharedInstance] blockchainIdentityRegistrationFundingDerivationPathForWallet:self.wallet]; - DSCreditFundingDerivationPath *derivationPathTopupFunding = [[DSDerivationPathFactory sharedInstance] blockchainIdentityTopupFundingDerivationPathForWallet:self.wallet]; - if ([derivationPathBLS hasExtendedPublicKey] && [derivationPathECDSA hasExtendedPublicKey] && [derivationPathRegistrationFunding hasExtendedPublicKey] && [derivationPathTopupFunding hasExtendedPublicKey]) { - return YES; - } else { - return NO; - } - } - if (_isOutgoingInvitation) { - DSCreditFundingDerivationPath *derivationPathInvitationFunding = [[DSDerivationPathFactory sharedInstance] blockchainIdentityInvitationFundingDerivationPathForWallet:self.wallet]; - return [derivationPathInvitationFunding hasExtendedPublicKey]; - } - return NO; -} - -- (void)generateBlockchainIdentityExtendedPublicKeysWithPrompt:(NSString *)prompt completion:(void (^_Nullable)(BOOL registered))completion { - NSAssert(_isLocal || _isOutgoingInvitation, @"This should not be performed on a non local blockchain identity (but can be done for an invitation)"); - if (!_isLocal && !_isOutgoingInvitation) return; - if ([self hasBlockchainIdentityExtendedPublicKeys]) { - if (completion) { - completion(YES); - } - return; - } - [[DSAuthenticationManager sharedInstance] seedWithPrompt:prompt - forWallet:self.wallet - forAmount:0 - forceAuthentication:NO - completion:^(NSData *_Nullable seed, BOOL cancelled) { - if (!seed) { - completion(NO); - return; - } - if (self->_isLocal) { - DSAuthenticationKeysDerivationPath *derivationPathBLS = [[DSDerivationPathFactory sharedInstance] blockchainIdentityBLSKeysDerivationPathForWallet:self.wallet]; - DSAuthenticationKeysDerivationPath *derivationPathECDSA = [[DSDerivationPathFactory sharedInstance] blockchainIdentityECDSAKeysDerivationPathForWallet:self.wallet]; - - [derivationPathBLS generateExtendedPublicKeyFromSeed:seed storeUnderWalletUniqueId:self.wallet.uniqueIDString]; - [derivationPathECDSA generateExtendedPublicKeyFromSeed:seed storeUnderWalletUniqueId:self.wallet.uniqueIDString]; - if (!self->_isFromIncomingInvitation) { - DSCreditFundingDerivationPath *derivationPathRegistrationFunding = [[DSDerivationPathFactory sharedInstance] blockchainIdentityRegistrationFundingDerivationPathForWallet:self.wallet]; - DSCreditFundingDerivationPath *derivationPathTopupFunding = [[DSDerivationPathFactory sharedInstance] blockchainIdentityTopupFundingDerivationPathForWallet:self.wallet]; - [derivationPathRegistrationFunding generateExtendedPublicKeyFromSeed:seed storeUnderWalletUniqueId:self.wallet.uniqueIDString]; - [derivationPathTopupFunding generateExtendedPublicKeyFromSeed:seed storeUnderWalletUniqueId:self.wallet.uniqueIDString]; - } - } - if (self->_isOutgoingInvitation) { - DSCreditFundingDerivationPath *derivationPathInvitationFunding = [[DSDerivationPathFactory sharedInstance] blockchainIdentityInvitationFundingDerivationPathForWallet:self.wallet]; - [derivationPathInvitationFunding generateExtendedPublicKeyFromSeed:seed storeUnderWalletUniqueId:self.wallet.uniqueIDString]; - } - completion(YES); - }]; -} - -- (void)registerInWalletForRegistrationFundingTransaction:(DSCreditFundingTransaction *)fundingTransaction { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) return; - self.registrationCreditFundingTransactionHash = fundingTransaction.txHash; - self.lockedOutpoint = fundingTransaction.lockedOutpoint; - [self registerInWalletForBlockchainIdentityUniqueId:fundingTransaction.creditBurnIdentityIdentifier]; - - //we need to also set the address of the funding transaction to being used so future identities past the initial gap limit are found - [fundingTransaction markAddressAsUsedInWallet:self.wallet]; -} - -- (void)registerInWalletForBlockchainIdentityUniqueId:(UInt256)blockchainIdentityUniqueId { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) return; - self.uniqueID = blockchainIdentityUniqueId; - [self registerInWallet]; -} - -- (BOOL)isRegisteredInWallet { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) return FALSE; - if (!self.wallet) return FALSE; - return [self.wallet containsBlockchainIdentity:self]; -} - -- (void)registerInWallet { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) return; - [self.wallet registerBlockchainIdentity:self]; - [self saveInitial]; -} - -- (BOOL)unregisterLocally { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) return FALSE; - if (self.isRegistered) return FALSE; //if it is already registered we can not unregister it from the wallet - [self.wallet unregisterBlockchainIdentity:self]; - [self deletePersistentObjectAndSave:YES inContext:self.platformContext]; - return TRUE; -} - -- (void)setInvitationUniqueId:(UInt256)uniqueId { - NSAssert(_isOutgoingInvitation, @"This can only be done on an invitation"); - if (!_isOutgoingInvitation) return; - self.uniqueID = uniqueId; -} - -- (void)setInvitationRegistrationCreditFundingTransaction:(DSCreditFundingTransaction *)creditFundingTransaction { - NSParameterAssert(creditFundingTransaction); - NSAssert(_isOutgoingInvitation, @"This can only be done on an invitation"); - if (!_isOutgoingInvitation) return; - self.registrationCreditFundingTransaction = creditFundingTransaction; - self.lockedOutpoint = creditFundingTransaction.lockedOutpoint; -} - -// MARK: - Read Only Property Helpers - -- (BOOL)isActive { - if (self.isLocal) { - if (!self.wallet) return NO; - return self.wallet.blockchainIdentities[self.uniqueIDData] != nil; - } else { - return [self.chain.chainManager.identitiesManager foreignBlockchainIdentityWithUniqueId:self.uniqueID] != nil; - } -} - -- (DSDashpayUserEntity *)matchingDashpayUserInViewContext { - if (!_matchingDashpayUserInViewContext) { - _matchingDashpayUserInViewContext = [self matchingDashpayUserInContext:[NSManagedObjectContext viewContext]]; - } - return _matchingDashpayUserInViewContext; -} - -- (DSDashpayUserEntity *)matchingDashpayUserInPlatformContext { - if (!_matchingDashpayUserInPlatformContext) { - _matchingDashpayUserInPlatformContext = [self matchingDashpayUserInContext:[NSManagedObjectContext platformContext]]; - } - return _matchingDashpayUserInPlatformContext; -} - -- (DSDashpayUserEntity *)matchingDashpayUserInContext:(NSManagedObjectContext *)context { - if (_matchingDashpayUserInViewContext || _matchingDashpayUserInPlatformContext) { - if (context == [_matchingDashpayUserInPlatformContext managedObjectContext]) return _matchingDashpayUserInPlatformContext; - if (context == [_matchingDashpayUserInViewContext managedObjectContext]) return _matchingDashpayUserInViewContext; - if (_matchingDashpayUserInPlatformContext) { - __block NSManagedObjectID *managedId; - [[NSManagedObjectContext platformContext] performBlockAndWait:^{ - managedId = _matchingDashpayUserInPlatformContext.objectID; - }]; - return [context objectWithID:managedId]; - } else { - __block NSManagedObjectID *managedId; - [[NSManagedObjectContext viewContext] performBlockAndWait:^{ - managedId = _matchingDashpayUserInViewContext.objectID; - }]; - return [context objectWithID:managedId]; - } - } else { - __block DSDashpayUserEntity *dashpayUserEntity = nil; - [context performBlockAndWait:^{ - dashpayUserEntity = [DSDashpayUserEntity anyObjectInContext:context matching:@"associatedBlockchainIdentity.uniqueID == %@", uint256_data(self.uniqueID)]; - }]; - return dashpayUserEntity; - } -} - -- (DSCreditFundingTransaction *)registrationCreditFundingTransaction { - if (!_registrationCreditFundingTransaction) { - _registrationCreditFundingTransaction = (DSCreditFundingTransaction *)[self.chain transactionForHash:self.registrationCreditFundingTransactionHash]; - } - return _registrationCreditFundingTransaction; -} - -- (NSData *)uniqueIDData { - return uint256_data(self.uniqueID); -} - -- (NSData *)lockedOutpointData { - return dsutxo_data(self.lockedOutpoint); -} - -- (NSString *)currentDashpayUsername { - return [self.dashpayUsernames firstObject]; -} - - -- (NSArray *)derivationPaths { - if (!_isLocal) return nil; - return [[DSDerivationPathFactory sharedInstance] unloadedSpecializedDerivationPathsForWallet:self.wallet]; -} - -- (NSString *)uniqueIdString { - return [uint256_data(self.uniqueID) base58String]; -} - - -- (dispatch_queue_t)networkingQueue { - return self.chain.networkingQueue; -} - -- (NSManagedObjectContext *)platformContext { - // NSAssert(![NSThread isMainThread], @"We should not be on main thread"); - return [NSManagedObjectContext platformContext]; -} - -- (DSIdentitiesManager *)identitiesManager { - return self.chain.chainManager.identitiesManager; -} - -// ECDSA -- (OpaqueKey *)registrationFundingPrivateKey { - return self.internalRegistrationFundingPrivateKey; -} - -// MARK: Dashpay helpers - -- (NSString *)avatarPath { - if (self.transientDashpayUser) { - return self.transientDashpayUser.avatarPath; - } else { - return self.matchingDashpayUserInViewContext.avatarPath; - } -} - -- (NSData *)avatarFingerprint { - if (self.transientDashpayUser) { - return self.transientDashpayUser.avatarFingerprint; - } else { - return self.matchingDashpayUserInViewContext.avatarFingerprint; - } -} - -- (NSData *)avatarHash { - if (self.transientDashpayUser) { - return self.transientDashpayUser.avatarHash; - } else { - return self.matchingDashpayUserInViewContext.avatarHash; - } -} - -- (NSString *)displayName { - if (self.transientDashpayUser) { - return self.transientDashpayUser.displayName; - } else { - return self.matchingDashpayUserInViewContext.displayName; - } -} - -- (NSString *)publicMessage { - if (self.transientDashpayUser) { - return self.transientDashpayUser.publicMessage; - } else { - return self.matchingDashpayUserInViewContext.publicMessage; - } -} - -- (uint64_t)dashpayProfileUpdatedAt { - if (self.transientDashpayUser) { - return self.transientDashpayUser.updatedAt; - } else { - return self.matchingDashpayUserInViewContext.updatedAt; - } -} - -- (uint64_t)dashpayProfileCreatedAt { - if (self.transientDashpayUser) { - return self.transientDashpayUser.createdAt; - } else { - return self.matchingDashpayUserInViewContext.createdAt; - } -} - -// MARK: - Keys - -- (void)createFundingPrivateKeyWithSeed:(NSData *)seed isForInvitation:(BOOL)isForInvitation completion:(void (^_Nullable)(BOOL success))completion { - DSCreditFundingDerivationPath *derivationPathRegistrationFunding; - if (isForInvitation) { - derivationPathRegistrationFunding = [[DSDerivationPathFactory sharedInstance] blockchainIdentityInvitationFundingDerivationPathForWallet:self.wallet]; - } else { - derivationPathRegistrationFunding = [[DSDerivationPathFactory sharedInstance] blockchainIdentityRegistrationFundingDerivationPathForWallet:self.wallet]; - } - - self.internalRegistrationFundingPrivateKey = [derivationPathRegistrationFunding privateKeyAtIndexPath:[NSIndexPath indexPathWithIndex:self.index] fromSeed:seed]; - if (self.internalRegistrationFundingPrivateKey) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(YES); - }); - } - } else { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(NO); - }); - } - } -} - -- (BOOL)setExternalFundingPrivateKey:(OpaqueKey *)privateKey { - if (!self.isFromIncomingInvitation) { - return FALSE; - } - self.internalRegistrationFundingPrivateKey = privateKey; - if (self.internalRegistrationFundingPrivateKey) { - return TRUE; - } else { - return FALSE; - } -} - -- (void)createFundingPrivateKeyForInvitationWithPrompt:(NSString *)prompt completion:(void (^_Nullable)(BOOL success, BOOL cancelled))completion { - [self createFundingPrivateKeyWithPrompt:prompt isForInvitation:YES completion:completion]; -} - -- (void)createFundingPrivateKeyWithPrompt:(NSString *)prompt completion:(void (^_Nullable)(BOOL success, BOOL cancelled))completion { - [self createFundingPrivateKeyWithPrompt:prompt isForInvitation:NO completion:completion]; -} - -- (void)createFundingPrivateKeyWithPrompt:(NSString *)prompt isForInvitation:(BOOL)isForInvitation completion:(void (^_Nullable)(BOOL success, BOOL cancelled))completion { - dispatch_async(dispatch_get_main_queue(), ^{ - [[DSAuthenticationManager sharedInstance] seedWithPrompt:prompt - forWallet:self.wallet - forAmount:0 - forceAuthentication:NO - completion:^(NSData *_Nullable seed, BOOL cancelled) { - if (!seed) { - if (completion) { - completion(NO, cancelled); - } - return; - } - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self createFundingPrivateKeyWithSeed:seed - isForInvitation:isForInvitation - completion:^(BOOL success) { - if (completion) { - completion(success, NO); - } - }]; - }); - }]; - }); -} - -- (BOOL)activePrivateKeysAreLoadedWithFetchingError:(NSError **)error { - BOOL loaded = TRUE; - for (NSNumber *index in self.keyInfoDictionaries) { - NSDictionary *keyDictionary = self.keyInfoDictionaries[index]; - DSBlockchainIdentityKeyStatus status = [keyDictionary[@(DSBlockchainIdentityKeyDictionary_KeyStatus)] unsignedIntValue]; - KeyKind keyType = [keyDictionary[@(DSBlockchainIdentityKeyDictionary_KeyType)] unsignedIntValue]; - if (status == DSBlockchainIdentityKeyStatus_Registered) { - loaded &= [self hasPrivateKeyAtIndex:[index unsignedIntValue] ofType:keyType error:error]; - if (*error) return FALSE; - } - } - return loaded; -} - -- (uint32_t)activeKeyCount { - uint32_t rActiveKeys = 0; - for (NSNumber *index in self.keyInfoDictionaries) { - NSDictionary *keyDictionary = self.keyInfoDictionaries[index]; - DSBlockchainIdentityKeyStatus status = [keyDictionary[@(DSBlockchainIdentityKeyDictionary_KeyStatus)] unsignedIntValue]; - if (status == DSBlockchainIdentityKeyStatus_Registered) rActiveKeys++; - } - return rActiveKeys; -} - -- (uint32_t)totalKeyCount { - return (uint32_t)self.keyInfoDictionaries.count; -} - -- (uint32_t)keyCountForKeyType:(KeyKind)keyType { - uint32_t keyCount = 0; - for (NSNumber *index in self.keyInfoDictionaries) { - NSDictionary *keyDictionary = self.keyInfoDictionaries[index]; - KeyKind type = [keyDictionary[@(DSBlockchainIdentityKeyDictionary_KeyType)] unsignedIntValue]; - if (type == keyType) keyCount++; - } - return keyCount; -} - -- (NSArray *)activeKeysForKeyType:(KeyKind)keyType { - NSMutableArray *activeKeys = [NSMutableArray array]; - for (NSNumber *index in self.keyInfoDictionaries) { - NSDictionary *keyDictionary = self.keyInfoDictionaries[index]; - KeyKind type = [keyDictionary[@(DSBlockchainIdentityKeyDictionary_KeyType)] unsignedIntValue]; - if (type == keyType) { - [activeKeys addObject:keyDictionary[@(DSBlockchainIdentityKeyDictionary_Key)]]; - } - } - return [activeKeys copy]; -} - -- (BOOL)verifyKeysForWallet:(DSWallet *)wallet { - DSWallet *originalWallet = self.wallet; - self.wallet = wallet; - for (uint32_t index = 0; index < self.keyInfoDictionaries.count; index++) { - KeyKind keyType = [self typeOfKeyAtIndex:index]; - OpaqueKey *key = [self keyAtIndex:index]; - if (!key) { - self.wallet = originalWallet; - return FALSE; - } - if (keyType != (int16_t) key->tag) { - self.wallet = originalWallet; - return FALSE; - } - OpaqueKey *derivedKey = [self publicKeyAtIndex:index ofType:keyType]; - if (![DSKeyManager keysPublicKeyDataIsEqual:derivedKey key2:key]) { - self.wallet = originalWallet; - return FALSE; - } - } - return TRUE; -} - -- (DSBlockchainIdentityKeyStatus)statusOfKeyAtIndex:(NSUInteger)index { - return [[[self.keyInfoDictionaries objectForKey:@(index)] objectForKey:@(DSBlockchainIdentityKeyDictionary_KeyStatus)] unsignedIntValue]; -} - -- (KeyKind)typeOfKeyAtIndex:(NSUInteger)index { - return [[[self.keyInfoDictionaries objectForKey:@(index)] objectForKey:@(DSBlockchainIdentityKeyDictionary_KeyType)] unsignedIntValue]; -} - -- (OpaqueKey *_Nullable)keyAtIndex:(NSUInteger)index { - NSValue *keyValue = (NSValue *)[[self.keyInfoDictionaries objectForKey:@(index)] objectForKey:@(DSBlockchainIdentityKeyDictionary_Key)]; - return keyValue.pointerValue; -} - -- (NSString *)localizedStatusOfKeyAtIndex:(NSUInteger)index { - DSBlockchainIdentityKeyStatus status = [self statusOfKeyAtIndex:index]; - return [[self class] localizedStatusOfKeyForBlockchainIdentityKeyStatus:status]; -} - -+ (NSString *)localizedStatusOfKeyForBlockchainIdentityKeyStatus:(DSBlockchainIdentityKeyStatus)status { - switch (status) { - case DSBlockchainIdentityKeyStatus_Unknown: - return DSLocalizedString(@"Unknown", @"Status of Key or Username is Unknown"); - case DSBlockchainIdentityKeyStatus_Registered: - return DSLocalizedString(@"Registered", @"Status of Key or Username is Registered"); - case DSBlockchainIdentityKeyStatus_Registering: - return DSLocalizedString(@"Registering", @"Status of Key or Username is Registering"); - case DSBlockchainIdentityKeyStatus_NotRegistered: - return DSLocalizedString(@"Not Registered", @"Status of Key or Username is Not Registered"); - case DSBlockchainIdentityKeyStatus_Revoked: - return DSLocalizedString(@"Revoked", @"Status of Key or Username is Revoked"); - default: - return @""; - } -} - -+ (DSAuthenticationKeysDerivationPath *)derivationPathForType:(KeyKind)type forWallet:(DSWallet *)wallet { - // TODO: ed25519 + bls basic - if (type == KeyKind_ECDSA) { - return [[DSDerivationPathFactory sharedInstance] blockchainIdentityECDSAKeysDerivationPathForWallet:wallet]; - } else if (type == KeyKind_BLS || type == KeyKind_BLSBasic) { - return [[DSDerivationPathFactory sharedInstance] blockchainIdentityBLSKeysDerivationPathForWallet:wallet]; - } - return nil; -} - -- (DSAuthenticationKeysDerivationPath *)derivationPathForType:(KeyKind)type { - if (!_isLocal) return nil; - return [DSBlockchainIdentity derivationPathForType:type forWallet:self.wallet]; -} - -- (BOOL)hasPrivateKeyAtIndex:(uint32_t)index ofType:(KeyKind)type error:(NSError **)error { - if (!_isLocal) return NO; - const NSUInteger indexes[] = {_index | BIP32_HARD, index | BIP32_HARD}; - NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; - - DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; - - return hasKeychainData([self identifierForKeyAtPath:indexPath fromDerivationPath:derivationPath], error); -} - -- (OpaqueKey *)privateKeyAtIndex:(uint32_t)index ofType:(KeyKind)type { - if (!_isLocal) return nil; - const NSUInteger indexes[] = {_index | BIP32_HARD, index | BIP32_HARD}; - NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; - - DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; - - NSError *error = nil; - NSData *keySecret = getKeychainData([self identifierForKeyAtPath:indexPath fromDerivationPath:derivationPath], &error); - - NSAssert(keySecret, @"This should be present"); - - if (!keySecret || error) return nil; - return [DSKeyManager keyWithPrivateKeyData:keySecret ofType:type]; -} - -- (OpaqueKey *)derivePrivateKeyAtIdentityKeyIndex:(uint32_t)index ofType:(KeyKind)type { - if (!_isLocal) return nil; - const NSUInteger indexes[] = {_index, index}; - NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; - - return [self derivePrivateKeyAtIndexPath:indexPath ofType:type]; -} - -- (OpaqueKey *)derivePrivateKeyAtIndexPath:(NSIndexPath *)indexPath ofType:(KeyKind)type { - if (!_isLocal) return nil; - - DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; - - return [derivationPath privateKeyAtIndexPath:[indexPath hardenAllItems]]; -} - -- (OpaqueKey *)privateKeyAtIndex:(uint32_t)index ofType:(KeyKind)type forSeed:(NSData *)seed { - if (!_isLocal) return nil; - const NSUInteger indexes[] = {_index | BIP32_HARD, index | BIP32_HARD}; - NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; - - DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; - - return [derivationPath privateKeyAtIndexPath:indexPath fromSeed:seed]; -} - -- (OpaqueKey *)publicKeyAtIndex:(uint32_t)index ofType:(KeyKind)type { - if (!_isLocal) return nil; - const NSUInteger indexes[] = {_index | BIP32_HARD, index | BIP32_HARD}; - NSIndexPath *hardenedIndexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; - - DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; - - return [derivationPath publicKeyAtIndexPath:hardenedIndexPath]; -} - -- (OpaqueKey *)createNewKeyOfType:(KeyKind)type saveKey:(BOOL)saveKey returnIndex:(uint32_t *)rIndex { - return [self createNewKeyOfType:type saveKey:saveKey returnIndex:rIndex inContext:[NSManagedObjectContext viewContext]]; -} - -- (OpaqueKey *)createNewKeyOfType:(KeyKind)type saveKey:(BOOL)saveKey returnIndex:(uint32_t *)rIndex inContext:(NSManagedObjectContext *)context { - if (!_isLocal) return nil; - uint32_t keyIndex = self.keysCreated; - const NSUInteger indexes[] = {_index | BIP32_HARD, keyIndex | BIP32_HARD}; - NSIndexPath *hardenedIndexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; - - DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; - - OpaqueKey *publicKey = [derivationPath publicKeyAtIndexPath:hardenedIndexPath]; - NSAssert([derivationPath hasExtendedPrivateKey], @"The derivation path should have an extended private key"); - OpaqueKey *privateKey = [derivationPath privateKeyAtIndexPath:hardenedIndexPath]; - NSAssert(privateKey, @"The private key should have been derived"); - NSAssert([DSKeyManager keysPublicKeyDataIsEqual:publicKey key2:privateKey], @"These should be equal"); - self.keysCreated++; - if (rIndex) { - *rIndex = keyIndex; - } - NSValue *publicKeyValue = [NSValue valueWithPointer:publicKey]; - NSDictionary *keyDictionary = @{@(DSBlockchainIdentityKeyDictionary_Key): publicKeyValue, @(DSBlockchainIdentityKeyDictionary_KeyType): @(type), @(DSBlockchainIdentityKeyDictionary_KeyStatus): @(DSBlockchainIdentityKeyStatus_Registering)}; - [self.keyInfoDictionaries setObject:keyDictionary forKey:@(keyIndex)]; - if (saveKey) { - [self saveNewKey:publicKey atPath:hardenedIndexPath withStatus:DSBlockchainIdentityKeyStatus_Registering fromDerivationPath:derivationPath inContext:context]; - } - return publicKey; -} - -- (uint32_t)firstIndexOfKeyOfType:(KeyKind)type createIfNotPresent:(BOOL)createIfNotPresent saveKey:(BOOL)saveKey { - for (NSNumber *indexNumber in self.keyInfoDictionaries) { - NSDictionary *keyDictionary = self.keyInfoDictionaries[indexNumber]; - KeyKind keyTypeAtIndex = [keyDictionary[@(DSBlockchainIdentityKeyDictionary_KeyType)] unsignedIntValue]; - if (keyTypeAtIndex == type) { - return [indexNumber unsignedIntValue]; - } - } - if (_isLocal && createIfNotPresent) { - uint32_t rIndex; - [self createNewKeyOfType:type saveKey:saveKey returnIndex:&rIndex]; - return rIndex; - } else { - return UINT32_MAX; - } -} - -- (OpaqueKey *)keyOfType:(KeyKind)type atIndex:(uint32_t)index { - if (!_isLocal) return nil; - const NSUInteger indexes[] = {_index | BIP32_HARD, index | BIP32_HARD}; - NSIndexPath *hardenedIndexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; - - DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; - - OpaqueKey *key = [derivationPath publicKeyAtIndexPath:hardenedIndexPath]; - return key; -} - -- (void)addKey:(OpaqueKey *)key atIndex:(uint32_t)index ofType:(KeyKind)type withStatus:(DSBlockchainIdentityKeyStatus)status save:(BOOL)save { - [self addKey:key atIndex:index ofType:type withStatus:status save:save inContext:self.platformContext]; -} - -- (void)addKey:(OpaqueKey *)key atIndex:(uint32_t)index ofType:(KeyKind)type withStatus:(DSBlockchainIdentityKeyStatus)status save:(BOOL)save inContext:(NSManagedObjectContext *)context { - if (self.isLocal) { - const NSUInteger indexes[] = {_index, index}; - NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; - [self addKey:key atIndexPath:indexPath ofType:type withStatus:status save:save inContext:context]; - } else { - if (self.keyInfoDictionaries[@(index)]) { - NSDictionary *keyDictionary = self.keyInfoDictionaries[@(index)]; - NSValue *keyToCheckInDictionary = keyDictionary[@(DSBlockchainIdentityKeyDictionary_Key)]; - DSBlockchainIdentityKeyStatus keyToCheckInDictionaryStatus = [keyDictionary[@(DSBlockchainIdentityKeyDictionary_KeyStatus)] unsignedIntegerValue]; - if ([DSKeyManager keysPublicKeyDataIsEqual:keyToCheckInDictionary.pointerValue key2:key]) { - if (save && status != keyToCheckInDictionaryStatus) { - [self updateStatus:status forKeyWithIndexID:index inContext:context]; - } - } else { - NSAssert(FALSE, @"these should really match up"); - DSLog(@"these should really match up"); - return; - } - } else { - self.keysCreated = MAX(self.keysCreated, index + 1); - if (save) { - [self saveNewRemoteIdentityKey:key forKeyWithIndexID:index withStatus:status inContext:context]; - } - } - NSDictionary *keyDictionary = @{@(DSBlockchainIdentityKeyDictionary_Key): [NSValue valueWithPointer:key], @(DSBlockchainIdentityKeyDictionary_KeyType): @(type), @(DSBlockchainIdentityKeyDictionary_KeyStatus): @(status)}; - [self.keyInfoDictionaries setObject:keyDictionary forKey:@(index)]; - } -} - -- (void)addKey:(OpaqueKey *)key atIndexPath:(NSIndexPath *)indexPath ofType:(KeyKind)type withStatus:(DSBlockchainIdentityKeyStatus)status save:(BOOL)save { - [self addKey:key atIndexPath:indexPath ofType:type withStatus:status save:save inContext:self.platformContext]; -} - -- (void)addKey:(OpaqueKey *)key atIndexPath:(NSIndexPath *)indexPath ofType:(KeyKind)type withStatus:(DSBlockchainIdentityKeyStatus)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 - - OpaqueKey *keyToCheck = [derivationPath publicKeyAtIndexPath:[indexPath hardenAllItems]]; - NSAssert(keyToCheck != nil, @"This key should be found"); - if ([DSKeyManager keysPublicKeyDataIsEqual:keyToCheck key2:key]) { //if it isn't local we shouldn't verify - uint32_t index = (uint32_t)[indexPath indexAtPosition:[indexPath length] - 1]; - if (self.keyInfoDictionaries[@(index)]) { - NSDictionary *keyDictionary = self.keyInfoDictionaries[@(index)]; - NSValue *keyToCheckInDictionaryValue = keyDictionary[@(DSBlockchainIdentityKeyDictionary_Key)]; - if ([DSKeyManager keysPublicKeyDataIsEqual:keyToCheckInDictionaryValue.pointerValue 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"); - return; - } - } else { - self.keysCreated = MAX(self.keysCreated, index + 1); - if (save) { - [self saveNewKey:key atPath:indexPath withStatus:status fromDerivationPath:derivationPath inContext:context]; - } - } - NSDictionary *keyDictionary = @{@(DSBlockchainIdentityKeyDictionary_Key): [NSValue valueWithPointer:key], @(DSBlockchainIdentityKeyDictionary_KeyType): @(type), @(DSBlockchainIdentityKeyDictionary_KeyStatus): @(status)}; - [self.keyInfoDictionaries setObject:keyDictionary forKey:@(index)]; - } else { - DSLog(@"these should really match up"); - } -} - -- (BOOL)registerKeyWithStatus:(DSBlockchainIdentityKeyStatus)status atIndexPath:(NSIndexPath *)indexPath ofType:(KeyKind)type { - DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; - OpaqueKey *key = [derivationPath publicKeyAtIndexPath:[indexPath hardenAllItems]]; - if (!key) return FALSE; - uint32_t index = (uint32_t)[indexPath indexAtPosition:[indexPath length] - 1]; - self.keysCreated = MAX(self.keysCreated, index + 1); - NSDictionary *keyDictionary = @{@(DSBlockchainIdentityKeyDictionary_Key): [NSValue valueWithPointer:key], @(DSBlockchainIdentityKeyDictionary_KeyType): @(type), @(DSBlockchainIdentityKeyDictionary_KeyStatus): @(status)}; - [self.keyInfoDictionaries setObject:keyDictionary forKey:@(index)]; - return TRUE; -} - -- (void)registerKey:(OpaqueKey *)key withStatus:(DSBlockchainIdentityKeyStatus)status atIndex:(uint32_t)index ofType:(KeyKind)type { - self.keysCreated = MAX(self.keysCreated, index + 1); - NSDictionary *keyDictionary = @{@(DSBlockchainIdentityKeyDictionary_Key): [NSValue valueWithPointer:key], @(DSBlockchainIdentityKeyDictionary_KeyType): @(type), @(DSBlockchainIdentityKeyDictionary_KeyStatus): @(status)}; - [self.keyInfoDictionaries setObject:keyDictionary forKey:@(index)]; -} - -// MARK: From Remote/Network -// TODO: make sure we determine 'legacy' correctly here -+ (OpaqueKey *)keyFromKeyDictionary:(NSDictionary *)dictionary rType:(uint32_t *)rType rIndex:(uint32_t *)rIndex { - NSData *keyData = dictionary[@"data"]; - NSNumber *keyId = dictionary[@"id"]; - NSNumber *type = dictionary[@"type"]; - if (keyData && keyId && type) { - OpaqueKey *key = [DSKeyManager keyWithPublicKeyData:keyData ofType:type.intValue]; - *rIndex = [keyId unsignedIntValue]; - *rType = [type unsignedIntValue]; - return key; - } - return nil; -} - -- (void)addKeyFromKeyDictionary:(NSDictionary *)dictionary save:(BOOL)save inContext:(NSManagedObjectContext *_Nullable)context { - uint32_t index = 0; - uint32_t type = 0; - OpaqueKey *key = [DSBlockchainIdentity keyFromKeyDictionary:dictionary rType:&type rIndex:&index]; - if (key) { - [self addKey:key atIndex:index ofType:type withStatus:DSBlockchainIdentityKeyStatus_Registered save:save inContext:context]; - } -} - -// MARK: - Funding - -- (NSString *)registrationFundingAddress { - if (self.registrationCreditFundingTransaction) { - return [DSKeyManager addressFromHash160:self.registrationCreditFundingTransaction.creditBurnPublicKeyHash forChain:self.chain]; - } else { - DSCreditFundingDerivationPath *derivationPathRegistrationFunding; - if (self.isOutgoingInvitation) { - derivationPathRegistrationFunding = [[DSDerivationPathFactory sharedInstance] blockchainIdentityInvitationFundingDerivationPathForWallet:self.wallet]; - } else { - derivationPathRegistrationFunding = [[DSDerivationPathFactory sharedInstance] blockchainIdentityRegistrationFundingDerivationPathForWallet:self.wallet]; - } - - return [derivationPathRegistrationFunding addressAtIndex:self.index]; - } -} - -- (void)fundingTransactionForTopupAmount:(uint64_t)topupAmount toAddress:(NSString *)address fundedByAccount:(DSAccount *)fundingAccount completion:(void (^_Nullable)(DSCreditFundingTransaction *fundingTransaction))completion { - DSCreditFundingTransaction *fundingTransaction = [fundingAccount creditFundingTransactionFor:topupAmount to:address withFee:YES]; - completion(fundingTransaction); -} - -// MARK: - Registration - -// MARK: Helpers - -- (BOOL)isRegistered { - return self.registrationStatus == DSBlockchainIdentityRegistrationStatus_Registered; -} - -- (NSString *)localizedRegistrationStatusString { - switch (self.registrationStatus) { - case DSBlockchainIdentityRegistrationStatus_Registered: - return DSLocalizedString(@"Registered", @"The Dash Identity is registered"); - case DSBlockchainIdentityRegistrationStatus_Unknown: - return DSLocalizedString(@"Unknown", @"It is Unknown if the Dash Identity is registered"); - case DSBlockchainIdentityRegistrationStatus_Registering: - return DSLocalizedString(@"Registering", @"The Dash Identity is being registered"); - case DSBlockchainIdentityRegistrationStatus_NotRegistered: - return DSLocalizedString(@"Not Registered", @"The Dash Identity is not registered"); - default: - break; - } - return @""; -} - -- (void)applyIdentityDictionary:(NSDictionary *)identityDictionary version:(uint32_t)version save:(BOOL)save inContext:(NSManagedObjectContext *_Nullable)context { - if (identityDictionary[@"balance"]) { - uint64_t creditBalance = (uint64_t)[identityDictionary[@"balance"] longLongValue]; - _creditBalance = creditBalance; - } - if (identityDictionary[@"publicKeys"]) { - for (NSDictionary *dictionary in identityDictionary[@"publicKeys"]) { - [self addKeyFromKeyDictionary:dictionary save:save inContext:context]; - } - } -} - -+ (OpaqueKey *)firstKeyInIdentityDictionary:(NSDictionary *)identityDictionary { - if (identityDictionary[@"publicKeys"]) { - for (NSDictionary *dictionary in identityDictionary[@"publicKeys"]) { - uint32_t index = 0; - uint32_t type = 0; - OpaqueKey *key = [DSBlockchainIdentity keyFromKeyDictionary:dictionary rType:&type rIndex:&index]; - if (index == 0) { - return key; - } - } - } - return nil; -} - -// MARK: Transition - -- (void)registrationTransitionSignedByPrivateKey:(OpaqueKey *)privateKey registeringPublicKeys:(NSDictionary *)publicKeys usingCreditFundingTransaction:(DSCreditFundingTransaction *)creditFundingTransaction completion:(void (^_Nullable)(DSBlockchainIdentityRegistrationTransition *blockchainIdentityRegistrationTransition))completion { - DSBlockchainIdentityRegistrationTransition *blockchainIdentityRegistrationTransition = [[DSBlockchainIdentityRegistrationTransition alloc] initWithVersion:1 registeringPublicKeys:publicKeys usingCreditFundingTransaction:creditFundingTransaction onChain:self.chain]; - [blockchainIdentityRegistrationTransition signWithKey:privateKey atIndex:UINT32_MAX fromIdentity:self]; - if (completion) { - completion(blockchainIdentityRegistrationTransition); - } -} - -- (void)registrationTransitionWithCompletion:(void (^_Nullable)(DSBlockchainIdentityRegistrationTransition *_Nullable blockchainIdentityRegistrationTransaction, NSError *_Nullable error))completion { - if (!self.internalRegistrationFundingPrivateKey) { - if (completion) { - completion(nil, [NSError errorWithCode:500 localizedDescriptionKey:@"The blockchain identity funding private key should be first created with createFundingPrivateKeyWithCompletion"]); - } - return; - } - - uint32_t index = [self firstIndexOfKeyOfType:KeyKind_ECDSA createIfNotPresent:YES saveKey:!self.wallet.isTransient]; - - OpaqueKey *publicKey = [self keyAtIndex:index]; - - NSAssert((index & ~(BIP32_HARD)) == 0, @"The index should be 0 here"); - - NSAssert(self.registrationCreditFundingTransaction, @"The registration credit funding transaction must be known"); - - if (!self.registrationCreditFundingTransaction.instantSendLockAwaitingProcessing && self.registrationCreditFundingTransaction.blockHeight == BLOCK_UNKNOWN_HEIGHT) { - if (completion) { - completion(nil, [NSError errorWithCode:500 localizedDescriptionKey:@"The registration credit funding transaction has not been mined yet and has no instant send lock"]); - } - return; - } - - [self registrationTransitionSignedByPrivateKey:self.internalRegistrationFundingPrivateKey - registeringPublicKeys:@{@(index): [NSValue valueWithPointer:publicKey]} - usingCreditFundingTransaction:self.registrationCreditFundingTransaction - completion:^(DSBlockchainIdentityRegistrationTransition *blockchainIdentityRegistrationTransaction) { - if (completion) { - completion(blockchainIdentityRegistrationTransaction, nil); - } - }]; -} - -// MARK: Registering - -- (void)createAndPublishRegistrationTransitionWithCompletion:(void (^)(NSDictionary *, NSError *))completion { - [self registrationTransitionWithCompletion:^(DSBlockchainIdentityRegistrationTransition *blockchainIdentityRegistrationTransition, NSError *registrationTransitionError) { - if (blockchainIdentityRegistrationTransition) { - [self.DAPIClient publishTransition:blockchainIdentityRegistrationTransition - completionQueue:self.identityQueue - success:^(NSDictionary *_Nonnull successDictionary, BOOL added) { - [self monitorForBlockchainIdentityWithRetryCount:5 - retryAbsentCount:5 - delay:4 - retryDelayType:DSBlockchainIdentityRetryDelayType_Linear - options:DSBlockchainIdentityMonitorOptions_None - inContext:self.platformContext - completion:^(BOOL success, BOOL found, NSError *error) { - if (completion) { - completion(successDictionary, error); - } - }]; - } - failure:^(NSError *_Nonnull error) { - if (error) { - [self monitorForBlockchainIdentityWithRetryCount:1 - retryAbsentCount:1 - delay:4 - retryDelayType:DSBlockchainIdentityRetryDelayType_Linear - options:DSBlockchainIdentityMonitorOptions_None - inContext:self.platformContext - completion:^(BOOL success, BOOL found, NSError *error) { - if (completion) { - completion(nil, found ? nil : error); - } - }]; - } else { - if (completion) { - completion(nil, [NSError errorWithCode:501 localizedDescriptionKey:@"Unable to register registration transition"]); - } - } - }]; - } else { - if (completion) { - NSError *error = [NSError errorWithCode:501 localizedDescriptionKey:@"Unable to create registration transition"]; - completion(nil, registrationTransitionError ? registrationTransitionError : error); - } - } - }]; -} - -// MARK: Retrieval - -- (void)fetchIdentityNetworkStateInformationWithCompletion:(void (^)(BOOL success, BOOL found, NSError *error))completion { - dispatch_async(self.identityQueue, ^{ - [self fetchIdentityNetworkStateInformationInContext:self.platformContext withCompletion:completion]; - }); -} - -- (void)fetchIdentityNetworkStateInformationInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(BOOL success, BOOL found, NSError *error))completion { - //a local identity might not have been published yet - //todo retryabsentcount should be 0 if it can be proved to be absent - [self monitorForBlockchainIdentityWithRetryCount:DEFAULT_FETCH_IDENTITY_RETRY_COUNT retryAbsentCount:DEFAULT_FETCH_IDENTITY_RETRY_COUNT delay:3 retryDelayType:DSBlockchainIdentityRetryDelayType_SlowingDown50Percent options:self.isLocal ? DSBlockchainIdentityMonitorOptions_AcceptNotFoundAsNotAnError : DSBlockchainIdentityMonitorOptions_None inContext:context completion:completion]; -} - -- (void)fetchAllNetworkStateInformationWithCompletion:(void (^)(DSBlockchainIdentityQueryStep failureStep, NSArray *errors))completion { - dispatch_async(self.identityQueue, ^{ - [self fetchAllNetworkStateInformationInContext:self.platformContext withCompletion:completion onCompletionQueue:dispatch_get_main_queue()]; - }); -} - -- (void)fetchAllNetworkStateInformationInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(DSBlockchainIdentityQueryStep failureStep, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - dispatch_async(self.identityQueue, ^{ - DSBlockchainIdentityQueryStep query = DSBlockchainIdentityQueryStep_None; - if ([DSOptionsManager sharedInstance].syncType & DSSyncType_BlockchainIdentities) { - query |= DSBlockchainIdentityQueryStep_Identity; - } - if ([DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) { - query |= DSBlockchainIdentityQueryStep_Username; - } - if ([DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) { - query |= DSBlockchainIdentityQueryStep_Profile; - if (self.isLocal) { - query |= DSBlockchainIdentityQueryStep_ContactRequests; - } - } - [self fetchNetworkStateInformation:query - inContext:context - withCompletion:completion - onCompletionQueue:completionQueue]; - }); -} - -- (void)fetchL3NetworkStateInformation:(DSBlockchainIdentityQueryStep)queryStep withCompletion:(void (^)(DSBlockchainIdentityQueryStep failureStep, NSArray *errors))completion { - [self fetchL3NetworkStateInformation:queryStep inContext:self.platformContext withCompletion:completion onCompletionQueue:dispatch_get_main_queue()]; -} - -- (void)fetchL3NetworkStateInformation:(DSBlockchainIdentityQueryStep)queryStep inContext:(NSManagedObjectContext *)context withCompletion:(void (^)(DSBlockchainIdentityQueryStep failureStep, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - if (!(queryStep & DSBlockchainIdentityQueryStep_Identity) && (!self.activeKeyCount)) { - //We need to fetch keys if we want to query other information - if (completion) { - completion(DSBlockchainIdentityQueryStep_BadQuery, @[[NSError errorWithCode:501 localizedDescriptionKey:@"Attempt to query DAPs for blockchain identity with no active keys"]]); - } - return; - } - - __block DSBlockchainIdentityQueryStep failureStep = DSBlockchainIdentityQueryStep_None; - __block NSMutableArray *groupedErrors = [NSMutableArray array]; - dispatch_group_t dispatchGroup = dispatch_group_create(); - if (queryStep & DSBlockchainIdentityQueryStep_Username) { - dispatch_group_enter(dispatchGroup); - [self fetchUsernamesInContext:context - withCompletion:^(BOOL success, NSError *error) { - failureStep |= success & DSBlockchainIdentityQueryStep_Username; - if (error) { - [groupedErrors addObject:error]; - } - dispatch_group_leave(dispatchGroup); - } - onCompletionQueue:self.identityQueue]; - } - - if (queryStep & DSBlockchainIdentityQueryStep_Profile) { - dispatch_group_enter(dispatchGroup); - [self fetchProfileInContext:context - withCompletion:^(BOOL success, NSError *error) { - failureStep |= success & DSBlockchainIdentityQueryStep_Profile; - if (error) { - [groupedErrors addObject:error]; - } - dispatch_group_leave(dispatchGroup); - } - onCompletionQueue:self.identityQueue]; - } - - if (queryStep & DSBlockchainIdentityQueryStep_OutgoingContactRequests) { - dispatch_group_enter(dispatchGroup); - [self fetchOutgoingContactRequestsInContext:context - withCompletion:^(BOOL success, NSArray *errors) { - failureStep |= success & DSBlockchainIdentityQueryStep_OutgoingContactRequests; - if ([errors count]) { - [groupedErrors addObjectsFromArray:errors]; - dispatch_group_leave(dispatchGroup); - } else { - if (queryStep & DSBlockchainIdentityQueryStep_IncomingContactRequests) { - [self fetchIncomingContactRequestsInContext:context - withCompletion:^(BOOL success, NSArray *errors) { - failureStep |= success & DSBlockchainIdentityQueryStep_IncomingContactRequests; - if ([errors count]) { - [groupedErrors addObjectsFromArray:errors]; - } - dispatch_group_leave(dispatchGroup); - } - onCompletionQueue:self.identityQueue]; - } else { - dispatch_group_leave(dispatchGroup); - } - } - } - onCompletionQueue:self.identityQueue]; - } else if (queryStep & DSBlockchainIdentityQueryStep_IncomingContactRequests) { - dispatch_group_enter(dispatchGroup); - [self fetchIncomingContactRequestsInContext:context - withCompletion:^(BOOL success, NSArray *errors) { - failureStep |= success & DSBlockchainIdentityQueryStep_IncomingContactRequests; - if ([errors count]) { - [groupedErrors addObjectsFromArray:errors]; - } - dispatch_group_leave(dispatchGroup); - } - onCompletionQueue:self.identityQueue]; - } - - __weak typeof(self) weakSelf = self; - if (completion) { - dispatch_group_notify(dispatchGroup, self.identityQueue, ^{ -#if DEBUG - DSLogPrivate(@"Completed fetching of blockchain identity information for user %@ (query %lu - failures %lu)", - self.currentDashpayUsername ? self.currentDashpayUsername : self.uniqueIdString, - (unsigned long)queryStep, - failureStep); -#else - DSLog(@"Completed fetching of blockchain identity information for user %@ (query %lu - failures %lu)", - @"", - (unsigned long)queryStep, - failureStep); -#endif /* DEBUG */ - if (!(failureStep & DSBlockchainIdentityQueryStep_ContactRequests)) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - return; - } - //todo This needs to be eventually set with the blockchain returned by platform. - strongSelf.dashpaySyncronizationBlockHash = strongSelf.chain.lastTerminalBlock.blockHash; - } - dispatch_async(completionQueue, ^{ - completion(failureStep, [groupedErrors copy]); - }); - }); - } -} - -- (void)fetchNetworkStateInformation:(DSBlockchainIdentityQueryStep)querySteps withCompletion:(void (^)(DSBlockchainIdentityQueryStep failureStep, NSArray *errors))completion { - [self fetchNetworkStateInformation:querySteps inContext:self.platformContext withCompletion:completion onCompletionQueue:dispatch_get_main_queue()]; -} - -- (void)fetchNetworkStateInformation:(DSBlockchainIdentityQueryStep)querySteps inContext:(NSManagedObjectContext *)context withCompletion:(void (^)(DSBlockchainIdentityQueryStep failureStep, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - if (querySteps & DSBlockchainIdentityQueryStep_Identity) { - [self fetchIdentityNetworkStateInformationWithCompletion:^(BOOL success, BOOL found, NSError *error) { - if (!success) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(DSBlockchainIdentityQueryStep_Identity, error ? @[error] : @[]); - }); - } - return; - } - if (!found) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(DSBlockchainIdentityQueryStep_NoIdentity, @[]); - }); - } - return; - } - [self fetchL3NetworkStateInformation:querySteps - inContext:context - withCompletion:completion - onCompletionQueue:completionQueue]; - }]; - } else { - NSAssert([self blockchainIdentityEntityInContext:context], @"Blockchain identity entity should be known"); - [self fetchL3NetworkStateInformation:querySteps inContext:context withCompletion:completion onCompletionQueue:completionQueue]; - } -} - -- (void)fetchIfNeededNetworkStateInformation:(DSBlockchainIdentityQueryStep)querySteps inContext:(NSManagedObjectContext *)context withCompletion:(void (^)(DSBlockchainIdentityQueryStep failureStep, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - dispatch_async(self.identityQueue, ^{ - if (!self.activeKeyCount) { - if (self.isLocal) { - [self fetchNetworkStateInformation:querySteps inContext:context withCompletion:completion onCompletionQueue:completionQueue]; - } else { - DSBlockchainIdentityQueryStep stepsNeeded = DSBlockchainIdentityQueryStep_None; - if ([DSOptionsManager sharedInstance].syncType & DSSyncType_BlockchainIdentities) { - stepsNeeded |= DSBlockchainIdentityQueryStep_Identity; - } - if (![self.dashpayUsernameFullPaths count] && self.lastCheckedUsernamesTimestamp == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) { - stepsNeeded |= DSBlockchainIdentityQueryStep_Username; - } - if ((self.lastCheckedProfileTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) { - stepsNeeded |= DSBlockchainIdentityQueryStep_Profile; - } - if (stepsNeeded == DSBlockchainIdentityQueryStep_None) { - if (completion) { - completion(DSBlockchainIdentityQueryStep_None, @[]); - } - } else { - [self fetchNetworkStateInformation:stepsNeeded & querySteps inContext:context withCompletion:completion onCompletionQueue:completionQueue]; - } - } - } else { - DSBlockchainIdentityQueryStep stepsNeeded = DSBlockchainIdentityQueryStep_None; - if (![self.dashpayUsernameFullPaths count] && self.lastCheckedUsernamesTimestamp == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) { - stepsNeeded |= DSBlockchainIdentityQueryStep_Username; - } - __block uint64_t createdAt; - [context performBlockAndWait:^{ - createdAt = [[self matchingDashpayUserInContext:context] createdAt]; - }]; - if (!createdAt && (self.lastCheckedProfileTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) { - stepsNeeded |= DSBlockchainIdentityQueryStep_Profile; - } - if (self.isLocal && (self.lastCheckedIncomingContactsTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) { - stepsNeeded |= DSBlockchainIdentityQueryStep_IncomingContactRequests; - } - if (self.isLocal && (self.lastCheckedOutgoingContactsTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) { - stepsNeeded |= DSBlockchainIdentityQueryStep_OutgoingContactRequests; - } - if (stepsNeeded == DSBlockchainIdentityQueryStep_None) { - if (completion) { - completion(DSBlockchainIdentityQueryStep_None, @[]); - } - } else { - [self fetchNetworkStateInformation:stepsNeeded & querySteps inContext:context withCompletion:completion onCompletionQueue:completionQueue]; - } - } - }); -} - -- (void)fetchNeededNetworkStateInformationWithCompletion:(void (^)(DSBlockchainIdentityQueryStep failureStep, NSArray *errors))completion { - [self fetchNeededNetworkStateInformationInContext:self.platformContext withCompletion:completion onCompletionQueue:dispatch_get_main_queue()]; -} - -- (void)fetchNeededNetworkStateInformationInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(DSBlockchainIdentityQueryStep failureStep, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - dispatch_async(self.identityQueue, ^{ - if (!self.activeKeyCount) { - if (self.isLocal) { - [self fetchAllNetworkStateInformationWithCompletion:completion]; - } else { - DSBlockchainIdentityQueryStep stepsNeeded = DSBlockchainIdentityQueryStep_None; - if ([DSOptionsManager sharedInstance].syncType & DSSyncType_BlockchainIdentities) { - stepsNeeded |= DSBlockchainIdentityQueryStep_Identity; - } - if (![self.dashpayUsernameFullPaths count] && self.lastCheckedUsernamesTimestamp == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) { - stepsNeeded |= DSBlockchainIdentityQueryStep_Username; - } - if ((self.lastCheckedProfileTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) { - stepsNeeded |= DSBlockchainIdentityQueryStep_Profile; - } - if (stepsNeeded == DSBlockchainIdentityQueryStep_None) { - if (completion) { - completion(DSBlockchainIdentityQueryStep_None, @[]); - } - } else { - [self fetchNetworkStateInformation:stepsNeeded inContext:context withCompletion:completion onCompletionQueue:completionQueue]; - } - } - } else { - DSBlockchainIdentityQueryStep stepsNeeded = DSBlockchainIdentityQueryStep_None; - if (![self.dashpayUsernameFullPaths count] && self.lastCheckedUsernamesTimestamp == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) { - stepsNeeded |= DSBlockchainIdentityQueryStep_Username; - } - if (![[self matchingDashpayUserInContext:context] createdAt] && (self.lastCheckedProfileTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) { - stepsNeeded |= DSBlockchainIdentityQueryStep_Profile; - } - if (self.isLocal && (self.lastCheckedIncomingContactsTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) { - stepsNeeded |= DSBlockchainIdentityQueryStep_IncomingContactRequests; - } - if (self.isLocal && (self.lastCheckedOutgoingContactsTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) { - stepsNeeded |= DSBlockchainIdentityQueryStep_OutgoingContactRequests; - } - if (stepsNeeded == DSBlockchainIdentityQueryStep_None) { - if (completion) { - completion(DSBlockchainIdentityQueryStep_None, @[]); - } - } else { - [self fetchNetworkStateInformation:stepsNeeded inContext:context withCompletion:completion onCompletionQueue:completionQueue]; - } - } - }); -} - -// MARK: - Platform Helpers - -- (DPDocumentFactory *)dashpayDocumentFactory { - if (!_dashpayDocumentFactory) { - DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; - NSAssert(contract, @"Contract must be defined"); - self.dashpayDocumentFactory = [[DPDocumentFactory alloc] initWithBlockchainIdentity:self contract:contract onChain:self.chain]; - } - return _dashpayDocumentFactory; -} - -- (DPDocumentFactory *)dpnsDocumentFactory { - if (!_dpnsDocumentFactory) { - DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dpnsContract; - NSAssert(contract, @"Contract must be defined"); - self.dpnsDocumentFactory = [[DPDocumentFactory alloc] initWithBlockchainIdentity:self contract:contract onChain:self.chain]; - } - return _dpnsDocumentFactory; -} - -- (DSDAPIClient *)DAPIClient { - return self.chain.chainManager.DAPIClient; -} - -- (DSDAPIPlatformNetworkService *)DAPINetworkService { - return self.DAPIClient.DAPIPlatformNetworkService; -} - -// MARK: - Signing and Encryption - -- (void)signStateTransition:(DSTransition *)transition forKeyIndex:(uint32_t)keyIndex ofType:(KeyKind)signingAlgorithm completion:(void (^_Nullable)(BOOL success))completion { - NSParameterAssert(transition); - - OpaqueKey *privateKey = [self privateKeyAtIndex:keyIndex ofType:signingAlgorithm]; - NSAssert(privateKey, @"The private key should exist"); - NSAssert([DSKeyManager keysPublicKeyDataIsEqual:privateKey key2:[self publicKeyAtIndex:keyIndex ofType:signingAlgorithm]], @"These should be equal"); - // NSLog(@"%@",uint160_hex(self.blockchainIdentityRegistrationTransition.pubkeyHash)); - // NSAssert(uint160_eq(privateKey.publicKeyData.hash160,self.blockchainIdentityRegistrationTransition.pubkeyHash),@"Keys aren't ok"); - [transition signWithKey:privateKey atIndex:keyIndex fromIdentity:self]; - if (completion) { - completion(YES); - } -} - -- (void)signStateTransition:(DSTransition *)transition completion:(void (^_Nullable)(BOOL success))completion { - if (!self.keysCreated) { - uint32_t index; - [self createNewKeyOfType:DEFAULT_SIGNING_ALGORITHM saveKey:!self.wallet.isTransient returnIndex:&index]; - } - return [self signStateTransition:transition forKeyIndex:self.currentMainKeyIndex ofType:self.currentMainKeyType completion:completion]; -} - -- (void)signMessageDigest:(UInt256)digest forKeyIndex:(uint32_t)keyIndex ofType:(KeyKind)signingAlgorithm completion:(void (^_Nullable)(BOOL success, NSData *signature))completion { - NSParameterAssert(completion); - OpaqueKey *privateKey = [self privateKeyAtIndex:keyIndex ofType:signingAlgorithm]; - NSAssert(privateKey, @"The private key should exist"); - NSAssert([DSKeyManager keysPublicKeyDataIsEqual:privateKey key2:[self publicKeyAtIndex:keyIndex ofType:signingAlgorithm]], @"These should be equal"); - NSParameterAssert(privateKey); - DSLogPrivate(@"Signing %@ with key %@", uint256_hex(digest), [DSKeyManager publicKeyData:privateKey].hexString); - NSData *signature = [DSKeyManager signMesasageDigest:privateKey digest:digest]; - completion(!signature.isZeroBytes, signature); -} - -- (BOOL)verifySignature:(NSData *)signature ofType:(KeyKind)signingAlgorithm forMessageDigest:(UInt256)messageDigest { - for (NSValue *publicKey in [self activeKeysForKeyType:signingAlgorithm]) { - BOOL verified = key_verify_message_digest(publicKey.pointerValue, messageDigest.u8, signature.bytes, signature.length); - if (verified) { - return TRUE; - } - } - return FALSE; -} - -- (BOOL)verifySignature:(NSData *)signature forKeyIndex:(uint32_t)keyIndex ofType:(KeyKind)signingAlgorithm forMessageDigest:(UInt256)messageDigest { - OpaqueKey *publicKey = [self publicKeyAtIndex:keyIndex ofType:signingAlgorithm]; - BOOL verified = [DSKeyManager verifyMessageDigest:publicKey digest:messageDigest signature:signature]; - // TODO: check we need to destroy here - processor_destroy_opaque_key(publicKey); - return verified; -} - -- (void)encryptData:(NSData *)data withKeyAtIndex:(uint32_t)index forRecipientKey:(OpaqueKey *)recipientPublicKey completion:(void (^_Nullable)(NSData *encryptedData))completion { - NSParameterAssert(data); - NSParameterAssert(recipientPublicKey); - OpaqueKey *privateKey = [self privateKeyAtIndex:index ofType:(int16_t) recipientPublicKey->tag]; - NSData *encryptedData = [data encryptWithSecretKey:privateKey forPublicKey:recipientPublicKey]; - // TODO: destroy opqaque pointer here? - processor_destroy_opaque_key(privateKey); - if (completion) { - completion(encryptedData); - } -} - -- (void)decryptData:(NSData *)encryptedData withKeyAtIndex:(uint32_t)index fromSenderKey:(OpaqueKey *)senderPublicKey completion:(void (^_Nullable)(NSData *decryptedData))completion { - OpaqueKey *privateKey = [self privateKeyAtIndex:index ofType:(KeyKind)senderPublicKey->tag]; - // TODO: destroy pointers here? - NSData *data = [encryptedData decryptWithSecretKey:privateKey fromPublicKey:senderPublicKey]; - if (completion) { - completion(data); - } -} - -// MARK: - Contracts - -- (void)fetchAndUpdateContract:(DPContract *)contract { - return [self fetchAndUpdateContract:contract inContext:self.platformContext]; -} - -- (void)fetchAndUpdateContract:(DPContract *)contract inContext:(NSManagedObjectContext *)context { - __weak typeof(contract) weakContract = contract; - __weak typeof(self) weakSelf = self; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //this is so we don't get DAPINetworkService immediately - BOOL isDPNSEmpty = [contract.name isEqual:@"DPNS"] && uint256_is_zero(self.chain.dpnsContractID); - BOOL isDashpayEmpty = [contract.name isEqual:@"DashPay"] && uint256_is_zero(self.chain.dashpayContractID); - BOOL isOtherContract = !([contract.name isEqual:@"DashPay"] || [contract.name isEqual:@"DPNS"]); - if (((isDPNSEmpty || isDashpayEmpty || isOtherContract) && uint256_is_zero(contract.registeredBlockchainIdentityUniqueID)) || contract.contractState == DPContractState_NotRegistered) { - [contract registerCreator:self inContext:context]; - __block DSContractTransition *transition = [contract contractRegistrationTransitionForIdentity:self]; - [self signStateTransition:transition - completion:^(BOOL success) { - if (success) { - [self.DAPIClient publishTransition:transition - completionQueue:self.identityQueue - success:^(NSDictionary *_Nonnull successDictionary, BOOL added) { - __strong typeof(weakContract) strongContract = weakContract; - if (!strongContract) { - return; - } - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - return; - } - [strongContract setContractState:DPContractState_Registering - inContext:context]; - [strongSelf monitorForContract:strongContract - withRetryCount:2 - inContext:context - completion:^(BOOL success, NSError *error){ - - }]; - } - failure:^(NSError *_Nonnull error) { - //maybe it was already registered - __strong typeof(weakContract) strongContract = weakContract; - if (!strongContract) { - return; - } - [strongContract setContractState:DPContractState_Unknown - inContext:context]; - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - return; - } - [strongSelf monitorForContract:strongContract - withRetryCount:2 - inContext:context - completion:^(BOOL success, NSError *error){ - - }]; - }]; - } - }]; - - } else if (contract.contractState == DPContractState_Registered || contract.contractState == DPContractState_Registering) { - DSLog(@"Fetching contract for verification %@", contract.base58ContractId); - [self.DAPINetworkService fetchContractForId:uint256_data(contract.contractId) - completionQueue:self.identityQueue - success:^(NSDictionary *_Nonnull contractDictionary) { - __strong typeof(weakContract) strongContract = weakContract; - if (!weakContract) { - return; - } - if (!contractDictionary[@"documents"]) { - [strongContract setContractState:DPContractState_NotRegistered inContext:context]; - return; - } - if (strongContract.contractState == DPContractState_Registered) { - NSSet *set1 = [NSSet setWithArray:[contractDictionary[@"documents"] allKeys]]; - NSSet *set2 = [NSSet setWithArray:[strongContract.documents allKeys]]; - - if (![set1 isEqualToSet:set2]) { - [strongContract setContractState:DPContractState_NotRegistered inContext:context]; - } - DSLog(@"Contract dictionary is %@", contractDictionary); - } - } - failure:^(NSError *_Nonnull error) { - NSString *debugDescription1 = [error.userInfo objectForKey:@"NSDebugDescription"]; - NSError *jsonError; - NSData *objectData = [debugDescription1 dataUsingEncoding:NSUTF8StringEncoding]; - NSDictionary *debugDescription = [NSJSONSerialization JSONObjectWithData:objectData options:0 error:&jsonError]; - //NSDictionary * debugDescription = - __unused NSString *errorMessage = debugDescription[@"grpc_message"]; //!OCLINT - if (TRUE) { //[errorMessage isEqualToString:@"Invalid argument: Contract not found"]) { - __strong typeof(weakContract) strongContract = weakContract; - if (!strongContract) { - return; - } - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - return; - } - [strongContract setContractState:DPContractState_NotRegistered - inContext:context]; - } - }]; - } - }); -} - -- (void)fetchAndUpdateContractWithBase58Identifier:(NSString *)base58Identifier { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //this is so we don't get DAPINetworkService immediately - [self.DAPINetworkService fetchContractForId:base58Identifier.base58ToData - completionQueue:self.identityQueue - success:^(NSDictionary *_Nonnull contract) { - //[DPContract contr] - } - failure:^(NSError *_Nonnull error){ - - }]; - }); -} - -// MARK: - DPNS - -// MARK: Usernames - -- (void)addDashpayUsername:(NSString *)username save:(BOOL)save { - [self addUsername:username inDomain:[self dashpayDomainName] status:DSBlockchainIdentityUsernameStatus_Initial save:save registerOnNetwork:YES]; -} - -- (void)addUsername:(NSString *)username inDomain:(NSString *)domain save:(BOOL)save { - [self addUsername:username inDomain:domain status:DSBlockchainIdentityUsernameStatus_Initial save:save registerOnNetwork:YES]; -} - -- (void)addUsername:(NSString *)username inDomain:(NSString *)domain status:(DSBlockchainIdentityUsernameStatus)status save:(BOOL)save registerOnNetwork:(BOOL)registerOnNetwork { - [self.usernameStatuses setObject:@{BLOCKCHAIN_USERNAME_STATUS: @(DSBlockchainIdentityUsernameStatus_Initial), BLOCKCHAIN_USERNAME_PROPER: username, BLOCKCHAIN_USERNAME_DOMAIN: domain} forKey:[self fullPathForUsername:username inDomain:domain]]; - if (save) { - dispatch_async(self.identityQueue, ^{ - [self saveNewUsername:username inDomain:domain status:DSBlockchainIdentityUsernameStatus_Initial inContext:self.platformContext]; - if (registerOnNetwork && self.registered && status != DSBlockchainIdentityUsernameStatus_Confirmed) { - [self registerUsernamesWithCompletion:^(BOOL success, NSError *_Nonnull error){ - - }]; - } - }); - } -} - -- (DSBlockchainIdentityUsernameStatus)statusOfUsername:(NSString *)username inDomain:(NSString *)domain { - return [self statusOfUsernameFullPath:[self fullPathForUsername:username inDomain:domain]]; -} - -- (DSBlockchainIdentityUsernameStatus)statusOfDashpayUsername:(NSString *)username { - return [self statusOfUsernameFullPath:[self fullPathForUsername:username inDomain:[self dashpayDomainName]]]; -} - -- (DSBlockchainIdentityUsernameStatus)statusOfUsernameFullPath:(NSString *)usernameFullPath { - return [[[self.usernameStatuses objectForKey:usernameFullPath] objectForKey:BLOCKCHAIN_USERNAME_STATUS] unsignedIntegerValue]; -} - -- (NSString *)usernameOfUsernameFullPath:(NSString *)usernameFullPath { - return [[self.usernameStatuses objectForKey:usernameFullPath] objectForKey:BLOCKCHAIN_USERNAME_PROPER]; -} - -- (NSString *)domainOfUsernameFullPath:(NSString *)usernameFullPath { - return [[self.usernameStatuses objectForKey:usernameFullPath] objectForKey:BLOCKCHAIN_USERNAME_DOMAIN]; -} - -- (NSString *)fullPathForUsername:(NSString *)username inDomain:(NSString *)domain { - NSString *fullPath = [[username lowercaseString] stringByAppendingFormat:@".%@", [domain lowercaseString]]; - return fullPath; -} - -- (NSArray *)dashpayUsernameFullPaths { - return [self.usernameStatuses allKeys]; -} - -- (NSArray *)dashpayUsernames { - NSMutableArray *usernameArray = [NSMutableArray array]; - for (NSString *usernameFullPath in self.usernameStatuses) { - [usernameArray addObject:[self usernameOfUsernameFullPath:usernameFullPath]]; - } - return [usernameArray copy]; -} - -- (NSArray *)unregisteredUsernameFullPaths { - return [self usernameFullPathsWithStatus:DSBlockchainIdentityUsernameStatus_Initial]; -} - -- (NSArray *)usernameFullPathsWithStatus:(DSBlockchainIdentityUsernameStatus)usernameStatus { - NSMutableArray *unregisteredUsernames = [NSMutableArray array]; - for (NSString *username in self.usernameStatuses) { - NSDictionary *usernameInfo = self.usernameStatuses[username]; - DSBlockchainIdentityUsernameStatus status = [usernameInfo[BLOCKCHAIN_USERNAME_STATUS] unsignedIntegerValue]; - if (status == usernameStatus) { - [unregisteredUsernames addObject:username]; - } - } - return [unregisteredUsernames copy]; -} - -- (NSArray *)preorderedUsernameFullPaths { - NSMutableArray *unregisteredUsernames = [NSMutableArray array]; - for (NSString *username in self.usernameStatuses) { - NSDictionary *usernameInfo = self.usernameStatuses[username]; - DSBlockchainIdentityUsernameStatus status = [usernameInfo[BLOCKCHAIN_USERNAME_STATUS] unsignedIntegerValue]; - if (status == DSBlockchainIdentityUsernameStatus_Preordered) { - [unregisteredUsernames addObject:username]; - } - } - return [unregisteredUsernames copy]; -} - -// MARK: Username Helpers - -- (NSData *)saltForUsernameFullPath:(NSString *)usernameFullPath saveSalt:(BOOL)saveSalt inContext:(NSManagedObjectContext *)context { - NSData *salt; - if ([self statusOfUsernameFullPath:usernameFullPath] == DSBlockchainIdentityUsernameStatus_Initial || !(salt = [self.usernameSalts objectForKey:usernameFullPath])) { - UInt256 random256 = uint256_random; - salt = uint256_data(random256); - [self.usernameSalts setObject:salt forKey:usernameFullPath]; - if (saveSalt) { - [self saveUsername:[self usernameOfUsernameFullPath:usernameFullPath] inDomain:[self domainOfUsernameFullPath:usernameFullPath] status:[self statusOfUsernameFullPath:usernameFullPath] salt:salt commitSave:YES inContext:context]; - } - } else { - salt = [self.usernameSalts objectForKey:usernameFullPath]; - } - return salt; -} - -- (NSMutableDictionary *)saltedDomainHashesForUsernameFullPaths:(NSArray *)usernameFullPaths inContext:(NSManagedObjectContext *)context { - NSMutableDictionary *mSaltedDomainHashes = [NSMutableDictionary dictionary]; - for (NSString *unregisteredUsernameFullPath in usernameFullPaths) { - NSMutableData *saltedDomain = [NSMutableData data]; - NSData *salt = [self saltForUsernameFullPath:unregisteredUsernameFullPath saveSalt:YES inContext:context]; - NSData *usernameDomainData = [unregisteredUsernameFullPath dataUsingEncoding:NSUTF8StringEncoding]; - [saltedDomain appendData:salt]; - [saltedDomain appendData:usernameDomainData]; - mSaltedDomainHashes[unregisteredUsernameFullPath] = uint256_data([saltedDomain SHA256_2]); - [self.usernameSalts setObject:salt forKey:unregisteredUsernameFullPath]; - } - return [mSaltedDomainHashes copy]; -} - -- (NSString *)dashpayDomainName { - return @"dash"; -} - -// MARK: Documents - -- (NSArray *)preorderDocumentsForUnregisteredUsernameFullPaths:(NSArray *)unregisteredUsernameFullPaths usingEntropyData:(NSData *)entropyData inContext:(NSManagedObjectContext *)context error:(NSError **)error { - NSMutableArray *usernamePreorderDocuments = [NSMutableArray array]; - for (NSData *saltedDomainHashData in [[self saltedDomainHashesForUsernameFullPaths:unregisteredUsernameFullPaths inContext:context] allValues]) { - DSStringValueDictionary *dataDictionary = @{ - @"saltedDomainHash": saltedDomainHashData - }; - DPDocument *document = [self.dpnsDocumentFactory documentOnTable:@"preorder" withDataDictionary:dataDictionary usingEntropy:entropyData error:error]; - if (*error) { - return nil; - } - [usernamePreorderDocuments addObject:document]; - } - return usernamePreorderDocuments; -} - -- (NSArray *)domainDocumentsForUnregisteredUsernameFullPaths:(NSArray *)unregisteredUsernameFullPaths usingEntropyData:(NSData *)entropyData inContext:(NSManagedObjectContext *)context error:(NSError **)error { - NSMutableArray *usernameDomainDocuments = [NSMutableArray array]; - for (NSString *usernameFullPath in [self saltedDomainHashesForUsernameFullPaths:unregisteredUsernameFullPaths inContext:context]) { - NSString *username = [self usernameOfUsernameFullPath:usernameFullPath]; - NSString *domain = [self domainOfUsernameFullPath:usernameFullPath]; - DSStringValueDictionary *dataDictionary = @{ - @"label": username, - @"normalizedLabel": [username lowercaseString], - @"normalizedParentDomainName": domain, - @"preorderSalt": [self.usernameSalts objectForKey:usernameFullPath], - @"records": @{@"identity": uint256_data(self.uniqueID)}, - @"subdomainRules": @{@"allowSubdomains": @NO} - }; - DPDocument *document = [self.dpnsDocumentFactory documentOnTable:@"domain" withDataDictionary:dataDictionary usingEntropy:entropyData error:error]; - if (*error) { - return nil; - } - [usernameDomainDocuments addObject:document]; - } - return usernameDomainDocuments; -} - -// MARK: Transitions - -- (DSDocumentTransition *)preorderTransitionForUnregisteredUsernameFullPaths:(NSArray *)unregisteredUsernameFullPaths inContext:(NSManagedObjectContext *)context error:(NSError **)error { - NSData *entropyData = uint256_random_data; - NSArray *usernamePreorderDocuments = [self preorderDocumentsForUnregisteredUsernameFullPaths:unregisteredUsernameFullPaths usingEntropyData:entropyData inContext:context error:error]; - if (![usernamePreorderDocuments count]) return nil; - DSDocumentTransition *transition = [[DSDocumentTransition alloc] initForDocuments:usernamePreorderDocuments withTransitionVersion:1 blockchainIdentityUniqueId:self.uniqueID onChain:self.chain]; - return transition; -} - -- (DSDocumentTransition *)domainTransitionForUnregisteredUsernameFullPaths:(NSArray *)unregisteredUsernameFullPaths inContext:(NSManagedObjectContext *)context error:(NSError **)error { - NSData *entropyData = uint256_random_data; - NSArray *usernamePreorderDocuments = [self domainDocumentsForUnregisteredUsernameFullPaths:unregisteredUsernameFullPaths usingEntropyData:entropyData inContext:context error:error]; - if (![usernamePreorderDocuments count]) return nil; - DSDocumentTransition *transition = [[DSDocumentTransition alloc] initForDocuments:usernamePreorderDocuments withTransitionVersion:1 blockchainIdentityUniqueId:self.uniqueID onChain:self.chain]; - return transition; -} - -// MARK: Registering - -- (void)registerUsernamesWithCompletion:(void (^_Nullable)(BOOL success, NSError *error))completion { - [self registerUsernamesAtStage:DSBlockchainIdentityUsernameStatus_Initial inContext:self.platformContext completion:completion onCompletionQueue:dispatch_get_main_queue()]; -} - -- (void)registerUsernamesAtStage:(DSBlockchainIdentityUsernameStatus)blockchainIdentityUsernameStatus inContext:(NSManagedObjectContext *)context completion:(void (^_Nullable)(BOOL success, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - DSLog(@"registerUsernamesAtStage %lu", (unsigned long)blockchainIdentityUsernameStatus); - switch (blockchainIdentityUsernameStatus) { - case DSBlockchainIdentityUsernameStatus_Initial: { - NSArray *usernameFullPaths = [self usernameFullPathsWithStatus:DSBlockchainIdentityUsernameStatus_Initial]; - if (usernameFullPaths.count) { - [self registerPreorderedSaltedDomainHashesForUsernameFullPaths:usernameFullPaths - inContext:context - completion:^(BOOL success, NSError *error) { - if (success) { - [self registerUsernamesAtStage:DSBlockchainIdentityUsernameStatus_PreorderRegistrationPending inContext:context completion:completion onCompletionQueue:completionQueue]; - } else { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, error); - }); - } - } - } - onCompletionQueue:self.identityQueue]; - } else { - [self registerUsernamesAtStage:DSBlockchainIdentityUsernameStatus_PreorderRegistrationPending inContext:context completion:completion onCompletionQueue:completionQueue]; - } - break; - } - case DSBlockchainIdentityUsernameStatus_PreorderRegistrationPending: { - NSArray *usernameFullPaths = [self usernameFullPathsWithStatus:DSBlockchainIdentityUsernameStatus_PreorderRegistrationPending]; - NSDictionary *saltedDomainHashes = [self saltedDomainHashesForUsernameFullPaths:usernameFullPaths inContext:context]; - if (saltedDomainHashes.count) { - [self monitorForDPNSPreorderSaltedDomainHashes:saltedDomainHashes - withRetryCount:4 - inContext:context - completion:^(BOOL allFound, NSError *error) { - if (!error) { - if (!allFound) { - //todo: This needs to be done per username and not for all usernames - [self setAndSaveUsernameFullPaths:usernameFullPaths toStatus:DSBlockchainIdentityUsernameStatus_Initial inContext:context]; - [self registerUsernamesAtStage:DSBlockchainIdentityUsernameStatus_Initial inContext:context completion:completion onCompletionQueue:completionQueue]; - } else { - [self registerUsernamesAtStage:DSBlockchainIdentityUsernameStatus_Preordered inContext:context completion:completion onCompletionQueue:completionQueue]; - } - } else { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, error); - }); - } - } - } - onCompletionQueue:self.identityQueue]; - } else { - [self registerUsernamesAtStage:DSBlockchainIdentityUsernameStatus_Preordered inContext:context completion:completion onCompletionQueue:completionQueue]; - } - break; - } - case DSBlockchainIdentityUsernameStatus_Preordered: { - NSArray *usernameFullPaths = [self usernameFullPathsWithStatus:DSBlockchainIdentityUsernameStatus_Preordered]; - if (usernameFullPaths.count) { - [self registerUsernameDomainsForUsernameFullPaths:usernameFullPaths - inContext:context - completion:^(BOOL success, NSError *error) { - if (success) { - [self registerUsernamesAtStage:DSBlockchainIdentityUsernameStatus_RegistrationPending inContext:context completion:completion onCompletionQueue:completionQueue]; - } else { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, error); - }); - } - } - } - onCompletionQueue:self.identityQueue]; - } else { - [self registerUsernamesAtStage:DSBlockchainIdentityUsernameStatus_RegistrationPending inContext:context completion:completion onCompletionQueue:completionQueue]; - } - break; - } - case DSBlockchainIdentityUsernameStatus_RegistrationPending: { - NSArray *usernameFullPaths = [self usernameFullPathsWithStatus:DSBlockchainIdentityUsernameStatus_RegistrationPending]; - if (usernameFullPaths.count) { - [self monitorForDPNSUsernameFullPaths:usernameFullPaths - withRetryCount:5 - inContext:context - completion:^(BOOL allFound, NSError *error) { - if (!error) { - if (!allFound) { - //todo: This needs to be done per username and not for all usernames - [self setAndSaveUsernameFullPaths:usernameFullPaths toStatus:DSBlockchainIdentityUsernameStatus_Preordered inContext:context]; - [self registerUsernamesAtStage:DSBlockchainIdentityUsernameStatus_Preordered inContext:context completion:completion onCompletionQueue:completionQueue]; - } else { - //all were found - if (completion) { - dispatch_async(completionQueue, ^{ - completion(YES, nil); - }); - } - } - - } else { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, error); - }); - } - } - } - onCompletionQueue:completionQueue]; - } else { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(YES, nil); - }); - } - } - break; - } - default: - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, nil); - }); - } - break; - } -} - -//Preorder stage -- (void)registerPreorderedSaltedDomainHashesForUsernameFullPaths:(NSArray *)usernameFullPaths inContext:(NSManagedObjectContext *)context completion:(void (^_Nullable)(BOOL success, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - NSError *error = nil; - DSDocumentTransition *transition = [self preorderTransitionForUnregisteredUsernameFullPaths:usernameFullPaths inContext:context error:&error]; - if (error || !transition) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, error); - }); - } - return; - } - [self signStateTransition:transition - completion:^(BOOL success) { - if (success) { - //let's start by putting the usernames in an undetermined state - [self setAndSaveUsernameFullPaths:usernameFullPaths toStatus:DSBlockchainIdentityUsernameStatus_PreorderRegistrationPending inContext:context]; - [self.DAPIClient publishTransition:transition - completionQueue:self.identityQueue - success:^(NSDictionary *_Nonnull successDictionary, BOOL added) { - [self setAndSaveUsernameFullPaths:usernameFullPaths toStatus:DSBlockchainIdentityUsernameStatus_Preordered inContext:context]; - if (completion) { - dispatch_async(completionQueue, ^{ - completion(YES, nil); - }); - } - } - failure:^(NSError *_Nonnull error) { - DSLogPrivate(@"%@", error); - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, error); - }); - } - }]; - } else { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, [NSError errorWithCode:501 localizedDescriptionKey:@"Unable to sign transition"]); - }); - } - } - }]; -} - -- (void)registerUsernameDomainsForUsernameFullPaths:(NSArray *)usernameFullPaths inContext:(NSManagedObjectContext *)context completion:(void (^_Nullable)(BOOL success, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - NSError *error = nil; - DSDocumentTransition *transition = [self domainTransitionForUnregisteredUsernameFullPaths:usernameFullPaths inContext:context error:&error]; - if (error || !transition) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, error); - }); - } - return; - } - [self signStateTransition:transition - completion:^(BOOL success) { - if (success) { - [self setAndSaveUsernameFullPaths:usernameFullPaths toStatus:DSBlockchainIdentityUsernameStatus_RegistrationPending inContext:context]; - [self.DAPIClient publishTransition:transition - completionQueue:self.identityQueue - success:^(NSDictionary *_Nonnull successDictionary, BOOL added) { - [self setAndSaveUsernameFullPaths:usernameFullPaths toStatus:DSBlockchainIdentityUsernameStatus_Confirmed inContext:context]; - if (completion) { - dispatch_async(completionQueue, ^{ - completion(YES, nil); - }); - } - } - failure:^(NSError *_Nonnull error) { - DSLogPrivate(@"%@", error); - - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, error); - }); - } - }]; - } - }]; -} - -// MARK: Retrieval - -- (void)fetchUsernamesWithCompletion:(void (^)(BOOL, NSError *_Nonnull))completion { - [self fetchUsernamesInContext:self.platformContext withCompletion:completion onCompletionQueue:dispatch_get_main_queue()]; -} - -- (void)fetchUsernamesInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(BOOL success, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - [self fetchUsernamesInContext:context retryCount:DEFAULT_FETCH_USERNAMES_RETRY_COUNT withCompletion:completion onCompletionQueue:completionQueue]; -} - -- (void)fetchUsernamesInContext:(NSManagedObjectContext *)context retryCount:(uint32_t)retryCount withCompletion:(void (^)(BOOL success, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - [self internalFetchUsernamesInContext:context - withCompletion:^(BOOL success, NSError *error) { - if (!success && retryCount > 0) { - [self fetchUsernamesInContext:context retryCount:retryCount - 1 withCompletion:completion onCompletionQueue:completionQueue]; - } else if (completion) { - completion(success, error); - } - } - onCompletionQueue:completionQueue]; -} - -- (void)internalFetchUsernamesInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(BOOL success, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - __weak typeof(self) weakSelf = self; - DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dpnsContract; - if (contract.contractState != DPContractState_Registered) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, [NSError errorWithCode:500 localizedDescriptionKey:@"DPNS Contract is not yet registered on network"]); - }); - } - return; - } - DSDAPIPlatformNetworkService *dapiNetworkService = self.DAPINetworkService; - [dapiNetworkService getDPNSDocumentsForIdentityWithUserId:self.uniqueIDData - completionQueue:self.identityQueue - success:^(NSArray *_Nonnull documents) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, [NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]); - }); - } - return; - } - if (![documents count]) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(YES, nil); - }); - } - return; - } - //todo verify return is true - for (NSDictionary *nameDictionary in documents) { - NSString *username = nameDictionary[@"label"]; - NSString *lowercaseUsername = nameDictionary[@"normalizedLabel"]; - NSString *domain = nameDictionary[@"normalizedParentDomainName"]; - if (username && lowercaseUsername && domain) { - NSMutableDictionary *usernameStatusDictionary = [[self.usernameStatuses objectForKey:[self fullPathForUsername:lowercaseUsername inDomain:domain]] mutableCopy]; - BOOL isNew = FALSE; - if (!usernameStatusDictionary) { - usernameStatusDictionary = [NSMutableDictionary dictionary]; - isNew = TRUE; - usernameStatusDictionary[BLOCKCHAIN_USERNAME_DOMAIN] = domain; - usernameStatusDictionary[BLOCKCHAIN_USERNAME_PROPER] = username; - } - usernameStatusDictionary[BLOCKCHAIN_USERNAME_STATUS] = @(DSBlockchainIdentityUsernameStatus_Confirmed); - [self.usernameStatuses setObject:[usernameStatusDictionary copy] forKey:[self fullPathForUsername:username inDomain:domain]]; - if (isNew) { - [self saveNewUsername:username inDomain:domain status:DSBlockchainIdentityUsernameStatus_Confirmed inContext:context]; - } else { - [self saveUsername:username inDomain:domain status:DSBlockchainIdentityUsernameStatus_Confirmed salt:nil commitSave:YES inContext:context]; - } - } - } - if (completion) { - dispatch_async(completionQueue, ^{ - completion(YES, nil); - }); - } - } - failure:^(NSError *_Nonnull error) { - if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node - [self.DAPIClient removeDAPINodeByAddress:dapiNetworkService.ipAddress]; - [self fetchUsernamesInContext:context withCompletion:completion onCompletionQueue:completionQueue]; - } else { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, error); - }); - } - } - }]; -} - - -// MARK: - Monitoring - -- (void)updateCreditBalance { - __weak typeof(self) weakSelf = self; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //this is so we don't get DAPINetworkService immediately - DSDAPIPlatformNetworkService *dapiNetworkService = self.DAPINetworkService; - [dapiNetworkService getIdentityById:self.uniqueIDData - completionQueue:self.identityQueue - success:^(NSDictionary *_Nullable profileDictionary) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - return; - } - dispatch_async(self.identityQueue, ^{ - uint64_t creditBalance = (uint64_t)[profileDictionary[@"balance"] longLongValue]; - strongSelf.creditBalance = creditBalance; - }); - } - failure:^(NSError *_Nonnull error) { - if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node - [self.DAPIClient removeDAPINodeByAddress:dapiNetworkService.ipAddress]; - } - }]; - }); -} - -- (void)monitorForBlockchainIdentityWithRetryCount:(uint32_t)retryCount retryAbsentCount:(uint32_t)retryAbsentCount delay:(NSTimeInterval)delay retryDelayType:(DSBlockchainIdentityRetryDelayType)retryDelayType options:(DSBlockchainIdentityMonitorOptions)options inContext:(NSManagedObjectContext *)context completion:(void (^)(BOOL success, BOOL found, NSError *error))completion { - __weak typeof(self) weakSelf = self; - DSDAPIPlatformNetworkService *dapiNetworkService = self.DAPINetworkService; - [dapiNetworkService getIdentityById:self.uniqueIDData - completionQueue:self.identityQueue - success:^(NSDictionary *_Nullable versionedIdentityDictionary) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - return; - } - if (!versionedIdentityDictionary) { - if (completion) { - if (options & DSBlockchainIdentityMonitorOptions_AcceptNotFoundAsNotAnError) { - completion(YES, NO, nil); - return; - } else { - completion(NO, NO, [NSError errorWithCode:500 localizedDescriptionKey:@"Platform returned no identity when one was expected"]); - return; - } - } - } - - if (![versionedIdentityDictionary respondsToSelector:@selector(objectForKey:)]) { - completion(YES, NO, nil); - return; - } - - NSNumber *version = [versionedIdentityDictionary objectForKey:@(DSPlatformStoredMessage_Version)]; - NSDictionary *identityDictionary = [versionedIdentityDictionary objectForKey:@(DSPlatformStoredMessage_Item)]; - if (!identityDictionary) { - if (completion) { - if (options & DSBlockchainIdentityMonitorOptions_AcceptNotFoundAsNotAnError) { - completion(YES, NO, nil); - } else { - completion(NO, NO, [NSError errorWithCode:500 localizedDescriptionKey:@"Platform returned no identity when one was expected"]); - } - } - } else { - if (identityDictionary.count) { - [strongSelf applyIdentityDictionary:identityDictionary version:[version intValue] save:!self.isTransient inContext:context]; - strongSelf.registrationStatus = DSBlockchainIdentityRegistrationStatus_Registered; - [self saveInContext:context]; - } - - if (completion) { - completion(YES, YES, nil); - } - } - } - failure:^(NSError *_Nonnull error) { - if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node - [self.DAPIClient removeDAPINodeByAddress:dapiNetworkService.ipAddress]; - } - uint32_t nextRetryAbsentCount = retryAbsentCount; - if ([[error localizedDescription] isEqualToString:@"Identity not found"]) { - if (!retryAbsentCount) { - if (completion) { - if (options & DSBlockchainIdentityMonitorOptions_AcceptNotFoundAsNotAnError) { - completion(YES, NO, nil); - } else { - completion(NO, NO, error); - } - } - return; - } - nextRetryAbsentCount--; - } - if (retryCount > 0) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - NSTimeInterval nextDelay = delay; - switch (retryDelayType) { - case DSBlockchainIdentityRetryDelayType_SlowingDown20Percent: - nextDelay = delay * 1.2; - break; - case DSBlockchainIdentityRetryDelayType_SlowingDown50Percent: - nextDelay = delay * 1.5; - break; - - default: - break; - } - [self monitorForBlockchainIdentityWithRetryCount:retryCount - 1 - retryAbsentCount:nextRetryAbsentCount - delay:nextDelay - retryDelayType:retryDelayType - options:options - inContext:context - completion:completion]; - }); - } else { - completion(NO, NO, error); - } - }]; -} - -- (void)monitorForDPNSUsernameFullPaths:(NSArray *)usernameFullPaths withRetryCount:(uint32_t)retryCount inContext:(NSManagedObjectContext *)context completion:(void (^)(BOOL allFound, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - NSMutableDictionary *domains = [NSMutableDictionary dictionary]; - for (NSString *usernameFullPath in usernameFullPaths) { - NSArray *components = [usernameFullPath componentsSeparatedByString:@"."]; - NSString *domain = @""; - NSString *name = components[0]; - if (components.count > 1) { - NSArray *domainComponents = [components subarrayWithRange:NSMakeRange(1, components.count - 1)]; - domain = [domainComponents 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) { - [self monitorForDPNSUsernames:domains[domain] - inDomain:domain - withRetryCount:retryCount - inContext:context - completion:^(BOOL allFound, NSError *error) { - if (finished) return; - if (error && !finished) { - finished = TRUE; - if (completion) { - completion(NO, error); - } - return; - } - if (allFound) { - countAllFound++; - } - countReturned++; - if (countReturned == domains.count) { - finished = TRUE; - if (completion) { - completion(countAllFound == domains.count, nil); - } - } - } - onCompletionQueue:completionQueue]; //we can use completion queue directly here - } -} - -- (void)monitorForDPNSUsernames:(NSArray *)usernames inDomain:(NSString *)domain withRetryCount:(uint32_t)retryCount inContext:(NSManagedObjectContext *)context completion:(void (^)(BOOL allFound, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - __weak typeof(self) weakSelf = self; - DSDAPIPlatformNetworkService *dapiNetworkService = self.DAPINetworkService; - [dapiNetworkService getDPNSDocumentsForUsernames:usernames - inDomain:domain - completionQueue:self.identityQueue - success:^(id _Nonnull domainDocumentArray) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - return; - } - if ([domainDocumentArray isKindOfClass:[NSArray class]]) { - NSMutableArray *usernamesLeft = [usernames mutableCopy]; - for (NSString *username in usernames) { - for (NSDictionary *domainDocument in domainDocumentArray) { - NSString *normalizedLabel = domainDocument[@"normalizedLabel"]; - NSString *label = domainDocument[@"label"]; - NSString *normalizedParentDomainName = domainDocument[@"normalizedParentDomainName"]; - if ([normalizedLabel isEqualToString:[username lowercaseString]]) { - NSMutableDictionary *usernameStatusDictionary = [[self.usernameStatuses objectForKey:username] mutableCopy]; - if (!usernameStatusDictionary) { - usernameStatusDictionary = [NSMutableDictionary dictionary]; - usernameStatusDictionary[BLOCKCHAIN_USERNAME_DOMAIN] = normalizedParentDomainName; - usernameStatusDictionary[BLOCKCHAIN_USERNAME_PROPER] = label; - } - usernameStatusDictionary[BLOCKCHAIN_USERNAME_STATUS] = @(DSBlockchainIdentityUsernameStatus_Confirmed); - [self.usernameStatuses setObject:[usernameStatusDictionary copy] forKey:[self fullPathForUsername:username inDomain:[self dashpayDomainName]]]; - [strongSelf saveUsername:username inDomain:normalizedParentDomainName status:DSBlockchainIdentityUsernameStatus_Confirmed salt:nil commitSave:YES inContext:context]; - [usernamesLeft removeObject:username]; - } - } - } - if ([usernamesLeft count] && retryCount > 0) { - [strongSelf monitorForDPNSUsernames:usernamesLeft inDomain:domain withRetryCount:retryCount - 1 inContext:context completion:completion onCompletionQueue:completionQueue]; - } else if ([usernamesLeft count]) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(FALSE, nil); - }); - } - } else { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(TRUE, nil); - }); - } - } - } else if (retryCount > 0) { - [strongSelf monitorForDPNSUsernames:usernames inDomain:domain withRetryCount:retryCount - 1 inContext:context completion:completion onCompletionQueue:completionQueue]; - } else { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, [NSError errorWithCode:501 localizedDescriptionKey:@"Malformed platform response"]); - }); - } - } - } - failure:^(NSError *_Nonnull error) { - if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node - [self.DAPIClient removeDAPINodeByAddress:dapiNetworkService.ipAddress]; - } - if (retryCount > 0) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), self.identityQueue, ^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - return; - } - [strongSelf monitorForDPNSUsernames:usernames - inDomain:domain - withRetryCount:retryCount - 1 - inContext:context - completion:completion - onCompletionQueue:completionQueue]; - }); - } else { - dispatch_async(completionQueue, ^{ - completion(FALSE, error); - }); - } - }]; -} - -- (void)monitorForDPNSPreorderSaltedDomainHashes:(NSDictionary *)saltedDomainHashes withRetryCount:(uint32_t)retryCount inContext:(NSManagedObjectContext *)context completion:(void (^)(BOOL allFound, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - __weak typeof(self) weakSelf = self; - DSDAPIPlatformNetworkService *dapiNetworkService = self.DAPINetworkService; - [dapiNetworkService getDPNSDocumentsForPreorderSaltedDomainHashes:[saltedDomainHashes allValues] - completionQueue:self.identityQueue - success:^(id _Nonnull preorderDocumentArray) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, [NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]); - }); - } - return; - } - if ([preorderDocumentArray isKindOfClass:[NSArray class]]) { - NSMutableArray *usernamesLeft = [[saltedDomainHashes allKeys] mutableCopy]; - for (NSString *usernameFullPath in saltedDomainHashes) { - NSData *saltedDomainHashData = saltedDomainHashes[usernameFullPath]; - for (NSDictionary *preorderDocument in preorderDocumentArray) { - if ([preorderDocument[@"saltedDomainHash"] isEqualToData:saltedDomainHashData]) { - NSMutableDictionary *usernameStatusDictionary = [[self.usernameStatuses objectForKey:usernameFullPath] mutableCopy]; - if (!usernameStatusDictionary) { - usernameStatusDictionary = [NSMutableDictionary dictionary]; - } - usernameStatusDictionary[BLOCKCHAIN_USERNAME_STATUS] = @(DSBlockchainIdentityUsernameStatus_Preordered); - [self.usernameStatuses setObject:[usernameStatusDictionary copy] forKey:usernameFullPath]; - [strongSelf saveUsernameFullPath:usernameFullPath status:DSBlockchainIdentityUsernameStatus_Preordered salt:nil commitSave:YES inContext:context]; - [usernamesLeft removeObject:usernameFullPath]; - } - } - } - if ([usernamesLeft count] && retryCount > 0) { - NSDictionary *saltedDomainHashesLeft = [saltedDomainHashes dictionaryWithValuesForKeys:usernamesLeft]; - [strongSelf monitorForDPNSPreorderSaltedDomainHashes:saltedDomainHashesLeft withRetryCount:retryCount - 1 inContext:context completion:completion onCompletionQueue:completionQueue]; - } else if ([usernamesLeft count]) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(FALSE, nil); - }); - } - } else { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(TRUE, nil); - }); - } - } - } else if (retryCount > 0) { - [strongSelf monitorForDPNSPreorderSaltedDomainHashes:saltedDomainHashes withRetryCount:retryCount - 1 inContext:context completion:completion onCompletionQueue:completionQueue]; - } else { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, [NSError errorWithCode:501 localizedDescriptionKey:@"Malformed platform response"]); - }); - } - } - } - failure:^(NSError *_Nonnull error) { - if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node - [self.DAPIClient removeDAPINodeByAddress:dapiNetworkService.ipAddress]; - } - if (retryCount > 0) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), self.identityQueue, ^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, [NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]); - }); - } - return; - } - [strongSelf monitorForDPNSPreorderSaltedDomainHashes:saltedDomainHashes - withRetryCount:retryCount - 1 - inContext:context - completion:completion - onCompletionQueue:completionQueue]; - }); - } else { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(FALSE, error); - }); - } - } - }]; -} - -- (void)monitorForContract:(DPContract *)contract withRetryCount:(uint32_t)retryCount inContext:(NSManagedObjectContext *)context completion:(void (^)(BOOL success, NSError *error))completion { - __weak typeof(self) weakSelf = self; - NSParameterAssert(contract); - if (!contract) return; - DSDAPIPlatformNetworkService *dapiNetworkService = self.DAPINetworkService; - [dapiNetworkService fetchContractForId:uint256_data(contract.contractId) - completionQueue:self.identityQueue - success:^(id _Nonnull contractDictionary) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - completion(NO, [NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]); - } - return; - } - DSLog(@"Contract dictionary is %@", contractDictionary); - if ([contractDictionary isKindOfClass:[NSDictionary class]] && [contractDictionary[@"$id"] isEqualToData:uint256_data(contract.contractId)]) { - [contract setContractState:DPContractState_Registered inContext:context]; - if (completion) { - completion(TRUE, nil); - } - } else if (retryCount > 0) { - [strongSelf monitorForContract:contract withRetryCount:retryCount - 1 inContext:context completion:completion]; - } else { - if (completion) { - completion(NO, [NSError errorWithCode:501 localizedDescriptionKey:@"Malformed platform response"]); - } - } - } - failure:^(NSError *_Nonnull error) { - if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node - [self.DAPIClient removeDAPINodeByAddress:dapiNetworkService.ipAddress]; - } - if (retryCount > 0) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - completion(NO, [NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]); - } - return; - } - [strongSelf monitorForContract:contract - withRetryCount:retryCount - 1 - inContext:context - completion:completion]; - }); - } else { - if (completion) { - completion(FALSE, error); - } - } - }]; -} - -//-(void)registerContract:(DPContract*)contract { -// __weak typeof(self) weakSelf = self; -// [self.DAPINetworkService getUserById:self.uniqueIdString success:^(NSDictionary * _Nonnull profileDictionary) { -// __strong typeof(weakSelf) strongSelf = weakSelf; -// if (!strongSelf) { -// return; -// } -// uint64_t creditBalance = (uint64_t)[profileDictionary[@"credits"] longLongValue]; -// strongSelf.creditBalance = creditBalance; -// strongSelf.registrationStatus = DSBlockchainIdentityRegistrationStatus_Registered; -// [self save]; -// } failure:^(NSError * _Nonnull error) { -// if (retryCount > 0) { -// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ -// [self monitorForBlockchainIdentityWithRetryCount:retryCount - 1]; -// }); -// } -// }]; -//} - -// MARK: - Dashpay - -// MARK: Helpers - -- (BOOL)isDashpayReady { - if (self.activeKeyCount == 0) { - return NO; - } - if (!self.isRegistered) { - return NO; - } - return YES; -} - -- (DPDocument *)matchingDashpayUserProfileDocumentInContext:(NSManagedObjectContext *)context { - //The revision must be at least at 1, otherwise nothing was ever done - DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; - if (matchingDashpayUser && matchingDashpayUser.localProfileDocumentRevision) { - __block DSMutableStringValueDictionary *dataDictionary = nil; - - __block NSData *entropyData = nil; - __block NSData *documentIdentifier = nil; - [context performBlockAndWait:^{ - dataDictionary = [@{ - @"$updatedAt": @(matchingDashpayUser.updatedAt), - } mutableCopy]; - if (matchingDashpayUser.createdAt == matchingDashpayUser.updatedAt) { - dataDictionary[@"$createdAt"] = @(matchingDashpayUser.createdAt); - } else { - dataDictionary[@"$revision"] = @(matchingDashpayUser.localProfileDocumentRevision); - } - if (matchingDashpayUser.publicMessage) { - dataDictionary[@"publicMessage"] = matchingDashpayUser.publicMessage; - } - if (matchingDashpayUser.avatarPath) { - dataDictionary[@"avatarUrl"] = matchingDashpayUser.avatarPath; - } - if (matchingDashpayUser.avatarFingerprint) { - dataDictionary[@"avatarFingerprint"] = matchingDashpayUser.avatarFingerprint; - } - if (matchingDashpayUser.avatarHash) { - dataDictionary[@"avatarHash"] = matchingDashpayUser.avatarHash; - } - if (matchingDashpayUser.displayName) { - dataDictionary[@"displayName"] = matchingDashpayUser.displayName; - } - entropyData = matchingDashpayUser.originalEntropyData; - documentIdentifier = matchingDashpayUser.documentIdentifier; - }]; - NSError *error = nil; - if (documentIdentifier == nil) { - NSAssert(entropyData, @"Entropy string must be present"); - DPDocument *document = [self.dashpayDocumentFactory documentOnTable:@"profile" withDataDictionary:dataDictionary usingEntropy:entropyData error:&error]; - return document; - } else { - DPDocument *document = [self.dashpayDocumentFactory documentOnTable:@"profile" withDataDictionary:dataDictionary usingDocumentIdentifier:documentIdentifier error:&error]; - return document; - } - } else { - return nil; - } -} - - -- (DSBlockchainIdentityFriendshipStatus)friendshipStatusForRelationshipWithBlockchainIdentity:(DSBlockchainIdentity *)otherBlockchainIdentity { - if (!self.matchingDashpayUserInViewContext) return DSBlockchainIdentityFriendshipStatus_Unknown; - __block BOOL isIncoming; - __block BOOL isOutgoing; - [self.matchingDashpayUserInViewContext.managedObjectContext performBlockAndWait:^{ - isIncoming = ([self.matchingDashpayUserInViewContext.incomingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"sourceContact.associatedBlockchainIdentity.uniqueID == %@", uint256_data(otherBlockchainIdentity.uniqueID)]].count > 0); - isOutgoing = ([self.matchingDashpayUserInViewContext.outgoingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"destinationContact.associatedBlockchainIdentity.uniqueID == %@", uint256_data(otherBlockchainIdentity.uniqueID)]].count > 0); - }]; - return ((isIncoming << 1) | isOutgoing); -} - - -// MARK: Sending a Friend Request - - -- (void)setDashpaySyncronizationBlockHash:(UInt256)dashpaySyncronizationBlockHash { - _dashpaySyncronizationBlockHash = dashpaySyncronizationBlockHash; - if (uint256_is_zero(_dashpaySyncronizationBlockHash)) { - _dashpaySyncronizationBlockHeight = 0; - } else { - _dashpaySyncronizationBlockHeight = [self.chain heightForBlockHash:_dashpaySyncronizationBlockHash]; - if (_dashpaySyncronizationBlockHeight == UINT32_MAX) { - _dashpaySyncronizationBlockHeight = 0; - } - } -} - -// MARK: Sending a Friend Request - -- (void)sendNewFriendRequestToBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity completion:(void (^)(BOOL success, NSArray *_Nullable errors))completion { - [self sendNewFriendRequestToBlockchainIdentity:blockchainIdentity inContext:self.platformContext completion:completion onCompletionQueue:dispatch_get_main_queue()]; -} - -- (void)sendNewFriendRequestToBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity inContext:(NSManagedObjectContext *)context completion:(void (^)(BOOL success, NSArray *_Nullable errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - if (blockchainIdentity.isTransient) { - blockchainIdentity.isTransient = FALSE; - [self.identitiesManager registerForeignBlockchainIdentity:blockchainIdentity]; - if (blockchainIdentity.transientDashpayUser) { - [blockchainIdentity applyProfileChanges:blockchainIdentity.transientDashpayUser - inContext:context - saveContext:YES - completion:^(BOOL success, NSError *_Nullable error) { - if (success && !error) { - DSDashpayUserEntity *dashpayUser = [blockchainIdentity matchingDashpayUserInContext:context]; - if (blockchainIdentity.transientDashpayUser.revision == dashpayUser.remoteProfileDocumentRevision) { - blockchainIdentity.transientDashpayUser = nil; - } - } - } - onCompletionQueue:self.identityQueue]; - } - } - [blockchainIdentity fetchNeededNetworkStateInformationInContext:context - withCompletion:^(DSBlockchainIdentityQueryStep failureStep, NSArray *_Nullable errors) { - if (failureStep && failureStep != DSBlockchainIdentityQueryStep_Profile) { //if profile fails we can still continue on - completion(NO, errors); - return; - } - if (![blockchainIdentity isDashpayReady]) { - dispatch_async(completionQueue, ^{ - completion(NO, @[[NSError errorWithCode:501 localizedDescriptionKey:@"User has actions to complete before being able to use Dashpay"]]); - }); - - return; - } - uint32_t destinationKeyIndex = [blockchainIdentity firstIndexOfKeyOfType:self.currentMainKeyType createIfNotPresent:NO saveKey:NO]; - uint32_t sourceKeyIndex = [self firstIndexOfKeyOfType:self.currentMainKeyType createIfNotPresent:NO saveKey:NO]; - - - if (sourceKeyIndex == UINT32_MAX) { //not found - //to do register a new key - NSAssert(FALSE, @"we shouldn't be getting here"); - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, @[[NSError errorWithCode:501 localizedDescriptionKey:@"Internal key handling error"]]); - }); - } - return; - } - DSAccount *account = [self.wallet accountWithNumber:0]; - DSPotentialOneWayFriendship *potentialFriendship = [[DSPotentialOneWayFriendship alloc] initWithDestinationBlockchainIdentity:blockchainIdentity destinationKeyIndex:destinationKeyIndex sourceBlockchainIdentity:self sourceKeyIndex:sourceKeyIndex account:account]; - - [potentialFriendship createDerivationPathAndSaveExtendedPublicKeyWithCompletion:^(BOOL success, DSIncomingFundsDerivationPath *_Nonnull incomingFundsDerivationPath) { - if (!success) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, @[[NSError errorWithCode:501 localizedDescriptionKey:@"Internal key handling error"]]); - }); - } - return; - } - [potentialFriendship encryptExtendedPublicKeyWithCompletion:^(BOOL success) { - if (!success) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, @[[NSError errorWithCode:501 localizedDescriptionKey:@"Internal key handling error"]]); - }); - } - return; - } - [self sendNewFriendRequestMatchingPotentialFriendship:potentialFriendship - inContext:context - completion:completion - onCompletionQueue:completionQueue]; - }]; - }]; - } - onCompletionQueue:self.identityQueue]; -} - -- (void)sendNewFriendRequestToPotentialContact:(DSPotentialContact *)potentialContact completion:(void (^)(BOOL success, NSArray *_Nullable errors))completion { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) return; - __weak typeof(self) weakSelf = self; - DSDAPIPlatformNetworkService *dapiNetworkService = self.DAPINetworkService; - [dapiNetworkService getIdentityByName:potentialContact.username - inDomain:[self dashpayDomainName] - completionQueue:self.identityQueue - success:^(NSDictionary *_Nonnull blockchainIdentityVersionedDictionary) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - completion(NO, @[[NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]]); - } - return; - } - NSNumber *_Nonnull version = blockchainIdentityVersionedDictionary[@(DSPlatformStoredMessage_Version)]; - NSDictionary *_Nonnull blockchainIdentityDictionary = blockchainIdentityVersionedDictionary[@(DSPlatformStoredMessage_Item)]; - NSData *identityIdData = nil; - if (!blockchainIdentityDictionary || !(identityIdData = blockchainIdentityDictionary[@"id"])) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(NO, @[[NSError errorWithCode:501 localizedDescriptionKey:@"Malformed platform response"]]); - }); - } - return; - } - - UInt256 blockchainIdentityContactUniqueId = identityIdData.UInt256; - - NSAssert(uint256_is_not_zero(blockchainIdentityContactUniqueId), @"blockchainIdentityContactUniqueId should not be null"); - - DSBlockchainIdentityEntity *potentialContactBlockchainIdentityEntity = [DSBlockchainIdentityEntity anyObjectInContext:self.platformContext matching:@"uniqueID == %@", uint256_data(blockchainIdentityContactUniqueId)]; - - DSBlockchainIdentity *potentialContactBlockchainIdentity = nil; - - if (potentialContactBlockchainIdentityEntity) { - potentialContactBlockchainIdentity = [self.chain blockchainIdentityForUniqueId:blockchainIdentityContactUniqueId]; - if (!potentialContactBlockchainIdentity) { - potentialContactBlockchainIdentity = [[DSBlockchainIdentity alloc] initWithBlockchainIdentityEntity:potentialContactBlockchainIdentityEntity]; - } - } else { - potentialContactBlockchainIdentity = [self.identitiesManager foreignBlockchainIdentityWithUniqueId:blockchainIdentityContactUniqueId createIfMissing:YES inContext:self.platformContext]; - } - [potentialContactBlockchainIdentity applyIdentityDictionary:blockchainIdentityDictionary - version:[version intValue] - save:YES - inContext:self.platformContext]; - [potentialContactBlockchainIdentity saveInContext:self.platformContext]; - - [self sendNewFriendRequestToBlockchainIdentity:potentialContactBlockchainIdentity completion:completion]; - } - failure:^(NSError *_Nonnull error) { - if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node - [self.DAPIClient removeDAPINodeByAddress:dapiNetworkService.ipAddress]; - } - DSLogPrivate(@"%@", error); - - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(NO, @[error]); - }); - } - }]; -} - -- (void)sendNewFriendRequestMatchingPotentialFriendship:(DSPotentialOneWayFriendship *)potentialFriendship completion:(void (^)(BOOL success, NSArray *errors))completion { - [self sendNewFriendRequestMatchingPotentialFriendship:potentialFriendship inContext:self.platformContext completion:completion onCompletionQueue:dispatch_get_main_queue()]; -} - -- (void)sendNewFriendRequestMatchingPotentialFriendship:(DSPotentialOneWayFriendship *)potentialFriendship inContext:(NSManagedObjectContext *)context completion:(void (^)(BOOL success, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) return; - DSDashpayUserEntity *destinationDashpayUser = [potentialFriendship.destinationBlockchainIdentity matchingDashpayUserInContext:context]; - if (!destinationDashpayUser) { - NSAssert([potentialFriendship.destinationBlockchainIdentity matchingDashpayUserInContext:context], @"There must be a destination contact if the destination blockchain identity is not known"); - return; - } - - __weak typeof(self) weakSelf = self; - DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; - NSData *entropyData = uint256_random_data; - DPDocument *document = [potentialFriendship contactRequestDocumentWithEntropy:entropyData]; - [self.DAPIClient sendDocument:document - forIdentity:self - contract:contract - completion:^(NSError *_Nullable error) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(NO, @[[NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]]); - }); - } - return; - } - - BOOL success = error == nil; - - if (!success) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(NO, @[error]); - }); - return; - } - - [context performBlockAndWait:^{ - [self addFriendship:potentialFriendship - inContext:context - completion:^(BOOL success, NSError *error){ - - }]; - // [self addFriendshipFromSourceBlockchainIdentity:potentialFriendship.sourceBlockchainIdentity sourceKeyIndex:potentialFriendship.so toRecipientBlockchainIdentity:<#(DSBlockchainIdentity *)#> recipientKeyIndex:<#(uint32_t)#> inContext:<#(NSManagedObjectContext *)#>] - // DSFriendRequestEntity * friendRequest = [potentialFriendship outgoingFriendRequestForDashpayUserEntity:potentialFriendship.destinationBlockchainIdentity.matchingDashpayUser]; - // [strongSelf.matchingDashpayUser addOutgoingRequestsObject:friendRequest]; - // - // if ([[friendRequest.destinationContact.outgoingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"destinationContact == %@",strongSelf.matchingDashpayUser]] count]) { - // [strongSelf.matchingDashpayUser addFriendsObject:friendRequest.destinationContact]; - // } - // [potentialFriendship storeExtendedPublicKeyAssociatedWithFriendRequest:friendRequest]; - // [DSFriendRequestEntity saveContext]; - // if (completion) { - // dispatch_async(dispatch_get_main_queue(), ^{ - // completion(success,error); - // }); - // } - }]; - - [self fetchOutgoingContactRequestsInContext:context - withCompletion:^(BOOL success, NSArray *_Nonnull errors) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(success, errors); - }); - } - } - onCompletionQueue:completionQueue]; - }]; -} - -- (void)acceptFriendRequestFromBlockchainIdentity:(DSBlockchainIdentity *)otherBlockchainIdentity completion:(void (^)(BOOL success, NSArray *errors))completion { - [self acceptFriendRequestFromBlockchainIdentity:otherBlockchainIdentity inContext:self.platformContext completion:completion onCompletionQueue:dispatch_get_main_queue()]; -} - -- (void)acceptFriendRequestFromBlockchainIdentity:(DSBlockchainIdentity *)otherBlockchainIdentity inContext:(NSManagedObjectContext *)context completion:(void (^)(BOOL success, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) { - if (completion) { - completion(NO, @[[NSError errorWithCode:501 localizedDescriptionKey:@"Accepting a friend request should only happen from a local blockchain identity"]]); - } - return; - } - - [context performBlockAndWait:^{ - DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; - DSFriendRequestEntity *friendRequest = [[matchingDashpayUser.incomingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"sourceContact.associatedBlockchainIdentity.uniqueID == %@", uint256_data(otherBlockchainIdentity.uniqueID)]] anyObject]; - if (!friendRequest) { - if (completion) { - completion(NO, @[[NSError errorWithCode:501 localizedDescriptionKey:@"You can only accept a friend request from blockchain identity who has sent you one, and none were found"]]); - } - } else { - [self acceptFriendRequest:friendRequest completion:completion onCompletionQueue:completionQueue]; - } - }]; -} - -- (void)acceptFriendRequest:(DSFriendRequestEntity *)friendRequest completion:(void (^)(BOOL success, NSArray *errors))completion { - [self acceptFriendRequest:friendRequest completion:completion onCompletionQueue:dispatch_get_main_queue()]; -} - -- (void)acceptFriendRequest:(DSFriendRequestEntity *)friendRequest completion:(void (^)(BOOL success, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); - if (!_isLocal) { - if (completion) { - completion(NO, @[[NSError errorWithCode:501 localizedDescriptionKey:@"Accepting a friend request should only happen from a local blockchain identity"]]); - } - return; - } - DSAccount *account = [self.wallet accountWithNumber:0]; - DSDashpayUserEntity *otherDashpayUser = friendRequest.sourceContact; - DSBlockchainIdentity *otherBlockchainIdentity = [self.chain blockchainIdentityForUniqueId:otherDashpayUser.associatedBlockchainIdentity.uniqueID.UInt256]; - - if (!otherBlockchainIdentity) { - otherBlockchainIdentity = [[DSBlockchainIdentity alloc] initWithBlockchainIdentityEntity:otherDashpayUser.associatedBlockchainIdentity]; - } - // DSPotentialContact *contact = [[DSPotentialContact alloc] initWithUsername:friendRequest.sourceContact.username avatarPath:friendRequest.sourceContact.avatarPath - // publicMessage:friendRequest.sourceContact.publicMessage]; - // [contact setAssociatedBlockchainIdentityUniqueId:friendRequest.sourceContact.associatedBlockchainIdentity.uniqueID.UInt256]; - // DSKey * friendsEncyptionKey = [otherBlockchainIdentity keyOfType:friendRequest.sourceEncryptionPublicKeyIndex atIndex:friendRequest.sourceEncryptionPublicKeyIndex]; - //[DSKey keyWithPublicKeyData:friendRequest.sourceContact.encryptionPublicKey forKeyType:friendRequest.sourceContact.encryptionPublicKeyType onChain:self.chain]; - // [contact addPublicKey:friendsEncyptionKey atIndex:friendRequest.sourceContact.encryptionPublicKeyIndex]; - // uint32_t sourceKeyIndex = [self firstIndexOfKeyOfType:friendRequest.sourceContact.encryptionPublicKeyType createIfNotPresent:NO]; - // if (sourceKeyIndex == UINT32_MAX) { //not found - // //to do register a new key - // NSAssert(FALSE, @"we shouldn't be getting here"); - // return; - // } - DSPotentialOneWayFriendship *potentialFriendship = [[DSPotentialOneWayFriendship alloc] initWithDestinationBlockchainIdentity:otherBlockchainIdentity destinationKeyIndex:friendRequest.sourceKeyIndex sourceBlockchainIdentity:self sourceKeyIndex:friendRequest.destinationKeyIndex account:account]; - [potentialFriendship createDerivationPathAndSaveExtendedPublicKeyWithCompletion:^(BOOL success, DSIncomingFundsDerivationPath *_Nonnull incomingFundsDerivationPath) { - if (success) { - [potentialFriendship encryptExtendedPublicKeyWithCompletion:^(BOOL success) { - if (!success) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(NO, @[[NSError errorWithCode:501 localizedDescriptionKey:@"Internal key handling error"]]); - }); - } - return; - } - [self sendNewFriendRequestMatchingPotentialFriendship:potentialFriendship - inContext:friendRequest.managedObjectContext - completion:completion - onCompletionQueue:completionQueue]; - }]; - } else { - if (completion) { - completion(NO, @[[NSError errorWithCode:500 localizedDescriptionKey:@"Could not create friendship derivation path"]]); - } - } - }]; -} - -// MARK: Profile - -- (DSDocumentTransition *)profileDocumentTransitionInContext:(NSManagedObjectContext *)context { - DPDocument *profileDocument = [self matchingDashpayUserProfileDocumentInContext:context]; - if (!profileDocument) return nil; - DSDocumentTransition *transition = [[DSDocumentTransition alloc] initForDocuments:@[profileDocument] withTransitionVersion:1 blockchainIdentityUniqueId:self.uniqueID onChain:self.chain]; - return transition; -} - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName { - [self updateDashpayProfileWithDisplayName:displayName inContext:self.platformContext]; -} - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName inContext:(NSManagedObjectContext *)context { - [context performBlockAndWait:^{ - DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; - matchingDashpayUser.displayName = displayName; - if (!matchingDashpayUser.remoteProfileDocumentRevision) { - matchingDashpayUser.createdAt = [[NSDate date] timeIntervalSince1970] * 1000; - if (!matchingDashpayUser.originalEntropyData) { - matchingDashpayUser.originalEntropyData = uint256_random_data; - } - } - matchingDashpayUser.updatedAt = [[NSDate date] timeIntervalSince1970] * 1000; - matchingDashpayUser.localProfileDocumentRevision++; - [context ds_save]; - }]; -} - -- (void)updateDashpayProfileWithPublicMessage:(NSString *)publicMessage { - [self updateDashpayProfileWithPublicMessage:publicMessage inContext:self.platformContext]; -} - -- (void)updateDashpayProfileWithPublicMessage:(NSString *)publicMessage inContext:(NSManagedObjectContext *)context { - [context performBlockAndWait:^{ - DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; - matchingDashpayUser.publicMessage = publicMessage; - if (!matchingDashpayUser.remoteProfileDocumentRevision) { - matchingDashpayUser.createdAt = [[NSDate date] timeIntervalSince1970] * 1000; - if (!matchingDashpayUser.originalEntropyData) { - matchingDashpayUser.originalEntropyData = uint256_random_data; - } - } - matchingDashpayUser.updatedAt = [[NSDate date] timeIntervalSince1970] * 1000; - matchingDashpayUser.localProfileDocumentRevision++; - [context ds_save]; - }]; -} - -- (void)updateDashpayProfileWithAvatarURLString:(NSString *)avatarURLString { - [self updateDashpayProfileWithAvatarURLString:avatarURLString inContext:self.platformContext]; -} - -- (void)updateDashpayProfileWithAvatarURLString:(NSString *)avatarURLString inContext:(NSManagedObjectContext *)context { - [context performBlockAndWait:^{ - DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; - matchingDashpayUser.avatarPath = avatarURLString; - if (!matchingDashpayUser.remoteProfileDocumentRevision) { - matchingDashpayUser.createdAt = [[NSDate date] timeIntervalSince1970] * 1000; - if (!matchingDashpayUser.originalEntropyData) { - matchingDashpayUser.originalEntropyData = uint256_random_data; - } - } - matchingDashpayUser.updatedAt = [[NSDate date] timeIntervalSince1970] * 1000; - matchingDashpayUser.localProfileDocumentRevision++; - [context ds_save]; - }]; -} - - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage { - [self updateDashpayProfileWithDisplayName:displayName publicMessage:publicMessage inContext:self.platformContext]; -} - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage inContext:(NSManagedObjectContext *)context { - [context performBlockAndWait:^{ - DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; - matchingDashpayUser.displayName = displayName; - matchingDashpayUser.publicMessage = publicMessage; - if (!matchingDashpayUser.remoteProfileDocumentRevision) { - matchingDashpayUser.createdAt = [[NSDate date] timeIntervalSince1970] * 1000; - if (!matchingDashpayUser.originalEntropyData) { - matchingDashpayUser.originalEntropyData = uint256_random_data; - } - } - matchingDashpayUser.updatedAt = [[NSDate date] timeIntervalSince1970] * 1000; - matchingDashpayUser.localProfileDocumentRevision++; - [context ds_save]; - }]; -} - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage avatarURLString:(NSString *)avatarURLString { - [self updateDashpayProfileWithDisplayName:displayName publicMessage:publicMessage avatarURLString:avatarURLString inContext:self.platformContext]; -} - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage avatarURLString:(NSString *)avatarURLString inContext:(NSManagedObjectContext *)context { - [context performBlockAndWait:^{ - DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; - matchingDashpayUser.displayName = displayName; - matchingDashpayUser.publicMessage = publicMessage; - matchingDashpayUser.avatarPath = avatarURLString; - if (!matchingDashpayUser.remoteProfileDocumentRevision) { - matchingDashpayUser.createdAt = [[NSDate date] timeIntervalSince1970] * 1000; - if (!matchingDashpayUser.originalEntropyData) { - matchingDashpayUser.originalEntropyData = uint256_random_data; - } - } - matchingDashpayUser.updatedAt = [[NSDate date] timeIntervalSince1970] * 1000; - matchingDashpayUser.localProfileDocumentRevision++; - [context ds_save]; - }]; -} - -#if TARGET_OS_IOS - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage avatarImage:(UIImage *)avatarImage avatarData:(NSData *)data avatarURLString:(NSString *)avatarURLString { - [self updateDashpayProfileWithDisplayName:displayName publicMessage:publicMessage avatarImage:avatarImage avatarData:data avatarURLString:avatarURLString inContext:self.platformContext]; -} - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage avatarImage:(UIImage *)avatarImage avatarData:(NSData *)avatarData avatarURLString:(NSString *)avatarURLString inContext:(NSManagedObjectContext *)context { - NSData *avatarHash = uint256_data(avatarData.SHA256); - uint64_t fingerprint = [[OSImageHashing sharedInstance] hashImage:avatarImage withProviderId:OSImageHashingProviderDHash]; - [self updateDashpayProfileWithDisplayName:displayName publicMessage:publicMessage avatarURLString:avatarURLString avatarHash:avatarHash avatarFingerprint:[NSData dataWithUInt64:fingerprint] inContext:context]; -} - -- (void)updateDashpayProfileWithAvatarImage:(UIImage *)avatarImage avatarData:(NSData *)data avatarURLString:(NSString *)avatarURLString { - [self updateDashpayProfileWithAvatarImage:avatarImage avatarData:data avatarURLString:avatarURLString inContext:self.platformContext]; -} - -- (void)updateDashpayProfileWithAvatarImage:(UIImage *)avatarImage avatarData:(NSData *)avatarData avatarURLString:(NSString *)avatarURLString inContext:(NSManagedObjectContext *)context { - NSData *avatarHash = uint256_data(avatarData.SHA256); - uint64_t fingerprint = [[OSImageHashing sharedInstance] hashImage:avatarImage withProviderId:OSImageHashingProviderDHash]; - [self updateDashpayProfileWithAvatarURLString:avatarURLString avatarHash:avatarHash avatarFingerprint:[NSData dataWithUInt64:fingerprint] inContext:context]; -} - -#else - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage avatarImage:(NSImage *)avatarImage avatarData:(NSData *)data avatarURLString:(NSString *)avatarURLString { - [self updateDashpayProfileWithDisplayName:displayName publicMessage:publicMessage avatarImage:avatarImage avatarData:data avatarURLString:avatarURLString inContext:self.platformContext]; -} - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage avatarImage:(NSImage *)avatarImage avatarData:(NSData *)avatarData avatarURLString:(NSString *)avatarURLString inContext:(NSManagedObjectContext *)context { - NSData *avatarHash = uint256_data(avatarData.SHA256); - uint64_t fingerprint = [[OSImageHashing sharedInstance] hashImage:avatarImage withProviderId:OSImageHashingProviderDHash]; - [self updateDashpayProfileWithDisplayName:displayName publicMessage:publicMessage avatarURLString:avatarURLString avatarHash:avatarHash avatarFingerprint:[NSData dataWithUInt64:fingerprint] inContext:context]; -} - -- (void)updateDashpayProfileWithAvatarImage:(NSImage *)avatarImage avatarData:(NSData *)data avatarURLString:(NSString *)avatarURLString { - [self updateDashpayProfileWithAvatarImage:avatarImage avatarData:data avatarURLString:avatarURLString inContext:self.platformContext]; -} - -- (void)updateDashpayProfileWithAvatarImage:(NSImage *)avatarImage avatarData:(NSData *)avatarData avatarURLString:(NSString *)avatarURLString inContext:(NSManagedObjectContext *)context { - NSData *avatarHash = uint256_data(avatarData.SHA256); - uint64_t fingerprint = [[OSImageHashing sharedInstance] hashImage:avatarImage withProviderId:OSImageHashingProviderDHash]; - [self updateDashpayProfileWithAvatarURLString:avatarURLString avatarHash:avatarHash avatarFingerprint:[NSData dataWithUInt64:fingerprint] inContext:context]; -} - -#endif - - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage avatarURLString:(NSString *)avatarURLString avatarHash:(NSData *)avatarHash avatarFingerprint:(NSData *)avatarFingerprint { - [self updateDashpayProfileWithDisplayName:displayName publicMessage:publicMessage avatarURLString:avatarURLString avatarHash:avatarHash avatarFingerprint:avatarFingerprint inContext:self.platformContext]; -} - -- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName publicMessage:(NSString *)publicMessage avatarURLString:(NSString *)avatarURLString avatarHash:(NSData *)avatarHash avatarFingerprint:(NSData *)avatarFingerprint inContext:(NSManagedObjectContext *)context { - [context performBlockAndWait:^{ - DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; - matchingDashpayUser.displayName = displayName; - matchingDashpayUser.publicMessage = publicMessage; - matchingDashpayUser.avatarPath = avatarURLString; - matchingDashpayUser.avatarFingerprint = avatarFingerprint; - matchingDashpayUser.avatarHash = avatarHash; - if (!matchingDashpayUser.remoteProfileDocumentRevision) { - matchingDashpayUser.createdAt = [[NSDate date] timeIntervalSince1970] * 1000; - if (!matchingDashpayUser.originalEntropyData) { - matchingDashpayUser.originalEntropyData = uint256_random_data; - } - } - matchingDashpayUser.updatedAt = [[NSDate date] timeIntervalSince1970] * 1000; - matchingDashpayUser.localProfileDocumentRevision++; - [context ds_save]; - }]; -} - -- (void)updateDashpayProfileWithAvatarURLString:(NSString *)avatarURLString avatarHash:(NSData *)avatarHash avatarFingerprint:(NSData *)avatarFingerprint { - [self updateDashpayProfileWithAvatarURLString:avatarURLString avatarHash:avatarHash avatarFingerprint:avatarFingerprint inContext:self.platformContext]; -} - -- (void)updateDashpayProfileWithAvatarURLString:(NSString *)avatarURLString avatarHash:(NSData *)avatarHash avatarFingerprint:(NSData *)avatarFingerprint inContext:(NSManagedObjectContext *)context { - [context performBlockAndWait:^{ - DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; - matchingDashpayUser.avatarPath = avatarURLString; - matchingDashpayUser.avatarFingerprint = avatarFingerprint; - matchingDashpayUser.avatarHash = avatarHash; - if (!matchingDashpayUser.remoteProfileDocumentRevision) { - matchingDashpayUser.createdAt = [[NSDate date] timeIntervalSince1970] * 1000; - if (!matchingDashpayUser.originalEntropyData) { - matchingDashpayUser.originalEntropyData = uint256_random_data; - } - } - matchingDashpayUser.updatedAt = [[NSDate date] timeIntervalSince1970] * 1000; - matchingDashpayUser.localProfileDocumentRevision++; - [context ds_save]; - }]; -} - -- (void)signedProfileDocumentTransitionInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(DSTransition *transition, BOOL cancelled, NSError *error))completion { - __weak typeof(self) weakSelf = self; - DSDocumentTransition *transition = [self profileDocumentTransitionInContext:context]; - if (!transition) { - if (completion) { - completion(nil, NO, [NSError errorWithCode:500 localizedDescriptionKey:@"Transition had nothing to update"]); - } - return; - } - [self signStateTransition:transition - completion:^(BOOL success) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - completion(nil, NO, [NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]); - } - return; - } - if (success) { - completion(transition, NO, nil); - } - }]; -} - -- (void)signAndPublishProfileWithCompletion:(void (^)(BOOL success, BOOL cancelled, NSError *error))completion { - [self signAndPublishProfileInContext:self.platformContext withCompletion:completion]; -} - -- (void)signAndPublishProfileInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(BOOL success, BOOL cancelled, NSError *error))completion { - __weak typeof(self) weakSelf = self; - __block uint32_t profileDocumentRevision; - [context performBlockAndWait:^{ - DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; - if (matchingDashpayUser.localProfileDocumentRevision > matchingDashpayUser.remoteProfileDocumentRevision) { - matchingDashpayUser.localProfileDocumentRevision = matchingDashpayUser.remoteProfileDocumentRevision + 1; - } - profileDocumentRevision = matchingDashpayUser.localProfileDocumentRevision; - [context ds_save]; - }]; - [self signedProfileDocumentTransitionInContext:context - withCompletion:^(DSTransition *transition, BOOL cancelled, NSError *error) { - if (!transition) { - if (completion) { - completion(NO, cancelled, error); - } - return; - } - [self.DAPIClient publishTransition:transition - completionQueue:self.identityQueue - success:^(NSDictionary *_Nonnull successDictionary, BOOL added) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - completion(NO, NO, [NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]); - } - return; - } - [context performBlockAndWait:^{ - [self matchingDashpayUserInContext:context].remoteProfileDocumentRevision = profileDocumentRevision; - [context ds_save]; - }]; - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(YES, NO, nil); - }); - } - } - failure:^(NSError *_Nonnull error) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(NO, NO, error); - }); - } - }]; - }]; -} - -// - -// MARK: Fetching - -- (void)fetchProfileWithCompletion:(void (^)(BOOL success, NSError *error))completion { - dispatch_async(self.identityQueue, ^{ - [self fetchProfileInContext:self.platformContext withCompletion:completion onCompletionQueue:dispatch_get_main_queue()]; - }); -} - -- (void)fetchProfileInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(BOOL success, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - [self fetchProfileInContext:context retryCount:DEFAULT_FETCH_PROFILE_RETRY_COUNT withCompletion:completion onCompletionQueue:completionQueue]; -} - -- (void)fetchProfileInContext:(NSManagedObjectContext *)context retryCount:(uint32_t)retryCount withCompletion:(void (^)(BOOL success, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - [self internalFetchProfileInContext:context - withCompletion:^(BOOL success, NSError *error) { - if (!success && retryCount > 0) { - [self fetchUsernamesInContext:context retryCount:retryCount - 1 withCompletion:completion onCompletionQueue:completionQueue]; - } else if (completion) { - completion(success, error); - } - } - onCompletionQueue:completionQueue]; -} - -- (void)internalFetchProfileInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(BOOL success, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - DPContract *dashpayContract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; - if ([dashpayContract contractState] != DPContractState_Registered) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, [NSError errorWithCode:500 localizedDescriptionKey:@"Dashpay Contract is not yet registered on network"]); - }); - } - return; - } - - [self.identitiesManager fetchProfileForBlockchainIdentity: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 (completion) { - dispatch_async(completionQueue, ^{ - completion(success, error); - }); - } - } - onCompletionQueue:self.identityQueue]; - } - onCompletionQueue:self.identityQueue]; -} - -- (void)fetchContactRequests:(void (^)(BOOL success, NSArray *errors))completion { - dispatch_async(self.identityQueue, ^{ - [self fetchContactRequestsInContext:self.platformContext withCompletion:completion onCompletionQueue:dispatch_get_main_queue()]; - }); -} - -#define DEFAULT_CONTACT_REQUEST_FETCH_RETRIES 5 - -- (void)fetchContactRequestsInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(BOOL success, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - __weak typeof(self) weakSelf = self; - [self fetchIncomingContactRequestsInContext:context - withCompletion:^(BOOL success, NSArray *errors) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - completion(NO, @[[NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]]); - } - return; - } - if (!success) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(success, errors); - }); - } - return; - } - - [strongSelf fetchOutgoingContactRequestsInContext:context - withCompletion:completion - onCompletionQueue:completionQueue]; - } - onCompletionQueue:self.identityQueue]; -} - -- (void)fetchIncomingContactRequests:(void (^_Nullable)(BOOL success, NSArray *errors))completion { - [self fetchIncomingContactRequestsInContext:self.platformContext withCompletion:completion onCompletionQueue:dispatch_get_main_queue()]; -} - -- (void)fetchIncomingContactRequestsInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(BOOL success, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - [self fetchIncomingContactRequestsInContext:context startAfter:nil retriesLeft:DEFAULT_CONTACT_REQUEST_FETCH_RETRIES withCompletion:completion onCompletionQueue:completionQueue]; -} - -- (void)fetchIncomingContactRequestsInContext:(NSManagedObjectContext *)context startAfter:(NSData*_Nullable)startAfter retriesLeft:(int32_t)retriesLeft withCompletion:(void (^)(BOOL success, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - [self internalFetchIncomingContactRequestsInContext:context - startAfter:startAfter - withCompletion:^(BOOL success, NSData*_Nullable hasMoreStartAfter, NSArray *errors) { - if (!success && retriesLeft > 0) { - [self fetchIncomingContactRequestsInContext:context startAfter:startAfter retriesLeft:retriesLeft - 1 withCompletion:completion onCompletionQueue:completionQueue]; - } else if (success && hasMoreStartAfter) { - [self fetchIncomingContactRequestsInContext:context startAfter:hasMoreStartAfter retriesLeft:DEFAULT_CONTACT_REQUEST_FETCH_RETRIES withCompletion:completion onCompletionQueue:completionQueue]; - } else if (completion) { - completion(success, errors); - } - } - onCompletionQueue:completionQueue]; -} - -- (void)internalFetchIncomingContactRequestsInContext:(NSManagedObjectContext *)context startAfter:(NSData*_Nullable)startAfter withCompletion:(void (^)(BOOL success, NSData*_Nullable hasMoreStartAfter, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - DPContract *dashpayContract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; - if (dashpayContract.contractState != DPContractState_Registered) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, nil, @[[NSError errorWithCode:500 localizedDescriptionKey:@"The Dashpay contract is not properly set up"]]); - }); - } - return; - } - NSError *error = nil; - if (![self activePrivateKeysAreLoadedWithFetchingError:&error]) { - //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 - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, nil, @[error ? error : [NSError errorWithCode:500 localizedDescriptionKey:@"The blockchain identity hasn't yet been locally activated"]]); - }); - } - return; - } - __weak typeof(self) weakSelf = self; - [self.DAPINetworkService getDashpayIncomingContactRequestsForUserId:self.uniqueIDData - since:self.lastCheckedIncomingContactsTimestamp ? (self.lastCheckedIncomingContactsTimestamp - HOUR_TIME_INTERVAL) : 0 - startAfter:startAfter - completionQueue:self.identityQueue - success:^(NSArray *_Nonnull documents) { - //todo chance the since parameter - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, nil, @[[NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]]); - }); - } - return; - } - - dispatch_async(self.identityQueue, ^{ - [strongSelf handleContactRequestObjects:documents - context:context - completion:^(BOOL success, NSArray *errors) { - BOOL hasMore = documents.count == DAPI_DOCUMENT_RESPONSE_COUNT_LIMIT; - if (!hasMore) { - [self.platformContext performBlockAndWait:^{ - self.lastCheckedIncomingContactsTimestamp = [[NSDate date] timeIntervalSince1970]; - }]; - } - if (completion) { - NSData * hasMoreStartAfter = documents.lastObject[@"$id"]; - dispatch_async(completionQueue, ^{ - completion(success, hasMoreStartAfter, errors); - }); - } - } - onCompletionQueue:self.identityQueue]; - }); - } - failure:^(NSError *_Nonnull error) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, nil, @[error]); - }); - } - }]; -} - -- (void)fetchOutgoingContactRequests:(void (^)(BOOL success, NSArray *errors))completion { - [self fetchOutgoingContactRequestsInContext:self.platformContext withCompletion:completion onCompletionQueue:dispatch_get_main_queue()]; -} - -- (void)fetchOutgoingContactRequestsInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(BOOL success, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - [self fetchOutgoingContactRequestsInContext:context startAfter:nil retriesLeft:DEFAULT_CONTACT_REQUEST_FETCH_RETRIES withCompletion:completion onCompletionQueue:completionQueue]; -} - -- (void)fetchOutgoingContactRequestsInContext:(NSManagedObjectContext *)context startAfter:(NSData*_Nullable)startAfter retriesLeft:(int32_t)retriesLeft withCompletion:(void (^)(BOOL success, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - [self internalFetchOutgoingContactRequestsInContext:context - startAfter:startAfter - withCompletion:^(BOOL success, NSData*_Nullable hasMoreStartAfter, NSArray *errors) { - if (!success && retriesLeft > 0) { - [self fetchOutgoingContactRequestsInContext:context startAfter:startAfter retriesLeft:retriesLeft - 1 withCompletion:completion onCompletionQueue:completionQueue]; - } else if (success && hasMoreStartAfter) { - [self fetchOutgoingContactRequestsInContext:context startAfter:hasMoreStartAfter retriesLeft:DEFAULT_CONTACT_REQUEST_FETCH_RETRIES withCompletion:completion onCompletionQueue:completionQueue]; - } else if (completion) { - completion(success, errors); - } - } - onCompletionQueue:completionQueue]; -} - -- (void)internalFetchOutgoingContactRequestsInContext:(NSManagedObjectContext *)context startAfter:(NSData*_Nullable)startAfter withCompletion:(void (^)(BOOL success, NSData*_Nullable hasMoreStartAfter, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - DPContract *dashpayContract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; - if (dashpayContract.contractState != DPContractState_Registered) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, nil, @[[NSError errorWithCode:500 localizedDescriptionKey:@"The Dashpay contract is not properly set up"]]); - }); - } - return; - } - NSError *error = nil; - if (![self activePrivateKeysAreLoadedWithFetchingError:&error]) { - //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 - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, nil, @[error ? error : [NSError errorWithCode:500 localizedDescriptionKey:@"The blockchain identity hasn't yet been locally activated"]]); - }); - } - return; - } - __weak typeof(self) weakSelf = self; - [self.DAPINetworkService getDashpayOutgoingContactRequestsForUserId:self.uniqueIDData - since:self.lastCheckedOutgoingContactsTimestamp ? (self.lastCheckedOutgoingContactsTimestamp - HOUR_TIME_INTERVAL) : 0 - startAfter:startAfter - completionQueue:self.identityQueue - success:^(NSArray *_Nonnull documents) { - //todo chance the since parameter - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, nil, @[[NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]]); - }); - } - return; - } - - dispatch_async(self.identityQueue, ^{ - [strongSelf handleContactRequestObjects:documents - context:context - completion:^(BOOL success, NSArray *errors) { - BOOL hasMore = documents.count == DAPI_DOCUMENT_RESPONSE_COUNT_LIMIT; - - if (!hasMore) { - [self.platformContext performBlockAndWait:^{ - self.lastCheckedOutgoingContactsTimestamp = [[NSDate date] timeIntervalSince1970]; - }]; - } - __block NSData * hasMoreStartAfter = success?documents.lastObject[@"$id"]:nil; - dispatch_async(completionQueue, ^{ - - completion(success, hasMoreStartAfter, errors); - }); - } - onCompletionQueue:self.identityQueue]; - }); - } - failure:^(NSError *_Nonnull error) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, nil, @[error]); - }); - } - }]; -} - -// MARK: Response Processing - -- (void)applyProfileChanges:(DSTransientDashpayUser *)transientDashpayUser inContext:(NSManagedObjectContext *)context saveContext:(BOOL)saveContext completion:(void (^)(BOOL success, NSError *error))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - if (![self isActive]) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, [NSError errorWithCode:500 localizedDescriptionKey:@"Identity no longer active in wallet"]); - }); - } - return; - } - __weak typeof(self) weakSelf = self; - dispatch_async(self.identityQueue, ^{ - [context performBlockAndWait:^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - completion(NO, [NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]); - } - return; - } - if (![self isActive]) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, [NSError errorWithCode:500 localizedDescriptionKey:@"Identity no longer active in wallet"]); - }); - } - return; - } - DSDashpayUserEntity *contact = [[self blockchainIdentityEntityInContext:context] matchingDashpayUser]; - if (!contact) { - NSAssert(FALSE, @"It is weird to get here"); - contact = [DSDashpayUserEntity anyObjectInContext:context matching:@"associatedBlockchainIdentity.uniqueID == %@", self.uniqueIDData]; - } - if (!contact || transientDashpayUser.updatedAt > contact.updatedAt) { - if (!contact) { - contact = [DSDashpayUserEntity managedObjectInBlockedContext:context]; - contact.chain = [strongSelf.wallet.chain chainEntityInContext:context]; - contact.associatedBlockchainIdentity = [strongSelf blockchainIdentityEntityInContext:context]; - } - - NSError *error = [contact applyTransientDashpayUser:transientDashpayUser save:saveContext]; - if (error) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, error); - }); - } - return; - } - } - - [self.platformContext performBlockAndWait:^{ - self.lastCheckedProfileTimestamp = [[NSDate date] timeIntervalSince1970]; - //[self saveInContext:self.platformContext]; - }]; - - if (completion) { - dispatch_async(completionQueue, ^{ - completion(YES, nil); - }); - } - }]; - }); -} - -/// Handle an array of contact requests. This method will split contact requests into either incoming contact requests or outgoing contact requests and then call methods for handling them if applicable. -/// @param rawContactRequests A dictionary of rawContactRequests, these are returned by the network. -/// @param context The managed object context in which to process results. -/// @param completion Completion callback with success boolean. -- (void)handleContactRequestObjects:(NSArray *)rawContactRequests context:(NSManagedObjectContext *)context completion:(void (^)(BOOL success, NSArray *errors))completion onCompletionQueue:(dispatch_queue_t)completionQueue { - NSAssert(completionQueue == self.identityQueue, @"we should be on identity queue"); - __block NSMutableArray *incomingNewRequests = [NSMutableArray array]; - __block NSMutableArray *outgoingNewRequests = [NSMutableArray array]; - __block NSMutableArray *rErrors = [NSMutableArray array]; - [context performBlockAndWait:^{ - for (NSDictionary *rawContact in rawContactRequests) { - DSContactRequest *contactRequest = [DSContactRequest contactRequestFromDictionary:rawContact onBlockchainIdentity:self]; - - if (uint256_eq(contactRequest.recipientBlockchainIdentityUniqueId, self.uniqueID)) { - //we are the recipient, this is an incoming request - DSFriendRequestEntity *friendRequest = [DSFriendRequestEntity anyObjectInContext:context matching:@"destinationContact == %@ && sourceContact.associatedBlockchainIdentity.uniqueID == %@", [self matchingDashpayUserInContext:context], uint256_data(contactRequest.senderBlockchainIdentityUniqueId)]; - if (!friendRequest) { - [incomingNewRequests addObject:contactRequest]; - } - } else if (uint256_eq(contactRequest.senderBlockchainIdentityUniqueId, self.uniqueID)) { - //we are the sender, this is an outgoing request - BOOL isNew = ![DSFriendRequestEntity countObjectsInContext:context matching:@"sourceContact == %@ && destinationContact.associatedBlockchainIdentity.uniqueID == %@", [self matchingDashpayUserInContext:context], [NSData dataWithUInt256:contactRequest.recipientBlockchainIdentityUniqueId]]; - if (isNew) { - [outgoingNewRequests addObject:contactRequest]; - } - } else { - //we should not have received this - NSAssert(FALSE, @"the contact request needs to be either outgoing or incoming"); - } - } - }]; - - - __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]; - } - 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, ^{ - if (completion) { - completion(succeeded, [rErrors copy]); - } - }); -} - -- (void)handleIncomingRequests:(NSArray *)incomingRequests - context:(NSManagedObjectContext *)context - completion:(void (^)(BOOL success, NSArray *errors))completion - onCompletionQueue:(dispatch_queue_t)completionQueue { - if (!self.isActive) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, @[[NSError errorWithCode:410 localizedDescriptionKey:@"Identity no longer active in wallet"]]); - }); - } - return; - } - [context performBlockAndWait:^{ - __block BOOL succeeded = YES; - __block NSMutableArray *errors = [NSMutableArray array]; - dispatch_group_t dispatchGroup = dispatch_group_create(); - - for (DSContactRequest *contactRequest in incomingRequests) { - DSBlockchainIdentityEntity *externalBlockchainIdentityEntity = [DSBlockchainIdentityEntity anyObjectInContext:context matching:@"uniqueID == %@", uint256_data(contactRequest.senderBlockchainIdentityUniqueId)]; - if (!externalBlockchainIdentityEntity) { - //no externalBlockchainIdentity exists yet, which means no dashpay user - dispatch_group_enter(dispatchGroup); - DSBlockchainIdentity *senderBlockchainIdentity = [self.identitiesManager foreignBlockchainIdentityWithUniqueId:contactRequest.senderBlockchainIdentityUniqueId createIfMissing:YES inContext:context]; - - [senderBlockchainIdentity fetchNeededNetworkStateInformationInContext:self.platformContext - withCompletion:^(DSBlockchainIdentityQueryStep failureStep, NSArray *_Nullable networkErrors) { - if (!failureStep) { - OpaqueKey *senderPublicKey = [senderBlockchainIdentity keyAtIndex:contactRequest.senderKeyIndex]; - NSData *extendedPublicKeyData = [contactRequest decryptedPublicKeyDataWithKey:senderPublicKey]; - OpaqueKey *extendedPublicKey = [DSKeyManager keyWithExtendedPublicKeyData:extendedPublicKeyData ofType:KeyKind_ECDSA]; - if (!extendedPublicKey) { - succeeded = FALSE; - [errors addObject:[NSError errorWithCode:500 localizedDescriptionKey:@"Incorrect key format after contact request decryption"]]; - } else { - DSDashpayUserEntity *senderDashpayUserEntity = [senderBlockchainIdentity blockchainIdentityEntityInContext:context].matchingDashpayUser; - NSAssert(senderDashpayUserEntity, @"The sender should exist"); - [self addIncomingRequestFromContact:senderDashpayUserEntity - forExtendedPublicKey:extendedPublicKey - atTimestamp:contactRequest.createdAt]; - } - } else { - [errors addObjectsFromArray:networkErrors]; - } - dispatch_group_leave(dispatchGroup); - } - onCompletionQueue:self.identityQueue]; - - } else { - if ([self.chain blockchainIdentityForUniqueId:externalBlockchainIdentityEntity.uniqueID.UInt256]) { - //it's also local (aka both contacts are local to this device), we should store the extended public key for the destination - DSBlockchainIdentity *sourceBlockchainIdentity = [self.chain blockchainIdentityForUniqueId:externalBlockchainIdentityEntity.uniqueID.UInt256]; - - DSAccount *account = [sourceBlockchainIdentity.wallet accountWithNumber:0]; - - DSPotentialOneWayFriendship *potentialFriendship = [[DSPotentialOneWayFriendship alloc] initWithDestinationBlockchainIdentity:self destinationKeyIndex:contactRequest.recipientKeyIndex sourceBlockchainIdentity:sourceBlockchainIdentity sourceKeyIndex:contactRequest.senderKeyIndex account:account]; - - if (![DSFriendRequestEntity existingFriendRequestEntityWithSourceIdentifier:sourceBlockchainIdentity.uniqueID destinationIdentifier:self.uniqueID onAccountIndex:account.accountNumber inContext:context]) { - dispatch_group_enter(dispatchGroup); - [potentialFriendship createDerivationPathAndSaveExtendedPublicKeyWithCompletion:^(BOOL success, DSIncomingFundsDerivationPath *_Nonnull incomingFundsDerivationPath) { - if (success) { - DSDashpayUserEntity *matchingDashpayUserInContext = [self matchingDashpayUserInContext:context]; - DSFriendRequestEntity *friendRequest = [potentialFriendship outgoingFriendRequestForDashpayUserEntity:matchingDashpayUserInContext atTimestamp:contactRequest.createdAt]; - [potentialFriendship storeExtendedPublicKeyAssociatedWithFriendRequest:friendRequest]; - [matchingDashpayUserInContext addIncomingRequestsObject:friendRequest]; - - if ([[friendRequest.sourceContact.incomingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"sourceContact == %@", matchingDashpayUserInContext]] count]) { - [matchingDashpayUserInContext addFriendsObject:friendRequest.sourceContact]; - } - - [account addIncomingDerivationPath:incomingFundsDerivationPath - forFriendshipIdentifier:friendRequest.friendshipIdentifier - inContext:context]; - [context ds_save]; - [self.chain.chainManager.transactionManager updateTransactionsBloomFilter]; - } else { - succeeded = FALSE; - [errors addObject:[NSError errorWithCode:500 localizedDescriptionKey:@"Could not create friendship derivation path"]]; - } - dispatch_group_leave(dispatchGroup); - }]; - } - - } else { - DSBlockchainIdentity *sourceBlockchainIdentity = [[DSBlockchainIdentity alloc] initWithBlockchainIdentityEntity:externalBlockchainIdentityEntity]; - NSAssert(sourceBlockchainIdentity, @"This should not be null"); - if ([sourceBlockchainIdentity activeKeyCount] > 0 && [sourceBlockchainIdentity keyAtIndex:contactRequest.senderKeyIndex]) { - //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 - OpaqueKey *key = [sourceBlockchainIdentity keyAtIndex:contactRequest.senderKeyIndex]; - NSData *decryptedExtendedPublicKeyData = [contactRequest decryptedPublicKeyDataWithKey:key]; - NSAssert(decryptedExtendedPublicKeyData, @"Data should be decrypted"); - OpaqueKey *extendedPublicKey = [DSKeyManager keyWithExtendedPublicKeyData:decryptedExtendedPublicKeyData ofType:KeyKind_ECDSA]; - if (!extendedPublicKey) { - succeeded = FALSE; - [errors addObject:[NSError errorWithCode:500 localizedDescriptionKey:@"Contact request extended public key is incorrectly encrypted."]]; - return; - } - [self addIncomingRequestFromContact:externalBlockchainIdentityEntity.matchingDashpayUser - forExtendedPublicKey:extendedPublicKey - atTimestamp:contactRequest.createdAt]; - - DSDashpayUserEntity *matchingDashpayUserInContext = [self matchingDashpayUserInContext:context]; - if ([[externalBlockchainIdentityEntity.matchingDashpayUser.incomingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"sourceContact == %@", matchingDashpayUserInContext]] count]) { - [matchingDashpayUserInContext addFriendsObject:[externalBlockchainIdentityEntity matchingDashpayUser]]; - [context ds_save]; - } - - } else { - //the blockchain identity is already known, but needs to updated to get the right key, create the incoming friend request, add a friendship if an outgoing friend request also exists - dispatch_group_enter(dispatchGroup); - [sourceBlockchainIdentity fetchNeededNetworkStateInformationInContext:context - withCompletion:^(DSBlockchainIdentityQueryStep failureStep, NSArray *networkStateInformationErrors) { - if (!failureStep) { - [context performBlockAndWait:^{ - OpaqueKey *key = [sourceBlockchainIdentity keyAtIndex:contactRequest.senderKeyIndex]; - NSAssert(key, @"key should be known"); - NSData *decryptedExtendedPublicKeyData = [contactRequest decryptedPublicKeyDataWithKey:key]; - NSAssert(decryptedExtendedPublicKeyData, @"Data should be decrypted"); - OpaqueKey *extendedPublicKey = [DSKeyManager keyWithExtendedPublicKeyData:decryptedExtendedPublicKeyData ofType:KeyKind_ECDSA]; - NSAssert(extendedPublicKey, @"A key should be recovered"); - [self addIncomingRequestFromContact:externalBlockchainIdentityEntity.matchingDashpayUser - forExtendedPublicKey:extendedPublicKey - atTimestamp:contactRequest.createdAt]; - DSDashpayUserEntity *matchingDashpayUserInContext = [self matchingDashpayUserInContext:context]; - if ([[externalBlockchainIdentityEntity.matchingDashpayUser.incomingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"sourceContact == %@", matchingDashpayUserInContext]] count]) { - [matchingDashpayUserInContext addFriendsObject:externalBlockchainIdentityEntity.matchingDashpayUser]; - [context ds_save]; - } - }]; - } else { - succeeded = FALSE; - [errors addObjectsFromArray:networkStateInformationErrors]; - } - dispatch_group_leave(dispatchGroup); - } - onCompletionQueue:self.identityQueue]; - } - } - } - } - - dispatch_group_notify(dispatchGroup, completionQueue, ^{ - if (completion) { - completion(succeeded, [errors copy]); - } - }); - }]; -} - -- (void)addFriendship:(DSPotentialOneWayFriendship *)friendship inContext:(NSManagedObjectContext *)context completion:(void (^)(BOOL success, NSError *error))completion { - //DSFriendRequestEntity * friendRequestEntity = [friendship outgoingFriendRequestForDashpayUserEntity:friendship.destinationBlockchainIdentity.matchingDashpayUser]; - DSFriendRequestEntity *friendRequestEntity = [DSFriendRequestEntity managedObjectInBlockedContext:context]; - friendRequestEntity.sourceContact = [friendship.sourceBlockchainIdentity matchingDashpayUserInContext:context]; - friendRequestEntity.destinationContact = [friendship.destinationBlockchainIdentity matchingDashpayUserInContext:context]; - friendRequestEntity.timestamp = friendship.createdAt; - NSAssert(friendRequestEntity.sourceContact != friendRequestEntity.destinationContact, @"This must be different contacts"); - - DSAccountEntity *accountEntity = [DSAccountEntity accountEntityForWalletUniqueID:self.wallet.uniqueIDString index:0 onChain:self.chain inContext:context]; - - friendRequestEntity.account = accountEntity; - - [friendRequestEntity finalizeWithFriendshipIdentifier]; - - [friendship createDerivationPathAndSaveExtendedPublicKeyWithCompletion:^(BOOL success, DSIncomingFundsDerivationPath *_Nonnull incomingFundsDerivationPath) { - if (!success) { - return; - } - friendRequestEntity.derivationPath = [friendship storeExtendedPublicKeyAssociatedWithFriendRequest:friendRequestEntity inContext:context]; - - DSAccount *account = [self.wallet accountWithNumber:0]; - if (friendship.destinationBlockchainIdentity.isLocal) { //the destination is also local - NSAssert(friendship.destinationBlockchainIdentity.wallet, @"Wallet should be known"); - DSAccount *recipientAccount = [friendship.destinationBlockchainIdentity.wallet accountWithNumber:0]; - NSAssert(recipientAccount, @"Recipient Wallet should exist"); - [recipientAccount addIncomingDerivationPath:incomingFundsDerivationPath forFriendshipIdentifier:friendRequestEntity.friendshipIdentifier inContext:context]; - if (recipientAccount != account) { - [account addOutgoingDerivationPath:incomingFundsDerivationPath forFriendshipIdentifier:friendRequestEntity.friendshipIdentifier inContext:context]; - } - } else { - //todo update outgoing derivation paths to incoming derivation paths as blockchain users come in - [account addIncomingDerivationPath:incomingFundsDerivationPath forFriendshipIdentifier:friendRequestEntity.friendshipIdentifier inContext:context]; - } - - NSAssert(friendRequestEntity.derivationPath, @"derivation path must be present"); - - DSDashpayUserEntity *dashpayUserInChildContext = [self matchingDashpayUserInContext:context]; - - [dashpayUserInChildContext addOutgoingRequestsObject:friendRequestEntity]; - - if ([[[friendship.destinationBlockchainIdentity matchingDashpayUserInContext:context].outgoingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"destinationContact == %@", dashpayUserInChildContext]] count]) { - [dashpayUserInChildContext addFriendsObject:[friendship.destinationBlockchainIdentity matchingDashpayUserInContext:context]]; - } - NSError *savingError = [context ds_save]; - [self.chain.chainManager.transactionManager updateTransactionsBloomFilter]; - if (completion) { - completion(savingError ? NO : YES, savingError); - } - }]; -} - -- (void)addFriendshipFromSourceBlockchainIdentity:(DSBlockchainIdentity *)sourceBlockchainIdentity sourceKeyIndex:(uint32_t)sourceKeyIndex toRecipientBlockchainIdentity:(DSBlockchainIdentity *)recipientBlockchainIdentity recipientKeyIndex:(uint32_t)recipientKeyIndex atTimestamp:(NSTimeInterval)timestamp inContext:(NSManagedObjectContext *)context { - [context performBlockAndWait:^{ - DSAccount *account = [self.wallet accountWithNumber:0]; - - DSPotentialOneWayFriendship *realFriendship = [[DSPotentialOneWayFriendship alloc] initWithDestinationBlockchainIdentity:recipientBlockchainIdentity destinationKeyIndex:recipientKeyIndex sourceBlockchainIdentity:self sourceKeyIndex:sourceKeyIndex account:account createdAt:timestamp]; - - if (![DSFriendRequestEntity existingFriendRequestEntityWithSourceIdentifier:self.uniqueID destinationIdentifier:recipientBlockchainIdentity.uniqueID onAccountIndex:account.accountNumber inContext:context]) { - //it was probably added already - //this could happen when have 2 blockchain identities in same wallet - //Identity A gets outgoing contacts - //Which are the same as Identity B incoming contacts, no need to add the friendships twice - [self addFriendship:realFriendship inContext:context completion:nil]; - } - }]; -} - -- (void)handleOutgoingRequests:(NSArray *)outgoingRequests - context:(NSManagedObjectContext *)context - completion:(void (^)(BOOL success, NSArray *errors))completion - onCompletionQueue:(dispatch_queue_t)completionQueue { - if (!self.isActive) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, @[[NSError errorWithCode:410 localizedDescriptionKey:@"Identity no longer active in wallet"]]); - }); - } - return; - } - [context performBlockAndWait:^{ - __block NSMutableArray *errors = [NSMutableArray array]; - - __block BOOL succeeded = YES; - dispatch_group_t dispatchGroup = dispatch_group_create(); - - for (DSContactRequest *contactRequest in outgoingRequests) { - DSBlockchainIdentityEntity *recipientBlockchainIdentityEntity = [DSBlockchainIdentityEntity anyObjectInContext:context matching:@"uniqueID == %@", uint256_data(contactRequest.recipientBlockchainIdentityUniqueId)]; - if (!recipientBlockchainIdentityEntity) { - //no contact exists yet - dispatch_group_enter(dispatchGroup); - DSBlockchainIdentity *recipientBlockchainIdentity = [self.identitiesManager foreignBlockchainIdentityWithUniqueId:contactRequest.recipientBlockchainIdentityUniqueId createIfMissing:YES inContext:context]; - NSAssert([recipientBlockchainIdentity blockchainIdentityEntityInContext:context], @"Entity should now exist"); - [recipientBlockchainIdentity fetchNeededNetworkStateInformationInContext:context - withCompletion:^(DSBlockchainIdentityQueryStep failureStep, NSArray *_Nullable networkErrors) { - if (!failureStep) { - [self addFriendshipFromSourceBlockchainIdentity:self sourceKeyIndex:contactRequest.senderKeyIndex toRecipientBlockchainIdentity:recipientBlockchainIdentity recipientKeyIndex:contactRequest.recipientKeyIndex atTimestamp:contactRequest.createdAt inContext:context]; - } else { - succeeded = FALSE; - [errors addObjectsFromArray:networkErrors]; - } - dispatch_group_leave(dispatchGroup); - } - onCompletionQueue:self.identityQueue]; - } else { - //the recipient blockchain identity is already known, meaning they had made a friend request to us before, and on another device we had accepted - //or the recipient blockchain identity is also local to the device - - DSWallet *recipientWallet = nil; - DSBlockchainIdentity *recipientBlockchainIdentity = [self.chain blockchainIdentityForUniqueId:recipientBlockchainIdentityEntity.uniqueID.UInt256 foundInWallet:&recipientWallet]; - BOOL isLocal = TRUE; - if (!recipientBlockchainIdentity) { - //this is not local - recipientBlockchainIdentity = [[DSBlockchainIdentity alloc] initWithBlockchainIdentityEntity:recipientBlockchainIdentityEntity]; - isLocal = FALSE; - } - - dispatch_group_enter(dispatchGroup); - [recipientBlockchainIdentity fetchIfNeededNetworkStateInformation:DSBlockchainIdentityQueryStep_Profile & DSBlockchainIdentityQueryStep_Username & DSBlockchainIdentityQueryStep_Identity - inContext:context - withCompletion:^(DSBlockchainIdentityQueryStep failureStep, NSArray *_Nullable networkErrors) { - if (!failureStep) { - [self addFriendshipFromSourceBlockchainIdentity:self sourceKeyIndex:contactRequest.senderKeyIndex toRecipientBlockchainIdentity:recipientBlockchainIdentity recipientKeyIndex:contactRequest.recipientKeyIndex atTimestamp:contactRequest.createdAt inContext:context]; - } else { - succeeded = FALSE; - [errors addObjectsFromArray:networkErrors]; - } - dispatch_group_leave(dispatchGroup); - } - onCompletionQueue:self.identityQueue]; - } - } - - dispatch_group_notify(dispatchGroup, completionQueue, ^{ - if (completion) { - completion(succeeded, [errors copy]); - } - }); - }]; -} - -- (void)addIncomingRequestFromContact:(DSDashpayUserEntity *)dashpayUserEntity - forExtendedPublicKey:(OpaqueKey *)extendedPublicKey - atTimestamp:(NSTimeInterval)timestamp { - NSManagedObjectContext *context = dashpayUserEntity.managedObjectContext; - DSFriendRequestEntity *friendRequestEntity = [DSFriendRequestEntity managedObjectInBlockedContext:context]; - friendRequestEntity.sourceContact = dashpayUserEntity; - friendRequestEntity.destinationContact = [self matchingDashpayUserInContext:dashpayUserEntity.managedObjectContext]; - friendRequestEntity.timestamp = timestamp; - NSAssert(friendRequestEntity.sourceContact != friendRequestEntity.destinationContact, @"This must be different contacts"); - - DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity managedObjectInBlockedContext:context]; - derivationPathEntity.chain = [self.chain chainEntityInContext:context]; - - friendRequestEntity.derivationPath = derivationPathEntity; - - NSAssert(friendRequestEntity.derivationPath, @"There must be a derivation path"); - - DSAccount *account = [self.wallet accountWithNumber:0]; - - DSAccountEntity *accountEntity = [DSAccountEntity accountEntityForWalletUniqueID:self.wallet.uniqueIDString index:account.accountNumber onChain:self.chain inContext:dashpayUserEntity.managedObjectContext]; - - derivationPathEntity.account = accountEntity; - - friendRequestEntity.account = accountEntity; - - [friendRequestEntity finalizeWithFriendshipIdentifier]; - - //NSLog(@"->created derivation path entity %@ %@", friendRequestEntity.friendshipIdentifier.hexString, [NSThread callStackSymbols]); - - DSIncomingFundsDerivationPath *derivationPath = [DSIncomingFundsDerivationPath externalDerivationPathWithExtendedPublicKey:extendedPublicKey withDestinationBlockchainIdentityUniqueId:[self matchingDashpayUserInContext:dashpayUserEntity.managedObjectContext].associatedBlockchainIdentity.uniqueID.UInt256 sourceBlockchainIdentityUniqueId:dashpayUserEntity.associatedBlockchainIdentity.uniqueID.UInt256 onChain:self.chain]; - - derivationPathEntity.publicKeyIdentifier = derivationPath.standaloneExtendedPublicKeyUniqueID; - - [derivationPath storeExternalDerivationPathExtendedPublicKeyToKeyChain]; - - //incoming request uses an outgoing derivation path - [account addOutgoingDerivationPath:derivationPath forFriendshipIdentifier:friendRequestEntity.friendshipIdentifier inContext:dashpayUserEntity.managedObjectContext]; - - DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:dashpayUserEntity.managedObjectContext]; - [matchingDashpayUser addIncomingRequestsObject:friendRequestEntity]; - - if ([[friendRequestEntity.sourceContact.incomingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"sourceContact == %@", matchingDashpayUser]] count]) { - [matchingDashpayUser addFriendsObject:friendRequestEntity.sourceContact]; - } - - [context ds_save]; - [self.chain.chainManager.transactionManager updateTransactionsBloomFilter]; -} - -// MARK: - Persistence - -// MARK: Saving - -- (void)saveInitial { - [self saveInitialInContext:self.platformContext]; -} - -- (DSBlockchainIdentityEntity *)initialEntityInContext:(NSManagedObjectContext *)context { - DSChainEntity *chainEntity = [self.chain chainEntityInContext:context]; - - DSBlockchainIdentityEntity *entity = [DSBlockchainIdentityEntity managedObjectInBlockedContext:context]; - entity.uniqueID = uint256_data(self.uniqueID); - entity.isLocal = self.isLocal; - entity.registrationStatus = self.registrationStatus; - if (self.isLocal) { - NSData *transactionHash = uint256_data(self.registrationCreditFundingTransaction.txHash); - DSCreditFundingTransactionEntity *transactionEntity = (DSCreditFundingTransactionEntity *)[DSTransactionEntity anyObjectInContext:context matching:@"transactionHash.txHash == %@", transactionHash]; - entity.registrationFundingTransaction = transactionEntity; - } - entity.chain = chainEntity; - for (NSString *usernameFullPath in self.usernameStatuses) { - DSBlockchainIdentityUsernameEntity *usernameEntity = [DSBlockchainIdentityUsernameEntity managedObjectInBlockedContext:context]; - usernameEntity.status = [self statusOfUsernameFullPath:usernameFullPath]; - usernameEntity.stringValue = [self usernameOfUsernameFullPath:usernameFullPath]; - usernameEntity.domain = [self domainOfUsernameFullPath:usernameFullPath]; - usernameEntity.blockchainIdentity = entity; - [entity addUsernamesObject:usernameEntity]; - [entity setDashpayUsername:usernameEntity]; - } - - for (NSNumber *index in self.keyInfoDictionaries) { - NSDictionary *keyDictionary = self.keyInfoDictionaries[index]; - DSBlockchainIdentityKeyStatus status = [keyDictionary[@(DSBlockchainIdentityKeyDictionary_KeyStatus)] unsignedIntValue]; - KeyKind keyType = [keyDictionary[@(DSBlockchainIdentityKeyDictionary_KeyType)] unsignedIntValue]; - NSValue *key = keyDictionary[@(DSBlockchainIdentityKeyDictionary_Key)]; - DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:keyType]; - const NSUInteger indexes[] = {_index, index.unsignedIntegerValue}; - NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; - [self createNewKey:key.pointerValue forIdentityEntity:entity atPath:indexPath withStatus:status fromDerivationPath:derivationPath inContext:context]; - } - - DSDashpayUserEntity *dashpayUserEntity = [DSDashpayUserEntity managedObjectInBlockedContext:context]; - dashpayUserEntity.chain = chainEntity; - entity.matchingDashpayUser = dashpayUserEntity; - - if (self.isOutgoingInvitation) { - DSBlockchainInvitationEntity *blockchainInvitationEntity = [DSBlockchainInvitationEntity managedObjectInBlockedContext:context]; - blockchainInvitationEntity.chain = chainEntity; - entity.associatedInvitation = blockchainInvitationEntity; - } - - return entity; -} - -- (void)saveInitialInContext:(NSManagedObjectContext *)context { - if (self.isTransient) return; - //no need for active check, in fact it will cause an infinite loop - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *entity = [self initialEntityInContext:context]; - DSDashpayUserEntity *dashpayUserEntity = entity.matchingDashpayUser; - - [context ds_saveInBlockAndWait]; - [[NSManagedObjectContext viewContext] performBlockAndWait:^{ - self.matchingDashpayUserInViewContext = [[NSManagedObjectContext viewContext] objectWithID:dashpayUserEntity.objectID]; - }]; - [[NSManagedObjectContext platformContext] performBlockAndWait:^{ - self.matchingDashpayUserInPlatformContext = [[NSManagedObjectContext platformContext] objectWithID:dashpayUserEntity.objectID]; - }]; - if ([self isLocal]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSBlockchainIdentityDidUpdateNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSBlockchainIdentityKey: self}]; - }); - } - }]; -} - -- (void)saveInContext:(NSManagedObjectContext *)context { - if (self.isTransient) return; - if (!self.isActive) return; - [context performBlockAndWait:^{ - BOOL changeOccured = NO; - NSMutableArray *updateEvents = [NSMutableArray array]; - DSBlockchainIdentityEntity *entity = [self blockchainIdentityEntityInContext:context]; - if (entity.creditBalance != self.creditBalance) { - entity.creditBalance = self.creditBalance; - changeOccured = YES; - [updateEvents addObject:DSBlockchainIdentityUpdateEventCreditBalance]; - } - if (entity.registrationStatus != self.registrationStatus) { - entity.registrationStatus = self.registrationStatus; - changeOccured = YES; - [updateEvents addObject:DSBlockchainIdentityUpdateEventRegistration]; - } - - if (!uint256_eq(entity.dashpaySyncronizationBlockHash.UInt256, self.dashpaySyncronizationBlockHash)) { - entity.dashpaySyncronizationBlockHash = uint256_data(self.dashpaySyncronizationBlockHash); - changeOccured = YES; - [updateEvents addObject:DSBlockchainIdentityUpdateEventDashpaySyncronizationBlockHash]; - } - - if (entity.lastCheckedUsernamesTimestamp != self.lastCheckedUsernamesTimestamp) { - entity.lastCheckedUsernamesTimestamp = self.lastCheckedUsernamesTimestamp; - changeOccured = YES; - } - - if (entity.lastCheckedProfileTimestamp != self.lastCheckedProfileTimestamp) { - entity.lastCheckedProfileTimestamp = self.lastCheckedProfileTimestamp; - changeOccured = YES; - } - - if (entity.lastCheckedIncomingContactsTimestamp != self.lastCheckedIncomingContactsTimestamp) { - entity.lastCheckedIncomingContactsTimestamp = self.lastCheckedIncomingContactsTimestamp; - changeOccured = YES; - } - - if (entity.lastCheckedOutgoingContactsTimestamp != self.lastCheckedOutgoingContactsTimestamp) { - entity.lastCheckedOutgoingContactsTimestamp = self.lastCheckedOutgoingContactsTimestamp; - changeOccured = YES; - } - - if (changeOccured) { - [context ds_save]; - if (updateEvents.count) { - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSBlockchainIdentityDidUpdateNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSBlockchainIdentityKey: self, DSBlockchainIdentityUpdateEvents: updateEvents}]; - }); - } - } - }]; -} - -- (NSString *)identifierForKeyAtPath:(NSIndexPath *)path fromDerivationPath:(DSDerivationPath *)derivationPath { - NSIndexPath *softenedPath = [path softenAllItems]; - return [NSString stringWithFormat:@"%@-%@-%@", self.uniqueIdString, derivationPath.standaloneExtendedPublicKeyUniqueID, [softenedPath indexPathString]]; -} - -- (BOOL)createNewKey:(OpaqueKey *)key forIdentityEntity:(DSBlockchainIdentityEntity *)blockchainIdentityEntity atPath:(NSIndexPath *)path withStatus:(DSBlockchainIdentityKeyStatus)status fromDerivationPath:(DSDerivationPath *)derivationPath inContext:(NSManagedObjectContext *)context { - NSAssert(blockchainIdentityEntity, @"Entity should be present"); - DSDerivationPathEntity *derivationPathEntity = [derivationPath derivationPathEntityInContext:context]; - NSUInteger count = [DSBlockchainIdentityKeyPathEntity countObjectsInContext:context matching:@"blockchainIdentity == %@ && derivationPath == %@ && path == %@", blockchainIdentityEntity, derivationPathEntity, path]; - if (!count) { - DSBlockchainIdentityKeyPathEntity *blockchainIdentityKeyPathEntity = [DSBlockchainIdentityKeyPathEntity managedObjectInBlockedContext:context]; - blockchainIdentityKeyPathEntity.derivationPath = derivationPathEntity; - blockchainIdentityKeyPathEntity.keyType = key->tag; - blockchainIdentityKeyPathEntity.keyStatus = status; - NSData *privateKeyData = [DSKeyManager privateKeyData:key]; - if (privateKeyData) { - setKeychainData(privateKeyData, [self identifierForKeyAtPath:path fromDerivationPath:derivationPath], YES); -#if DEBUG - DSLogPrivate(@"Saving key at %@ for user %@", [self identifierForKeyAtPath:path fromDerivationPath:derivationPath], self.currentDashpayUsername); -#else - DSLog(@"Saving key at %@ for user %@", @"", @""); -#endif - } else { - OpaqueKey *privateKey = [self derivePrivateKeyAtIndexPath:path ofType:(int16_t) key->tag]; - NSAssert([DSKeyManager keysPublicKeyDataIsEqual:privateKey key2:key], @"The keys don't seem to match up"); - NSData *privateKeyData = [DSKeyManager privateKeyData:privateKey]; - NSAssert(privateKeyData, @"Private key data should exist"); - setKeychainData(privateKeyData, [self identifierForKeyAtPath:path fromDerivationPath:derivationPath], YES); -#if DEBUG - DSLogPrivate(@"Saving key after rederivation %@ for user %@", [self identifierForKeyAtPath:path fromDerivationPath:derivationPath], self.currentDashpayUsername ? self.currentDashpayUsername : self.uniqueIdString); -#else - DSLog(@"Saving key after rederivation %@ for user %@", @"", @""); -#endif - } - - blockchainIdentityKeyPathEntity.path = path; - blockchainIdentityKeyPathEntity.publicKeyData = [DSKeyManager publicKeyData:key]; - blockchainIdentityKeyPathEntity.keyID = (uint32_t)[path indexAtPosition:path.length - 1]; - [blockchainIdentityEntity addKeyPathsObject:blockchainIdentityKeyPathEntity]; - return YES; - } else { -#if DEBUG - DSLogPrivate(@"Already had saved this key %@", path); -#else - DSLog(@"Already had saved this key %@", @""); -#endif - return NO; //no need to save the context - } -} - -- (void)saveNewKey:(OpaqueKey *)key atPath:(NSIndexPath *)path withStatus:(DSBlockchainIdentityKeyStatus)status fromDerivationPath:(DSDerivationPath *)derivationPath inContext:(NSManagedObjectContext *)context { - NSAssert(self.isLocal, @"This should only be called on local blockchain identities"); - if (!self.isLocal) return; - if (self.isTransient) return; - if (!self.isActive) return; - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *blockchainIdentityEntity = [self blockchainIdentityEntityInContext:context]; - if ([self createNewKey:key forIdentityEntity:blockchainIdentityEntity atPath:path withStatus:status fromDerivationPath:derivationPath inContext:context]) { - [context ds_save]; - } - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSBlockchainIdentityDidUpdateNotification - object:nil - userInfo:@{DSChainManagerNotificationChainKey: self.chain, - DSBlockchainIdentityKey: self, - DSBlockchainIdentityUpdateEvents: @[DSBlockchainIdentityUpdateEventKeyUpdate]}]; - }); - }]; -} - -- (void)saveNewRemoteIdentityKey:(OpaqueKey *)key forKeyWithIndexID:(uint32_t)keyID withStatus:(DSBlockchainIdentityKeyStatus)status inContext:(NSManagedObjectContext *)context { - NSAssert(self.isLocal == FALSE, @"This should only be called on non local blockchain identities"); - if (self.isLocal) return; - if (self.isTransient) return; - if (!self.isActive) return; - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *blockchainIdentityEntity = [self blockchainIdentityEntityInContext:context]; - NSUInteger count = [DSBlockchainIdentityKeyPathEntity countObjectsInContext:context matching:@"blockchainIdentity == %@ && keyID == %@", blockchainIdentityEntity, @(keyID)]; - if (!count) { - DSBlockchainIdentityKeyPathEntity *blockchainIdentityKeyPathEntity = [DSBlockchainIdentityKeyPathEntity managedObjectInBlockedContext:context]; - blockchainIdentityKeyPathEntity.keyType = key->tag; - blockchainIdentityKeyPathEntity.keyStatus = status; - blockchainIdentityKeyPathEntity.keyID = keyID; - blockchainIdentityKeyPathEntity.publicKeyData = [DSKeyManager publicKeyData:key]; - [blockchainIdentityEntity addKeyPathsObject:blockchainIdentityKeyPathEntity]; - [context ds_save]; - } - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSBlockchainIdentityDidUpdateNotification - object:nil - userInfo:@{DSChainManagerNotificationChainKey: self.chain, - DSBlockchainIdentityKey: self, - DSBlockchainIdentityUpdateEvents: @[DSBlockchainIdentityUpdateEventKeyUpdate]}]; - }); - }]; -} - - -- (void)updateStatus:(DSBlockchainIdentityKeyStatus)status forKeyAtPath:(NSIndexPath *)path fromDerivationPath:(DSDerivationPath *)derivationPath inContext:(NSManagedObjectContext *)context { - NSAssert(self.isLocal, @"This should only be called on local blockchain identities"); - if (!self.isLocal) return; - if (self.isTransient) return; - if (!self.isActive) return; - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *entity = [self blockchainIdentityEntityInContext:context]; - DSDerivationPathEntity *derivationPathEntity = [derivationPath derivationPathEntityInContext:context]; - DSBlockchainIdentityKeyPathEntity *blockchainIdentityKeyPathEntity = [[DSBlockchainIdentityKeyPathEntity objectsInContext:context matching:@"blockchainIdentity == %@ && derivationPath == %@ && path == %@", entity, derivationPathEntity, path] firstObject]; - if (blockchainIdentityKeyPathEntity && (blockchainIdentityKeyPathEntity.keyStatus != status)) { - blockchainIdentityKeyPathEntity.keyStatus = status; - [context ds_save]; - } - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSBlockchainIdentityDidUpdateNotification - object:nil - userInfo:@{DSChainManagerNotificationChainKey: self.chain, - DSBlockchainIdentityKey: self, - DSBlockchainIdentityUpdateEvents: @[DSBlockchainIdentityUpdateEventKeyUpdate]}]; - }); - }]; -} - -- (void)updateStatus:(DSBlockchainIdentityKeyStatus)status forKeyWithIndexID:(uint32_t)keyID inContext:(NSManagedObjectContext *)context { - NSAssert(self.isLocal == FALSE, @"This should only be called on non local blockchain identities"); - if (self.isLocal) return; - if (self.isTransient) return; - if (!self.isActive) return; - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *entity = [self blockchainIdentityEntityInContext:context]; - DSBlockchainIdentityKeyPathEntity *blockchainIdentityKeyPathEntity = [[DSBlockchainIdentityKeyPathEntity objectsInContext:context matching:@"blockchainIdentity == %@ && derivationPath == NULL && keyID == %@", entity, @(keyID)] firstObject]; - if (blockchainIdentityKeyPathEntity) { - DSBlockchainIdentityKeyPathEntity *blockchainIdentityKeyPathEntity = [DSBlockchainIdentityKeyPathEntity managedObjectInBlockedContext:context]; - blockchainIdentityKeyPathEntity.keyStatus = status; - [context ds_save]; - } - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSBlockchainIdentityDidUpdateNotification - object:nil - userInfo:@{DSChainManagerNotificationChainKey: self.chain, - DSBlockchainIdentityKey: self, - DSBlockchainIdentityUpdateEvents: @[DSBlockchainIdentityUpdateEventKeyUpdate]}]; - }); - }]; -} - -- (void)saveNewUsername:(NSString *)username inDomain:(NSString *)domain status:(DSBlockchainIdentityUsernameStatus)status inContext:(NSManagedObjectContext *)context { - NSAssert([username containsString:@"."] == FALSE, @"This is most likely an error"); - NSAssert(domain, @"Domain must not be nil"); - if (self.isTransient) return; - if (!self.isActive) return; - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *entity = [self blockchainIdentityEntityInContext:context]; - DSBlockchainIdentityUsernameEntity *usernameEntity = [DSBlockchainIdentityUsernameEntity managedObjectInBlockedContext:context]; - usernameEntity.status = status; - usernameEntity.stringValue = username; - usernameEntity.salt = [self saltForUsernameFullPath:[self fullPathForUsername:username inDomain:domain] saveSalt:NO inContext:context]; - usernameEntity.domain = domain; - [entity addUsernamesObject:usernameEntity]; - [entity setDashpayUsername:usernameEntity]; - [context ds_save]; - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSBlockchainIdentityDidUpdateUsernameStatusNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSBlockchainIdentityKey: self}]; - }); - }]; -} - -- (void)setUsernameFullPaths:(NSArray *)usernameFullPaths toStatus:(DSBlockchainIdentityUsernameStatus)status { - for (NSString *string in usernameFullPaths) { - NSMutableDictionary *usernameStatusDictionary = [[self.usernameStatuses objectForKey:string] mutableCopy]; - if (!usernameStatusDictionary) { - usernameStatusDictionary = [NSMutableDictionary dictionary]; - } - usernameStatusDictionary[BLOCKCHAIN_USERNAME_STATUS] = @(status); - [self.usernameStatuses setObject:[usernameStatusDictionary copy] forKey:string]; - } -} - -- (void)setAndSaveUsernameFullPaths:(NSArray *)usernameFullPaths toStatus:(DSBlockchainIdentityUsernameStatus)status inContext:(NSManagedObjectContext *)context { - [self setUsernameFullPaths:usernameFullPaths toStatus:status]; - [self saveUsernamesInDictionary:[self.usernameStatuses dictionaryWithValuesForKeys:usernameFullPaths] toStatus:status inContext:context]; -} - -- (void)saveUsernameFullPaths:(NSArray *)usernameFullPaths toStatus:(DSBlockchainIdentityUsernameStatus)status inContext:(NSManagedObjectContext *)context { - [self saveUsernamesInDictionary:[self.usernameStatuses dictionaryWithValuesForKeys:usernameFullPaths] toStatus:status inContext:context]; -} - -- (void)saveUsernamesInDictionary:(NSDictionary *)fullPathUsernamesDictionary toStatus:(DSBlockchainIdentityUsernameStatus)status inContext:(NSManagedObjectContext *)context { - if (self.isTransient) return; - if (!self.isActive) return; - [context performBlockAndWait:^{ - for (NSString *fullPathUsername in fullPathUsernamesDictionary) { - NSString *username = [fullPathUsernamesDictionary[fullPathUsername] objectForKey:BLOCKCHAIN_USERNAME_PROPER]; - NSString *domain = [fullPathUsernamesDictionary[fullPathUsername] objectForKey:BLOCKCHAIN_USERNAME_DOMAIN]; - [self saveUsername:username inDomain:domain status:status salt:nil commitSave:NO inContext:context]; - } - [context ds_save]; - }]; -} - -//-(void)saveUsernamesToStatuses:(NSDictionary*)dictionary { -// if (self.isTransient) return; -// [self.managedObjectContext performBlockAndWait:^{ -// for (NSString * username in statusDictionary) { -// DSBlockchainIdentityUsernameStatus status = [statusDictionary[username] intValue]; -// NSString * domain = domainDictionary[username]; -// [self saveUsername:username inDomain:domain status:status salt:nil commitSave:NO]; -// } -// [self.managedObjectContext ds_save]; -// }]; -//} - -- (void)saveUsernameFullPath:(NSString *)usernameFullPath status:(DSBlockchainIdentityUsernameStatus)status salt:(NSData *)salt commitSave:(BOOL)commitSave inContext:(NSManagedObjectContext *)context { - if (self.isTransient) return; - if (!self.isActive) return; - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *entity = [self blockchainIdentityEntityInContext:context]; - NSSet *usernamesPassingTest = [entity.usernames objectsPassingTest:^BOOL(DSBlockchainIdentityUsernameEntity *_Nonnull obj, BOOL *_Nonnull stop) { - if ([[self fullPathForUsername:obj.stringValue inDomain:obj.domain] isEqualToString:usernameFullPath]) { - *stop = TRUE; - return TRUE; - - } else { - return FALSE; - } - }]; - 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]; - } - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSBlockchainIdentityDidUpdateUsernameStatusNotification object:nil userInfo:@{ - DSChainManagerNotificationChainKey: self.chain, - DSBlockchainIdentityKey: self, - DSBlockchainIdentityUsernameKey: usernameEntity.stringValue, - DSBlockchainIdentityUsernameDomainKey: usernameEntity.stringValue}]; - }); - } - }]; -} - -- (void)saveUsername:(NSString *)username inDomain:(NSString *)domain status:(DSBlockchainIdentityUsernameStatus)status salt:(NSData *)salt commitSave:(BOOL)commitSave inContext:(NSManagedObjectContext *)context { - if (self.isTransient) return; - if (!self.isActive) return; - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *entity = [self blockchainIdentityEntityInContext:context]; - NSSet *usernamesPassingTest = [entity.usernames objectsPassingTest:^BOOL(DSBlockchainIdentityUsernameEntity *_Nonnull obj, BOOL *_Nonnull stop) { - if ([obj.stringValue isEqualToString:username]) { - *stop = TRUE; - return TRUE; - - } else { - return FALSE; - } - }]; - 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]; - } - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSBlockchainIdentityDidUpdateUsernameStatusNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSBlockchainIdentityKey: self, DSBlockchainIdentityUsernameKey: username, DSBlockchainIdentityUsernameDomainKey: domain}]; - }); - } - }]; -} - -// MARK: Deletion - -- (void)deletePersistentObjectAndSave:(BOOL)save inContext:(NSManagedObjectContext *)context { - [context performBlockAndWait:^{ - DSBlockchainIdentityEntity *blockchainIdentityEntity = [self blockchainIdentityEntityInContext:context]; - if (blockchainIdentityEntity) { - NSSet *friendRequests = [blockchainIdentityEntity.matchingDashpayUser outgoingRequests]; - for (DSFriendRequestEntity *friendRequest in friendRequests) { - uint32_t accountNumber = friendRequest.account.index; - DSAccount *account = [self.wallet accountWithNumber:accountNumber]; - [account removeIncomingDerivationPathForFriendshipWithIdentifier:friendRequest.friendshipIdentifier]; - } - [blockchainIdentityEntity deleteObjectAndWait]; - if (save) { - [context ds_save]; - } - } - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSBlockchainIdentityDidUpdateNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSBlockchainIdentityKey: self}]; - }); - }]; -} - -// MARK: Entity - -- (DSBlockchainIdentityEntity *)blockchainIdentityEntity { - return [self blockchainIdentityEntityInContext:[NSManagedObjectContext viewContext]]; -} - -- (DSBlockchainIdentityEntity *)blockchainIdentityEntityInContext:(NSManagedObjectContext *)context { - __block DSBlockchainIdentityEntity *entity = nil; - [context performBlockAndWait:^{ - entity = [DSBlockchainIdentityEntity anyObjectInContext:context matching:@"uniqueID == %@", self.uniqueIDData]; - }]; - NSAssert(entity, @"An entity should always be found"); - return entity; -} - - -//-(DSBlockchainIdentityRegistrationTransition*)blockchainIdentityRegistrationTransition { -// if (!_blockchainIdentityRegistrationTransition) { -// _blockchainIdentityRegistrationTransition = (DSBlockchainIdentityRegistrationTransition*)[self.wallet.specialTransactionsHolder transactionForHash:self.registrationTransitionHash]; -// } -// return _blockchainIdentityRegistrationTransition; -//} - -//-(UInt256)lastTransitionHash { -// //this is not effective, do this locally in the future -// return [[self allTransitions] lastObject].transitionHash; -//} - - -- (NSString *)debugDescription { - return [[super debugDescription] stringByAppendingString:[NSString stringWithFormat:@" {%@-%@}", self.currentDashpayUsername, self.uniqueIdString]]; -} - -@end diff --git a/DashSync/shared/Models/Identity/DSBlockchainInvitation+Protected.h b/DashSync/shared/Models/Identity/DSBlockchainInvitation+Protected.h deleted file mode 100644 index 8e9265edb..000000000 --- a/DashSync/shared/Models/Identity/DSBlockchainInvitation+Protected.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// Created by Samuel Westrich -// Copyright © 2564 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "BigIntTypes.h" -#import "DSBlockchainInvitation.h" -#import "DSBlockchainInvitationEntity+CoreDataClass.h" - -NS_ASSUME_NONNULL_BEGIN - -@class DSChain; - -@interface DSBlockchainInvitation (Protected) - -- (instancetype)initAtIndex:(uint32_t)index withLockedOutpoint:(DSUTXO)lockedOutpoint inWallet:(DSWallet *)wallet; - -- (instancetype)initAtIndex:(uint32_t)index withLockedOutpoint:(DSUTXO)lockedOutpoint inWallet:(DSWallet *)wallet withBlockchainInvitationEntity:(DSBlockchainInvitationEntity *)blockchainInvitationEntity; - -- (instancetype)initWithUniqueId:(UInt256)uniqueId isTransient:(BOOL)isTransient onChain:(DSChain *)chain; - -- (instancetype)initAtIndex:(uint32_t)index inWallet:(DSWallet *)wallet; - -- (instancetype)initAtIndex:(uint32_t)index withFundingTransaction:(DSCreditFundingTransaction *)transaction inWallet:(DSWallet *)wallet; - -- (void)registerInWalletForBlockchainIdentityUniqueId:(UInt256)blockchainIdentityUniqueId; -- (void)registerInWalletForRegistrationFundingTransaction:(DSCreditFundingTransaction *)fundingTransaction; - -- (void)deletePersistentObjectAndSave:(BOOL)save inContext:(NSManagedObjectContext *)context; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Identity/DSBlockchainInvitation.m b/DashSync/shared/Models/Identity/DSBlockchainInvitation.m deleted file mode 100644 index 0a19369b1..000000000 --- a/DashSync/shared/Models/Identity/DSBlockchainInvitation.m +++ /dev/null @@ -1,457 +0,0 @@ -// -// Created by Samuel Westrich -// Copyright © 2564 Dash Core Group. All rights reserved. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/MIT -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "DSBlockchainInvitation.h" -#import "DSAuthenticationManager.h" -#import "DSBlockchainIdentity+Protected.h" -#import "DSBlockchainInvitationEntity+CoreDataClass.h" -#import "DSChainManager.h" -#import "DSCreditFundingDerivationPath.h" -#import "DSCreditFundingTransaction.h" -#import "DSDAPICoreNetworkService.h" -#import "DSDerivationPathFactory.h" -#import "DSIdentitiesManager+Protected.h" -#import "DSInstantSendTransactionLock.h" -#import "DSWallet.h" -#import "NSData+DSHash.h" -#import "NSError+Dash.h" -#import "NSManagedObject+Sugar.h" -#import "NSManagedObjectContext+DSSugar.h" -#import "NSString+Dash.h" - -@interface DSBlockchainInvitation () - -@property (nonatomic, weak) DSWallet *wallet; -@property (nonatomic, strong) DSChain *chain; -@property (nonatomic, copy) NSString *link; -@property (nonatomic, strong) DSBlockchainIdentity *identity; -@property (nonatomic, assign) BOOL isTransient; -@property (nonatomic, assign) BOOL needsIdentityRetrieval; -@property (nonatomic, assign) BOOL createdLocally; - -@end - -@implementation DSBlockchainInvitation - -- (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.isTransient = FALSE; - self.createdLocally = YES; - self.identity = [[DSBlockchainIdentity alloc] initAtIndex:index inWallet:wallet]; - [self.identity setAssociatedInvitation:self]; - self.chain = wallet.chain; - self.needsIdentityRetrieval = NO; - return self; -} - -- (instancetype)initAtIndex:(uint32_t)index withFundingTransaction:(DSCreditFundingTransaction *)transaction inWallet:(DSWallet *)wallet { - NSParameterAssert(wallet); - if (![transaction isCreditFundingTransaction]) return nil; - NSAssert(index != UINT32_MAX, @"index must be found"); - if (!(self = [super init])) return nil; - self.wallet = wallet; - self.isTransient = FALSE; - self.createdLocally = YES; - self.identity = [[DSBlockchainIdentity alloc] initAtIndex:index withFundingTransaction:transaction withUsernameDictionary:nil inWallet:wallet]; - [self.identity setAssociatedInvitation:self]; - self.chain = wallet.chain; - self.needsIdentityRetrieval = NO; - return self; -} - -- (instancetype)initAtIndex:(uint32_t)index withLockedOutpoint:(DSUTXO)lockedOutpoint inWallet:(DSWallet *)wallet { - NSParameterAssert(wallet); - NSAssert(index != UINT32_MAX, @"index must be found"); - if (!(self = [super init])) return nil; - self.wallet = wallet; - self.isTransient = FALSE; - self.createdLocally = YES; - self.identity = [[DSBlockchainIdentity alloc] initAtIndex:index withLockedOutpoint:lockedOutpoint inWallet:wallet]; - [self.identity setAssociatedInvitation:self]; - self.chain = wallet.chain; - self.needsIdentityRetrieval = NO; - return self; -} - -- (instancetype)initAtIndex:(uint32_t)index withLockedOutpoint:(DSUTXO)lockedOutpoint inWallet:(DSWallet *)wallet withBlockchainInvitationEntity:(DSBlockchainInvitationEntity *)blockchainInvitationEntity { - if (!(self = [super init])) return nil; - self.wallet = wallet; - self.isTransient = FALSE; - self.createdLocally = YES; - self.identity = [[DSBlockchainIdentity alloc] initAtIndex:index withLockedOutpoint:lockedOutpoint inWallet:wallet withBlockchainIdentityEntity:blockchainInvitationEntity.blockchainIdentity associatedToInvitation:self]; - self.link = blockchainInvitationEntity.link; - self.name = blockchainInvitationEntity.name; - self.tag = blockchainInvitationEntity.tag; - self.chain = wallet.chain; - self.needsIdentityRetrieval = NO; - return self; -} - -- (instancetype)initWithInvitationLink:(NSString *)invitationLink inWallet:(DSWallet *)wallet { - if (!(self = [super init])) return nil; - self.link = invitationLink; - self.wallet = wallet; - self.chain = wallet.chain; - self.needsIdentityRetrieval = YES; - self.createdLocally = NO; - return self; -} - -- (void)generateBlockchainInvitationsExtendedPublicKeysWithPrompt:(NSString *)prompt completion:(void (^_Nullable)(BOOL registered))completion { - __block DSCreditFundingDerivationPath *derivationPathInvitationFunding = [[DSDerivationPathFactory sharedInstance] blockchainIdentityInvitationFundingDerivationPathForWallet:self.wallet]; - if ([derivationPathInvitationFunding hasExtendedPublicKey]) { - completion(YES); - return; - } - [[DSAuthenticationManager sharedInstance] seedWithPrompt:prompt - forWallet:self.wallet - forAmount:0 - forceAuthentication:NO - completion:^(NSData *_Nullable seed, BOOL cancelled) { - if (!seed) { - completion(NO); - return; - } - [derivationPathInvitationFunding generateExtendedPublicKeyFromSeed:seed - storeUnderWalletUniqueId:self.wallet.uniqueIDString]; - completion(YES); - }]; -} - - -- (void)registerInWalletForRegistrationFundingTransaction:(DSCreditFundingTransaction *)fundingTransaction { - NSAssert(self.identity != nil, @"The identity must already exist"); - [self.identity setInvitationRegistrationCreditFundingTransaction:fundingTransaction]; - [self registerInWalletForBlockchainIdentityUniqueId:fundingTransaction.creditBurnIdentityIdentifier]; - - //we need to also set the address of the funding transaction to being used so future identities past the initial gap limit are found - [fundingTransaction markInvitationAddressAsUsedInWallet:self.wallet]; -} - -- (void)registerInWalletForBlockchainIdentityUniqueId:(UInt256)blockchainIdentityUniqueId { - [self.identity setInvitationUniqueId:blockchainIdentityUniqueId]; - [self registerInWallet]; -} - -- (BOOL)isRegisteredInWallet { - if (!self.wallet) return FALSE; - return [self.wallet containsBlockchainInvitation:self]; -} - -- (void)registerInWallet { - NSAssert(self.identity.isOutgoingInvitation, @"The underlying identity is not from an invitation"); - if (!self.identity.isOutgoingInvitation) return; - [self.wallet registerBlockchainInvitation:self]; - [self.identity saveInitial]; - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSBlockchainInvitationDidUpdateNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSBlockchainInvitationKey: self}]; - }); -} - -- (void)updateInWallet { - [self saveInContext:[NSManagedObjectContext platformContext]]; -} - -- (BOOL)unregisterLocally { - NSAssert(self.identity.isOutgoingInvitation, @"The underlying identity is not from an invitation"); - if (!self.identity.isOutgoingInvitation) return FALSE; - if (self.identity.isRegistered) return FALSE; //if the invitation has already been used we can not unregister it - [self.wallet unregisterBlockchainInvitation:self]; - [self deletePersistentObjectAndSave:YES inContext:[NSManagedObjectContext platformContext]]; - return TRUE; -} - -- (void)verifyInvitationLinkWithCompletion:(void (^_Nullable)(DSTransaction *transaction, bool spent, NSError *error))completion completionQueue:(dispatch_queue_t)completionQueue { - [DSBlockchainInvitation verifyInvitationLink:self.link onChain:self.wallet.chain completion:completion completionQueue:completionQueue]; -} - -+ (void)verifyInvitationLink:(NSString *)invitationLink onChain:(DSChain *)chain completion:(void (^_Nullable)(DSTransaction *transaction, bool spent, NSError *error))completion completionQueue:(dispatch_queue_t)completionQueue { - DSDAPICoreNetworkService *coreNetworkService = chain.chainManager.DAPIClient.DAPICoreNetworkService; - NSURLComponents *components = [NSURLComponents componentsWithString:invitationLink]; - NSArray *queryItems = components.queryItems; - UInt256 assetLockTransactionHash = UINT256_ZERO; - BOOL isEmptyFundingPrivateKey = true; - for (NSURLQueryItem *queryItem in queryItems) { - if ([queryItem.name isEqualToString:@"assetlocktx"]) { - assetLockTransactionHash = queryItem.value.hexToData.UInt256; - } else if ([queryItem.name isEqualToString:@"pk"]) { - isEmptyFundingPrivateKey = key_ecdsa_secret_key_is_empty([queryItem.value UTF8String], chain.chainType); - } - } - if (uint256_is_zero(assetLockTransactionHash)) { - if (completion) { - completion(nil, NO, [NSError errorWithCode:400 localizedDescriptionKey:@"Invitation format is not valid"]); - } - return; - } - - if (isEmptyFundingPrivateKey) { - if (completion) { - completion(nil, NO, [NSError errorWithCode:400 localizedDescriptionKey:@"Funding private key is not valid"]); - } - return; - } - - [coreNetworkService getTransactionWithHash:assetLockTransactionHash - completionQueue:completionQueue - success:^(DSTransaction *_Nonnull transaction) { - NSAssert(transaction, @"transaction must not be null"); - if (!transaction || ![transaction isKindOfClass:[DSCreditFundingTransaction class]]) { - if (completion) { - completion(nil, NO, [NSError errorWithCode:400 localizedDescriptionKey:@"Invitation transaction is not valid"]); - } - return; - } - if (completion) { - completion(transaction, NO, nil); - } - } - failure:^(NSError *_Nonnull error) { - if (completion) { - completion(nil, NO, [NSError errorWithCode:400 localizedDescriptionKey:@"Invitation format is not valid"]); - } - }]; -} - -- (void)acceptInvitationUsingWalletIndex:(uint32_t)index setDashpayUsername:(NSString *)dashpayUsername authenticationPrompt:(NSString *)authenticationMessage identityRegistrationSteps:(DSBlockchainIdentityRegistrationStep)identityRegistrationSteps stepCompletion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepCompleted))stepCompletion completion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepsCompleted, NSError *error))completion completionQueue:(dispatch_queue_t)completionQueue { - DSDAPICoreNetworkService *coreNetworkService = self.chain.chainManager.DAPIClient.DAPICoreNetworkService; - NSURLComponents *components = [NSURLComponents componentsWithString:self.link]; - NSArray *queryItems = components.queryItems; - UInt256 assetLockTransactionHash = UINT256_ZERO; - OpaqueKey *fundingPrivateKey = nil; - for (NSURLQueryItem *queryItem in queryItems) { - if ([queryItem.name isEqualToString:@"assetlocktx"]) { - assetLockTransactionHash = queryItem.value.hexToData.UInt256; - } else if ([queryItem.name isEqualToString:@"pk"]) { - fundingPrivateKey = [DSKeyManager keyWithPrivateKeyString:queryItem.value ofKeyType:KeyKind_ECDSA forChainType:self.chain.chainType]; - } - } - if (uint256_is_zero(assetLockTransactionHash)) { - if (completion) { - completion(DSBlockchainIdentityRegistrationStep_None, [NSError errorWithCode:400 localizedDescriptionKey:@"Invitation format is not valid"]); - } - return; - } - if (!fundingPrivateKey || !key_has_private_key(fundingPrivateKey)) { - if (completion) { - completion(DSBlockchainIdentityRegistrationStep_None, [NSError errorWithCode:400 localizedDescriptionKey:@"Funding private key is not valid"]); - } - return; - } - - [coreNetworkService getTransactionWithHash:assetLockTransactionHash - completionQueue:self.chain.chainManager.identitiesManager.identityQueue - success:^(DSTransaction *_Nonnull transaction) { - NSAssert(transaction, @"transaction must not be null"); - if (!transaction || ![transaction isKindOfClass:[DSCreditFundingTransaction class]]) { - if (completion) { - completion(DSBlockchainIdentityRegistrationStep_None, [NSError errorWithCode:400 localizedDescriptionKey:@"Invitation transaction is not valid"]); - } - return; - } - self.identity = [[DSBlockchainIdentity alloc] initAtIndex:index withFundingTransaction:(DSCreditFundingTransaction *)transaction withUsernameDictionary:nil inWallet:self.wallet]; - [self.identity setAssociatedInvitation:self]; - [self.identity addDashpayUsername:dashpayUsername save:NO]; - [self.identity registerInWalletForRegistrationFundingTransaction: (DSCreditFundingTransaction *)transaction]; - BOOL success = [self.identity setExternalFundingPrivateKey:fundingPrivateKey]; - if (!success && fundingPrivateKey != NULL) - processor_destroy_opaque_key(fundingPrivateKey); - NSAssert(success, @"We must be able to set the external funding private key"); - if (success) { - [self.identity generateBlockchainIdentityExtendedPublicKeysWithPrompt:authenticationMessage - completion:^(BOOL registered) { - if (registered) { - [self.identity continueRegisteringIdentityOnNetwork:identityRegistrationSteps stepsCompleted:DSBlockchainIdentityRegistrationStep_L1Steps stepCompletion:stepCompletion completion:completion]; - } else if (completion) { - completion(DSBlockchainIdentityRegistrationStep_None, [NSError errorWithCode:500 localizedDescriptionKey:@"Error generating Identity keys"]); - } - }]; - } else { - if (completion) { - completion(DSBlockchainIdentityRegistrationStep_None, [NSError errorWithCode:500 localizedDescriptionKey:@"Error setting the external funding private key"]); - } - } - } - failure:^(NSError *_Nonnull error) { - if (completion) { - completion(DSBlockchainIdentityRegistrationStep_None, [NSError errorWithCode:400 localizedDescriptionKey:@"Invitation format is not valid"]); - } - }]; -} - -- (void)createInvitationFullLinkFromIdentity:(DSBlockchainIdentity *)identity completion:(void (^_Nullable)(BOOL cancelled, NSString *invitationFullLink))completion { - if (!self.identity.registrationCreditFundingTransaction.instantSendLockAwaitingProcessing) { - if (completion) { - completion(NO, nil); - } - return; - } - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSString *senderUsername = identity.currentDashpayUsername; - NSString *senderDisplayName = identity.displayName; - NSString *senderAvatarPath = identity.avatarPath; - NSString *fundingTransactionHexString = uint256_reverse_hex(self.identity.registrationCreditFundingTransaction.txHash); - __block OpaqueKey *registrationFundingPrivateKey = self.identity.registrationFundingPrivateKey; - __block BOOL rCancelled = FALSE; - - if (!registrationFundingPrivateKey) { - dispatch_semaphore_t sem = dispatch_semaphore_create(0); - dispatch_async(dispatch_get_main_queue(), ^{ - [[DSAuthenticationManager sharedInstance] seedWithPrompt:DSLocalizedString(@"Would you like to share this invitation?", nil) - forWallet:self.wallet - forAmount:0 - forceAuthentication:NO - completion:^(NSData *_Nullable seed, BOOL cancelled) { - rCancelled = cancelled; - if (seed) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - DSCreditFundingDerivationPath *derivationPathRegistrationFunding = [[DSDerivationPathFactory sharedInstance] blockchainIdentityInvitationFundingDerivationPathForWallet:self.wallet]; - // TODO: cleanup? - registrationFundingPrivateKey = [derivationPathRegistrationFunding privateKeyAtIndexPath:[NSIndexPath indexPathWithIndex:self.identity.index] fromSeed:seed]; - dispatch_semaphore_signal(sem); - }); - } else { - dispatch_semaphore_signal(sem); - } - }]; - }); - dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); - } - if (!registrationFundingPrivateKey) { - dispatch_async(dispatch_get_main_queue(), ^{ - if (completion) { - completion(rCancelled, nil); - } - }); - return; - } - //in WIF format - NSString *registrationFundingPrivateKeyString = [DSKeyManager serializedPrivateKey:registrationFundingPrivateKey chainType:self.chain.chainType]; - - NSString *serializedISLock = [self.identity.registrationCreditFundingTransaction.instantSendLockAwaitingProcessing.toData hexString]; - - NSURLComponents *components = [NSURLComponents componentsWithString:@"https://invitations.dashpay.io/applink"]; - NSMutableArray *queryItems = [NSMutableArray array]; - if (senderUsername) { - NSURLQueryItem *senderUsernameQueryItem = [NSURLQueryItem queryItemWithName:@"du" value:senderUsername]; - [queryItems addObject:senderUsernameQueryItem]; - } - if (senderDisplayName) { - NSURLQueryItem *senderDisplayNameQueryItem = [NSURLQueryItem queryItemWithName:@"display-name" value:senderDisplayName]; - [queryItems addObject:senderDisplayNameQueryItem]; - } - if (senderAvatarPath) { - NSURLQueryItem *senderAvatarPathQueryItem = [NSURLQueryItem queryItemWithName:@"avatar-url" value:senderAvatarPath]; - [queryItems addObject:senderAvatarPathQueryItem]; - } - - NSURLQueryItem *fundingTransactionQueryItem = [NSURLQueryItem queryItemWithName:@"assetlocktx" value:fundingTransactionHexString.lowercaseString]; - [queryItems addObject:fundingTransactionQueryItem]; - - NSURLQueryItem *registrationFundingPrivateKeyQueryItem = [NSURLQueryItem queryItemWithName:@"pk" value:registrationFundingPrivateKeyString]; - [queryItems addObject:registrationFundingPrivateKeyQueryItem]; - - NSURLQueryItem *serializedISLockQueryItem = [NSURLQueryItem queryItemWithName:@"islock" value:serializedISLock.lowercaseString]; - [queryItems addObject:serializedISLockQueryItem]; - - components.queryItems = queryItems; - - dispatch_async(dispatch_get_main_queue(), ^{ - if (completion) { - completion(NO, components.URL.absoluteString); - } - }); - }); -} - -// MARK: Saving - -- (void)saveInContext:(NSManagedObjectContext *)context { - if (self.isTransient) return; - [context performBlockAndWait:^{ - BOOL changeOccured = NO; - NSMutableArray *updateEvents = [NSMutableArray array]; - DSBlockchainInvitationEntity *entity = [self blockchainInvitationEntityInContext:context]; - if (entity.tag != self.tag) { - entity.tag = self.tag; - changeOccured = YES; - [updateEvents addObject:DSBlockchainInvitationUpdateEvents]; - } - if (entity.name != self.name) { - entity.name = self.name; - changeOccured = YES; - [updateEvents addObject:DSBlockchainInvitationUpdateEvents]; - } - if (entity.link != self.link) { - entity.link = self.link; - changeOccured = YES; - [updateEvents addObject:DSBlockchainInvitationUpdateEventLink]; - } - if (changeOccured) { - [context ds_save]; - if (updateEvents.count) { - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSBlockchainInvitationDidUpdateNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSBlockchainInvitationKey: self, DSBlockchainInvitationUpdateEvents: updateEvents}]; - }); - } - } - }]; -} - -// MARK: Deletion - -- (void)deletePersistentObjectAndSave:(BOOL)save inContext:(NSManagedObjectContext *)context { - [context performBlockAndWait:^{ - DSBlockchainInvitationEntity *blockchainInvitationEntity = [self blockchainInvitationEntityInContext:context]; - if (blockchainInvitationEntity) { - [blockchainInvitationEntity deleteObjectAndWait]; - if (save) { - [context ds_save]; - } - } - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSBlockchainInvitationDidUpdateNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSBlockchainInvitationKey: self}]; - }); - }]; -} - -// MARK: Entity - -- (DSBlockchainInvitationEntity *)blockchainInvitationEntity { - return [self blockchainInvitationEntityInContext:[NSManagedObjectContext viewContext]]; -} - -- (DSBlockchainInvitationEntity *)blockchainInvitationEntityInContext:(NSManagedObjectContext *)context { - __block DSBlockchainInvitationEntity *entity = nil; - [context performBlockAndWait:^{ - entity = [DSBlockchainInvitationEntity anyObjectInContext:context matching:@"blockchainIdentity.uniqueID == %@", self.identity.uniqueIDData]; - }]; - NSAssert(entity, @"An entity should always be found"); - return entity; -} - -- (NSString *)debugDescription { - return [[super debugDescription] stringByAppendingString:[NSString stringWithFormat:@" {%d-%@-%@}", self.identity.index, self.identity.currentDashpayUsername, self.identity.uniqueIdString]]; -} - - -@end diff --git a/DashSync/shared/Models/Identity/DSIdentity+ContactRequest.h b/DashSync/shared/Models/Identity/DSIdentity+ContactRequest.h new file mode 100644 index 000000000..b2848fd3d --- /dev/null +++ b/DashSync/shared/Models/Identity/DSIdentity+ContactRequest.h @@ -0,0 +1,37 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "DSIdentity.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSIdentity (ContactRequest) + +- (void)fetchContactRequests:(void (^_Nullable)(BOOL success, NSArray *errors))completion; +- (void)fetchOutgoingContactRequests:(void (^_Nullable)(BOOL success, NSArray *errors))completion; +- (void)fetchOutgoingContactRequestsInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(BOOL success, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue; +- (void)fetchIncomingContactRequests:(void (^_Nullable)(BOOL success, NSArray *errors))completion; +- (void)fetchIncomingContactRequestsInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(BOOL success, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Identity/DSIdentity+ContactRequest.m b/DashSync/shared/Models/Identity/DSIdentity+ContactRequest.m new file mode 100644 index 000000000..4d794ac85 --- /dev/null +++ b/DashSync/shared/Models/Identity/DSIdentity+ContactRequest.m @@ -0,0 +1,553 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DPContract.h" +#import "DSAccount.h" +#import "DSAccountEntity+CoreDataClass.h" +#import "DSBlockchainIdentityEntity+CoreDataClass.h" +#import "DSChain+Identity.h" +#import "DSChainManager.h" +#import "DSContactRequest.h" +#import "DSDashpayUserEntity+CoreDataClass.h" +#import "DSDashPlatform.h" +#import "DSDerivationPathEntity+CoreDataClass.h" +#import "DSFriendRequestEntity+CoreDataClass.h" +#import "DSIdentitiesManager+CoreData.h" +#import "DSIdentity+ContactRequest.h" +#import "DSIdentity+Friendship.h" +#import "DSIdentity+Protected.h" +#import "DSPotentialOneWayFriendship.h" +#import "DSTransactionManager+Protected.h" +#import "NSError+Dash.h" +#import "NSManagedObject+Sugar.h" + +#define DEFAULT_CONTACT_REQUEST_FETCH_RETRIES 5 + +#define ERROR_DASHPAY_CONTRACT_IMPROPER_SETUP [NSError errorWithCode:500 localizedDescriptionKey:@"The Dashpay contract is not properly set up"] +#define ERROR_IDENTITY_NOT_ACTIVATED [NSError errorWithCode:500 localizedDescriptionKey:@"The blockchain identity hasn't yet been locally activated"] +#define ERROR_IDENTITY_NO_LONGER_ACTIVE [NSError errorWithCode:410 localizedDescriptionKey:@"Identity no longer active in wallet"] +#define ERROR_KEY_FORMAT_DECRYPTION [NSError errorWithCode:500 localizedDescriptionKey:@"Incorrect key format after contact request decryption"] +#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."] + +@implementation DSIdentity (ContactRequest) + +- (void)fetchContactRequests:(void (^)(BOOL success, NSArray *errors))completion { + dispatch_async(self.identityQueue, ^{ + [self fetchContactRequestsInContext:self.platformContext + withCompletion:completion + onCompletionQueue:dispatch_get_main_queue()]; + }); +} + + +- (void)fetchContactRequestsInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(BOOL success, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + __weak typeof(self) weakSelf = self; + [self fetchIncomingContactRequestsInContext:context + withCompletion:^(BOOL success, NSArray *errors) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + if (completion) completion(NO, @[ERROR_MEM_ALLOC]); + return; + } + if (!success) { + if (completion) dispatch_async(completionQueue, ^{ completion(success, errors); }); + return; + } + [strongSelf fetchOutgoingContactRequestsInContext:context + withCompletion:completion + onCompletionQueue:completionQueue]; + } + onCompletionQueue:self.identityQueue]; +} + +- (void)fetchIncomingContactRequests:(void (^_Nullable)(BOOL success, NSArray *errors))completion { + [self fetchIncomingContactRequestsInContext:self.platformContext + withCompletion:completion + onCompletionQueue:dispatch_get_main_queue()]; +} + +- (void)fetchIncomingContactRequestsInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(BOOL success, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + [self fetchIncomingContactRequestsInContext:context + startAfter:nil + retriesLeft:DEFAULT_CONTACT_REQUEST_FETCH_RETRIES + withCompletion:completion + onCompletionQueue:completionQueue]; +} + +- (void)fetchIncomingContactRequestsInContext:(NSManagedObjectContext *)context + startAfter:(NSData*_Nullable)startAfter + retriesLeft:(int32_t)retriesLeft + withCompletion:(void (^)(BOOL success, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + [self internalFetchIncomingContactRequestsInContext:context + startAfter:startAfter + withCompletion:^(BOOL success, NSData*_Nullable hasMoreStartAfter, NSArray *errors) { + if (!success && retriesLeft > 0) { + [self fetchIncomingContactRequestsInContext:context startAfter:startAfter retriesLeft:retriesLeft - 1 withCompletion:completion onCompletionQueue:completionQueue]; + } else if (success && hasMoreStartAfter) { + [self fetchIncomingContactRequestsInContext:context startAfter:hasMoreStartAfter retriesLeft:DEFAULT_CONTACT_REQUEST_FETCH_RETRIES withCompletion:completion onCompletionQueue:completionQueue]; + } else if (completion) { + completion(success, errors); + } + } + onCompletionQueue:completionQueue]; +} + +- (void)internalFetchIncomingContactRequestsInContext:(NSManagedObjectContext *)context + startAfter:(NSData*_Nullable)startAfter + withCompletion:(void (^)(BOOL success, NSData*_Nullable hasMoreStartAfter, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + DPContract *dashpayContract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; + if (dashpayContract.contractState != DPContractState_Registered) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, @[ERROR_DASHPAY_CONTRACT_IMPROPER_SETUP]); }); + return; + } + NSError *error = nil; + if (![self activePrivateKeysAreLoadedWithFetchingError:&error]) { + // 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 + if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, @[error ? error : ERROR_IDENTITY_NOT_ACTIVATED]); }); + return; + } + __weak typeof(self) weakSelf = self; + [self.DAPINetworkService getDashpayIncomingContactRequestsForUserId:self.uniqueIDData + since:self.lastCheckedIncomingContactsTimestamp ? (self.lastCheckedIncomingContactsTimestamp - HOUR_TIME_INTERVAL) : 0 + startAfter:startAfter + completionQueue:self.identityQueue + success:^(NSArray *_Nonnull documents) { + //todo chance the since parameter + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, @[ERROR_MEM_ALLOC]); }); + return; + } + + dispatch_async(self.identityQueue, ^{ + [strongSelf handleContactRequestObjects:documents + context:context + completion:^(BOOL success, NSArray *errors) { + BOOL hasMore = documents.count == DAPI_DOCUMENT_RESPONSE_COUNT_LIMIT; + if (!hasMore) + [self.platformContext performBlockAndWait:^{ + self.lastCheckedIncomingContactsTimestamp = [[NSDate date] timeIntervalSince1970]; + }]; + if (completion) { + NSData * hasMoreStartAfter = documents.lastObject[@"$id"]; + dispatch_async(completionQueue, ^{ completion(success, hasMoreStartAfter, errors); }); + } + } + onCompletionQueue:self.identityQueue]; + }); + } + failure:^(NSError *_Nonnull error) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, @[error]); }); + }]; +} + +- (void)fetchOutgoingContactRequests:(void (^)(BOOL success, NSArray *errors))completion { + [self fetchOutgoingContactRequestsInContext:self.platformContext + withCompletion:completion + onCompletionQueue:dispatch_get_main_queue()]; +} + +- (void)fetchOutgoingContactRequestsInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(BOOL success, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + [self fetchOutgoingContactRequestsInContext:context + startAfter:nil + retriesLeft:DEFAULT_CONTACT_REQUEST_FETCH_RETRIES + withCompletion:completion + onCompletionQueue:completionQueue]; +} + +- (void)fetchOutgoingContactRequestsInContext:(NSManagedObjectContext *)context + startAfter:(NSData*_Nullable)startAfter + retriesLeft:(int32_t)retriesLeft + withCompletion:(void (^)(BOOL success, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + [self internalFetchOutgoingContactRequestsInContext:context + startAfter:startAfter + withCompletion:^(BOOL success, NSData*_Nullable hasMoreStartAfter, NSArray *errors) { + if (!success && retriesLeft > 0) { + [self fetchOutgoingContactRequestsInContext:context startAfter:startAfter retriesLeft:retriesLeft - 1 withCompletion:completion onCompletionQueue:completionQueue]; + } else if (success && hasMoreStartAfter) { + [self fetchOutgoingContactRequestsInContext:context startAfter:hasMoreStartAfter retriesLeft:DEFAULT_CONTACT_REQUEST_FETCH_RETRIES withCompletion:completion onCompletionQueue:completionQueue]; + } else if (completion) { + completion(success, errors); + } + } + onCompletionQueue:completionQueue]; +} + +- (void)internalFetchOutgoingContactRequestsInContext:(NSManagedObjectContext *)context + startAfter:(NSData*_Nullable)startAfter + withCompletion:(void (^)(BOOL success, NSData*_Nullable hasMoreStartAfter, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + DPContract *dashpayContract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; + if (dashpayContract.contractState != DPContractState_Registered) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, @[ERROR_DASHPAY_CONTRACT_IMPROPER_SETUP]); }); + return; + } + NSError *error = nil; + if (![self activePrivateKeysAreLoadedWithFetchingError:&error]) { + //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 + if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, @[error ? error : ERROR_IDENTITY_NOT_ACTIVATED]); }); + return; + } + __weak typeof(self) weakSelf = self; + [self.DAPINetworkService getDashpayOutgoingContactRequestsForUserId:self.uniqueIDData + since:self.lastCheckedOutgoingContactsTimestamp ? (self.lastCheckedOutgoingContactsTimestamp - HOUR_TIME_INTERVAL) : 0 + startAfter:startAfter + completionQueue:self.identityQueue + success:^(NSArray *_Nonnull documents) { + //todo chance the since parameter + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, @[ERROR_MEM_ALLOC]); }); + return; + } + + dispatch_async(self.identityQueue, ^{ + [strongSelf handleContactRequestObjects:documents + context:context + completion:^(BOOL success, NSArray *errors) { + BOOL hasMore = documents.count == DAPI_DOCUMENT_RESPONSE_COUNT_LIMIT; + if (!hasMore) + [self.platformContext performBlockAndWait:^{ + self.lastCheckedOutgoingContactsTimestamp = [[NSDate date] timeIntervalSince1970]; + }]; + __block NSData * hasMoreStartAfter = success?documents.lastObject[@"$id"]:nil; + dispatch_async(completionQueue, ^{ completion(success, hasMoreStartAfter, errors); }); + } + onCompletionQueue:self.identityQueue]; + }); + } + failure:^(NSError *_Nonnull error) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, @[error]); }); + }]; +} + +// MARK: Response Processing + + + +/// Handle an array of contact requests. This method will split contact requests into either incoming contact requests or outgoing contact requests and then call methods for handling them if applicable. +/// @param rawContactRequests A dictionary of rawContactRequests, these are returned by the network. +/// @param context The managed object context in which to process results. +/// @param completion Completion callback with success boolean. +- (void)handleContactRequestObjects:(NSArray *)rawContactRequests + context:(NSManagedObjectContext *)context + completion:(void (^)(BOOL success, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + NSAssert(completionQueue == self.identityQueue, @"we should be on identity queue"); + __block NSMutableArray *incomingNewRequests = [NSMutableArray array]; + __block NSMutableArray *outgoingNewRequests = [NSMutableArray array]; + __block NSMutableArray *rErrors = [NSMutableArray array]; + [context performBlockAndWait:^{ + for (NSDictionary *rawContact in rawContactRequests) { + DSContactRequest *contactRequest = [DSContactRequest contactRequestFromDictionary:rawContact onIdentity:self]; + if (uint256_eq(contactRequest.recipientIdentityUniqueId, self.uniqueID)) { + //we are the recipient, this is an incoming request + DSFriendRequestEntity *friendRequest = [DSFriendRequestEntity anyObjectInContext:context matching:@"destinationContact == %@ && sourceContact.associatedBlockchainIdentity.uniqueID == %@", [self matchingDashpayUserInContext:context], uint256_data(contactRequest.senderIdentityUniqueId)]; + if (!friendRequest) + [incomingNewRequests addObject:contactRequest]; + } else if (uint256_eq(contactRequest.senderIdentityUniqueId, self.uniqueID)) { + //we are the sender, this is an outgoing request + BOOL isNew = ![DSFriendRequestEntity countObjectsInContext:context matching:@"sourceContact == %@ && destinationContact.associatedBlockchainIdentity.uniqueID == %@", [self matchingDashpayUserInContext:context], [NSData dataWithUInt256:contactRequest.recipientIdentityUniqueId]]; + if (isNew) [outgoingNewRequests addObject:contactRequest]; + } else { + //we should not have received this + NSAssert(FALSE, @"the contact request needs to be either outgoing or incoming"); + } + } + }]; + __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]; + } + 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, ^{ if (completion) completion(succeeded, [rErrors copy]); }); +} + +- (void)handleIncomingRequests:(NSArray *)incomingRequests + context:(NSManagedObjectContext *)context + completion:(void (^)(BOOL success, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + if (!self.isActive) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[ERROR_IDENTITY_NO_LONGER_ACTIVE]); }); + return; + } + [context performBlockAndWait:^{ + __block BOOL succeeded = YES; + __block NSMutableArray *errors = [NSMutableArray array]; + dispatch_group_t dispatchGroup = dispatch_group_create(); + + for (DSContactRequest *contactRequest in incomingRequests) { + DSBlockchainIdentityEntity *externalIdentityEntity = [DSBlockchainIdentityEntity anyObjectInContext:context matching:@"uniqueID == %@", uint256_data(contactRequest.senderIdentityUniqueId)]; + if (!externalIdentityEntity) { + //no externalBlockchainIdentity exists yet, which means no dashpay user + dispatch_group_enter(dispatchGroup); + DSIdentity *senderIdentity = [self.identitiesManager foreignIdentityWithUniqueId:contactRequest.senderIdentityUniqueId createIfMissing:YES inContext:context]; + [senderIdentity fetchNeededNetworkStateInformationInContext:self.platformContext + withCompletion:^(DSIdentityQueryStep failureStep, NSArray *_Nullable networkErrors) { + if (!failureStep) { + DMaybeOpaqueKey *senderPublicKey = [senderIdentity keyAtIndex:contactRequest.senderKeyIndex]; + NSData *extendedPublicKeyData = [contactRequest decryptedPublicKeyDataWithKey:senderPublicKey->ok]; + DMaybeOpaqueKey *extendedPublicKey = [DSKeyManager keyWithExtendedPublicKeyData:extendedPublicKeyData ofType:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor()]; + if (!extendedPublicKey) { + succeeded = FALSE; + [errors addObject:ERROR_KEY_FORMAT_DECRYPTION]; + } else { + DSDashpayUserEntity *senderDashpayUserEntity = [senderIdentity identityEntityInContext:context].matchingDashpayUser; + NSAssert(senderDashpayUserEntity, @"The sender should exist"); + [self addIncomingRequestFromContact:senderDashpayUserEntity + forExtendedPublicKey:extendedPublicKey + atTimestamp:contactRequest.createdAt]; + } + } else { + [errors addObjectsFromArray:networkErrors]; + } + dispatch_group_leave(dispatchGroup); + } + onCompletionQueue:self.identityQueue]; + + } else { + if ([self.chain identityForUniqueId:externalIdentityEntity.uniqueID.UInt256]) { + //it's also local (aka both contacts are local to this device), we should store the extended public key for the destination + DSIdentity *sourceIdentity = [self.chain identityForUniqueId:externalIdentityEntity.uniqueID.UInt256]; + DSAccount *account = [sourceIdentity.wallet accountWithNumber:0]; + DSPotentialOneWayFriendship *potentialFriendship = [[DSPotentialOneWayFriendship alloc] initWithDestinationIdentity:self destinationKeyIndex:contactRequest.recipientKeyIndex sourceIdentity:sourceIdentity sourceKeyIndex:contactRequest.senderKeyIndex account:account]; + if (![DSFriendRequestEntity existingFriendRequestEntityWithSourceIdentifier:sourceIdentity.uniqueID destinationIdentifier:self.uniqueID onAccountIndex:account.accountNumber inContext:context]) { + dispatch_group_enter(dispatchGroup); + [potentialFriendship createDerivationPathAndSaveExtendedPublicKeyWithCompletion:^(BOOL success, DSIncomingFundsDerivationPath *_Nonnull incomingFundsDerivationPath) { + if (success) { + DSDashpayUserEntity *matchingDashpayUserInContext = [self matchingDashpayUserInContext:context]; + DSFriendRequestEntity *friendRequest = [potentialFriendship outgoingFriendRequestForDashpayUserEntity:matchingDashpayUserInContext atTimestamp:contactRequest.createdAt]; + [potentialFriendship storeExtendedPublicKeyAssociatedWithFriendRequest:friendRequest]; + [matchingDashpayUserInContext addIncomingRequestsObject:friendRequest]; + if ([[friendRequest.sourceContact.incomingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"sourceContact == %@", matchingDashpayUserInContext]] count]) + [matchingDashpayUserInContext addFriendsObject:friendRequest.sourceContact]; + [account addIncomingDerivationPath:incomingFundsDerivationPath + forFriendshipIdentifier:friendRequest.friendshipIdentifier + inContext:context]; + [context ds_save]; + [self.chain.chainManager.transactionManager updateTransactionsBloomFilter]; + } else { + succeeded = FALSE; + [errors addObject:ERROR_DERIVATION_FRIENDSHIP]; + } + dispatch_group_leave(dispatchGroup); + }]; + } + + } else { + DSIdentity *sourceIdentity = [[DSIdentity alloc] initWithIdentityEntity:externalIdentityEntity]; + NSAssert(sourceIdentity, @"This should not be null"); + if ([sourceIdentity activeKeyCount] > 0 && [sourceIdentity keyAtIndex:contactRequest.senderKeyIndex]) { + //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 + DMaybeOpaqueKey *key = [sourceIdentity keyAtIndex:contactRequest.senderKeyIndex]; + NSData *decryptedExtendedPublicKeyData = [contactRequest decryptedPublicKeyDataWithKey:key->ok]; + NSAssert(decryptedExtendedPublicKeyData, @"Data should be decrypted"); + dash_spv_crypto_keys_key_KeyKind *kind = dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor(); + DMaybeOpaqueKey *extendedPublicKey = [DSKeyManager keyWithExtendedPublicKeyData:decryptedExtendedPublicKeyData ofType:kind]; +// dash_spv_crypto_keys_key_KeyKind_destroy(kind); + if (!extendedPublicKey) { + succeeded = FALSE; + [errors addObject:ERROR_CONTACT_REQUEST_KEY_ENCRYPTION]; + return; + } + [self addIncomingRequestFromContact:externalIdentityEntity.matchingDashpayUser + forExtendedPublicKey:extendedPublicKey + atTimestamp:contactRequest.createdAt]; + + DSDashpayUserEntity *matchingDashpayUserInContext = [self matchingDashpayUserInContext:context]; + if ([[externalIdentityEntity.matchingDashpayUser.incomingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"sourceContact == %@", matchingDashpayUserInContext]] count]) { + [matchingDashpayUserInContext addFriendsObject:[externalIdentityEntity matchingDashpayUser]]; + [context ds_save]; + } + + } else { + //the blockchain identity is already known, but needs to updated to get the right key, create the incoming friend request, add a friendship if an outgoing friend request also exists + dispatch_group_enter(dispatchGroup); + [sourceIdentity fetchNeededNetworkStateInformationInContext:context + withCompletion:^(DSIdentityQueryStep failureStep, NSArray *networkStateInformationErrors) { + if (!failureStep) { + [context performBlockAndWait:^{ + DMaybeOpaqueKey *key = [sourceIdentity keyAtIndex:contactRequest.senderKeyIndex]; + NSAssert(key, @"key should be known"); + NSData *decryptedExtendedPublicKeyData = [contactRequest decryptedPublicKeyDataWithKey:key->ok]; + NSAssert(decryptedExtendedPublicKeyData, @"Data should be decrypted"); + DMaybeOpaqueKey *extendedPublicKey = [DSKeyManager keyWithExtendedPublicKeyData:decryptedExtendedPublicKeyData ofType:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor()]; + NSAssert(extendedPublicKey, @"A key should be recovered"); + [self addIncomingRequestFromContact:externalIdentityEntity.matchingDashpayUser + forExtendedPublicKey:extendedPublicKey + atTimestamp:contactRequest.createdAt]; + DSDashpayUserEntity *matchingDashpayUserInContext = [self matchingDashpayUserInContext:context]; + if ([[externalIdentityEntity.matchingDashpayUser.incomingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"sourceContact == %@", matchingDashpayUserInContext]] count]) { + [matchingDashpayUserInContext addFriendsObject:externalIdentityEntity.matchingDashpayUser]; + [context ds_save]; + } + }]; + } else { + succeeded = FALSE; + [errors addObjectsFromArray:networkStateInformationErrors]; + } + dispatch_group_leave(dispatchGroup); + } + onCompletionQueue:self.identityQueue]; + } + } + } + } + dispatch_group_notify(dispatchGroup, completionQueue, ^{ if (completion) completion(succeeded, [errors copy]); }); + }]; +} + +- (void)handleOutgoingRequests:(NSArray *)outgoingRequests + context:(NSManagedObjectContext *)context + completion:(void (^)(BOOL success, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + if (!self.isActive) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[ERROR_IDENTITY_NO_LONGER_ACTIVE]); }); + return; + } + [context performBlockAndWait:^{ + __block NSMutableArray *errors = [NSMutableArray array]; + __block BOOL succeeded = YES; + dispatch_group_t dispatchGroup = dispatch_group_create(); + for (DSContactRequest *contactRequest in outgoingRequests) { + DSBlockchainIdentityEntity *recipientIdentityEntity = [DSBlockchainIdentityEntity anyObjectInContext:context matching:@"uniqueID == %@", uint256_data(contactRequest.recipientIdentityUniqueId)]; + if (!recipientIdentityEntity) { + //no contact exists yet + dispatch_group_enter(dispatchGroup); + DSIdentity *recipientIdentity = [self.identitiesManager foreignIdentityWithUniqueId:contactRequest.recipientIdentityUniqueId + createIfMissing:YES + inContext:context]; + NSAssert([recipientIdentity identityEntityInContext:context], @"Entity should now exist"); + [recipientIdentity fetchNeededNetworkStateInformationInContext:context + withCompletion:^(DSIdentityQueryStep failureStep, NSArray *_Nullable networkErrors) { + if (!failureStep) { + [self addFriendshipFromSourceIdentity:self + sourceKeyIndex:contactRequest.senderKeyIndex + toRecipientIdentity:recipientIdentity + recipientKeyIndex:contactRequest.recipientKeyIndex + atTimestamp:contactRequest.createdAt + inContext:context]; + } else { + succeeded = FALSE; + [errors addObjectsFromArray:networkErrors]; + } + dispatch_group_leave(dispatchGroup); + } + onCompletionQueue:self.identityQueue]; + } else { + //the recipient blockchain identity is already known, meaning they had made a friend request to us before, and on another device we had accepted + //or the recipient blockchain identity is also local to the device + + DSWallet *recipientWallet = nil; + DSIdentity *recipientIdentity = [self.chain identityForUniqueId:recipientIdentityEntity.uniqueID.UInt256 + foundInWallet:&recipientWallet]; + BOOL isLocal = TRUE; + if (!recipientIdentity) { + //this is not local + recipientIdentity = [[DSIdentity alloc] initWithIdentityEntity:recipientIdentityEntity]; + isLocal = FALSE; + } + + dispatch_group_enter(dispatchGroup); + [recipientIdentity fetchIfNeededNetworkStateInformation:DSIdentityQueryStep_Profile & DSIdentityQueryStep_Username & DSIdentityQueryStep_Identity + inContext:context + withCompletion:^(DSIdentityQueryStep failureStep, NSArray *_Nullable networkErrors) { + if (!failureStep) { + [self addFriendshipFromSourceIdentity:self + sourceKeyIndex:contactRequest.senderKeyIndex + toRecipientIdentity:recipientIdentity + recipientKeyIndex:contactRequest.recipientKeyIndex + atTimestamp:contactRequest.createdAt + inContext:context]; + } else { + succeeded = FALSE; + [errors addObjectsFromArray:networkErrors]; + } + dispatch_group_leave(dispatchGroup); + } + onCompletionQueue:self.identityQueue]; + } + } + dispatch_group_notify(dispatchGroup, completionQueue, ^{ if (completion) completion(succeeded, [errors copy]); }); + }]; +} + +- (void)addIncomingRequestFromContact:(DSDashpayUserEntity *)dashpayUserEntity + forExtendedPublicKey:(DMaybeOpaqueKey *)extendedPublicKey + atTimestamp:(NSTimeInterval)timestamp { + NSManagedObjectContext *context = dashpayUserEntity.managedObjectContext; + DSFriendRequestEntity *friendRequestEntity = [DSFriendRequestEntity managedObjectInBlockedContext:context]; + friendRequestEntity.sourceContact = dashpayUserEntity; + friendRequestEntity.destinationContact = [self matchingDashpayUserInContext:dashpayUserEntity.managedObjectContext]; + friendRequestEntity.timestamp = timestamp; + NSAssert(friendRequestEntity.sourceContact != friendRequestEntity.destinationContact, @"This must be different contacts"); + DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity managedObjectInBlockedContext:context]; + derivationPathEntity.chain = [self.chain chainEntityInContext:context]; + friendRequestEntity.derivationPath = derivationPathEntity; + NSAssert(friendRequestEntity.derivationPath, @"There must be a derivation path"); + DSAccount *account = [self.wallet accountWithNumber:0]; + DSAccountEntity *accountEntity = [DSAccountEntity accountEntityForWalletUniqueID:self.wallet.uniqueIDString index:account.accountNumber onChain:self.chain inContext:dashpayUserEntity.managedObjectContext]; + derivationPathEntity.account = accountEntity; + friendRequestEntity.account = accountEntity; + [friendRequestEntity finalizeWithFriendshipIdentifier]; + //NSLog(@"->created derivation path entity %@ %@", friendRequestEntity.friendshipIdentifier.hexString, [NSThread callStackSymbols]); + DSIncomingFundsDerivationPath *derivationPath = [DSIncomingFundsDerivationPath externalDerivationPathWithExtendedPublicKey:extendedPublicKey withDestinationIdentityUniqueId:[self matchingDashpayUserInContext:dashpayUserEntity.managedObjectContext].associatedBlockchainIdentity.uniqueID.UInt256 sourceIdentityUniqueId:dashpayUserEntity.associatedBlockchainIdentity.uniqueID.UInt256 onChain:self.chain]; + derivationPathEntity.publicKeyIdentifier = derivationPath.standaloneExtendedPublicKeyUniqueID; + [derivationPath storeExternalDerivationPathExtendedPublicKeyToKeyChain]; + //incoming request uses an outgoing derivation path + [account addOutgoingDerivationPath:derivationPath forFriendshipIdentifier:friendRequestEntity.friendshipIdentifier inContext:dashpayUserEntity.managedObjectContext]; + DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:dashpayUserEntity.managedObjectContext]; + [matchingDashpayUser addIncomingRequestsObject:friendRequestEntity]; + if ([[friendRequestEntity.sourceContact.incomingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"sourceContact == %@", matchingDashpayUser]] count]) + [matchingDashpayUser addFriendsObject:friendRequestEntity.sourceContact]; + [context ds_save]; + [self.chain.chainManager.transactionManager updateTransactionsBloomFilter]; +} +@end diff --git a/DashSync/shared/Models/Identity/DSIdentity+Friendship.h b/DashSync/shared/Models/Identity/DSIdentity+Friendship.h new file mode 100644 index 000000000..c467dbecc --- /dev/null +++ b/DashSync/shared/Models/Identity/DSIdentity+Friendship.h @@ -0,0 +1,48 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "DSIdentity.h" +#import "DSFriendRequestEntity+CoreDataClass.h" +#import "DSPotentialContact.h" +#import "DSPotentialOneWayFriendship.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSIdentity (Friendship) + +- (void)sendNewFriendRequestToIdentity:(DSIdentity *)identity + completion:(void (^)(BOOL success, NSArray *_Nullable errors))completion; +- (void)sendNewFriendRequestToPotentialContact:(DSPotentialContact *)potentialContact + completion:(void (^_Nullable)(BOOL success, NSArray *errors))completion; +- (void)sendNewFriendRequestMatchingPotentialFriendship:(DSPotentialOneWayFriendship *)potentialFriendship + completion:(void (^_Nullable)(BOOL success, NSArray *errors))completion; +- (void)acceptFriendRequestFromIdentity:(DSIdentity *)otherIdentity + completion:(void (^)(BOOL success, NSArray *errors))completion; +- (void)acceptFriendRequest:(DSFriendRequestEntity *)friendRequest + completion:(void (^_Nullable)(BOOL success, NSArray *errors))completion; +- (void)addFriendshipFromSourceIdentity:(DSIdentity *)sourceIdentity + sourceKeyIndex:(uint32_t)sourceKeyIndex + toRecipientIdentity:(DSIdentity *)recipientIdentity + recipientKeyIndex:(uint32_t)recipientKeyIndex + atTimestamp:(NSTimeInterval)timestamp + inContext:(NSManagedObjectContext *)context; +- (DSIdentityFriendshipStatus)friendshipStatusForRelationshipWithIdentity:(DSIdentity *)otherIdentity; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Identity/DSIdentity+Friendship.m b/DashSync/shared/Models/Identity/DSIdentity+Friendship.m new file mode 100644 index 000000000..5933e40de --- /dev/null +++ b/DashSync/shared/Models/Identity/DSIdentity+Friendship.m @@ -0,0 +1,392 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DSAccount.h" +#import "DSAccountEntity+CoreDataClass.h" +#import "DSBlockchainIdentityEntity+CoreDataClass.h" +#import "DSChain+Identity.h" +#import "DSChainManager.h" +#import "DSDashpayUserEntity+CoreDataClass.h" +#import "DSIdentity+ContactRequest.h" +#import "DSIdentity+Friendship.h" +#import "DSIdentity+Profile.h" +#import "DSIdentity+Protected.h" +#import "DSIdentitiesManager+CoreData.h" + +#import "DSTransactionManager+Protected.h" +#import "DSTransientDashpayUser.h" +#import "NSError+Dash.h" +#import "NSManagedObject+Sugar.h" + +#define ERROR_KEY_HANDLING [NSError errorWithCode:501 localizedDescriptionKey:@"Internal key handling error"] +#define ERROR_INCOMPLETE_ACTIONS [NSError errorWithCode:501 localizedDescriptionKey:@"User has actions to complete before being able to use Dashpay"] +#define ERROR_DERIVATION_FRIENDSHIP [NSError errorWithCode:500 localizedDescriptionKey:@"Could not create friendship derivation path"] +#define ERROR_FRIEND_REQUEST_NONE_FOUND [NSError errorWithCode:501 localizedDescriptionKey:@"You can only accept a friend request from identity who has sent you one, and none were found"] +#define ERROR_FRIEND_REQUEST_ACCEPT_FROM_NON_LOCAL_IDENTITY [NSError errorWithCode:501 localizedDescriptionKey:@"Accepting a friend request should only happen from a local identity"] + + +@implementation DSIdentity (Friendship) + +// MARK: Sending a Friend Request + +- (void)sendNewFriendRequestToIdentity:(DSIdentity *)identity + completion:(void (^)(BOOL success, NSArray *_Nullable errors))completion { + [self sendNewFriendRequestToIdentity:identity + inContext:self.platformContext + completion:completion + onCompletionQueue:dispatch_get_main_queue()]; +} + +- (void)sendNewFriendRequestToIdentity:(DSIdentity *)identity + inContext:(NSManagedObjectContext *)context + completion:(void (^)(BOOL success, NSArray *_Nullable errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + if (identity.isTransient) { + identity.isTransient = FALSE; + [self.identitiesManager registerForeignIdentity:identity]; + if (identity.transientDashpayUser) { + [identity applyProfileChanges:identity.transientDashpayUser + inContext:context + saveContext:YES + completion:^(BOOL success, NSError *_Nullable error) { + if (success && !error) { + DSDashpayUserEntity *dashpayUser = [identity matchingDashpayUserInContext:context]; + if (identity.transientDashpayUser.revision == dashpayUser.remoteProfileDocumentRevision) + identity.transientDashpayUser = nil; + } + } + onCompletionQueue:self.identityQueue]; + } + } + [identity fetchNeededNetworkStateInformationInContext:context + withCompletion:^(DSIdentityQueryStep failureStep, NSArray *_Nullable errors) { + if (failureStep && failureStep != DSIdentityQueryStep_Profile) { //if profile fails we can still continue on + completion(NO, errors); + return; + } + if (![identity isDashpayReady]) { + dispatch_async(completionQueue, ^{ completion(NO, @[ERROR_INCOMPLETE_ACTIONS]); }); + return; + } + uint32_t destinationKeyIndex = [identity firstIndexOfKeyOfType:self.currentMainKeyType createIfNotPresent:NO saveKey:NO]; + uint32_t sourceKeyIndex = [self firstIndexOfKeyOfType:self.currentMainKeyType createIfNotPresent:NO saveKey:NO]; + + + if (sourceKeyIndex == UINT32_MAX) { //not found + //to do register a new key + NSAssert(FALSE, @"we shouldn't be getting here"); + if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[ERROR_KEY_HANDLING]); }); + return; + } + DSPotentialOneWayFriendship *potentialFriendship = [[DSPotentialOneWayFriendship alloc] initWithDestinationIdentity:identity + destinationKeyIndex:destinationKeyIndex + sourceIdentity:self + sourceKeyIndex:sourceKeyIndex + account:[self.wallet accountWithNumber:0]]; + [potentialFriendship createDerivationPathAndSaveExtendedPublicKeyWithCompletion:^(BOOL success, DSIncomingFundsDerivationPath *_Nonnull incomingFundsDerivationPath) { + if (!success) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[ERROR_KEY_HANDLING]); }); + return; + } + [potentialFriendship encryptExtendedPublicKeyWithCompletion:^(BOOL success) { + if (!success) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, @[ERROR_KEY_HANDLING]); }); + return; + } + [self sendNewFriendRequestMatchingPotentialFriendship:potentialFriendship + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + }]; + }]; + } + onCompletionQueue:self.identityQueue]; +} + +- (void)sendNewFriendRequestToPotentialContact:(DSPotentialContact *)potentialContact + completion:(void (^)(BOOL success, NSArray *_Nullable errors))completion { + NSAssert(self.isLocal, @"This should not be performed on a non local blockchain identity"); + if (!self.isLocal) return; + __weak typeof(self) weakSelf = self; + DSDAPIPlatformNetworkService *dapiNetworkService = self.DAPINetworkService; + [dapiNetworkService getIdentityByName:potentialContact.username + inDomain:@"dash" + completionQueue:self.identityQueue + success:^(NSDictionary *_Nonnull identityVersionedDictionary) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + if (completion) completion(NO, @[ERROR_MEM_ALLOC]); + return; + } + NSNumber *_Nonnull version = identityVersionedDictionary[@(DSPlatformStoredMessage_Version)]; + NSDictionary *_Nonnull identityDictionary = identityVersionedDictionary[@(DSPlatformStoredMessage_Item)]; + NSData *identityIdData = nil; + if (!identityDictionary || !(identityIdData = identityDictionary[@"id"])) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(NO, @[ERROR_MALFORMED_RESPONSE]); }); + return; + } + UInt256 identityContactUniqueId = identityIdData.UInt256; + NSAssert(uint256_is_not_zero(identityContactUniqueId), @"identityContactUniqueId should not be null"); + DSBlockchainIdentityEntity *potentialContactIdentityEntity = [DSBlockchainIdentityEntity anyObjectInContext:self.platformContext matching:@"uniqueID == %@", uint256_data(identityContactUniqueId)]; + DSIdentity *potentialContactIdentity = nil; + if (potentialContactIdentityEntity) { + potentialContactIdentity = [self.chain identityForUniqueId:identityContactUniqueId]; + if (!potentialContactIdentity) + potentialContactIdentity = [[DSIdentity alloc] initWithIdentityEntity:potentialContactIdentityEntity]; + } else { + potentialContactIdentity = [self.identitiesManager foreignIdentityWithUniqueId:identityContactUniqueId + createIfMissing:YES + inContext:self.platformContext]; + } + [potentialContactIdentity applyIdentityDictionary:identityDictionary + version:[version intValue] + save:YES + inContext:self.platformContext]; + [potentialContactIdentity saveInContext:self.platformContext]; + [self sendNewFriendRequestToIdentity:potentialContactIdentity completion:completion]; + } + failure:^(NSError *_Nonnull error) { + if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node + [self.DAPIClient removeDAPINodeByAddress:dapiNetworkService.ipAddress]; + } + DSLogPrivate(@"%@", error); + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(NO, @[error]); }); + }]; +} + +- (void)sendNewFriendRequestMatchingPotentialFriendship:(DSPotentialOneWayFriendship *)potentialFriendship + completion:(void (^)(BOOL success, NSArray *errors))completion { + [self sendNewFriendRequestMatchingPotentialFriendship:potentialFriendship + inContext:self.platformContext + completion:completion + onCompletionQueue:dispatch_get_main_queue()]; +} + +- (void)sendNewFriendRequestMatchingPotentialFriendship:(DSPotentialOneWayFriendship *)potentialFriendship + inContext:(NSManagedObjectContext *)context + completion:(void (^)(BOOL success, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + NSAssert(self.isLocal, @"This should not be performed on a non local blockchain identity"); + if (!self.isLocal) return; + DSDashpayUserEntity *destinationDashpayUser = [potentialFriendship.destinationIdentity matchingDashpayUserInContext:context]; + if (!destinationDashpayUser) { + NSAssert([potentialFriendship.destinationIdentity matchingDashpayUserInContext:context], @"There must be a destination contact if the destination identity is not known"); + return; + } + __weak typeof(self) weakSelf = self; + DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; + NSData *entropyData = uint256_random_data; + DPDocument *document = [potentialFriendship contactRequestDocumentWithEntropy:entropyData]; + [self.DAPIClient sendDocument:document + forIdentity:self + contract:contract + completion:^(NSError *_Nullable error) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(NO, @[ERROR_MEM_ALLOC]); }); + return; + } + + BOOL success = error == nil; + + if (!success) { + dispatch_async(dispatch_get_main_queue(), ^{ completion(NO, @[error]); }); + return; + } + + [context performBlockAndWait:^{ + [self addFriendship:potentialFriendship + inContext:context + completion:^(BOOL success, NSError *error){ + + }]; + // [self addFriendshipFromSourceIdentity:potentialFriendship.sourceIdentity sourceKeyIndex:potentialFriendship.so toRecipientIdentity:(DSIdentity *) recipientKeyIndex:<#(uint32_t)#> inContext:<#(NSManagedObjectContext *)#>] + // DSFriendRequestEntity * friendRequest = [potentialFriendship outgoingFriendRequestForDashpayUserEntity:potentialFriendship.destinationIdentity.matchingDashpayUser]; + // [strongSelf.matchingDashpayUser addOutgoingRequestsObject:friendRequest]; + // + // if ([[friendRequest.destinationContact.outgoingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"destinationContact == %@",strongSelf.matchingDashpayUser]] count]) { + // [strongSelf.matchingDashpayUser addFriendsObject:friendRequest.destinationContact]; + // } + // [potentialFriendship storeExtendedPublicKeyAssociatedWithFriendRequest:friendRequest]; + // [DSFriendRequestEntity saveContext]; + // if (completion) { + // dispatch_async(dispatch_get_main_queue(), ^{ + // completion(success,error); + // }); + // } + }]; + + [self fetchOutgoingContactRequestsInContext:context + withCompletion:^(BOOL success, NSArray *_Nonnull errors) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(success, errors); }); + } + onCompletionQueue:completionQueue]; + }]; +} + +- (void)acceptFriendRequestFromIdentity:(DSIdentity *)otherIdentity + completion:(void (^)(BOOL success, NSArray *errors))completion { + [self acceptFriendRequestFromIdentity:otherIdentity + inContext:self.platformContext + completion:completion + onCompletionQueue:dispatch_get_main_queue()]; +} + +- (void)acceptFriendRequestFromIdentity:(DSIdentity *)otherIdentity + inContext:(NSManagedObjectContext *)context + completion:(void (^)(BOOL success, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + NSAssert(self.isLocal, @"This should not be performed on a non local blockchain identity"); + if (!self.isLocal) { + if (completion) completion(NO, @[ERROR_FRIEND_REQUEST_ACCEPT_FROM_NON_LOCAL_IDENTITY]); + return; + } + + [context performBlockAndWait:^{ + DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; + DSFriendRequestEntity *friendRequest = [[matchingDashpayUser.incomingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"sourceContact.associatedBlockchainIdentity.uniqueID == %@", uint256_data(otherIdentity.uniqueID)]] anyObject]; + if (friendRequest) { + [self acceptFriendRequest:friendRequest + completion:completion + onCompletionQueue:completionQueue]; + } else if (completion) { + completion(NO, @[ERROR_FRIEND_REQUEST_NONE_FOUND]); + } + }]; +} + +- (void)acceptFriendRequest:(DSFriendRequestEntity *)friendRequest + completion:(void (^)(BOOL success, NSArray *errors))completion { + [self acceptFriendRequest:friendRequest + completion:completion + onCompletionQueue:dispatch_get_main_queue()]; +} + +- (void)acceptFriendRequest:(DSFriendRequestEntity *)friendRequest + completion:(void (^)(BOOL success, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + NSAssert(self.isLocal, @"This should not be performed on a non local blockchain identity"); + if (!self.isLocal) { + if (completion) completion(NO, @[ERROR_FRIEND_REQUEST_ACCEPT_FROM_NON_LOCAL_IDENTITY]); + return; + } + DSAccount *account = [self.wallet accountWithNumber:0]; + DSDashpayUserEntity *otherDashpayUser = friendRequest.sourceContact; + DSIdentity *otherIdentity = [self.chain identityForUniqueId:otherDashpayUser.associatedBlockchainIdentity.uniqueID.UInt256]; + if (!otherIdentity) + otherIdentity = [[DSIdentity alloc] initWithIdentityEntity:otherDashpayUser.associatedBlockchainIdentity]; + // DSPotentialContact *contact = [[DSPotentialContact alloc] initWithUsername:friendRequest.sourceContact.username avatarPath:friendRequest.sourceContact.avatarPath + // publicMessage:friendRequest.sourceContact.publicMessage]; + // [contact setAssociatedIdentityUniqueId:friendRequest.sourceContact.associatedBlockchainIdentity.uniqueID.UInt256]; + // DSKey * friendsEncyptionKey = [otherIdentity keyOfType:friendRequest.sourceEncryptionPublicKeyIndex atIndex:friendRequest.sourceEncryptionPublicKeyIndex]; + //[DSKey keyWithPublicKeyData:friendRequest.sourceContact.encryptionPublicKey forKeyType:friendRequest.sourceContact.encryptionPublicKeyType onChain:self.chain]; + // [contact addPublicKey:friendsEncyptionKey atIndex:friendRequest.sourceContact.encryptionPublicKeyIndex]; + // uint32_t sourceKeyIndex = [self firstIndexOfKeyOfType:friendRequest.sourceContact.encryptionPublicKeyType createIfNotPresent:NO]; + // if (sourceKeyIndex == UINT32_MAX) { //not found + // //to do register a new key + // NSAssert(FALSE, @"we shouldn't be getting here"); + // return; + // } + DSPotentialOneWayFriendship *potentialFriendship = [[DSPotentialOneWayFriendship alloc] initWithDestinationIdentity:otherIdentity destinationKeyIndex:friendRequest.sourceKeyIndex sourceIdentity:self sourceKeyIndex:friendRequest.destinationKeyIndex account:account]; + [potentialFriendship createDerivationPathAndSaveExtendedPublicKeyWithCompletion:^(BOOL success, DSIncomingFundsDerivationPath *_Nonnull incomingFundsDerivationPath) { + if (success) { + [potentialFriendship encryptExtendedPublicKeyWithCompletion:^(BOOL success) { + if (!success) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(NO, @[ERROR_KEY_HANDLING]); }); + return; + } + [self sendNewFriendRequestMatchingPotentialFriendship:potentialFriendship + inContext:friendRequest.managedObjectContext + completion:completion + onCompletionQueue:completionQueue]; + }]; + } else if (completion) { + completion(NO, @[ERROR_DERIVATION_FRIENDSHIP]); + } + }]; +} + +- (void)addFriendship:(DSPotentialOneWayFriendship *)friendship + inContext:(NSManagedObjectContext *)context + completion:(void (^)(BOOL success, NSError *error))completion { + //DSFriendRequestEntity * friendRequestEntity = [friendship outgoingFriendRequestForDashpayUserEntity:friendship.destinationIdentity.matchingDashpayUser]; + DSFriendRequestEntity *friendRequestEntity = [DSFriendRequestEntity managedObjectInBlockedContext:context]; + friendRequestEntity.sourceContact = [friendship.sourceIdentity matchingDashpayUserInContext:context]; + friendRequestEntity.destinationContact = [friendship.destinationIdentity matchingDashpayUserInContext:context]; + friendRequestEntity.timestamp = friendship.createdAt; + NSAssert(friendRequestEntity.sourceContact != friendRequestEntity.destinationContact, @"This must be different contacts"); + DSAccountEntity *accountEntity = [DSAccountEntity accountEntityForWalletUniqueID:self.wallet.uniqueIDString index:0 onChain:self.chain inContext:context]; + friendRequestEntity.account = accountEntity; + [friendRequestEntity finalizeWithFriendshipIdentifier]; + [friendship createDerivationPathAndSaveExtendedPublicKeyWithCompletion:^(BOOL success, DSIncomingFundsDerivationPath *_Nonnull incomingFundsDerivationPath) { + if (!success) return; + friendRequestEntity.derivationPath = [friendship storeExtendedPublicKeyAssociatedWithFriendRequest:friendRequestEntity inContext:context]; + DSAccount *account = [self.wallet accountWithNumber:0]; + if (friendship.destinationIdentity.isLocal) { //the destination is also local + NSAssert(friendship.destinationIdentity.wallet, @"Wallet should be known"); + DSAccount *recipientAccount = [friendship.destinationIdentity.wallet accountWithNumber:0]; + NSAssert(recipientAccount, @"Recipient Wallet should exist"); + [recipientAccount addIncomingDerivationPath:incomingFundsDerivationPath forFriendshipIdentifier:friendRequestEntity.friendshipIdentifier inContext:context]; + if (recipientAccount != account) + [account addOutgoingDerivationPath:incomingFundsDerivationPath forFriendshipIdentifier:friendRequestEntity.friendshipIdentifier inContext:context]; + } else { + //todo update outgoing derivation paths to incoming derivation paths as blockchain users come in + [account addIncomingDerivationPath:incomingFundsDerivationPath forFriendshipIdentifier:friendRequestEntity.friendshipIdentifier inContext:context]; + } + NSAssert(friendRequestEntity.derivationPath, @"derivation path must be present"); + DSDashpayUserEntity *dashpayUserInChildContext = [self matchingDashpayUserInContext:context]; + [dashpayUserInChildContext addOutgoingRequestsObject:friendRequestEntity]; + if ([[[friendship.destinationIdentity matchingDashpayUserInContext:context].outgoingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"destinationContact == %@", dashpayUserInChildContext]] count]) + [dashpayUserInChildContext addFriendsObject:[friendship.destinationIdentity matchingDashpayUserInContext:context]]; + NSError *savingError = [context ds_save]; + [self.chain.chainManager.transactionManager updateTransactionsBloomFilter]; + if (completion) completion(savingError ? NO : YES, savingError); + }]; +} + +- (void)addFriendshipFromSourceIdentity:(DSIdentity *)sourceIdentity + sourceKeyIndex:(uint32_t)sourceKeyIndex + toRecipientIdentity:(DSIdentity *)recipientIdentity + recipientKeyIndex:(uint32_t)recipientKeyIndex + atTimestamp:(NSTimeInterval)timestamp + inContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + DSAccount *account = [self.wallet accountWithNumber:0]; + DSPotentialOneWayFriendship *realFriendship = [[DSPotentialOneWayFriendship alloc] initWithDestinationIdentity:recipientIdentity destinationKeyIndex:recipientKeyIndex sourceIdentity:self sourceKeyIndex:sourceKeyIndex account:account createdAt:timestamp]; + if (![DSFriendRequestEntity existingFriendRequestEntityWithSourceIdentifier:self.uniqueID destinationIdentifier:recipientIdentity.uniqueID onAccountIndex:account.accountNumber inContext:context]) { + //it was probably added already + //this could happen when have 2 blockchain identities in same wallet + //Identity A gets outgoing contacts + //Which are the same as Identity B incoming contacts, no need to add the friendships twice + [self addFriendship:realFriendship inContext:context completion:nil]; + } + }]; +} + +- (DSIdentityFriendshipStatus)friendshipStatusForRelationshipWithIdentity:(DSIdentity *)otherIdentity { + if (!self.matchingDashpayUserInViewContext) return DSIdentityFriendshipStatus_Unknown; + __block BOOL isIncoming; + __block BOOL isOutgoing; + [self.matchingDashpayUserInViewContext.managedObjectContext performBlockAndWait:^{ + isIncoming = ([self.matchingDashpayUserInViewContext.incomingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"sourceContact.associatedBlockchainIdentity.uniqueID == %@", uint256_data(otherIdentity.uniqueID)]].count > 0); + isOutgoing = ([self.matchingDashpayUserInViewContext.outgoingRequests filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"destinationContact.associatedBlockchainIdentity.uniqueID == %@", uint256_data(otherIdentity.uniqueID)]].count > 0); + }]; + return ((isIncoming << 1) | isOutgoing); +} + + +@end diff --git a/DashSync/shared/Models/Identity/DSIdentity+Profile.h b/DashSync/shared/Models/Identity/DSIdentity+Profile.h new file mode 100644 index 000000000..25e326290 --- /dev/null +++ b/DashSync/shared/Models/Identity/DSIdentity+Profile.h @@ -0,0 +1,102 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "DSIdentity.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSIdentity (Profile) + +// MARK: - Dashpay + +/*! @brief This is a helper to easily get the avatar path of the matching dashpay user. */ +@property (nonatomic, readonly, nullable) NSString *avatarPath; + +/*! @brief This is a helper to easily get the avatar fingerprint of the matching dashpay user. */ +@property (nonatomic, readonly) NSData *avatarFingerprint; + +/*! @brief This is a helper to easily get the avatar hash of the matching dashpay user. */ +@property (nonatomic, readonly, nullable) NSData *avatarHash; + +/*! @brief This is a helper to easily get the display name of the matching dashpay user. */ +@property (nonatomic, readonly, nullable) NSString *displayName; + +/*! @brief This is a helper to easily get the public message of the matching dashpay user. */ +@property (nonatomic, readonly, nullable) NSString *publicMessage; + +/*! @brief This is a helper to easily get the last time the profile was updated of the matching dashpay user. */ +@property (nonatomic, readonly) uint64_t dashpayProfileUpdatedAt; + +/*! @brief This is a helper to easily get the creation time of the profile of the matching dashpay user. */ +@property (nonatomic, readonly) uint64_t dashpayProfileCreatedAt; + +- (void)fetchProfileWithCompletion:(void (^_Nullable)(BOOL success, NSError *error))completion; +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName; +- (void)updateDashpayProfileWithPublicMessage:(NSString *)publicMessage; + +- (void)updateDashpayProfileWithAvatarURLString:(NSString *)avatarURLString; +- (void)updateDashpayProfileWithAvatarURLString:(NSString *)avatarURLString + avatarHash:(NSData *)avatarHash + avatarFingerprint:(NSData *)avatarFingerprint; +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage; +#if TARGET_OS_IOS +- (void)updateDashpayProfileWithAvatarImage:(UIImage *)avatarImage + avatarData:(NSData *)data + avatarURLString:(NSString *)avatarURLString; +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage + avatarImage:(UIImage *)avatarImage + avatarData:(NSData *)data + avatarURLString:(NSString *)avatarURLString; +#else +- (void)updateDashpayProfileWithAvatarImage:(NSImage *)avatarImage + avatarData:(NSData *)data + avatarURLString:(NSString *)avatarURLString; +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage + avatarImage:(NSImage *)avatarImage + avatarData:(NSData *)data + avatarURLString:(NSString *)avatarURLString; +#endif +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage + avatarURLString:(NSString *)avatarURLString; +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage + avatarURLString:(NSString *)avatarURLString + avatarHash:(NSData *)avatarHash + avatarFingerprint:(NSData *)avatarFingerprint; +- (void)signedProfileDocumentTransitionInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(DSTransition *transition, BOOL cancelled, NSError *error))completion; +- (void)signAndPublishProfileWithCompletion:(void (^)(BOOL success, BOOL cancelled, NSError *error))completion; + + +- (void)fetchProfileInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(BOOL success, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue; + +- (void)applyProfileChanges:(DSTransientDashpayUser *)transientDashpayUser + inContext:(NSManagedObjectContext *)context + saveContext:(BOOL)saveContext + completion:(void (^_Nullable)(BOOL success, NSError *_Nullable error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Identity/DSIdentity+Profile.m b/DashSync/shared/Models/Identity/DSIdentity+Profile.m new file mode 100644 index 000000000..d07a0fc27 --- /dev/null +++ b/DashSync/shared/Models/Identity/DSIdentity+Profile.m @@ -0,0 +1,597 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DSDashpayUserEntity+CoreDataClass.h" +#import "DPDocument.h" +#import "DPDocumentFactory.h" +#import "DSDocumentTransition.h" +#import "DSIdentity+Profile.h" +#import "DSIdentity+Protected.h" +#import "DSIdentity+Username.h" +#import "DSTransientDashpayUser.h" +#import "DSWallet.h" +#import "NSError+Dash.h" +#import "NSManagedObject+Sugar.h" +#import + +#define DEFAULT_FETCH_PROFILE_RETRY_COUNT 5 + +#define ERROR_TRANSITION_NO_UPDATE [NSError errorWithCode:500 localizedDescriptionKey:@"Transition had nothing to update"] +#define ERROR_DASHPAY_CONTRACT_NOT_REGISTERED [NSError errorWithCode:500 localizedDescriptionKey:@"Dashpay Contract is not yet registered on network"] +#define ERROR_IDENTITY_NO_LONGER_ACTIVE [NSError errorWithCode:410 localizedDescriptionKey:@"Identity no longer active in wallet"] + +@implementation DSIdentity (Profile) + +- (NSString *)avatarPath { + if (self.transientDashpayUser) { + return self.transientDashpayUser.avatarPath; + } else { + return self.matchingDashpayUserInViewContext.avatarPath; + } +} + +- (NSData *)avatarFingerprint { + if (self.transientDashpayUser) { + return self.transientDashpayUser.avatarFingerprint; + } else { + return self.matchingDashpayUserInViewContext.avatarFingerprint; + } +} + +- (NSData *)avatarHash { + if (self.transientDashpayUser) { + return self.transientDashpayUser.avatarHash; + } else { + return self.matchingDashpayUserInViewContext.avatarHash; + } +} + +- (NSString *)displayName { + if (self.transientDashpayUser) { + return self.transientDashpayUser.displayName; + } else { + return self.matchingDashpayUserInViewContext.displayName; + } +} + +- (NSString *)publicMessage { + if (self.transientDashpayUser) { + return self.transientDashpayUser.publicMessage; + } else { + return self.matchingDashpayUserInViewContext.publicMessage; + } +} + +- (uint64_t)dashpayProfileUpdatedAt { + if (self.transientDashpayUser) { + return self.transientDashpayUser.updatedAt; + } else { + return self.matchingDashpayUserInViewContext.updatedAt; + } +} + +- (uint64_t)dashpayProfileCreatedAt { + if (self.transientDashpayUser) { + return self.transientDashpayUser.createdAt; + } else { + return self.matchingDashpayUserInViewContext.createdAt; + } +} + +// MARK: Profile + +- (DPDocument *)matchingDashpayUserProfileDocumentInContext:(NSManagedObjectContext *)context { + //The revision must be at least at 1, otherwise nothing was ever done + DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; + if (matchingDashpayUser && matchingDashpayUser.localProfileDocumentRevision) { + __block DSMutableStringValueDictionary *dataDictionary = nil; + + __block NSData *entropyData = nil; + __block NSData *documentIdentifier = nil; + [context performBlockAndWait:^{ + dataDictionary = [@{@"$updatedAt": @(matchingDashpayUser.updatedAt), } mutableCopy]; + if (matchingDashpayUser.createdAt == matchingDashpayUser.updatedAt) + dataDictionary[@"$createdAt"] = @(matchingDashpayUser.createdAt); + else + dataDictionary[@"$revision"] = @(matchingDashpayUser.localProfileDocumentRevision); + if (matchingDashpayUser.publicMessage) + dataDictionary[@"publicMessage"] = matchingDashpayUser.publicMessage; + if (matchingDashpayUser.avatarPath) + dataDictionary[@"avatarUrl"] = matchingDashpayUser.avatarPath; + if (matchingDashpayUser.avatarFingerprint) + dataDictionary[@"avatarFingerprint"] = matchingDashpayUser.avatarFingerprint; + if (matchingDashpayUser.avatarHash) + dataDictionary[@"avatarHash"] = matchingDashpayUser.avatarHash; + if (matchingDashpayUser.displayName) + dataDictionary[@"displayName"] = matchingDashpayUser.displayName; + entropyData = matchingDashpayUser.originalEntropyData; + documentIdentifier = matchingDashpayUser.documentIdentifier; + }]; + NSError *error = nil; + if (documentIdentifier == nil) { + NSAssert(entropyData, @"Entropy string must be present"); + return [self.dashpayDocumentFactory documentOnTable:@"profile" + withDataDictionary:dataDictionary + usingEntropy:entropyData + error:&error]; + } else { + return [self.dashpayDocumentFactory documentOnTable:@"profile" + withDataDictionary:dataDictionary + usingDocumentIdentifier:documentIdentifier + error:&error]; + } + } else { + return nil; + } +} + +- (DSDocumentTransition *)profileDocumentTransitionInContext:(NSManagedObjectContext *)context { + DPDocument *profileDocument = [self matchingDashpayUserProfileDocumentInContext:context]; + return profileDocument ? [[DSDocumentTransition alloc] initForDocuments:@[profileDocument] withTransitionVersion:1 identityUniqueId:self.uniqueID onChain:self.chain] : nil; +} + +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName { + [self updateDashpayProfileWithDisplayName:displayName + inContext:self.platformContext]; +} + +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + inContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; + matchingDashpayUser.displayName = displayName; + if (!matchingDashpayUser.remoteProfileDocumentRevision) { + matchingDashpayUser.createdAt = [[NSDate date] timeIntervalSince1970] * 1000; + if (!matchingDashpayUser.originalEntropyData) + matchingDashpayUser.originalEntropyData = uint256_random_data; + } + matchingDashpayUser.updatedAt = [[NSDate date] timeIntervalSince1970] * 1000; + matchingDashpayUser.localProfileDocumentRevision++; + [context ds_save]; + }]; +} + +- (void)updateDashpayProfileWithPublicMessage:(NSString *)publicMessage { + [self updateDashpayProfileWithPublicMessage:publicMessage + inContext:self.platformContext]; +} + +- (void)updateDashpayProfileWithPublicMessage:(NSString *)publicMessage + inContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; + matchingDashpayUser.publicMessage = publicMessage; + if (!matchingDashpayUser.remoteProfileDocumentRevision) { + matchingDashpayUser.createdAt = [[NSDate date] timeIntervalSince1970] * 1000; + if (!matchingDashpayUser.originalEntropyData) + matchingDashpayUser.originalEntropyData = uint256_random_data; + } + matchingDashpayUser.updatedAt = [[NSDate date] timeIntervalSince1970] * 1000; + matchingDashpayUser.localProfileDocumentRevision++; + [context ds_save]; + }]; +} + +- (void)updateDashpayProfileWithAvatarURLString:(NSString *)avatarURLString { + [self updateDashpayProfileWithAvatarURLString:avatarURLString + inContext:self.platformContext]; +} + +- (void)updateDashpayProfileWithAvatarURLString:(NSString *)avatarURLString + inContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; + matchingDashpayUser.avatarPath = avatarURLString; + if (!matchingDashpayUser.remoteProfileDocumentRevision) { + matchingDashpayUser.createdAt = [[NSDate date] timeIntervalSince1970] * 1000; + if (!matchingDashpayUser.originalEntropyData) + matchingDashpayUser.originalEntropyData = uint256_random_data; + } + matchingDashpayUser.updatedAt = [[NSDate date] timeIntervalSince1970] * 1000; + matchingDashpayUser.localProfileDocumentRevision++; + [context ds_save]; + }]; +} + + +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage { + [self updateDashpayProfileWithDisplayName:displayName + publicMessage:publicMessage + inContext:self.platformContext]; +} + +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage + inContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; + matchingDashpayUser.displayName = displayName; + matchingDashpayUser.publicMessage = publicMessage; + if (!matchingDashpayUser.remoteProfileDocumentRevision) { + matchingDashpayUser.createdAt = [[NSDate date] timeIntervalSince1970] * 1000; + if (!matchingDashpayUser.originalEntropyData) + matchingDashpayUser.originalEntropyData = uint256_random_data; + } + matchingDashpayUser.updatedAt = [[NSDate date] timeIntervalSince1970] * 1000; + matchingDashpayUser.localProfileDocumentRevision++; + [context ds_save]; + }]; +} + +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage + avatarURLString:(NSString *)avatarURLString { + [self updateDashpayProfileWithDisplayName:displayName + publicMessage:publicMessage + avatarURLString:avatarURLString + inContext:self.platformContext]; +} + +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage + avatarURLString:(NSString *)avatarURLString + inContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; + matchingDashpayUser.displayName = displayName; + matchingDashpayUser.publicMessage = publicMessage; + matchingDashpayUser.avatarPath = avatarURLString; + if (!matchingDashpayUser.remoteProfileDocumentRevision) { + matchingDashpayUser.createdAt = [[NSDate date] timeIntervalSince1970] * 1000; + if (!matchingDashpayUser.originalEntropyData) + matchingDashpayUser.originalEntropyData = uint256_random_data; + } + matchingDashpayUser.updatedAt = [[NSDate date] timeIntervalSince1970] * 1000; + matchingDashpayUser.localProfileDocumentRevision++; + [context ds_save]; + }]; +} + +#if TARGET_OS_IOS + +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage + avatarImage:(UIImage *)avatarImage + avatarData:(NSData *)data + avatarURLString:(NSString *)avatarURLString { + [self updateDashpayProfileWithDisplayName:displayName + publicMessage:publicMessage + avatarImage:avatarImage + avatarData:data + avatarURLString:avatarURLString + inContext:self.platformContext]; +} + +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage + avatarImage:(UIImage *)avatarImage + avatarData:(NSData *)avatarData + avatarURLString:(NSString *)avatarURLString + inContext:(NSManagedObjectContext *)context { + NSData *avatarHash = uint256_data(avatarData.SHA256); + uint64_t fingerprint = [[OSImageHashing sharedInstance] hashImage:avatarImage withProviderId:OSImageHashingProviderDHash]; + [self updateDashpayProfileWithDisplayName:displayName + publicMessage:publicMessage + avatarURLString:avatarURLString + avatarHash:avatarHash + avatarFingerprint:[NSData dataWithUInt64:fingerprint] + inContext:context]; +} + +- (void)updateDashpayProfileWithAvatarImage:(UIImage *)avatarImage + avatarData:(NSData *)data + avatarURLString:(NSString *)avatarURLString { + [self updateDashpayProfileWithAvatarImage:avatarImage + avatarData:data + avatarURLString:avatarURLString + inContext:self.platformContext]; +} + +- (void)updateDashpayProfileWithAvatarImage:(UIImage *)avatarImage + avatarData:(NSData *)avatarData + avatarURLString:(NSString *)avatarURLString + inContext:(NSManagedObjectContext *)context { + NSData *avatarHash = uint256_data(avatarData.SHA256); + uint64_t fingerprint = [[OSImageHashing sharedInstance] hashImage:avatarImage withProviderId:OSImageHashingProviderDHash]; + [self updateDashpayProfileWithAvatarURLString:avatarURLString + avatarHash:avatarHash + avatarFingerprint:[NSData dataWithUInt64:fingerprint] + inContext:context]; +} + +#else + +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage + avatarImage:(NSImage *)avatarImage + avatarData:(NSData *)data + avatarURLString:(NSString *)avatarURLString { + [self updateDashpayProfileWithDisplayName:displayName + publicMessage:publicMessage + avatarImage:avatarImage + avatarData:data + avatarURLString:avatarURLString + inContext:self.platformContext]; +} + +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage + avatarImage:(NSImage *)avatarImage + avatarData:(NSData *)avatarData + avatarURLString:(NSString *)avatarURLString + inContext:(NSManagedObjectContext *)context { + NSData *avatarHash = uint256_data(avatarData.SHA256); + uint64_t fingerprint = [[OSImageHashing sharedInstance] hashImage:avatarImage withProviderId:OSImageHashingProviderDHash]; + [self updateDashpayProfileWithDisplayName:displayName + publicMessage:publicMessage + avatarURLString:avatarURLString + avatarHash:avatarHash + avatarFingerprint:[NSData dataWithUInt64:fingerprint] + inContext:context]; +} + +- (void)updateDashpayProfileWithAvatarImage:(NSImage *)avatarImage + avatarData:(NSData *)data + avatarURLString:(NSString *)avatarURLString { + [self updateDashpayProfileWithAvatarImage:avatarImage + avatarData:data + avatarURLString:avatarURLString + inContext:self.platformContext]; +} + +- (void)updateDashpayProfileWithAvatarImage:(NSImage *)avatarImage + avatarData:(NSData *)avatarData + avatarURLString:(NSString *)avatarURLString + inContext:(NSManagedObjectContext *)context { + NSData *avatarHash = uint256_data(avatarData.SHA256); + uint64_t fingerprint = [[OSImageHashing sharedInstance] hashImage:avatarImage withProviderId:OSImageHashingProviderDHash]; + [self updateDashpayProfileWithAvatarURLString:avatarURLString + avatarHash:avatarHash + avatarFingerprint:[NSData dataWithUInt64:fingerprint] + inContext:context]; +} + +#endif + + +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage + avatarURLString:(NSString *)avatarURLString + avatarHash:(NSData *)avatarHash + avatarFingerprint:(NSData *)avatarFingerprint { + [self updateDashpayProfileWithDisplayName:displayName + publicMessage:publicMessage + avatarURLString:avatarURLString + avatarHash:avatarHash + avatarFingerprint:avatarFingerprint + inContext:self.platformContext]; +} + +- (void)updateDashpayProfileWithDisplayName:(NSString *)displayName + publicMessage:(NSString *)publicMessage + avatarURLString:(NSString *)avatarURLString + avatarHash:(NSData *)avatarHash + avatarFingerprint:(NSData *)avatarFingerprint + inContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; + matchingDashpayUser.displayName = displayName; + matchingDashpayUser.publicMessage = publicMessage; + matchingDashpayUser.avatarPath = avatarURLString; + matchingDashpayUser.avatarFingerprint = avatarFingerprint; + matchingDashpayUser.avatarHash = avatarHash; + if (!matchingDashpayUser.remoteProfileDocumentRevision) { + matchingDashpayUser.createdAt = [[NSDate date] timeIntervalSince1970] * 1000; + if (!matchingDashpayUser.originalEntropyData) + matchingDashpayUser.originalEntropyData = uint256_random_data; + } + matchingDashpayUser.updatedAt = [[NSDate date] timeIntervalSince1970] * 1000; + matchingDashpayUser.localProfileDocumentRevision++; + [context ds_save]; + }]; +} + +- (void)updateDashpayProfileWithAvatarURLString:(NSString *)avatarURLString + avatarHash:(NSData *)avatarHash + avatarFingerprint:(NSData *)avatarFingerprint { + [self updateDashpayProfileWithAvatarURLString:avatarURLString + avatarHash:avatarHash + avatarFingerprint:avatarFingerprint + inContext:self.platformContext]; +} + +- (void)updateDashpayProfileWithAvatarURLString:(NSString *)avatarURLString + avatarHash:(NSData *)avatarHash + avatarFingerprint:(NSData *)avatarFingerprint + inContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; + matchingDashpayUser.avatarPath = avatarURLString; + matchingDashpayUser.avatarFingerprint = avatarFingerprint; + matchingDashpayUser.avatarHash = avatarHash; + if (!matchingDashpayUser.remoteProfileDocumentRevision) { + matchingDashpayUser.createdAt = [[NSDate date] timeIntervalSince1970] * 1000; + if (!matchingDashpayUser.originalEntropyData) + matchingDashpayUser.originalEntropyData = uint256_random_data; + } + matchingDashpayUser.updatedAt = [[NSDate date] timeIntervalSince1970] * 1000; + matchingDashpayUser.localProfileDocumentRevision++; + [context ds_save]; + }]; +} + +- (void)signedProfileDocumentTransitionInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(DSTransition *transition, BOOL cancelled, NSError *error))completion { + __weak typeof(self) weakSelf = self; + DSDocumentTransition *transition = [self profileDocumentTransitionInContext:context]; + if (!transition) { + if (completion) completion(nil, NO, ERROR_TRANSITION_NO_UPDATE); + return; + } + if ([self signStateTransition:transition]) + completion(transition, NO, nil); +} + +- (void)signAndPublishProfileWithCompletion:(void (^)(BOOL success, BOOL cancelled, NSError *error))completion { + [self signAndPublishProfileInContext:self.platformContext + withCompletion:completion]; +} + +- (void)signAndPublishProfileInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(BOOL success, BOOL cancelled, NSError *error))completion { + __weak typeof(self) weakSelf = self; + __block uint32_t profileDocumentRevision; + [context performBlockAndWait:^{ + DSDashpayUserEntity *matchingDashpayUser = [self matchingDashpayUserInContext:context]; + if (matchingDashpayUser.localProfileDocumentRevision > matchingDashpayUser.remoteProfileDocumentRevision) + matchingDashpayUser.localProfileDocumentRevision = matchingDashpayUser.remoteProfileDocumentRevision + 1; + profileDocumentRevision = matchingDashpayUser.localProfileDocumentRevision; + [context ds_save]; + }]; + [self signedProfileDocumentTransitionInContext:context + withCompletion:^(DSTransition *transition, BOOL cancelled, NSError *error) { + if (!transition) { + if (completion) completion(NO, cancelled, error); + return; + } + [self.DAPIClient publishTransition:transition + completionQueue:self.identityQueue + success:^(NSDictionary *_Nonnull successDictionary, BOOL added) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + if (completion) completion(NO, NO, ERROR_MEM_ALLOC); + return; + } + [context performBlockAndWait:^{ + [self matchingDashpayUserInContext:context].remoteProfileDocumentRevision = profileDocumentRevision; + [context ds_save]; + }]; + if (completion) + dispatch_async(dispatch_get_main_queue(), ^{ completion(YES, NO, nil); }); + } + failure:^(NSError *_Nonnull error) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(NO, NO, error); }); + }]; + }]; +} + +// + +// MARK: Fetching + +- (void)fetchProfileWithCompletion:(void (^)(BOOL success, NSError *error))completion { + dispatch_async(self.identityQueue, ^{ + [self fetchProfileInContext:self.platformContext + withCompletion:completion + onCompletionQueue:dispatch_get_main_queue()]; + }); +} + +- (void)fetchProfileInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(BOOL success, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + [self fetchProfileInContext:context + retryCount:DEFAULT_FETCH_PROFILE_RETRY_COUNT + withCompletion:completion + onCompletionQueue:completionQueue]; +} + +- (void)fetchProfileInContext:(NSManagedObjectContext *)context + retryCount:(uint32_t)retryCount + withCompletion:(void (^)(BOOL success, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + [self internalFetchProfileInContext:context + withCompletion:^(BOOL success, NSError *error) { + if (!success && retryCount > 0) { + [self fetchUsernamesInContext:context retryCount:retryCount - 1 withCompletion:completion onCompletionQueue:completionQueue]; + } else if (completion) { + completion(success, error); + } + } + onCompletionQueue:completionQueue]; +} + +- (void)internalFetchProfileInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(BOOL success, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + DPContract *dashpayContract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; + if ([dashpayContract contractState] != DPContractState_Registered) { + 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 + inContext:context + saveContext:YES + completion:^(BOOL success, NSError *_Nullable error) { + if (completion) dispatch_async(completionQueue, ^{ completion(success, error); }); + } + onCompletionQueue:self.identityQueue]; + } + onCompletionQueue:self.identityQueue]; +} + +- (void)applyProfileChanges:(DSTransientDashpayUser *)transientDashpayUser + inContext:(NSManagedObjectContext *)context + saveContext:(BOOL)saveContext + completion:(void (^)(BOOL success, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + if (![self isActive]) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, ERROR_IDENTITY_NO_LONGER_ACTIVE); }); + return; + } + __weak typeof(self) weakSelf = self; + dispatch_async(self.identityQueue, ^{ + [context performBlockAndWait:^{ + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + if (completion) completion(NO, ERROR_MEM_ALLOC); + return; + } + if (![self isActive]) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, ERROR_IDENTITY_NO_LONGER_ACTIVE); }); + return; + } + DSDashpayUserEntity *contact = [[self identityEntityInContext:context] matchingDashpayUser]; + 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) { + contact = [DSDashpayUserEntity managedObjectInBlockedContext:context]; + contact.chain = [strongSelf.wallet.chain chainEntityInContext:context]; + contact.associatedBlockchainIdentity = [strongSelf identityEntityInContext:context]; + } + NSError *error = [contact applyTransientDashpayUser:transientDashpayUser save:saveContext]; + if (error) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, error); }); + return; + } + } + [self saveProfileTimestamp]; + if (completion) dispatch_async(completionQueue, ^{ completion(YES, nil); }); + }]; + }); +} +@end diff --git a/DashSync/shared/Models/Identity/DSIdentity+Protected.h b/DashSync/shared/Models/Identity/DSIdentity+Protected.h new file mode 100644 index 000000000..c30abc1ca --- /dev/null +++ b/DashSync/shared/Models/Identity/DSIdentity+Protected.h @@ -0,0 +1,173 @@ +// +// Created by Sam Westrich +// Copyright © 2020 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DSIdentity.h" +#import "DSDAPIClient.h" +#import "DSDAPIPlatformNetworkService.h" +#import "DSIdentitiesManager.h" + +NS_ASSUME_NONNULL_BEGIN + +@class DSBlockchainIdentityEntity; + +@interface DSIdentity () +@property (nonatomic, weak) DSWallet *wallet; + +@property (nonatomic, readonly) DSBlockchainIdentityEntity *identityEntity; +@property (nullable, nonatomic, strong) DSTransientDashpayUser *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; +@property (nonatomic, strong) dispatch_queue_t identityQueue; +@property (nonatomic, strong) DSChain *chain; +@property (nonatomic, readonly) DSDAPIClient *DAPIClient; +@property (nonatomic, readonly) DSDAPIPlatformNetworkService *DAPINetworkService; +@property (nonatomic, readonly) DSIdentitiesManager *identitiesManager; +@property (nonatomic, assign) DKeyKind *currentMainKeyType; +@property (nonatomic, assign) BOOL isTransient; + +@property (nonatomic, assign) uint64_t lastCheckedIncomingContactsTimestamp; +@property (nonatomic, assign) uint64_t lastCheckedOutgoingContactsTimestamp; + +- (BOOL)isDashpayReady; +- (void)saveProfileTimestamp; + +- (DSBlockchainIdentityEntity *)identityEntityInContext:(NSManagedObjectContext *)context; + +- (instancetype)initWithIdentityEntity:(DSBlockchainIdentityEntity *)entity; + +//This one is called for a local identity that is being recreated from the network +- (instancetype)initAtIndex:(uint32_t)index + withUniqueId:(UInt256)uniqueId + inWallet:(DSWallet *)wallet + withIdentityEntity:(DSBlockchainIdentityEntity *)entity; + +//This one is called from an identity that was created locally by creating a credit funding transaction +- (instancetype)initAtIndex:(uint32_t)index + withLockedOutpoint:(DSUTXO)lockedOutpoint + inWallet:(DSWallet *)wallet + withIdentityEntity:(DSBlockchainIdentityEntity *)entity; + +- (instancetype)initAtIndex:(uint32_t)index + withLockedOutpoint:(DSUTXO)lockedOutpoint + inWallet:(DSWallet *)wallet + withIdentityEntity:(DSBlockchainIdentityEntity *)entity + associatedToInvitation:(DSInvitation *)invitation; + +- (instancetype)initWithUniqueId:(UInt256)uniqueId + isTransient:(BOOL)isTransient + onChain:(DSChain *)chain; + +- (instancetype)initAtIndex:(uint32_t)index + inWallet:(DSWallet *)wallet; + +- (instancetype)initAtIndex:(uint32_t)index + withLockedOutpoint:(DSUTXO)lockedOutpoint + inWallet:(DSWallet *)wallet; + +- (instancetype)initAtIndex:(uint32_t)index + withUniqueId:(UInt256)uniqueId + inWallet:(DSWallet *)wallet; + +- (instancetype)initAtIndex:(uint32_t)index + withIdentityDictionary:(NSDictionary *)identityDictionary + version:(uint32_t)version + inWallet:(DSWallet *)wallet; +- (instancetype)initAtIndex:(uint32_t)index + uniqueId:(UInt256)uniqueId + balance:(uint64_t)balance + public_keys:(std_collections_Map_keys_dpp_identity_identity_public_key_KeyID_values_dpp_identity_identity_public_key_IdentityPublicKey *)public_keys + inWallet:(DSWallet *)wallet; + +- (instancetype)initAtIndex:(uint32_t)index + withAssetLockTransaction:(DSAssetLockTransaction *)transaction + withUsernameDictionary:(NSDictionary *_Nullable)usernameDictionary + inWallet:(DSWallet *)wallet; + +- (instancetype)initAtIndex:(uint32_t)index + withAssetLockTransaction:(DSAssetLockTransaction *)transaction + withUsernameDictionary:(NSDictionary *_Nullable)usernameDictionary + havingCredits:(uint64_t)credits + registrationStatus:(DSIdentityRegistrationStatus)registrationStatus + inWallet:(DSWallet *)wallet; + + +- (void)addKey:(DMaybeOpaqueKey *)key + atIndex:(uint32_t)index + ofType:(DKeyKind *)type + withStatus:(DSIdentityKeyStatus)status + save:(BOOL)save; +- (void)addKey:(DMaybeOpaqueKey *)key + atIndexPath:(NSIndexPath *)indexPath + ofType:(DKeyKind *)type + withStatus:(DSIdentityKeyStatus)status + save:(BOOL)save; +- (BOOL)registerKeyWithStatus:(DSIdentityKeyStatus)status + atIndexPath:(NSIndexPath *)indexPath + ofType:(DKeyKind *)type; +- (DMaybeOpaqueKey *_Nullable)privateKeyAtIndex:(uint32_t)index + ofType:(DKeyKind *)type; +- (DMaybeOpaqueKey *_Nullable)privateKeyAtIndex:(uint32_t)index + ofType:(DKeyKind *)type + forSeed:(NSData *)seed; +- (void)deletePersistentObjectAndSave:(BOOL)save + inContext:(NSManagedObjectContext *)context; + +- (void)saveInitial; + +- (void)saveInitialInContext:(NSManagedObjectContext *)context; + +- (void)registerInWalletForIdentityUniqueId:(UInt256)identityUniqueId; + +//- (void)registrationTransitionWithCompletion:(void (^_Nullable)(DSIdentityRegistrationTransition *_Nullable identityRegistrationTransition, NSError *_Nullable error))completion; + +- (void)createFundingPrivateKeyWithSeed:(NSData *)seed + isForInvitation:(BOOL)isForInvitation + completion:(void (^_Nullable)(BOOL success))completion; + + +- (void)setInvitationUniqueId:(UInt256)uniqueId; + +- (void)setInvitationAssetLockTransaction:(DSAssetLockTransaction *)transaction; + +//-(void)topupTransitionForForFundingTransaction:(DSTransaction*)fundingTransaction completion:(void (^ _Nullable)(DSIdentityTopupTransition * identityTopupTransition))completion; +// +//-(void)updateTransitionUsingNewIndex:(uint32_t)index completion:(void (^ _Nullable)(DSIdentityUpdateTransition * identityUpdateTransition))completion; + +- (void)fetchIfNeededNetworkStateInformation:(DSIdentityQueryStep)querySteps + inContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue; + +- (void)fetchNeededNetworkStateInformationInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue; +- (void)saveInContext:(NSManagedObjectContext *)context; +- (void)applyIdentityDictionary:(NSDictionary *)identityDictionary + version:(uint32_t)version + save:(BOOL)save + inContext:(NSManagedObjectContext *_Nullable)context; +- (uint32_t)firstIndexOfKeyOfType:(DKeyKind *)type + createIfNotPresent:(BOOL)createIfNotPresent + saveKey:(BOOL)saveKey; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Identity/DSIdentity+Username.h b/DashSync/shared/Models/Identity/DSIdentity+Username.h new file mode 100644 index 000000000..ade215b84 --- /dev/null +++ b/DashSync/shared/Models/Identity/DSIdentity+Username.h @@ -0,0 +1,63 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "DSBlockchainIdentityEntity+CoreDataClass.h" +#import "DSBlockchainIdentityUsernameEntity+CoreDataClass.h" +#import "DSIdentity.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSIdentity (Username) + +/*! @brief Related to DPNS. This is the list of usernames with their .dash domain that are associated to the identity in the domain "dash". These usernames however might not yet be registered or might be invalid. This can be used in tandem with the statusOfUsername: method */ +@property (nonatomic, readonly) NSArray *dashpayUsernameFullPaths; + +/*! @brief Related to DPNS. This is the list of usernames that are associated to the identity in the domain "dash". These usernames however might not yet be registered or might be invalid. This can be used in tandem with the statusOfUsername: method */ +@property (nonatomic, readonly) NSArray *dashpayUsernames; + +- (void)setupUsernames; +- (void)setupUsernames:(NSMutableDictionary *)statuses + salts:(NSMutableDictionary *)salts; + +- (void)applyUsernameEntitiesFromIdentityEntity:(DSBlockchainIdentityEntity *)identityEntity; +- (void)collectUsernameEntitiesIntoIdentityEntityInContext:(DSBlockchainIdentityEntity *)identityEntity + context:(NSManagedObjectContext *)context; + +- (void)addDashpayUsername:(NSString *)username save:(BOOL)save; +- (void)addUsername:(NSString *)username inDomain:(NSString *)domain save:(BOOL)save; +- (void)addUsername:(NSString *)username inDomain:(NSString *)domain status:(DSIdentityUsernameStatus)status save:(BOOL)save registerOnNetwork:(BOOL)registerOnNetwork; +- (DSIdentityUsernameStatus)statusOfUsername:(NSString *)username inDomain:(NSString *)domain; +- (DSIdentityUsernameStatus)statusOfDashpayUsername:(NSString *)username; +- (void)registerUsernamesWithCompletion:(void (^_Nullable)(BOOL success, NSError *error))completion; +- (void)fetchUsernamesWithCompletion:(void (^_Nullable)(BOOL success, NSError *error))completion; + +- (NSArray *)unregisteredUsernameFullPaths; +- (NSArray *)usernameFullPathsWithStatus:(DSIdentityUsernameStatus)usernameStatus; + +- (void)fetchUsernamesInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(BOOL success, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue; + +- (void)fetchUsernamesInContext:(NSManagedObjectContext *)context + retryCount:(uint32_t)retryCount + withCompletion:(void (^)(BOOL success, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Identity/DSIdentity+Username.m b/DashSync/shared/Models/Identity/DSIdentity+Username.m new file mode 100644 index 000000000..7f6d30f2e --- /dev/null +++ b/DashSync/shared/Models/Identity/DSIdentity+Username.m @@ -0,0 +1,951 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DPContract.h" +#import "DPDocumentFactory.h" +#import "DSIdentity+Protected.h" +#import "DSIdentity+Username.h" +#import "DSChainManager.h" +#import "DSDashPlatform.h" +#import "DSDocumentTransition.h" +#import "NSError+Dash.h" +#import "NSManagedObject+Sugar.h" +#import + +#define DEFAULT_FETCH_USERNAMES_RETRY_COUNT 5 + +#define ERROR_DPNS_CONTRACT_NOT_REGISTERED [NSError errorWithCode:500 localizedDescriptionKey:@"DPNS Contract is not yet registered on network"] +#define ERROR_TRANSITION_SIGNING [NSError errorWithCode:501 localizedDescriptionKey:@"Unable to sign transition"] + +NSString const *usernameStatusesKey = @"usernameStatusesKey"; +NSString const *usernameSaltsKey = @"usernameSaltsKey"; +NSString const *usernameDomainsKey = @"usernameDomainsKey"; + +@implementation DSIdentity (Username) + +- (NSMutableDictionary *)usernameStatuses { + return objc_getAssociatedObject(self, &usernameStatusesKey); +} +- (void)setUsernameStatuses:(NSMutableDictionary *)usernameStatuses { + objc_setAssociatedObject(self, &usernameStatusesKey, usernameStatuses, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSMutableDictionary *)usernameSalts { + return objc_getAssociatedObject(self, &usernameSaltsKey); +} +- (void)setUsernameSalts:(NSMutableDictionary *)usernameSalts { + objc_setAssociatedObject(self, &usernameSaltsKey, usernameSalts, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSMutableDictionary *)usernameDomains { + return objc_getAssociatedObject(self, &usernameDomainsKey); +} +- (void)setUsernameDomains:(NSMutableDictionary *)usernameDomains { + objc_setAssociatedObject(self, &usernameDomainsKey, usernameDomains, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void)setupUsernames { + self.usernameStatuses = [NSMutableDictionary dictionary]; + self.usernameSalts = [NSMutableDictionary dictionary]; +} +- (void)setupUsernames:(NSMutableDictionary *)statuses salts:(NSMutableDictionary *)salts { + self.usernameStatuses = statuses; + self.usernameSalts = salts; +} + +- (void)applyUsernameEntitiesFromIdentityEntity:(DSBlockchainIdentityEntity *)identityEntity { + for (DSBlockchainIdentityUsernameEntity *usernameEntity in identityEntity.usernames) { + NSData *salt = usernameEntity.salt; + if (salt) { + [self.usernameStatuses setObject:@{BLOCKCHAIN_USERNAME_PROPER: usernameEntity.stringValue, BLOCKCHAIN_USERNAME_DOMAIN: usernameEntity.domain ? usernameEntity.domain : @"", BLOCKCHAIN_USERNAME_STATUS: @(usernameEntity.status), BLOCKCHAIN_USERNAME_SALT: usernameEntity.salt} forKey:[self fullPathForUsername:usernameEntity.stringValue inDomain:usernameEntity.domain]]; + [self.usernameSalts setObject:usernameEntity.salt forKey:usernameEntity.stringValue]; + } else { + [self.usernameStatuses setObject:@{BLOCKCHAIN_USERNAME_PROPER: usernameEntity.stringValue, BLOCKCHAIN_USERNAME_DOMAIN: usernameEntity.domain ? usernameEntity.domain : @"", BLOCKCHAIN_USERNAME_STATUS: @(usernameEntity.status)} forKey:[self fullPathForUsername:usernameEntity.stringValue inDomain:usernameEntity.domain]]; + } + } +} + +- (void)collectUsernameEntitiesIntoIdentityEntityInContext:(DSBlockchainIdentityEntity *)identityEntity context:(NSManagedObjectContext *)context { + for (NSString *usernameFullPath in self.usernameStatuses) { + DSBlockchainIdentityUsernameEntity *usernameEntity = [DSBlockchainIdentityUsernameEntity managedObjectInBlockedContext:context]; + usernameEntity.status = [self statusOfUsernameFullPath:usernameFullPath]; + usernameEntity.stringValue = [self usernameOfUsernameFullPath:usernameFullPath]; + usernameEntity.domain = [self domainOfUsernameFullPath:usernameFullPath]; + usernameEntity.blockchainIdentity = identityEntity; + [identityEntity addUsernamesObject:usernameEntity]; + [identityEntity setDashpayUsername:usernameEntity]; + } +} + +// MARK: Usernames + +- (void)addDashpayUsername:(NSString *)username + save:(BOOL)save { + [self addUsername:username + inDomain:@"dash" + status:DSIdentityUsernameStatus_Initial + save:save + registerOnNetwork:YES]; +} + +- (void)addUsername:(NSString *)username + inDomain:(NSString *)domain + save:(BOOL)save { + [self addUsername:username + inDomain:domain + status:DSIdentityUsernameStatus_Initial + save:save + registerOnNetwork:YES]; +} + +- (void)addUsername:(NSString *)username + inDomain:(NSString *)domain + status:(DSIdentityUsernameStatus)status + save:(BOOL)save + registerOnNetwork:(BOOL)registerOnNetwork { + [self.usernameStatuses setObject:@{BLOCKCHAIN_USERNAME_STATUS: @(DSIdentityUsernameStatus_Initial), BLOCKCHAIN_USERNAME_PROPER: username, BLOCKCHAIN_USERNAME_DOMAIN: domain} + forKey:[self fullPathForUsername:username inDomain:domain]]; + if (save) + dispatch_async(self.identityQueue, ^{ + [self saveNewUsername:username + inDomain:domain + status:DSIdentityUsernameStatus_Initial + inContext:self.platformContext]; + if (registerOnNetwork && self.registered && status != DSIdentityUsernameStatus_Confirmed) + [self registerUsernamesWithCompletion:^(BOOL success, NSError *_Nonnull error) {}]; + }); +} + +- (DSIdentityUsernameStatus)statusOfUsername:(NSString *)username + inDomain:(NSString *)domain { + return [self statusOfUsernameFullPath:[self fullPathForUsername:username inDomain:domain]]; +} + +- (DSIdentityUsernameStatus)statusOfDashpayUsername:(NSString *)username { + return [self statusOfUsernameFullPath:[self fullPathForUsername:username inDomain:@"dash"]]; +} + +- (DSIdentityUsernameStatus)statusOfUsernameFullPath:(NSString *)usernameFullPath { + return [[[self.usernameStatuses objectForKey:usernameFullPath] objectForKey:BLOCKCHAIN_USERNAME_STATUS] unsignedIntegerValue]; +} + +- (NSString *)usernameOfUsernameFullPath:(NSString *)usernameFullPath { + return [[self.usernameStatuses objectForKey:usernameFullPath] objectForKey:BLOCKCHAIN_USERNAME_PROPER]; +} + +- (NSString *)domainOfUsernameFullPath:(NSString *)usernameFullPath { + return [[self.usernameStatuses objectForKey:usernameFullPath] objectForKey:BLOCKCHAIN_USERNAME_DOMAIN]; +} + +- (NSString *)fullPathForUsername:(NSString *)username + inDomain:(NSString *)domain { + return [[username lowercaseString] stringByAppendingFormat:@".%@", [domain lowercaseString]]; +} + +- (NSArray *)dashpayUsernameFullPaths { + return [self.usernameStatuses allKeys]; +} + +- (NSArray *)dashpayUsernames { + NSMutableArray *usernameArray = [NSMutableArray array]; + for (NSString *usernameFullPath in self.usernameStatuses) { + [usernameArray addObject:[self usernameOfUsernameFullPath:usernameFullPath]]; + } + return [usernameArray copy]; +} + +- (NSArray *)unregisteredUsernameFullPaths { + return [self usernameFullPathsWithStatus:DSIdentityUsernameStatus_Initial]; +} + +- (NSArray *)usernameFullPathsWithStatus:(DSIdentityUsernameStatus)usernameStatus { + NSMutableArray *unregisteredUsernames = [NSMutableArray array]; + for (NSString *username in self.usernameStatuses) { + NSDictionary *usernameInfo = self.usernameStatuses[username]; + DSIdentityUsernameStatus status = [usernameInfo[BLOCKCHAIN_USERNAME_STATUS] unsignedIntegerValue]; + if (status == usernameStatus) + [unregisteredUsernames addObject:username]; + } + return [unregisteredUsernames copy]; +} + +- (NSArray *)preorderedUsernameFullPaths { + NSMutableArray *unregisteredUsernames = [NSMutableArray array]; + for (NSString *username in self.usernameStatuses) { + NSDictionary *usernameInfo = self.usernameStatuses[username]; + DSIdentityUsernameStatus status = [usernameInfo[BLOCKCHAIN_USERNAME_STATUS] unsignedIntegerValue]; + if (status == DSIdentityUsernameStatus_Preordered) + [unregisteredUsernames addObject:username]; + } + return [unregisteredUsernames copy]; +} + +// MARK: Username Helpers + +- (NSData *)saltForUsernameFullPath:(NSString *)usernameFullPath + saveSalt:(BOOL)saveSalt + inContext:(NSManagedObjectContext *)context { + NSData *salt; + if ([self statusOfUsernameFullPath:usernameFullPath] == DSIdentityUsernameStatus_Initial || !(salt = [self.usernameSalts objectForKey:usernameFullPath])) { + UInt256 random256 = uint256_random; + salt = uint256_data(random256); + [self.usernameSalts setObject:salt forKey:usernameFullPath]; + if (saveSalt) + [self saveUsername:[self usernameOfUsernameFullPath:usernameFullPath] + inDomain:[self domainOfUsernameFullPath:usernameFullPath] + status:[self statusOfUsernameFullPath:usernameFullPath] + salt:salt + commitSave:YES inContext:context]; + } else { + salt = [self.usernameSalts objectForKey:usernameFullPath]; + } + return salt; +} + +- (NSMutableDictionary *)saltedDomainHashesForUsernameFullPaths:(NSArray *)usernameFullPaths + inContext:(NSManagedObjectContext *)context { + NSMutableDictionary *mSaltedDomainHashes = [NSMutableDictionary dictionary]; + for (NSString *unregisteredUsernameFullPath in usernameFullPaths) { + NSMutableData *saltedDomain = [NSMutableData data]; + NSData *salt = [self saltForUsernameFullPath:unregisteredUsernameFullPath saveSalt:YES inContext:context]; + NSData *usernameDomainData = [unregisteredUsernameFullPath dataUsingEncoding:NSUTF8StringEncoding]; + [saltedDomain appendData:salt]; + [saltedDomain appendData:usernameDomainData]; + mSaltedDomainHashes[unregisteredUsernameFullPath] = uint256_data([saltedDomain SHA256_2]); + [self.usernameSalts setObject:salt forKey:unregisteredUsernameFullPath]; + } + return [mSaltedDomainHashes copy]; +} + +- (void)saveNewUsername:(NSString *)username + inDomain:(NSString *)domain + status:(DSIdentityUsernameStatus)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; + usernameEntity.salt = [self saltForUsernameFullPath:[self fullPathForUsername:username inDomain:domain] saveSalt:NO inContext:context]; + usernameEntity.domain = domain; + [entity addUsernamesObject:usernameEntity]; + [entity setDashpayUsername:usernameEntity]; + [context ds_save]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSIdentityDidUpdateUsernameStatusNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSIdentityKey: self}]; + }); + }]; +} + +- (void)setUsernameFullPaths:(NSArray *)usernameFullPaths + toStatus:(DSIdentityUsernameStatus)status { + for (NSString *string in usernameFullPaths) { + NSMutableDictionary *usernameStatusDictionary = [[self.usernameStatuses objectForKey:string] mutableCopy]; + if (!usernameStatusDictionary) { + usernameStatusDictionary = [NSMutableDictionary dictionary]; + } + usernameStatusDictionary[BLOCKCHAIN_USERNAME_STATUS] = @(status); + [self.usernameStatuses setObject:[usernameStatusDictionary copy] forKey:string]; + } +} + +- (void)setAndSaveUsernameFullPaths:(NSArray *)usernameFullPaths + toStatus:(DSIdentityUsernameStatus)status + inContext:(NSManagedObjectContext *)context { + [self setUsernameFullPaths:usernameFullPaths toStatus:status]; + [self saveUsernamesInDictionary:[self.usernameStatuses dictionaryWithValuesForKeys:usernameFullPaths] toStatus:status inContext:context]; +} + +- (void)saveUsernameFullPaths:(NSArray *)usernameFullPaths + toStatus:(DSIdentityUsernameStatus)status + inContext:(NSManagedObjectContext *)context { + [self saveUsernamesInDictionary:[self.usernameStatuses dictionaryWithValuesForKeys:usernameFullPaths] toStatus:status inContext:context]; +} + +- (void)saveUsernamesInDictionary:(NSDictionary *)fullPathUsernamesDictionary + toStatus:(DSIdentityUsernameStatus)status + inContext:(NSManagedObjectContext *)context { + if (self.isTransient) return; + if (!self.isActive) return; + [context performBlockAndWait:^{ + for (NSString *fullPathUsername in fullPathUsernamesDictionary) { + NSString *username = [fullPathUsernamesDictionary[fullPathUsername] objectForKey:BLOCKCHAIN_USERNAME_PROPER]; + NSString *domain = [fullPathUsernamesDictionary[fullPathUsername] objectForKey:BLOCKCHAIN_USERNAME_DOMAIN]; + [self saveUsername:username inDomain:domain status:status salt:nil commitSave:NO inContext:context]; + } + [context ds_save]; + }]; +} + +- (void)saveUsernameFullPath:(NSString *)usernameFullPath + status:(DSIdentityUsernameStatus)status + salt:(NSData *)salt + commitSave:(BOOL)commitSave + inContext:(NSManagedObjectContext *)context { + if (self.isTransient) return; + if (!self.isActive) return; + [context performBlockAndWait:^{ + DSBlockchainIdentityEntity *entity = [self identityEntityInContext:context]; + NSSet *usernamesPassingTest = [entity.usernames objectsPassingTest:^BOOL(DSBlockchainIdentityUsernameEntity *_Nonnull obj, BOOL *_Nonnull stop) { + if ([[self fullPathForUsername:obj.stringValue inDomain:obj.domain] isEqualToString:usernameFullPath]) { + *stop = TRUE; + return TRUE; + + } else { + return FALSE; + } + }]; + 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]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSIdentityDidUpdateUsernameStatusNotification object:nil userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSIdentityKey: self, + DSIdentityUsernameKey: usernameEntity.stringValue, + DSIdentityUsernameDomainKey: usernameEntity.stringValue}]; + }); + } + }]; +} + +- (void)saveUsername:(NSString *)username + inDomain:(NSString *)domain + status:(DSIdentityUsernameStatus)status + salt:(NSData *)salt + commitSave:(BOOL)commitSave + inContext:(NSManagedObjectContext *)context { + if (self.isTransient) return; + if (!self.isActive) return; + [context performBlockAndWait:^{ + DSBlockchainIdentityEntity *entity = [self identityEntityInContext:context]; + NSSet *usernamesPassingTest = [entity.usernames objectsPassingTest:^BOOL(DSBlockchainIdentityUsernameEntity *_Nonnull obj, BOOL *_Nonnull stop) { + if ([obj.stringValue isEqualToString:username]) { + *stop = TRUE; + return TRUE; + + } else { + return FALSE; + } + }]; + 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]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSIdentityDidUpdateUsernameStatusNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSIdentityKey: self, DSIdentityUsernameKey: username, DSIdentityUsernameDomainKey: domain}]; + }); + } + }]; +} + + + +- (void)fetchUsernamesWithCompletion:(void (^)(BOOL, NSError *_Nonnull))completion { + [self fetchUsernamesInContext:self.platformContext + withCompletion:completion + onCompletionQueue:dispatch_get_main_queue()]; +} + +- (void)fetchUsernamesInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(BOOL success, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + [self fetchUsernamesInContext:context + retryCount:DEFAULT_FETCH_USERNAMES_RETRY_COUNT + withCompletion:completion + onCompletionQueue:completionQueue]; +} + +- (void)fetchUsernamesInContext:(NSManagedObjectContext *)context + retryCount:(uint32_t)retryCount + withCompletion:(void (^)(BOOL success, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + [self internalFetchUsernamesInContext:context + withCompletion:^(BOOL success, NSError *error) { + if (!success && retryCount > 0) { + [self fetchUsernamesInContext:context + retryCount:retryCount - 1 + withCompletion:completion + onCompletionQueue:completionQueue]; + } else if (completion) { + completion(success, error); + } + } + onCompletionQueue:completionQueue]; +} + +- (void)internalFetchUsernamesInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(BOOL success, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + __weak typeof(self) weakSelf = self; + DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dpnsContract; + if (contract.contractState != DPContractState_Registered) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, ERROR_DPNS_CONTRACT_NOT_REGISTERED); }); + return; + } + DSDAPIPlatformNetworkService *dapiNetworkService = self.DAPINetworkService; + [dapiNetworkService getDPNSDocumentsForIdentityWithUserId:self.uniqueIDData + completionQueue:self.identityQueue + success:^(NSArray *_Nonnull documents) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, ERROR_MEM_ALLOC); }); + return; + } + if (![documents count]) { + if (completion) dispatch_async(completionQueue, ^{ completion(YES, nil); }); + return; + } + //todo verify return is true + for (NSDictionary *nameDictionary in documents) { + NSString *username = nameDictionary[@"label"]; + NSString *lowercaseUsername = nameDictionary[@"normalizedLabel"]; + NSString *domain = nameDictionary[@"normalizedParentDomainName"]; + if (username && lowercaseUsername && domain) { + NSMutableDictionary *usernameStatusDictionary = [[self.usernameStatuses objectForKey:[self fullPathForUsername:lowercaseUsername inDomain:domain]] mutableCopy]; + BOOL isNew = FALSE; + if (!usernameStatusDictionary) { + usernameStatusDictionary = [NSMutableDictionary dictionary]; + isNew = TRUE; + usernameStatusDictionary[BLOCKCHAIN_USERNAME_DOMAIN] = domain; + usernameStatusDictionary[BLOCKCHAIN_USERNAME_PROPER] = username; + } + usernameStatusDictionary[BLOCKCHAIN_USERNAME_STATUS] = @(DSIdentityUsernameStatus_Confirmed); + [self.usernameStatuses setObject:[usernameStatusDictionary copy] forKey:[self fullPathForUsername:username inDomain:domain]]; + if (isNew) { + [self saveNewUsername:username + inDomain:domain + status:DSIdentityUsernameStatus_Confirmed + inContext:context]; + } else { + [self saveUsername:username + inDomain:domain + status:DSIdentityUsernameStatus_Confirmed + salt:nil + commitSave:YES + inContext:context]; + } + } + } + if (completion) dispatch_async(completionQueue, ^{ completion(YES, nil); }); + } + failure:^(NSError *_Nonnull error) { + if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node + [self.DAPIClient removeDAPINodeByAddress:dapiNetworkService.ipAddress]; + [self fetchUsernamesInContext:context + withCompletion:completion + onCompletionQueue:completionQueue]; + } else if (completion) { + dispatch_async(completionQueue, ^{ completion(NO, error); }); + } + }]; +} + +- (void)registerUsernamesWithCompletion:(void (^_Nullable)(BOOL success, NSError *error))completion { + [self registerUsernamesAtStage:DSIdentityUsernameStatus_Initial + inContext:self.platformContext completion:completion + onCompletionQueue:dispatch_get_main_queue()]; +} + +- (void)registerUsernamesAtStage:(DSIdentityUsernameStatus)status + inContext:(NSManagedObjectContext *)context + completion:(void (^_Nullable)(BOOL success, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + DSLog(@"registerUsernamesAtStage %lu", (unsigned long)status); + switch (status) { + case DSIdentityUsernameStatus_Initial: { + NSArray *usernameFullPaths = [self usernameFullPathsWithStatus:DSIdentityUsernameStatus_Initial]; + if (usernameFullPaths.count) { + [self registerPreorderedSaltedDomainHashesForUsernameFullPaths:usernameFullPaths + inContext:context + completion:^(BOOL success, NSError *error) { + if (success) { + [self registerUsernamesAtStage:DSIdentityUsernameStatus_PreorderRegistrationPending + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + } else if (completion) { + dispatch_async(completionQueue, ^{ completion(NO, error); }); + } + } + onCompletionQueue:self.identityQueue]; + } else { + [self registerUsernamesAtStage:DSIdentityUsernameStatus_PreorderRegistrationPending + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + } + break; + } + case DSIdentityUsernameStatus_PreorderRegistrationPending: { + NSArray *usernameFullPaths = [self usernameFullPathsWithStatus:DSIdentityUsernameStatus_PreorderRegistrationPending]; + NSDictionary *saltedDomainHashes = [self saltedDomainHashesForUsernameFullPaths:usernameFullPaths inContext:context]; + if (saltedDomainHashes.count) { + [self monitorForDPNSPreorderSaltedDomainHashes:saltedDomainHashes + withRetryCount:4 + inContext:context + completion:^(BOOL allFound, NSError *error) { + if (!error) { + if (!allFound) { + //todo: This needs to be done per username and not for all usernames + [self setAndSaveUsernameFullPaths:usernameFullPaths + toStatus:DSIdentityUsernameStatus_Initial + inContext:context]; + [self registerUsernamesAtStage:DSIdentityUsernameStatus_Initial + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + } else { + [self registerUsernamesAtStage:DSIdentityUsernameStatus_Preordered + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + } + } else { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, error); }); + } + } + onCompletionQueue:self.identityQueue]; + } else { + [self registerUsernamesAtStage:DSIdentityUsernameStatus_Preordered + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + } + break; + } + case DSIdentityUsernameStatus_Preordered: { + NSArray *usernameFullPaths = [self usernameFullPathsWithStatus:DSIdentityUsernameStatus_Preordered]; + if (usernameFullPaths.count) { + [self registerUsernameDomainsForUsernameFullPaths:usernameFullPaths + inContext:context + completion:^(BOOL success, NSError *error) { + if (success) { + [self registerUsernamesAtStage:DSIdentityUsernameStatus_RegistrationPending + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + } else if (completion) { + dispatch_async(completionQueue, ^{ completion(NO, error); }); + } + } + onCompletionQueue:self.identityQueue]; + } else { + [self registerUsernamesAtStage:DSIdentityUsernameStatus_RegistrationPending + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + } + break; + } + case DSIdentityUsernameStatus_RegistrationPending: { + NSArray *usernameFullPaths = [self usernameFullPathsWithStatus:DSIdentityUsernameStatus_RegistrationPending]; + if (usernameFullPaths.count) { + [self monitorForDPNSUsernameFullPaths:usernameFullPaths + withRetryCount:5 + inContext:context + completion:^(BOOL allFound, NSError *error) { + if (!error) { + if (!allFound) { + //todo: This needs to be done per username and not for all usernames + [self setAndSaveUsernameFullPaths:usernameFullPaths + toStatus:DSIdentityUsernameStatus_Preordered + inContext:context]; + [self registerUsernamesAtStage:DSIdentityUsernameStatus_Preordered + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + } else if (completion) { //all were found + dispatch_async(completionQueue, ^{ completion(YES, nil); }); + } + } else if (completion) { + dispatch_async(completionQueue, ^{ completion(NO, error); }); + } + } + onCompletionQueue:completionQueue]; + } else if (completion) { + dispatch_async(completionQueue, ^{ completion(YES, nil); }); + } + break; + } + default: + if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil); }); + break; + } +} + +// MARK: Documents + +- (NSArray *)preorderDocumentsForUnregisteredUsernameFullPaths:(NSArray *)unregisteredUsernameFullPaths + usingEntropyData:(NSData *)entropyData + inContext:(NSManagedObjectContext *)context + error:(NSError **)error { + NSMutableArray *usernamePreorderDocuments = [NSMutableArray array]; + for (NSData *saltedDomainHashData in [[self saltedDomainHashesForUsernameFullPaths:unregisteredUsernameFullPaths inContext:context] allValues]) { + DSStringValueDictionary *dataDictionary = @{ + @"saltedDomainHash": saltedDomainHashData + }; + DPDocument *document = [self.dpnsDocumentFactory documentOnTable:@"preorder" withDataDictionary:dataDictionary usingEntropy:entropyData error:error]; + if (*error) return nil; + [usernamePreorderDocuments addObject:document]; + } + return usernamePreorderDocuments; +} + +- (NSArray *)domainDocumentsForUnregisteredUsernameFullPaths:(NSArray *)unregisteredUsernameFullPaths + usingEntropyData:(NSData *)entropyData + inContext:(NSManagedObjectContext *)context + error:(NSError **)error { + NSMutableArray *usernameDomainDocuments = [NSMutableArray array]; + for (NSString *usernameFullPath in [self saltedDomainHashesForUsernameFullPaths:unregisteredUsernameFullPaths inContext:context]) { + NSString *username = [self usernameOfUsernameFullPath:usernameFullPath]; + NSString *domain = [self domainOfUsernameFullPath:usernameFullPath]; + DSStringValueDictionary *dataDictionary = @{ + @"label": username, + @"normalizedLabel": [username lowercaseString], + @"normalizedParentDomainName": domain, + @"preorderSalt": [self.usernameSalts objectForKey:usernameFullPath], + @"records": @{@"identity": uint256_data(self.uniqueID)}, + @"subdomainRules": @{@"allowSubdomains": @NO} + }; + DPDocument *document = [self.dpnsDocumentFactory documentOnTable:@"domain" + withDataDictionary:dataDictionary + usingEntropy:entropyData + error:error]; + if (*error) return nil; + [usernameDomainDocuments addObject:document]; + } + return usernameDomainDocuments; +} + +// MARK: Transitions + +- (DSDocumentTransition *)preorderTransitionForUnregisteredUsernameFullPaths:(NSArray *)unregisteredUsernameFullPaths + inContext:(NSManagedObjectContext *)context + error:(NSError **)error { + NSData *entropyData = uint256_random_data; + NSArray *usernamePreorderDocuments = [self preorderDocumentsForUnregisteredUsernameFullPaths:unregisteredUsernameFullPaths + usingEntropyData:entropyData + inContext:context + error:error]; + if (![usernamePreorderDocuments count]) return nil; + return [[DSDocumentTransition alloc] initForDocuments:usernamePreorderDocuments + withTransitionVersion:1 + identityUniqueId:self.uniqueID + onChain:self.chain]; +} + +- (DSDocumentTransition *)domainTransitionForUnregisteredUsernameFullPaths:(NSArray *)unregisteredUsernameFullPaths + inContext:(NSManagedObjectContext *)context + error:(NSError **)error { + NSData *entropyData = uint256_random_data; + NSArray *usernamePreorderDocuments = [self domainDocumentsForUnregisteredUsernameFullPaths:unregisteredUsernameFullPaths + usingEntropyData:entropyData + inContext:context + error:error]; + if (![usernamePreorderDocuments count]) return nil; + return [[DSDocumentTransition alloc] initForDocuments:usernamePreorderDocuments + withTransitionVersion:1 + identityUniqueId:self.uniqueID + onChain:self.chain]; +} + + +//Preorder stage +- (void)registerPreorderedSaltedDomainHashesForUsernameFullPaths:(NSArray *)usernameFullPaths + inContext:(NSManagedObjectContext *)context + completion:(void (^_Nullable)(BOOL success, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + NSError *error = nil; + DSDocumentTransition *transition = [self preorderTransitionForUnregisteredUsernameFullPaths:usernameFullPaths inContext:context error:&error]; + if (error || !transition) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, error); }); + return; + } + if ([self signStateTransition:transition]) { + //let's start by putting the usernames in an undetermined state + [self setAndSaveUsernameFullPaths:usernameFullPaths + toStatus:DSIdentityUsernameStatus_PreorderRegistrationPending + inContext:context]; + [self.DAPIClient publishTransition:transition + completionQueue:self.identityQueue + success:^(NSDictionary *_Nonnull successDictionary, BOOL added) { + [self setAndSaveUsernameFullPaths:usernameFullPaths + toStatus:DSIdentityUsernameStatus_Preordered + inContext:context]; + if (completion) dispatch_async(completionQueue, ^{ completion(YES, nil); }); + } + failure:^(NSError *_Nonnull error) { + DSLogPrivate(@"%@", error); + if (completion) dispatch_async(completionQueue, ^{ completion(NO, error); }); + }]; + } else { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, ERROR_TRANSITION_SIGNING); }); + } +} + +- (void)registerUsernameDomainsForUsernameFullPaths:(NSArray *)usernameFullPaths + inContext:(NSManagedObjectContext *)context + completion:(void (^_Nullable)(BOOL success, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + NSError *error = nil; + DSDocumentTransition *transition = [self domainTransitionForUnregisteredUsernameFullPaths:usernameFullPaths + inContext:context + error:&error]; + if (error || !transition) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, error); }); + return; + } + if ([self signStateTransition:transition]) { + [self setAndSaveUsernameFullPaths:usernameFullPaths + toStatus:DSIdentityUsernameStatus_RegistrationPending + inContext:context]; + [self.DAPIClient publishTransition:transition + completionQueue:self.identityQueue + success:^(NSDictionary *_Nonnull successDictionary, BOOL added) { + [self setAndSaveUsernameFullPaths:usernameFullPaths + toStatus:DSIdentityUsernameStatus_Confirmed + inContext:context]; + if (completion) dispatch_async(completionQueue, ^{ completion(YES, nil); }); + } + failure:^(NSError *_Nonnull error) { + DSLogPrivate(@"%@", error); + if (completion) dispatch_async(completionQueue, ^{ completion(NO, error); }); + }]; + } +} + + + + +- (void)monitorForDPNSUsernameFullPaths:(NSArray *)usernameFullPaths + withRetryCount:(uint32_t)retryCount + inContext:(NSManagedObjectContext *)context + completion:(void (^)(BOOL allFound, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + NSMutableDictionary *domains = [NSMutableDictionary dictionary]; + for (NSString *usernameFullPath in usernameFullPaths) { + NSArray *components = [usernameFullPath componentsSeparatedByString:@"."]; + NSString *domain = @""; + NSString *name = components[0]; + if (components.count > 1) { + NSArray *domainComponents = [components subarrayWithRange:NSMakeRange(1, components.count - 1)]; + domain = [domainComponents 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) { + [self monitorForDPNSUsernames:domains[domain] + inDomain:domain + withRetryCount:retryCount + inContext:context + completion:^(BOOL allFound, NSError *error) { + if (finished) return; + if (error && !finished) { + finished = TRUE; + if (completion) completion(NO, error); + return; + } + if (allFound) countAllFound++; + countReturned++; + if (countReturned == domains.count) { + finished = TRUE; + if (completion) completion(countAllFound == domains.count, nil); + } + } + onCompletionQueue:completionQueue]; //we can use completion queue directly here + } +} + +- (void)monitorForDPNSUsernames:(NSArray *)usernames + inDomain:(NSString *)domain + withRetryCount:(uint32_t)retryCount + inContext:(NSManagedObjectContext *)context + completion:(void (^)(BOOL allFound, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + __weak typeof(self) weakSelf = self; + DSDAPIPlatformNetworkService *dapiNetworkService = self.DAPINetworkService; + [dapiNetworkService getDPNSDocumentsForUsernames:usernames + inDomain:domain + completionQueue:self.identityQueue + success:^(id _Nonnull domainDocumentArray) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) return; + if ([domainDocumentArray isKindOfClass:[NSArray class]]) { + NSMutableArray *usernamesLeft = [usernames mutableCopy]; + for (NSString *username in usernames) { + for (NSDictionary *domainDocument in domainDocumentArray) { + NSString *normalizedLabel = domainDocument[@"normalizedLabel"]; + NSString *label = domainDocument[@"label"]; + NSString *normalizedParentDomainName = domainDocument[@"normalizedParentDomainName"]; + if ([normalizedLabel isEqualToString:[username lowercaseString]]) { + NSMutableDictionary *usernameStatusDictionary = [[self.usernameStatuses objectForKey:username] mutableCopy]; + if (!usernameStatusDictionary) { + usernameStatusDictionary = [NSMutableDictionary dictionary]; + usernameStatusDictionary[BLOCKCHAIN_USERNAME_DOMAIN] = normalizedParentDomainName; + usernameStatusDictionary[BLOCKCHAIN_USERNAME_PROPER] = label; + } + usernameStatusDictionary[BLOCKCHAIN_USERNAME_STATUS] = @(DSIdentityUsernameStatus_Confirmed); + [self.usernameStatuses setObject:[usernameStatusDictionary copy] + forKey:[self fullPathForUsername:username inDomain:@"dash"]]; + [strongSelf saveUsername:username + inDomain:normalizedParentDomainName + status:DSIdentityUsernameStatus_Confirmed + salt:nil + commitSave:YES + inContext:context]; + [usernamesLeft removeObject:username]; + } + } + } + if ([usernamesLeft count] && retryCount > 0) { + [strongSelf monitorForDPNSUsernames:usernamesLeft + inDomain:domain + withRetryCount:retryCount - 1 + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + } else if (completion) { + dispatch_async(completionQueue, ^{ completion(![usernamesLeft count], nil); }); + } + } else if (retryCount > 0) { + [strongSelf monitorForDPNSUsernames:usernames + inDomain:domain + withRetryCount:retryCount - 1 + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + } else if (completion) { + dispatch_async(completionQueue, ^{ completion(NO, ERROR_MALFORMED_RESPONSE); }); + } + } + failure:^(NSError *_Nonnull error) { + if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node + [self.DAPIClient removeDAPINodeByAddress:dapiNetworkService.ipAddress]; + } + if (retryCount > 0) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), self.identityQueue, ^{ + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) return; + [strongSelf monitorForDPNSUsernames:usernames + inDomain:domain + withRetryCount:retryCount - 1 + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + }); + } else { + dispatch_async(completionQueue, ^{ completion(FALSE, error); }); + } + }]; +} + + +- (void)monitorForDPNSPreorderSaltedDomainHashes:(NSDictionary *)saltedDomainHashes + withRetryCount:(uint32_t)retryCount + inContext:(NSManagedObjectContext *)context + completion:(void (^)(BOOL allFound, NSError *error))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + __weak typeof(self) weakSelf = self; + DSDAPIPlatformNetworkService *dapiNetworkService = self.DAPINetworkService; + [dapiNetworkService getDPNSDocumentsForPreorderSaltedDomainHashes:[saltedDomainHashes allValues] + completionQueue:self.identityQueue + success:^(id _Nonnull preorderDocumentArray) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, ERROR_MEM_ALLOC); }); + return; + } + if ([preorderDocumentArray isKindOfClass:[NSArray class]]) { + NSMutableArray *usernamesLeft = [[saltedDomainHashes allKeys] mutableCopy]; + for (NSString *usernameFullPath in saltedDomainHashes) { + NSData *saltedDomainHashData = saltedDomainHashes[usernameFullPath]; + for (NSDictionary *preorderDocument in preorderDocumentArray) { + if ([preorderDocument[@"saltedDomainHash"] isEqualToData:saltedDomainHashData]) { + NSMutableDictionary *usernameStatusDictionary = [[self.usernameStatuses objectForKey:usernameFullPath] mutableCopy]; + if (!usernameStatusDictionary) + usernameStatusDictionary = [NSMutableDictionary dictionary]; + usernameStatusDictionary[BLOCKCHAIN_USERNAME_STATUS] = @(DSIdentityUsernameStatus_Preordered); + [self.usernameStatuses setObject:[usernameStatusDictionary copy] forKey:usernameFullPath]; + [strongSelf saveUsernameFullPath:usernameFullPath status:DSIdentityUsernameStatus_Preordered salt:nil commitSave:YES inContext:context]; + [usernamesLeft removeObject:usernameFullPath]; + } + } + } + if ([usernamesLeft count] && retryCount > 0) { + [strongSelf monitorForDPNSPreorderSaltedDomainHashes:[saltedDomainHashes dictionaryWithValuesForKeys:usernamesLeft] + withRetryCount:retryCount - 1 + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + } else if (completion) { + dispatch_async(completionQueue, ^{ completion(![usernamesLeft count], nil); }); + } + } else if (retryCount > 0) { + [strongSelf monitorForDPNSPreorderSaltedDomainHashes:saltedDomainHashes + withRetryCount:retryCount - 1 + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + } else if (completion) { + dispatch_async(completionQueue, ^{ completion(NO, ERROR_MALFORMED_RESPONSE); }); + } + } + failure:^(NSError *_Nonnull error) { + if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node + [self.DAPIClient removeDAPINodeByAddress:dapiNetworkService.ipAddress]; + } + if (retryCount > 0) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), self.identityQueue, ^{ + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + if (completion) dispatch_async(completionQueue, ^{ completion(NO, ERROR_MEM_ALLOC); }); + return; + } + [strongSelf monitorForDPNSPreorderSaltedDomainHashes:saltedDomainHashes + withRetryCount:retryCount - 1 + inContext:context + completion:completion + onCompletionQueue:completionQueue]; + }); + } else if (completion) { + dispatch_async(completionQueue, ^{ completion(FALSE, error); }); + } + }]; +} + +@end diff --git a/DashSync/shared/Models/Identity/DSIdentity.h b/DashSync/shared/Models/Identity/DSIdentity.h new file mode 100644 index 000000000..41c49d56e --- /dev/null +++ b/DashSync/shared/Models/Identity/DSIdentity.h @@ -0,0 +1,292 @@ +// +// DSIdentity.h +// DashSync +// +// Created by Sam Westrich on 7/26/18. +// + +#import "BigIntTypes.h" +#import "DSDerivationPath.h" +#import "DSKeyManager.h" +#import + +NS_ASSUME_NONNULL_BEGIN +@class DSWallet, DSIdentityRegistrationTransition, DSIdentityTopupTransition, DSIdentityUpdateTransition, DSIdentityCloseTransition, DSAccount, DSChain, DSTransition, DSDashpayUserEntity, DSPotentialOneWayFriendship, DSTransaction, DSFriendRequestEntity, DSPotentialContact, DSAssetLockTransaction, DSDocumentTransition, DPDocumentFactory, DSTransientDashpayUser, DSInvitation, DSAuthenticationKeysDerivationPath, UIImage; + +typedef NS_ENUM(NSUInteger, DSIdentityRegistrationStep) +{ + DSIdentityRegistrationStep_None = 0, + DSIdentityRegistrationStep_FundingTransactionCreation = 1, + DSIdentityRegistrationStep_FundingTransactionAccepted = 2, + DSIdentityRegistrationStep_LocalInWalletPersistence = 4, + DSIdentityRegistrationStep_ProofAvailable = 8, + DSIdentityRegistrationStep_L1Steps = DSIdentityRegistrationStep_FundingTransactionCreation | DSIdentityRegistrationStep_FundingTransactionAccepted | DSIdentityRegistrationStep_LocalInWalletPersistence | DSIdentityRegistrationStep_ProofAvailable, + DSIdentityRegistrationStep_Identity = 16, + DSIdentityRegistrationStep_RegistrationSteps = DSIdentityRegistrationStep_L1Steps | DSIdentityRegistrationStep_Identity, + DSIdentityRegistrationStep_Username = 32, + DSIdentityRegistrationStep_RegistrationStepsWithUsername = DSIdentityRegistrationStep_RegistrationSteps | DSIdentityRegistrationStep_Username, + DSIdentityRegistrationStep_Profile = 64, + DSIdentityRegistrationStep_RegistrationStepsWithUsernameAndDashpayProfile = DSIdentityRegistrationStep_RegistrationStepsWithUsername | DSIdentityRegistrationStep_Profile, + DSIdentityRegistrationStep_All = DSIdentityRegistrationStep_RegistrationStepsWithUsernameAndDashpayProfile, + 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 + DSIdentityQueryStep_Identity = DSIdentityRegistrationStep_Identity, //16 + DSIdentityQueryStep_Username = DSIdentityRegistrationStep_Username, //32 + DSIdentityQueryStep_Profile = DSIdentityRegistrationStep_Profile, //64 + DSIdentityQueryStep_IncomingContactRequests = 128, + DSIdentityQueryStep_OutgoingContactRequests = 256, + DSIdentityQueryStep_ContactRequests = DSIdentityQueryStep_IncomingContactRequests | DSIdentityQueryStep_OutgoingContactRequests, + DSIdentityQueryStep_AllForForeignIdentity = DSIdentityQueryStep_Identity | DSIdentityQueryStep_Username | DSIdentityQueryStep_Profile, + DSIdentityQueryStep_AllForLocalIdentity = DSIdentityQueryStep_Identity | DSIdentityQueryStep_Username | DSIdentityQueryStep_Profile | DSIdentityQueryStep_ContactRequests, + DSIdentityQueryStep_NoIdentity = 1 << 28, + DSIdentityQueryStep_BadQuery = 1 << 29, + DSIdentityQueryStep_Cancelled = 1 << 30 +}; + +typedef NS_ENUM(NSUInteger, DSIdentityRegistrationStatus) +{ + DSIdentityRegistrationStatus_Unknown = 0, + DSIdentityRegistrationStatus_Registered = 1, + DSIdentityRegistrationStatus_Registering = 2, + DSIdentityRegistrationStatus_NotRegistered = 3, //sent to DAPI, not yet confirmed +}; + +typedef NS_ENUM(NSUInteger, DSIdentityUsernameStatus) +{ + DSIdentityUsernameStatus_NotPresent = 0, + DSIdentityUsernameStatus_Initial = 1, + DSIdentityUsernameStatus_PreorderRegistrationPending = 2, + DSIdentityUsernameStatus_Preordered = 3, + DSIdentityUsernameStatus_RegistrationPending = 4, //sent to DAPI, not yet confirmed + DSIdentityUsernameStatus_Confirmed = 5, + DSIdentityUsernameStatus_TakenOnNetwork = 6, +}; + +typedef NS_ENUM(NSUInteger, DSIdentityFriendshipStatus) +{ + DSIdentityFriendshipStatus_Unknown = NSUIntegerMax, + DSIdentityFriendshipStatus_None = 0, + DSIdentityFriendshipStatus_Outgoing = 1, + DSIdentityFriendshipStatus_Incoming = 2, + DSIdentityFriendshipStatus_Friends = DSIdentityFriendshipStatus_Outgoing | DSIdentityFriendshipStatus_Incoming, +}; + +typedef NS_ENUM(NSUInteger, DSIdentityRetryDelayType) +{ + DSIdentityRetryDelayType_Linear = 0, + DSIdentityRetryDelayType_SlowingDown20Percent = 1, + DSIdentityRetryDelayType_SlowingDown50Percent = 2, +}; + +typedef NS_ENUM(NSUInteger, DSIdentityKeyStatus) +{ + DSIdentityKeyStatus_Unknown = 0, + DSIdentityKeyStatus_Registered = 1, + DSIdentityKeyStatus_Registering = 2, + DSIdentityKeyStatus_NotRegistered = 3, + DSIdentityKeyStatus_Revoked = 4, +}; + +#define BLOCKCHAIN_USERNAME_STATUS @"BLOCKCHAIN_USERNAME_STATUS" +#define BLOCKCHAIN_USERNAME_PROPER @"BLOCKCHAIN_USERNAME_PROPER" +#define BLOCKCHAIN_USERNAME_DOMAIN @"BLOCKCHAIN_USERNAME_DOMAIN" +#define BLOCKCHAIN_USERNAME_SALT @"BLOCKCHAIN_USERNAME_SALT" + +#define ERROR_MEM_ALLOC [NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"] +#define ERROR_MALFORMED_RESPONSE [NSError errorWithCode:501 localizedDescriptionKey:@"Malformed platform response"] + +FOUNDATION_EXPORT NSString *const DSIdentityDidUpdateNotification; +FOUNDATION_EXPORT NSString *const DSIdentityDidUpdateUsernameStatusNotification; +FOUNDATION_EXPORT NSString *const DSIdentityKey; +FOUNDATION_EXPORT NSString *const DSIdentityUsernameKey; +FOUNDATION_EXPORT NSString *const DSIdentityUsernameDomainKey; + +FOUNDATION_EXPORT NSString *const DSIdentityUpdateEvents; +FOUNDATION_EXPORT NSString *const DSIdentityUpdateEventKeyUpdate; +FOUNDATION_EXPORT NSString *const DSIdentityUpdateEventRegistration; +FOUNDATION_EXPORT NSString *const DSIdentityUpdateEventCreditBalance; +FOUNDATION_EXPORT NSString *const DSIdentityUpdateEventType; +FOUNDATION_EXPORT NSString *const DSIdentityUpdateEventDashpaySyncronizationBlockHash; + +@interface DSIdentity : NSObject + +/*! @brief This is the unique identifier representing the blockchain identity. It is derived from the credit funding transaction credit burn UTXO (as of dpp v10). Returned as a 256 bit number */ +@property (nonatomic, readonly) UInt256 uniqueID; +/*! @brief This is the unique identifier representing the blockchain identity. It is derived from the credit funding transaction credit burn UTXO (as of dpp v10). Returned as a base 58 string of a 256 bit number */ +@property (nonatomic, readonly) NSString *uniqueIdString; +/*! @brief This is the unique identifier representing the blockchain identity. It is derived from the credit funding transaction credit burn UTXO (as of dpp v10). Returned as a NSData of a 256 bit number */ +@property (nonatomic, readonly) NSData *uniqueIDData; +/*! @brief This is the outpoint of the registration credit funding transaction. It is used to determine the unique ID by double SHA256 its value. Returned as a UTXO { .hash , .n } */ +@property (nonatomic, readonly) DSUTXO lockedOutpoint; +/*! @brief This is the outpoint of the registration credit funding transaction. It is used to determine the unique ID by double SHA256 its value. Returned as an NSData of a UTXO { .hash , .n } */ +@property (nonatomic, readonly) NSData *lockedOutpointData; +/*! @brief This is if the blockchain identity is present in wallets or not. If this is false then the blockchain identity is known for example from being a dashpay friend. */ +@property (nonatomic, readonly) BOOL isLocal; +/*! @brief This is if the blockchain identity is made for being an invitation. All invitations should be marked as non local as well. */ +@property (nonatomic, readonly) BOOL isOutgoingInvitation; +/*! @brief This is if the blockchain identity is made from an invitation we received. */ +@property (nonatomic, readonly) BOOL isFromIncomingInvitation; +/*! @brief This is TRUE if the blockchain identity is an effemeral identity returned when searching. */ +@property (nonatomic, readonly) BOOL isTransient; +/*! @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; +/*! @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. */ +@property (nonatomic, weak, readonly) DSWallet *wallet; +/*! @brief This is invitation that is identity originated from. */ +@property (nonatomic, weak, readonly) DSInvitation *associatedInvitation; +/*! @brief This is the index of the blockchain identity in the wallet. The index is the top derivation used to derive an extended set of keys for the identity. No two local blockchain identities should be allowed to have the same index in a wallet. For example m/.../.../.../index/key */ +@property (nonatomic, readonly) uint32_t index; +/*! @brief Related to DPNS. This is current and most likely username associated to the identity. It is not necessarily registered yet on L2 however so its state should be determined with the statusOfUsername: method + @discussion There are situations where this is nil as it is not yet known or if no username has yet been set. */ +@property (nullable, nonatomic, readonly) NSString *currentDashpayUsername; +/*! @brief Related to registering the identity. This is the address used to fund the registration of the identity. Dash sent to this address in the special credit funding transaction will be converted to L2 credits */ +@property (nonatomic, readonly) NSString *registrationFundingAddress; +/*! @brief The known balance in credits of the identity */ +@property (nonatomic, readonly) uint64_t creditBalance; +/*! @brief The number of registered active keys that the blockchain identity has */ +@property (nonatomic, readonly) uint32_t activeKeyCount; +/*! @brief The number of all keys that the blockchain identity has, registered, in registration, or inactive */ +@property (nonatomic, readonly) uint32_t totalKeyCount; +/*! @brief This is the transaction on L1 that has an output that is used to fund the creation of this blockchain identity. + @discussion There are situations where this is nil as it is not yet known ; if the blockchain identity is being retrieved from L2 or if we are resyncing the chain. */ +@property (nullable, nonatomic, readonly) DSAssetLockTransaction *registrationAssetLockTransaction; +/*! @brief This is the hash of the transaction on L1 that has an output that is used to fund the creation of this blockchain identity. + @discussion There are situations where this is nil as it is not yet known ; if the blockchain identity is being retrieved from L2 or if we are resyncing the chain. */ +@property (nonatomic, readonly) UInt256 registrationAssetLockTransactionHash; +/*! @brief In our system a contact is a vue on a blockchain identity for Dashpay. A blockchain identity is therefore represented by a contact that will have relationships in the system. This is in the default backgroundContext. */ +@property (nonatomic, readonly) DSDashpayUserEntity *matchingDashpayUserInViewContext; +/*! @brief This is the status of the registration of the identity. It starts off in an initial status, and ends in a confirmed status */ +@property (nonatomic, readonly) DSIdentityRegistrationStatus registrationStatus; +/*! @brief This is the localized status of the registration of the identity returned as a string. It starts off in an initial status, and ends in a confirmed status */ +@property (nonatomic, readonly) NSString *localizedRegistrationStatusString; +/*! @brief This is a convenience method that checks to see if registrationStatus is confirmed */ +@property (nonatomic, readonly, getter=isRegistered) BOOL registered; +/*! @brief This is a convenience factory to quickly make dashpay documents */ +@property (nonatomic, readonly) DPDocumentFactory *dashpayDocumentFactory; +/*! @brief This is a convenience factory to quickly make dpns documents */ +@property (nonatomic, readonly) DPDocumentFactory *dpnsDocumentFactory; +/*! @brief DashpaySyncronizationBlock represents the last L1 block height for which Dashpay would be synchronized, if this isn't at the end of the chain then we need to query L2 to make sure we don't need to update our bloom filter */ +@property (nonatomic, readonly) uint32_t dashpaySyncronizationBlockHeight; +/*! @brief DashpaySyncronizationBlock represents the last L1 block hash for which Dashpay would be synchronized */ +@property (nonatomic, readonly) UInt256 dashpaySyncronizationBlockHash; + +// MARK: - Contracts + +- (void)fetchAndUpdateContract:(DPContract *)contract; + +// MARK: - Helpers + +- (DSDashpayUserEntity *)matchingDashpayUserInContext:(NSManagedObjectContext *)context; + +// MARK: - Identity + +- (void)registerOnNetwork:(DSIdentityRegistrationStep)steps + withFundingAccount:(DSAccount *)account + forTopupAmount:(uint64_t)topupDuffAmount + pinPrompt:(NSString *)prompt + stepCompletion:(void (^_Nullable)(DSIdentityRegistrationStep stepCompleted))stepCompletion + completion:(void (^_Nullable)(DSIdentityRegistrationStep stepsCompleted, NSError *error))completion; + +- (void)continueRegisteringOnNetwork:(DSIdentityRegistrationStep)steps + withFundingAccount:(DSAccount *)fundingAccount + forTopupAmount:(uint64_t)topupDuffAmount + pinPrompt:(NSString *)prompt + stepCompletion:(void (^_Nullable)(DSIdentityRegistrationStep stepCompleted))stepCompletion + completion:(void (^_Nullable)(DSIdentityRegistrationStep stepsCompleted, NSError *error))completion; + +- (void)continueRegisteringIdentityOnNetwork:(DSIdentityRegistrationStep)steps + stepsCompleted:(DSIdentityRegistrationStep)stepsAlreadyCompleted + stepCompletion:(void (^_Nullable)(DSIdentityRegistrationStep stepCompleted))stepCompletion + completion:(void (^_Nullable)(DSIdentityRegistrationStep stepsCompleted, NSError *error))completion; + +- (void)fetchIdentityNetworkStateInformationWithCompletion:(void (^)(BOOL success, BOOL found, NSError *error))completion; +- (void)fetchAllNetworkStateInformationWithCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion; +- (void)fetchNeededNetworkStateInformationWithCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion; +- (BOOL)signStateTransition:(DSTransition *)transition; +- (BOOL)signStateTransition:(DSTransition *)transition + forKeyIndex:(uint32_t)keyIndex + ofType:(DKeyKind *)signingAlgorithm; +//- (void)signMessageDigest:(UInt256)digest +// forKeyIndex:(uint32_t)keyIndex +// ofType:(DKeyKind *)signingAlgorithm +// completion:(void (^_Nullable)(BOOL success, NSData *signature))completion; +- (BOOL)verifySignature:(NSData *)signature + forKeyIndex:(uint32_t)keyIndex + ofType:(DKeyKind *)signingAlgorithm + forMessageDigest:(UInt256)messageDigest; +- (BOOL)verifySignature:(NSData *)signature + ofType:(DKeyKind *)signingAlgorithm + forMessageDigest:(UInt256)messageDigest; +- (void)createFundingPrivateKeyWithPrompt:(NSString *)prompt + completion:(void (^_Nullable)(BOOL success, BOOL cancelled))completion; +- (void)createFundingPrivateKeyForInvitationWithPrompt:(NSString *)prompt + completion:(void (^_Nullable)(BOOL success, BOOL cancelled))completion; +- (void)createAndPublishRegistrationTransitionWithCompletion:(void (^_Nullable)(BOOL success, NSError *_Nullable error))completion; + + +- (void)encryptData:(NSData *)data withKeyAtIndex:(uint32_t)index + forRecipientKey:(DOpaqueKey *)recipientKey + completion:(void (^_Nullable)(NSData *encryptedData))completion; + +/*! @brief Register the blockchain identity to its wallet. This should only be done once on the creation of the blockchain identity. +*/ +- (void)registerInWallet; + +/*! @brief Unregister the blockchain identity from the wallet. This should only be used if the blockchain identity is not yet registered or if a progressive wallet wipe is happening. + @discussion When a blockchain identity is registered on the network it is automatically retrieved from the L1 chain on resync. If a client wallet wishes to change their default blockchain identity in a wallet it should be done by marking the default blockchain identity index in the wallet. Clients should not try to delete a registered blockchain identity from a wallet. + */ +- (BOOL)unregisterLocally; + +/*! @brief Register the blockchain identity to its wallet from a credit funding registration transaction. This should only be done once on the creation of the blockchain identity. + @param fundingTransaction The funding transaction used to initially fund the blockchain identity. +*/ +- (void)registerInWalletForAssetLockTransaction:(DSAssetLockTransaction *)fundingTransaction; + +// MARK: - Keys + +/*! @brief Register the blockchain identity to its wallet from a credit funding registration transaction. This should only be done once on the creation of the blockchain identity. +*/ + +- (void)generateIdentityExtendedPublicKeysWithPrompt:(NSString *)prompt + completion:(void (^_Nullable)(BOOL registered))completion; +- (BOOL)setExternalFundingPrivateKey:(DMaybeOpaqueKey *)privateKey; +- (BOOL)hasIdentityExtendedPublicKeys; +- (DSIdentityKeyStatus)statusOfKeyAtIndex:(NSUInteger)index; +- (DKeyKind *)typeOfKeyAtIndex:(NSUInteger)index; +- (DMaybeOpaqueKey *_Nullable)keyAtIndex:(NSUInteger)index; +- (uint32_t)keyCountForKeyType:(DKeyKind *)keyType; ++ (NSString *)localizedStatusOfKeyForIdentityKeyStatus:(DSIdentityKeyStatus)status; +- (NSString *)localizedStatusOfKeyAtIndex:(NSUInteger)index; +- (DMaybeOpaqueKey *_Nullable)createNewKeyOfType:(DKeyKind *)type + saveKey:(BOOL)saveKey + returnIndex:(uint32_t *)rIndex; +- (DMaybeOpaqueKey *)keyOfType:(DKeyKind *)type atIndex:(uint32_t)rIndex; ++ (DSAuthenticationKeysDerivationPath *_Nullable)derivationPathForType:(DKeyKind *)type + forWallet:(DSWallet *)wallet; ++ (DMaybeOpaqueKey *_Nullable)keyFromKeyDictionary:(NSDictionary *)dictionary + rType:(uint32_t *)rType + rIndex:(uint32_t *)rIndex; ++ (DMaybeOpaqueKey *_Nullable)firstKeyInIdentityDictionary:(NSDictionary *)identityDictionary; + +- (BOOL)activePrivateKeysAreLoadedWithFetchingError:(NSError **)error; +- (BOOL)verifyKeysForWallet:(DSWallet *)wallet; + + + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Identity/DSIdentity.m b/DashSync/shared/Models/Identity/DSIdentity.m new file mode 100644 index 000000000..69afb83d0 --- /dev/null +++ b/DashSync/shared/Models/Identity/DSIdentity.m @@ -0,0 +1,2814 @@ +// +// DSIdentity.m +// DashSync +// +// Created by Sam Westrich on 7/26/18. +// +#import "DSIdentity.h" +#import "DPContract+Protected.h" +#import "DPDocumentFactory.h" +#import "DSAccount.h" +#import "DSAccountEntity+CoreDataClass.h" +#import "DSAssetLockDerivationPath.h" +#import "DSAssetLockTransaction.h" +#import "DSAuthenticationKeysDerivationPath.h" +#import "DSAuthenticationManager.h" +//#import "DSBlockchainIdentityEntity+CoreDataClass.h" +#import "DSBlockchainIdentityKeyPathEntity+CoreDataClass.h" +//#import "DSChain+Identity.h" +#import "DSChain+Params.h" +#import "DSChain+Protected.h" +#import "DSChain+Transaction.h" +#import "DSChain+Wallet.h" +#import "DSChainEntity+CoreDataClass.h" +#import "DSChainManager.h" +//#import "DSContactRequest.h" +////#import "DSContractTransition.h" +//#import "DSAssetLockDerivationPath.h" +#import "DSDashpayUserEntity+CoreDataClass.h" +#import "DSDerivationPathFactory.h" +//#import "DSDocumentTransition.h" +#import "DSIdentitiesManager+Protected.h" +#import "DSIdentitiesManager+CoreData.h" +#import "DSIdentity+ContactRequest.h" +//#import "DSIdentity+Friendship.h" +#import "DSIdentity+Profile.h" +#import "DSIdentity+Protected.h" +#import "DSIdentity+Username.h" +//#import "DSIdentityRegistrationTransition.h" +#import "DSInstantSendTransactionLock.h" +#import "DSInvitation+Protected.h" +#import "DSFriendRequestEntity+CoreDataClass.h" +#import "DSMerkleBlock.h" +#import "DSOptionsManager.h" +#import "DSTransactionHashEntity+CoreDataClass.h" +#import "DSTransition.h" +//#import "DSTransactionInput.h" +//#import "DSTransactionManager+Protected.h" +//#import "DSTransactionOutput.h" +//#import "DSTransientDashpayUser.h" +#import "DSWallet+Identity.h" +#import "NSData+Encryption.h" +#import "NSError+Dash.h" +#import "NSError+Platform.h" +#import "NSIndexPath+Dash.h" +#import "NSManagedObject+Sugar.h" +//#import "NSString+Bitcoin.h" + +#define BLOCKCHAIN_USER_UNIQUE_IDENTIFIER_KEY @"BLOCKCHAIN_USER_UNIQUE_IDENTIFIER_KEY" +#define DEFAULT_FETCH_IDENTITY_RETRY_COUNT 5 + +#define ERROR_REGISTER_KEYS_BEFORE_IDENTITY [NSError errorWithCode:500 localizedDescriptionKey:@"The blockchain identity extended public keys need to be registered before you can register a identity."] +#define ERROR_FUNDING_TX_CREATION [NSError errorWithCode:500 localizedDescriptionKey:@"Funding transaction could not be created"] +#define ERROR_FUNDING_TX_SIGNING [NSError errorWithCode:500 localizedDescriptionKey:@"Transaction could not be signed"] +#define ERROR_FUNDING_TX_TIMEOUT [NSError errorWithCode:500 localizedDescriptionKey:@"Timeout while waiting for funding transaction to be accepted by network"] +#define ERROR_FUNDING_TX_ISD_TIMEOUT [NSError errorWithCode:500 localizedDescriptionKey:@"Timeout while waiting for funding transaction to acquire an instant send lock"] +#define ERROR_REG_TRANSITION [NSError errorWithCode:501 localizedDescriptionKey:@"Unable to register registration transition"] +#define ERROR_REG_TRANSITION_CREATION [NSError errorWithCode:501 localizedDescriptionKey:@"Unable to create registration transition"] +#define ERROR_ATTEMPT_QUERY_WITHOUT_KEYS [NSError errorWithCode:501 localizedDescriptionKey:@"Attempt to query DAPs for identity with no active keys"] +#define ERROR_NO_FUNDING_PRV_KEY [NSError errorWithCode:500 localizedDescriptionKey:@"The blockchain identity funding private key should be first created with createFundingPrivateKeyWithCompletion"] +#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, +}; + +@interface DSIdentity () + +@property (nonatomic, assign) UInt256 uniqueID; +@property (nonatomic, assign) BOOL isOutgoingInvitation; +@property (nonatomic, assign) BOOL isFromIncomingInvitation; +@property (nonatomic, assign) DSUTXO lockedOutpoint; +@property (nonatomic, assign) uint32_t index; +@property (nonatomic, assign) DSIdentityRegistrationStatus registrationStatus; +@property (nonatomic, assign) uint64_t creditBalance; +@property (nonatomic, assign) uint32_t keysCreated; +@property (nonatomic, strong) NSMutableDictionary *keyInfoDictionaries; +@property (nonatomic, assign) uint32_t currentMainKeyIndex; +@property (nonatomic, strong) DPDocumentFactory *dashpayDocumentFactory; +@property (nonatomic, strong) DPDocumentFactory *dpnsDocumentFactory; +@property (nonatomic, strong) DSDashpayUserEntity *matchingDashpayUserInViewContext; +@property (nonatomic, strong) DSDashpayUserEntity *matchingDashpayUserInPlatformContext; +@property (nonatomic, assign) DMaybeOpaqueKey *internalRegistrationFundingPrivateKey; +@property (nonatomic, assign) UInt256 dashpaySyncronizationBlockHash; +@property (nonatomic, strong) DSAssetLockTransaction *registrationAssetLockTransaction; +@property (nonatomic, assign) uint64_t lastCheckedUsernamesTimestamp; +@property (nonatomic, assign) uint64_t lastCheckedProfileTimestamp; + +@end + +@implementation DSIdentity + +- (void)dealloc { + if (_internalRegistrationFundingPrivateKey != NULL) { + DMaybeOpaqueKeyDtor(_internalRegistrationFundingPrivateKey); + } +} +// MARK: - Initialization + +- (instancetype)initWithUniqueId:(UInt256)uniqueId + isTransient:(BOOL)isTransient onChain:(DSChain *)chain { + //this is the initialization of a non local blockchain 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 = dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor(); + [self setupUsernames]; + self.keyInfoDictionaries = [NSMutableDictionary dictionary]; + _registrationStatus = DSIdentityRegistrationStatus_Registered; + self.chain = chain; + return self; +} + +- (instancetype)initWithUniqueId:(UInt256)uniqueId isTransient:(BOOL)isTransient withCredits:(uint32_t)credits onChain:(DSChain *)chain { + //this is the initialization of a non local blockchain identity + if (!(self = [self initWithUniqueId:uniqueId isTransient:isTransient onChain:chain])) return nil; + _creditBalance = credits; + return self; +} + +- (void)saveProfileTimestamp { + [self.platformContext performBlockAndWait:^{ + self.lastCheckedProfileTimestamp = [[NSDate date] timeIntervalSince1970]; + //[self saveInContext:self.platformContext]; + }]; +} + +- (void)registerKeyFromKeyPathEntity:(DSBlockchainIdentityKeyPathEntity *)entity { + DKeyKind *keyType = dash_spv_crypto_keys_key_key_kind_from_index(entity.keyType); + DMaybeOpaqueKey *key = dash_spv_crypto_keys_key_KeyKind_key_with_public_key_data(keyType, slice_ctor(entity.publicKeyData)); + [self registerKey:key withStatus:entity.keyStatus atIndex:entity.keyID ofType:keyType]; + +} +- (void)applyIdentityEntity:(DSBlockchainIdentityEntity *)identityEntity { + [self applyUsernameEntitiesFromIdentityEntity:identityEntity]; + _creditBalance = identityEntity.creditBalance; + _registrationStatus = identityEntity.registrationStatus; + _lastCheckedProfileTimestamp = identityEntity.lastCheckedProfileTimestamp; + _lastCheckedUsernamesTimestamp = identityEntity.lastCheckedUsernamesTimestamp; + _lastCheckedIncomingContactsTimestamp = identityEntity.lastCheckedIncomingContactsTimestamp; + _lastCheckedOutgoingContactsTimestamp = identityEntity.lastCheckedOutgoingContactsTimestamp; + + self.dashpaySyncronizationBlockHash = identityEntity.dashpaySyncronizationBlockHash.UInt256; + for (DSBlockchainIdentityKeyPathEntity *keyPath in identityEntity.keyPaths) { + NSIndexPath *keyIndexPath = (NSIndexPath *)[keyPath path]; + + DKeyKind *keyType = dash_spv_crypto_keys_key_key_kind_from_index(keyPath.keyType); + if (keyIndexPath) { + BOOL success = [self registerKeyWithStatus:keyPath.keyStatus atIndexPath:[keyIndexPath softenAllItems] ofType:keyType]; + if (!success) + [self registerKeyFromKeyPathEntity:keyPath]; + } else { + [self registerKeyFromKeyPathEntity:keyPath]; + } + } + if (self.isLocal || self.isOutgoingInvitation) { + if (identityEntity.registrationFundingTransaction) { + self.registrationAssetLockTransactionHash = identityEntity.registrationFundingTransaction.transactionHash.txHash.UInt256; + } else { + NSData *transactionHashData = uint256_data(uint256_reverse(self.lockedOutpoint.hash)); + DSTransactionEntity *assetLockEntity = [DSTransactionEntity anyObjectInContext:identityEntity.managedObjectContext matching:@"transactionHash.txHash == %@", transactionHashData]; + if (assetLockEntity) { + self.registrationAssetLockTransactionHash = assetLockEntity.transactionHash.txHash.UInt256; + + DSAssetLockTransaction *registrationAssetLockTransaction = (DSAssetLockTransaction *)[assetLockEntity transactionForChain:self.chain]; + BOOL correctIndex; + if (self.isOutgoingInvitation) { + correctIndex = [registrationAssetLockTransaction checkInvitationDerivationPathIndexForWallet:self.wallet isIndex:self.index]; + } else { + correctIndex = [registrationAssetLockTransaction checkDerivationPathIndexForWallet:self.wallet isIndex:self.index]; + } + if (!correctIndex) { + NSAssert(FALSE, @"We should implement this"); + } + } + } + } +} + +- (instancetype)initWithIdentityEntity:(DSBlockchainIdentityEntity *)entity { + if (!(self = [self initWithUniqueId:entity.uniqueID.UInt256 isTransient:FALSE onChain:entity.chain.chain])) return nil; + [self applyIdentityEntity:entity]; + return self; +} + +- (instancetype)initAtIndex:(uint32_t)index + withLockedOutpoint:(DSUTXO)lockedOutpoint + inWallet:(DSWallet *)wallet + withIdentityEntity:(DSBlockchainIdentityEntity *)entity { + if (!(self = [self initAtIndex:index withLockedOutpoint:lockedOutpoint inWallet:wallet])) return nil; + [self applyIdentityEntity:entity]; + return self; +} + +- (instancetype)initAtIndex:(uint32_t)index + withUniqueId:(UInt256)uniqueId + inWallet:(DSWallet *)wallet + withIdentityEntity:(DSBlockchainIdentityEntity *)entity { + if (!(self = [self initAtIndex:index withUniqueId:uniqueId inWallet:wallet])) return nil; + [self applyIdentityEntity:entity]; + return self; +} + +- (instancetype)initAtIndex:(uint32_t)index + withLockedOutpoint:(DSUTXO)lockedOutpoint + inWallet:(DSWallet *)wallet + withIdentityEntity:(DSBlockchainIdentityEntity *)entity + associatedToInvitation:(DSInvitation *)invitation { + if (!(self = [self initAtIndex:index withLockedOutpoint:lockedOutpoint inWallet:wallet])) return nil; + [self setAssociatedInvitation:invitation]; + [self applyIdentityEntity:entity]; + 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.isLocal = YES; + self.isOutgoingInvitation = NO; + self.isTransient = FALSE; + self.keysCreated = 0; + self.currentMainKeyIndex = 0; + self.currentMainKeyType = dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor(); + self.index = index; + self.keyInfoDictionaries = [NSMutableDictionary dictionary]; + self.registrationStatus = DSIdentityRegistrationStatus_Unknown; + [self setupUsernames]; + self.chain = wallet.chain; + return self; +} + +- (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; + } +} + +- (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; +} + +- (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"); + self.lockedOutpoint = lockedOutpoint; + self.uniqueID = [dsutxo_data(lockedOutpoint) SHA256_2]; + return self; +} + +- (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; +} + +- (instancetype)initAtIndex:(uint32_t)index + withAssetLockTransaction:(DSAssetLockTransaction *)transaction + withUsernameDictionary:(NSDictionary *)usernameDictionary + inWallet:(DSWallet *)wallet { + NSAssert(index != UINT32_MAX, @"index must be found"); + if (!(self = [self initAtIndex:index withAssetLockTransaction:transaction inWallet:wallet])) return nil; + if (usernameDictionary) { + NSMutableDictionary *usernameSalts = [NSMutableDictionary dictionary]; + for (NSString *username in usernameDictionary) { + NSDictionary *subDictionary = usernameDictionary[username]; + NSData *salt = subDictionary[BLOCKCHAIN_USERNAME_SALT]; + if (salt) + usernameSalts[username] = salt; + } + [self setupUsernames:[usernameDictionary mutableCopy] salts:usernameSalts]; + } + return self; +} + +- (instancetype)initAtIndex:(uint32_t)index + withAssetLockTransaction:(DSAssetLockTransaction *)transaction + withUsernameDictionary:(NSDictionary *_Nullable)usernameDictionary + havingCredits:(uint64_t)credits + registrationStatus:(DSIdentityRegistrationStatus)registrationStatus + inWallet:(DSWallet *)wallet { + if (!(self = [self initAtIndex:index withAssetLockTransaction:transaction + withUsernameDictionary:usernameDictionary + inWallet:wallet])) return nil; + self.creditBalance = credits; + self.registrationStatus = registrationStatus; + return self; +} + +- (instancetype)initAtIndex:(uint32_t)index + withIdentityDictionary:(NSDictionary *)identityDictionary + version:(uint32_t)version + inWallet:(DSWallet *)wallet { + NSParameterAssert(wallet); + if (!(self = [super init])) return nil; + self.wallet = wallet; + self.isLocal = YES; + self.isOutgoingInvitation = NO; + self.isTransient = FALSE; + self.keysCreated = 0; + self.currentMainKeyIndex = 0; + self.currentMainKeyType = dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor(); + NSData *identityIdData = [identityDictionary objectForKey:@"id"]; + self.uniqueID = identityIdData.UInt256; + self.keyInfoDictionaries = [NSMutableDictionary dictionary]; + self.registrationStatus = DSIdentityRegistrationStatus_Registered; + [self setupUsernames]; + self.chain = wallet.chain; + self.index = index; + [self applyIdentityDictionary:identityDictionary version:version save:NO inContext:nil]; + return self; +} + +- (instancetype)initAtIndex:(uint32_t)index + uniqueId:(UInt256)uniqueId + balance:(uint64_t)balance + public_keys:(std_collections_Map_keys_dpp_identity_identity_public_key_KeyID_values_dpp_identity_identity_public_key_IdentityPublicKey *)public_keys + inWallet:(DSWallet *)wallet { + NSParameterAssert(wallet); + if (!(self = [super init])) return nil; + self.wallet = wallet; + self.isLocal = YES; + self.isOutgoingInvitation = NO; + self.isTransient = FALSE; + self.keysCreated = 0; + self.currentMainKeyIndex = 0; + self.currentMainKeyType = dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor(); + self.uniqueID = uniqueId; + self.keyInfoDictionaries = [NSMutableDictionary dictionary]; + self.registrationStatus = DSIdentityRegistrationStatus_Registered; + [self setupUsernames]; + self.chain = wallet.chain; + self.index = index; + self.creditBalance = balance; + for (int k = 0; k < public_keys->count; k++) { + [self addKey:dash_spv_platform_identity_manager_opaque_key_from_identity_public_key(public_keys->values[k]) + atIndex:index + ofType:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() + withStatus:DSIdentityKeyStatus_Registered + save:NO + inContext:nil]; + } + return self; +} + +- (dispatch_queue_t)identityQueue { + if (_identityQueue) return _identityQueue; + _identityQueue = self.chain.chainManager.identitiesManager.identityQueue; + return _identityQueue; +} + +// MARK: - Full Registration agglomerate + +- (DSIdentityRegistrationStep)stepsCompleted { + DSIdentityRegistrationStep stepsCompleted = DSIdentityRegistrationStep_None; + if (self.isRegistered) { + stepsCompleted = DSIdentityRegistrationStep_RegistrationSteps; + if ([self usernameFullPathsWithStatus:DSIdentityUsernameStatus_Confirmed].count) + stepsCompleted |= DSIdentityRegistrationStep_Username; + } else if (self.registrationAssetLockTransaction) { + stepsCompleted |= DSIdentityRegistrationStep_FundingTransactionCreation; + DSAccount *account = [self.chain firstAccountThatCanContainTransaction:self.registrationAssetLockTransaction]; + if (self.registrationAssetLockTransaction.blockHeight != TX_UNCONFIRMED || [account transactionIsVerified:self.registrationAssetLockTransaction]) + stepsCompleted |= DSIdentityRegistrationStep_FundingTransactionAccepted; + if ([self isRegisteredInWallet]) + stepsCompleted |= DSIdentityRegistrationStep_LocalInWalletPersistence; + if (self.registrationAssetLockTransaction.instantSendLockAwaitingProcessing) + stepsCompleted |= DSIdentityRegistrationStep_ProofAvailable; + } + return stepsCompleted; +} + +- (void)continueRegisteringProfileOnNetwork:(DSIdentityRegistrationStep)steps + stepsCompleted:(DSIdentityRegistrationStep)stepsAlreadyCompleted + stepCompletion:(void (^_Nullable)(DSIdentityRegistrationStep stepCompleted))stepCompletion + completion:(void (^_Nullable)(DSIdentityRegistrationStep stepsCompleted, NSError *error))completion { + __block DSIdentityRegistrationStep stepsCompleted = stepsAlreadyCompleted; + if (!(steps & DSIdentityRegistrationStep_Profile)) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(stepsCompleted, nil); }); + return; + } + //todo:we need to still do profile + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(stepsCompleted, nil); }); +} + +- (void)continueRegisteringUsernamesOnNetwork:(DSIdentityRegistrationStep)steps + stepsCompleted:(DSIdentityRegistrationStep)stepsAlreadyCompleted + stepCompletion:(void (^_Nullable)(DSIdentityRegistrationStep stepCompleted))stepCompletion + completion:(void (^_Nullable)(DSIdentityRegistrationStep stepsCompleted, NSError *error))completion { + __block DSIdentityRegistrationStep stepsCompleted = stepsAlreadyCompleted; + + if (!(steps & DSIdentityRegistrationStep_Username)) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(stepsCompleted, nil); }); + return; + } + [self registerUsernamesWithCompletion:^(BOOL success, NSError *_Nonnull error) { + if (!success) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(stepsCompleted, error); }); + return; + } + if (stepCompletion) dispatch_async(dispatch_get_main_queue(), ^{ stepCompletion(DSIdentityRegistrationStep_Username); }); + stepsCompleted |= DSIdentityRegistrationStep_Username; + [self continueRegisteringProfileOnNetwork:steps + stepsCompleted:stepsCompleted + stepCompletion:stepCompletion + completion:completion]; + }]; +} + +- (void)continueRegisteringIdentityOnNetwork:(DSIdentityRegistrationStep)steps + stepsCompleted:(DSIdentityRegistrationStep)stepsAlreadyCompleted + stepCompletion:(void (^_Nullable)(DSIdentityRegistrationStep stepCompleted))stepCompletion + completion:(void (^_Nullable)(DSIdentityRegistrationStep stepsCompleted, NSError *error))completion { + __block DSIdentityRegistrationStep stepsCompleted = stepsAlreadyCompleted; + if (!(steps & DSIdentityRegistrationStep_Identity)) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(stepsCompleted, nil); }); + return; + } + [self createAndPublishRegistrationTransitionWithCompletion:^(BOOL success, NSError *_Nullable error) { + if (error) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(stepsCompleted, error); }); + return; + } + if (stepCompletion) dispatch_async(dispatch_get_main_queue(), ^{ stepCompletion(DSIdentityRegistrationStep_Identity); }); + stepsCompleted |= DSIdentityRegistrationStep_Identity; + [self continueRegisteringUsernamesOnNetwork:steps + stepsCompleted:stepsCompleted + stepCompletion:stepCompletion + completion:completion]; + }]; +} + +- (void)continueRegisteringOnNetwork:(DSIdentityRegistrationStep)steps + withFundingAccount:(DSAccount *)fundingAccount + forTopupAmount:(uint64_t)topupDuffAmount + pinPrompt:(NSString *)prompt stepCompletion:(void (^_Nullable)(DSIdentityRegistrationStep stepCompleted))stepCompletion + completion:(void (^_Nullable)(DSIdentityRegistrationStep stepsCompleted, NSError *error))completion { + [self continueRegisteringOnNetwork:steps + withFundingAccount:fundingAccount + forTopupAmount:topupDuffAmount + pinPrompt:prompt + inContext:self.platformContext + stepCompletion:stepCompletion + completion:completion]; +} + +- (void)continueRegisteringOnNetwork:(DSIdentityRegistrationStep)steps + withFundingAccount:(DSAccount *)fundingAccount + forTopupAmount:(uint64_t)topupDuffAmount + pinPrompt:(NSString *)prompt + inContext:(NSManagedObjectContext *)context + stepCompletion:(void (^_Nullable)(DSIdentityRegistrationStep stepCompleted))stepCompletion + completion:(void (^_Nullable)(DSIdentityRegistrationStep stepsCompleted, NSError *error))completion { + if (!self.registrationAssetLockTransaction) { + [self registerOnNetwork:steps + withFundingAccount:fundingAccount + forTopupAmount:topupDuffAmount + pinPrompt:prompt + stepCompletion:stepCompletion + completion:completion]; + } else if (self.registrationStatus != DSIdentityRegistrationStatus_Registered) { + [self continueRegisteringIdentityOnNetwork:steps + stepsCompleted:DSIdentityRegistrationStep_L1Steps + stepCompletion:stepCompletion + completion:completion]; + } else if ([self.unregisteredUsernameFullPaths count]) { + [self continueRegisteringUsernamesOnNetwork:steps + stepsCompleted:DSIdentityRegistrationStep_L1Steps | DSIdentityRegistrationStep_Identity + stepCompletion:stepCompletion + completion:completion]; + } else if ([self matchingDashpayUserInContext:context].remoteProfileDocumentRevision < 1) { + [self continueRegisteringProfileOnNetwork:steps + stepsCompleted:DSIdentityRegistrationStep_L1Steps | DSIdentityRegistrationStep_Identity + stepCompletion:stepCompletion + completion:completion]; + } +} + + +- (void)registerOnNetwork:(DSIdentityRegistrationStep)steps + withFundingAccount:(DSAccount *)fundingAccount + forTopupAmount:(uint64_t)topupDuffAmount + pinPrompt:(NSString *)prompt + stepCompletion:(void (^_Nullable)(DSIdentityRegistrationStep stepCompleted))stepCompletion + completion:(void (^_Nullable)(DSIdentityRegistrationStep stepsCompleted, NSError *error))completion { + __block DSIdentityRegistrationStep stepsCompleted = DSIdentityRegistrationStep_None; + if (![self hasIdentityExtendedPublicKeys]) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(stepsCompleted, ERROR_REGISTER_KEYS_BEFORE_IDENTITY); }); + return; + } + if (!(steps & DSIdentityRegistrationStep_FundingTransactionCreation)) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(stepsCompleted, nil); }); + return; + } + NSString *assetLockRegistrationAddress = [self registrationFundingAddress]; + DSAssetLockTransaction *assetLockTransaction = [fundingAccount assetLockTransactionFor:topupDuffAmount to:assetLockRegistrationAddress withFee:YES]; + if (!assetLockTransaction) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(stepsCompleted, ERROR_FUNDING_TX_CREATION); }); + return; + } + [fundingAccount signTransaction:assetLockTransaction + withPrompt:prompt + completion:^(BOOL signedTransaction, BOOL cancelled) { + if (!signedTransaction) { + if (completion) + dispatch_async(dispatch_get_main_queue(), ^{ + if (cancelled) stepsCompleted |= DSIdentityRegistrationStep_Cancelled; + completion(stepsCompleted, cancelled ? nil : ERROR_FUNDING_TX_SIGNING); + }); + return; + } + if (stepCompletion) dispatch_async(dispatch_get_main_queue(), ^{ stepCompletion(DSIdentityRegistrationStep_FundingTransactionCreation); }); + stepsCompleted |= DSIdentityRegistrationStep_FundingTransactionCreation; + + //In wallet registration occurs now + + if (!(steps & DSIdentityRegistrationStep_LocalInWalletPersistence)) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(stepsCompleted, nil); }); + return; + } + if (self.isOutgoingInvitation) { + [self.associatedInvitation registerInWalletForAssetLockTransaction:assetLockTransaction]; + } else { + [self registerInWalletForAssetLockTransaction:assetLockTransaction]; + } + if (stepCompletion) dispatch_async(dispatch_get_main_queue(), ^{ stepCompletion(DSIdentityRegistrationStep_LocalInWalletPersistence); }); + stepsCompleted |= DSIdentityRegistrationStep_LocalInWalletPersistence; + if (!(steps & DSIdentityRegistrationStep_FundingTransactionAccepted)) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(stepsCompleted, nil); }); + return; + } + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + __block BOOL transactionSuccessfullyPublished = FALSE; + __block DSInstantSendTransactionLock *instantSendLock = nil; + __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:DSTransactionManagerTransactionStatusDidChangeNotification + object:nil + queue:nil + usingBlock:^(NSNotification *note) { + DSTransaction *tx = [note.userInfo objectForKey:DSTransactionManagerNotificationTransactionKey]; + if ([tx isEqual:assetLockTransaction]) { + NSDictionary *changes = [note.userInfo objectForKey:DSTransactionManagerNotificationTransactionChangesKey]; + if (changes) { + NSNumber *accepted = changes[DSTransactionManagerNotificationTransactionAcceptedStatusKey]; + NSNumber *lockVerified = changes[DSTransactionManagerNotificationInstantSendTransactionLockVerifiedKey]; + DSInstantSendTransactionLock *lock = changes[DSTransactionManagerNotificationInstantSendTransactionLockKey]; + if ([lockVerified boolValue] && lock != nil) { + instantSendLock = lock; + transactionSuccessfullyPublished = TRUE; + dispatch_semaphore_signal(sem); + } else if ([accepted boolValue]) { + transactionSuccessfullyPublished = TRUE; + } + } + } + }]; + [self.chain.chainManager.transactionManager publishTransaction:assetLockTransaction + completion:^(NSError *_Nullable error) { + if (error) { + [[NSNotificationCenter defaultCenter] removeObserver:observer]; + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(stepsCompleted, error); }); + return; + } + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, 25 * NSEC_PER_SEC)); + [[NSNotificationCenter defaultCenter] removeObserver:observer]; + if (!transactionSuccessfullyPublished) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(stepsCompleted, ERROR_FUNDING_TX_TIMEOUT); }); + return; + } + if (stepCompletion) dispatch_async(dispatch_get_main_queue(), ^{ stepCompletion(DSIdentityRegistrationStep_FundingTransactionAccepted); }); + stepsCompleted |= DSIdentityRegistrationStep_FundingTransactionAccepted; + if (!instantSendLock) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(stepsCompleted, ERROR_FUNDING_TX_ISD_TIMEOUT); }); + return; + } + if (stepCompletion) dispatch_async(dispatch_get_main_queue(), ^{ stepCompletion(DSIdentityRegistrationStep_ProofAvailable); }); + stepsCompleted |= DSIdentityRegistrationStep_ProofAvailable; + [self continueRegisteringIdentityOnNetwork:steps + stepsCompleted:stepsCompleted + stepCompletion:stepCompletion + completion:completion]; + }); + }]; + }]; +} + +// 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) { + DSAuthenticationKeysDerivationPath *derivationPathBLS = [[DSDerivationPathFactory sharedInstance] identityBLSKeysDerivationPathForWallet:self.wallet]; + DSAuthenticationKeysDerivationPath *derivationPathECDSA = [[DSDerivationPathFactory sharedInstance] identityECDSAKeysDerivationPathForWallet:self.wallet]; + DSAssetLockDerivationPath *derivationPathRegistrationFunding = [[DSDerivationPathFactory sharedInstance] identityRegistrationFundingDerivationPathForWallet:self.wallet]; + DSAssetLockDerivationPath *derivationPathTopupFunding = [[DSDerivationPathFactory sharedInstance] identityTopupFundingDerivationPathForWallet:self.wallet]; + return [derivationPathBLS hasExtendedPublicKey] + && [derivationPathECDSA hasExtendedPublicKey] + && [derivationPathRegistrationFunding hasExtendedPublicKey] + && [derivationPathTopupFunding hasExtendedPublicKey]; + } + if (_isOutgoingInvitation) { + DSAssetLockDerivationPath *derivationPathInvitationFunding = [[DSDerivationPathFactory sharedInstance] identityInvitationFundingDerivationPathForWallet:self.wallet]; + return [derivationPathInvitationFunding hasExtendedPublicKey]; + } + return NO; +} + +- (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; + if ([self hasIdentityExtendedPublicKeys]) { + if (completion) { + completion(YES); + } + return; + } + [[DSAuthenticationManager sharedInstance] seedWithPrompt:prompt + forWallet:self.wallet + forAmount:0 + forceAuthentication:NO + completion:^(NSData *_Nullable seed, BOOL cancelled) { + if (!seed) { + completion(NO); + return; + } + 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) { + 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) { + DSAssetLockDerivationPath *derivationPathInvitationFunding = [[DSDerivationPathFactory sharedInstance] identityInvitationFundingDerivationPathForWallet:self.wallet]; + [derivationPathInvitationFunding generateExtendedPublicKeyFromSeed:seed storeUnderWalletUniqueId:self.wallet.uniqueIDString]; + } + completion(YES); + }]; +} + +- (void)registerInWalletForAssetLockTransaction:(DSAssetLockTransaction *)fundingTransaction { + NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); + if (!_isLocal) return; + self.registrationAssetLockTransactionHash = fundingTransaction.txHash; + self.lockedOutpoint = fundingTransaction.lockedOutpoint; + [self registerInWalletForIdentityUniqueId:fundingTransaction.creditBurnIdentityIdentifier]; + //we need to also set the address of the funding transaction to being used so future identities past the initial gap limit are found + [fundingTransaction markAddressAsUsedInWallet:self.wallet]; +} + +- (void)registerInWalletForIdentityUniqueId:(UInt256)identityUniqueId { + NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); + if (!_isLocal) return; + self.uniqueID = identityUniqueId; + [self registerInWallet]; +} + +- (BOOL)isRegisteredInWallet { + NSAssert(_isLocal, @"This should not be performed on a non local blockchain identity"); + if (!_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; + [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; + 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]; + return TRUE; +} + +- (void)setInvitationUniqueId:(UInt256)uniqueId { + NSAssert(_isOutgoingInvitation, @"This can only be done on an invitation"); + if (!_isOutgoingInvitation) return; + self.uniqueID = uniqueId; +} + +- (void)setInvitationAssetLockTransaction:(DSAssetLockTransaction *)transaction { + NSParameterAssert(transaction); + NSAssert(_isOutgoingInvitation, @"This can only be done on an invitation"); + if (!_isOutgoingInvitation) return; + self.registrationAssetLockTransaction = transaction; + self.lockedOutpoint = transaction.lockedOutpoint; + +} + +// MARK: - Read Only Property Helpers + +- (BOOL)isActive { + if (self.isLocal) { + if (!self.wallet) return NO; + return self.wallet.identities[self.uniqueIDData] != nil; + } else { + return [self.chain.chainManager.identitiesManager foreignIdentityWithUniqueId:self.uniqueID] != nil; + } +} + +- (DSAssetLockTransaction *)registrationAssetLockTransaction { + if (!_registrationAssetLockTransaction) { + _registrationAssetLockTransaction = (DSAssetLockTransaction *)[self.chain transactionForHash:self.registrationAssetLockTransactionHash]; + } + return _registrationAssetLockTransaction; +} + +- (NSData *)uniqueIDData { + return uint256_data(self.uniqueID); +} + +- (NSData *)lockedOutpointData { + return dsutxo_data(self.lockedOutpoint); +} + +- (NSString *)currentDashpayUsername { + return [self.dashpayUsernames firstObject]; +} + +- (NSArray *)derivationPaths { + if (!_isLocal) return nil; + return [[DSDerivationPathFactory sharedInstance] unloadedSpecializedDerivationPathsForWallet:self.wallet]; +} + +- (NSString *)uniqueIdString { + return [uint256_data(self.uniqueID) base58String]; +} + +- (dispatch_queue_t)networkingQueue { + return self.chain.networkingQueue; +} + +- (NSManagedObjectContext *)platformContext { + // NSAssert(![NSThread isMainThread], @"We should not be on main thread"); + return [NSManagedObjectContext platformContext]; +} + +- (DSIdentitiesManager *)identitiesManager { + return self.chain.chainManager.identitiesManager; +} + +// ECDSA +- (DMaybeOpaqueKey *)registrationFundingPrivateKey { + return self.internalRegistrationFundingPrivateKey; +} + +// MARK: - Keys + +- (void)createFundingPrivateKeyWithSeed:(NSData *)seed + isForInvitation:(BOOL)isForInvitation + completion:(void (^_Nullable)(BOOL success))completion { + DSAssetLockDerivationPath *derivationPathRegistrationFunding; + if (isForInvitation) { + derivationPathRegistrationFunding = [[DSDerivationPathFactory sharedInstance] identityInvitationFundingDerivationPathForWallet:self.wallet]; + } else { + derivationPathRegistrationFunding = [[DSDerivationPathFactory sharedInstance] identityRegistrationFundingDerivationPathForWallet:self.wallet]; + } + + self.internalRegistrationFundingPrivateKey = [derivationPathRegistrationFunding privateKeyAtIndexPath:[NSIndexPath indexPathWithIndex:self.index] fromSeed:seed]; + BOOL ok = self.internalRegistrationFundingPrivateKey; + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(ok); }); +} + +- (BOOL)setExternalFundingPrivateKey:(DMaybeOpaqueKey *)privateKey { + if (!self.isFromIncomingInvitation) { + return FALSE; + } + self.internalRegistrationFundingPrivateKey = privateKey; + return self.internalRegistrationFundingPrivateKey; +} + +- (void)createFundingPrivateKeyForInvitationWithPrompt:(NSString *)prompt + completion:(void (^_Nullable)(BOOL success, BOOL cancelled))completion { + [self createFundingPrivateKeyWithPrompt:prompt + isForInvitation:YES + completion:completion]; +} + +- (void)createFundingPrivateKeyWithPrompt:(NSString *)prompt + completion:(void (^_Nullable)(BOOL success, BOOL cancelled))completion { + [self createFundingPrivateKeyWithPrompt:prompt + isForInvitation:NO + completion:completion]; +} + +- (void)createFundingPrivateKeyWithPrompt:(NSString *)prompt + isForInvitation:(BOOL)isForInvitation + completion:(void (^_Nullable)(BOOL success, BOOL cancelled))completion { + dispatch_async(dispatch_get_main_queue(), ^{ + [[DSAuthenticationManager sharedInstance] seedWithPrompt:prompt + forWallet:self.wallet + forAmount:0 + forceAuthentication:NO + completion:^(NSData *_Nullable seed, BOOL cancelled) { + if (!seed) { + if (completion) completion(NO, cancelled); + return; + } + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self createFundingPrivateKeyWithSeed:seed + isForInvitation:isForInvitation + completion:^(BOOL success) { + if (completion) completion(success, NO); + }]; + }); + }]; + }); +} + +- (BOOL)activePrivateKeysAreLoadedWithFetchingError:(NSError **)error { + BOOL loaded = TRUE; + for (NSNumber *index in self.keyInfoDictionaries) { + NSDictionary *keyDictionary = self.keyInfoDictionaries[index]; + DSIdentityKeyStatus status = [keyDictionary[@(DSIdentityKeyDictionary_KeyStatus)] unsignedIntValue]; + DKeyKind *keyType = [keyDictionary[@(DSIdentityKeyDictionary_KeyType)] pointerValue]; + if (status == DSIdentityKeyStatus_Registered) { + loaded &= [self hasPrivateKeyAtIndex:[index unsignedIntValue] ofType:keyType error:error]; + if (*error) return FALSE; + } + } + return loaded; +} + +- (uint32_t)activeKeyCount { + uint32_t rActiveKeys = 0; + for (NSNumber *index in self.keyInfoDictionaries) { + NSDictionary *keyDictionary = self.keyInfoDictionaries[index]; + DSIdentityKeyStatus status = [keyDictionary[@(DSIdentityKeyDictionary_KeyStatus)] unsignedIntValue]; + if (status == DSIdentityKeyStatus_Registered) rActiveKeys++; + } + return rActiveKeys; +} + +- (uint32_t)totalKeyCount { + return (uint32_t)self.keyInfoDictionaries.count; +} + +- (uint32_t)keyCountForKeyType:(DKeyKind *)keyType { + uint32_t keyCount = 0; + for (NSNumber *index in self.keyInfoDictionaries) { + NSDictionary *keyDictionary = self.keyInfoDictionaries[index]; + DKeyKind *type = [keyDictionary[@(DSIdentityKeyDictionary_KeyType)] pointerValue]; + if (dash_spv_crypto_keys_key_KeyKind_index(type) == dash_spv_crypto_keys_key_KeyKind_index(keyType)) keyCount++; + } + return keyCount; +} + +- (NSArray *)activeKeysForKeyType:(DKeyKind *)keyType { + NSMutableArray *activeKeys = [NSMutableArray array]; + for (NSNumber *index in self.keyInfoDictionaries) { + NSDictionary *keyDictionary = self.keyInfoDictionaries[index]; + DKeyKind *type = [keyDictionary[@(DSIdentityKeyDictionary_KeyType)] pointerValue]; + if (dash_spv_crypto_keys_key_KeyKind_index(type) == dash_spv_crypto_keys_key_KeyKind_index(keyType)) + [activeKeys addObject:keyDictionary[@(DSIdentityKeyDictionary_Key)]]; + } + return [activeKeys copy]; +} + +- (BOOL)verifyKeysForWallet:(DSWallet *)wallet { + DSWallet *originalWallet = self.wallet; + self.wallet = wallet; + for (uint32_t index = 0; index < self.keyInfoDictionaries.count; index++) { + DKeyKind *keyType = [self typeOfKeyAtIndex:index]; + DMaybeOpaqueKey *key = [self keyAtIndex:index]; + if (!key || !key->ok) { + self.wallet = originalWallet; + return FALSE; + } + + if (dash_spv_crypto_keys_key_KeyKind_index(keyType) != (int16_t) key->ok->tag) { + self.wallet = originalWallet; + return FALSE; + } + DMaybeOpaqueKey *derivedKey = [self publicKeyAtIndex:index ofType:keyType]; + if (!derivedKey || !derivedKey->ok) return NO; + BOOL isEqual = [DSKeyManager keysPublicKeyDataIsEqual:derivedKey->ok key2:key->ok]; + DMaybeOpaqueKeyDtor(derivedKey); + if (!isEqual) { + self.wallet = originalWallet; + return FALSE; + } + } + return TRUE; +} + +- (DSIdentityKeyStatus)statusOfKeyAtIndex:(NSUInteger)index { + return [[[self.keyInfoDictionaries objectForKey:@(index)] objectForKey:@(DSIdentityKeyDictionary_KeyStatus)] unsignedIntValue]; +} + +- (DKeyKind *)typeOfKeyAtIndex:(NSUInteger)index { + return [[[self.keyInfoDictionaries objectForKey:@(index)] objectForKey:@(DSIdentityKeyDictionary_KeyType)] pointerValue]; +} + +- (DMaybeOpaqueKey *_Nullable)keyAtIndex:(NSUInteger)index { + NSValue *keyValue = (NSValue *)[[self.keyInfoDictionaries objectForKey:@(index)] objectForKey:@(DSIdentityKeyDictionary_Key)]; + return keyValue.pointerValue; +} + +- (NSString *)localizedStatusOfKeyAtIndex:(NSUInteger)index { + return [[self class] localizedStatusOfKeyForIdentityKeyStatus:[self statusOfKeyAtIndex:index]]; +} + ++ (NSString *)localizedStatusOfKeyForIdentityKeyStatus:(DSIdentityKeyStatus)status { + switch (status) { + case DSIdentityKeyStatus_Unknown: + return DSLocalizedString(@"Unknown", @"Status of Key or Username is Unknown"); + case DSIdentityKeyStatus_Registered: + return DSLocalizedString(@"Registered", @"Status of Key or Username is Registered"); + case DSIdentityKeyStatus_Registering: + return DSLocalizedString(@"Registering", @"Status of Key or Username is Registering"); + case DSIdentityKeyStatus_NotRegistered: + return DSLocalizedString(@"Not Registered", @"Status of Key or Username is Not Registered"); + case DSIdentityKeyStatus_Revoked: + return DSLocalizedString(@"Revoked", @"Status of Key or Username is Revoked"); + default: + return @""; + } +} + ++ (DSAuthenticationKeysDerivationPath *)derivationPathForType:(DKeyKind *)type forWallet:(DSWallet *)wallet { +// uint16_t kind = &type; + // TODO: ed25519 + bls basic + int16_t index = dash_spv_crypto_keys_key_KeyKind_index(type); + if (index == dash_spv_crypto_keys_key_KeyKind_ECDSA) { + return [[DSDerivationPathFactory sharedInstance] identityECDSAKeysDerivationPathForWallet:wallet]; + } else if (index == dash_spv_crypto_keys_key_KeyKind_BLS || index == dash_spv_crypto_keys_key_KeyKind_BLSBasic) { + return [[DSDerivationPathFactory sharedInstance] identityBLSKeysDerivationPathForWallet:wallet]; + } + return nil; +} + +- (DSAuthenticationKeysDerivationPath *)derivationPathForType:(DKeyKind *)type { + return _isLocal ? [DSIdentity derivationPathForType:type forWallet:self.wallet] : nil; +} + +- (BOOL)hasPrivateKeyAtIndex:(uint32_t)index ofType:(DKeyKind *)type error:(NSError **)error { + if (!_isLocal) return NO; + const NSUInteger indexes[] = {_index | BIP32_HARD, index | BIP32_HARD}; + NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; + DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; + return hasKeychainData([self identifierForKeyAtPath:indexPath fromDerivationPath:derivationPath], error); +} + +- (DMaybeOpaqueKey *)privateKeyAtIndex:(uint32_t)index ofType:(DKeyKind *)type { + if (!_isLocal) return nil; + const NSUInteger indexes[] = {_index | BIP32_HARD, index | BIP32_HARD}; + NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; + DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; + NSError *error = nil; + NSData *keySecret = getKeychainData([self identifierForKeyAtPath:indexPath fromDerivationPath:derivationPath], &error); + NSAssert(keySecret, @"This should be present"); + if (!keySecret || error) return nil; + return [DSKeyManager keyWithPrivateKeyData:keySecret ofType:type]; +} + +//- (DMaybeOpaqueKey *)derivePrivateKeyAtIdentityKeyIndex:(uint32_t)index ofType:(DKeyKind *)type { +// if (!_isLocal) return nil; +// const NSUInteger indexes[] = {_index, index}; +// NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; +// return [self derivePrivateKeyAtIndexPath:indexPath ofType:*type]; +//} + +- (DMaybeOpaqueKey *)derivePrivateKeyAtIndexPath:(NSIndexPath *)indexPath ofType:(DKeyKind *)type { + if (!_isLocal) return nil; + DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; + return [derivationPath privateKeyAtIndexPath:[indexPath hardenAllItems]]; +} + +- (DMaybeOpaqueKey *)privateKeyAtIndex:(uint32_t)index ofType:(DKeyKind *)type forSeed:(NSData *)seed { + if (!_isLocal) return nil; + const NSUInteger indexes[] = {_index | BIP32_HARD, index | BIP32_HARD}; + NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; + DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; + return [derivationPath privateKeyAtIndexPath:indexPath fromSeed:seed]; +} + +- (DMaybeOpaqueKey *_Nullable)publicKeyAtIndex:(uint32_t)index ofType:(DKeyKind *)type { + if (!_isLocal) return nil; + const NSUInteger indexes[] = {_index | BIP32_HARD, index | BIP32_HARD}; + NSIndexPath *hardenedIndexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; + DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; + return [derivationPath publicKeyAtIndexPath:hardenedIndexPath]; +} + +- (DMaybeOpaqueKey *)createNewKeyOfType:(DKeyKind *)type + saveKey:(BOOL)saveKey + returnIndex:(uint32_t *)rIndex { + return [self createNewKeyOfType:type saveKey:saveKey returnIndex:rIndex inContext:[NSManagedObjectContext viewContext]]; +} + +- (DMaybeOpaqueKey *)createNewKeyOfType:(DKeyKind *)type + saveKey:(BOOL)saveKey + returnIndex:(uint32_t *)rIndex + inContext:(NSManagedObjectContext *)context { + if (!_isLocal) return nil; + uint32_t keyIndex = self.keysCreated; + const NSUInteger indexes[] = {_index | BIP32_HARD, keyIndex | BIP32_HARD}; + NSIndexPath *hardenedIndexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; + DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; + 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"); + self.keysCreated++; + if (rIndex) { + *rIndex = keyIndex; + } + NSDictionary *keyDictionary = @{ + @(DSIdentityKeyDictionary_Key): [NSValue valueWithPointer:publicKey], + @(DSIdentityKeyDictionary_KeyType): [NSValue valueWithPointer:type], + @(DSIdentityKeyDictionary_KeyStatus): @(DSIdentityKeyStatus_Registering) + }; + [self.keyInfoDictionaries setObject:keyDictionary forKey:@(keyIndex)]; + if (saveKey) { + [self saveNewKey:publicKey atPath:hardenedIndexPath withStatus:DSIdentityKeyStatus_Registering fromDerivationPath:derivationPath inContext:context]; + } + return publicKey; +} + +- (uint32_t)firstIndexOfKeyOfType:(DKeyKind *)type + createIfNotPresent:(BOOL)createIfNotPresent + saveKey:(BOOL)saveKey { + for (NSNumber *indexNumber in self.keyInfoDictionaries) { + NSDictionary *keyDictionary = self.keyInfoDictionaries[indexNumber]; + DKeyKind *keyType = [keyDictionary[@(DSIdentityKeyDictionary_KeyType)] pointerValue]; + if (dash_spv_crypto_keys_key_KeyKind_index(keyType) == dash_spv_crypto_keys_key_KeyKind_index(type)) { + return [indexNumber unsignedIntValue]; + } + } + if (_isLocal && createIfNotPresent) { + uint32_t rIndex; + [self createNewKeyOfType:type saveKey:saveKey returnIndex:&rIndex]; + return rIndex; + } else { + return UINT32_MAX; + } +} + +- (DMaybeOpaqueKey *)keyOfType:(DKeyKind *)type + atIndex:(uint32_t)index { + if (!_isLocal) return nil; + const NSUInteger indexes[] = {_index | BIP32_HARD, index | BIP32_HARD}; + NSIndexPath *hardenedIndexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; + DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; + return [derivationPath publicKeyAtIndexPath:hardenedIndexPath]; +} + +- (void)addKey:(DMaybeOpaqueKey *)key + atIndex:(uint32_t)index + ofType:(DKeyKind *)type + withStatus:(DSIdentityKeyStatus)status + save:(BOOL)save { + [self addKey:key atIndex:index ofType:type withStatus:status save:save inContext:self.platformContext]; +} + +- (void)addKey:(DMaybeOpaqueKey *)key + atIndex:(uint32_t)index + ofType:(DKeyKind *)type + withStatus:(DSIdentityKeyStatus)status + save:(BOOL)save + inContext:(NSManagedObjectContext *)context { + if (self.isLocal) { + const NSUInteger indexes[] = {_index, index}; + NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; + [self addKey:key atIndexPath:indexPath ofType:type withStatus:status save:save inContext:context]; + } else { + if (self.keyInfoDictionaries[@(index)]) { + NSDictionary *keyDictionary = self.keyInfoDictionaries[@(index)]; + NSValue *keyToCheckInDictionary = keyDictionary[@(DSIdentityKeyDictionary_Key)]; + DSIdentityKeyStatus keyToCheckInDictionaryStatus = [keyDictionary[@(DSIdentityKeyDictionary_KeyStatus)] unsignedIntegerValue]; + if ([DSKeyManager keysPublicKeyDataIsEqual:keyToCheckInDictionary.pointerValue key2:key->ok]) { + if (save && status != keyToCheckInDictionaryStatus) { + [self updateStatus:status forKeyWithIndexID:index inContext:context]; + } + } else { + NSAssert(FALSE, @"these should really match up"); + DSLog(@"these should really match up"); + return; + } + } else { + self.keysCreated = MAX(self.keysCreated, index + 1); + if (save) { + [self saveNewRemoteIdentityKey:key forKeyWithIndexID:index withStatus:status inContext:context]; + } + } + NSDictionary *keyDictionary = @{ + @(DSIdentityKeyDictionary_Key): [NSValue valueWithPointer:key], + @(DSIdentityKeyDictionary_KeyType): [NSValue valueWithPointer:type], + @(DSIdentityKeyDictionary_KeyStatus): @(status) + }; + [self.keyInfoDictionaries setObject:keyDictionary forKey:@(index)]; + } +} + +- (void)addKey:(DMaybeOpaqueKey *)key + atIndexPath:(NSIndexPath *)indexPath + ofType:(DKeyKind *)type + withStatus:(DSIdentityKeyStatus)status + save:(BOOL)save { + [self addKey:key atIndexPath:indexPath ofType:type withStatus:status save:save inContext:self.platformContext]; +} + +- (void)addKey:(DMaybeOpaqueKey *)key + atIndexPath:(NSIndexPath *)indexPath + ofType:(DKeyKind *)type + withStatus:(DSIdentityKeyStatus)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->ok]) { //if it isn't local we shouldn't verify + uint32_t index = (uint32_t)[indexPath indexAtPosition:[indexPath length] - 1]; + if (self.keyInfoDictionaries[@(index)]) { + NSDictionary *keyDictionary = self.keyInfoDictionaries[@(index)]; + NSValue *keyToCheckInDictionaryValue = keyDictionary[@(DSIdentityKeyDictionary_Key)]; + if ([DSKeyManager keysPublicKeyDataIsEqual:keyToCheckInDictionaryValue.pointerValue key2:key->ok]) { + 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"); + return; + } + } else { + self.keysCreated = MAX(self.keysCreated, index + 1); + if (save) { + [self saveNewKey:key atPath:indexPath withStatus:status fromDerivationPath:derivationPath inContext:context]; + } + } + NSDictionary *keyDictionary = @{ + @(DSIdentityKeyDictionary_Key): [NSValue valueWithPointer:key], + @(DSIdentityKeyDictionary_KeyType): [NSValue valueWithPointer:key], + @(DSIdentityKeyDictionary_KeyStatus): @(status) + }; + [self.keyInfoDictionaries setObject:keyDictionary forKey:@(index)]; + } else { + DSLog(@"these should really match up"); + } +} + +- (BOOL)registerKeyWithStatus:(DSIdentityKeyStatus)status + atIndexPath:(NSIndexPath *)indexPath + ofType:(DKeyKind *)type { + DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:type]; + DMaybeOpaqueKey *key = [derivationPath publicKeyAtIndexPath:[indexPath hardenAllItems]]; + if (!key) return FALSE; + uint32_t index = (uint32_t)[indexPath indexAtPosition:[indexPath length] - 1]; + self.keysCreated = MAX(self.keysCreated, index + 1); + NSDictionary *keyDictionary = @{ + @(DSIdentityKeyDictionary_Key): [NSValue valueWithPointer:key], + @(DSIdentityKeyDictionary_KeyType): [NSValue valueWithPointer:key], + @(DSIdentityKeyDictionary_KeyStatus): @(status) + }; + [self.keyInfoDictionaries setObject:keyDictionary forKey:@(index)]; + return TRUE; +} + +- (void)registerKey:(DMaybeOpaqueKey *)key + withStatus:(DSIdentityKeyStatus)status + atIndex:(uint32_t)index + ofType:(DKeyKind *)type { + self.keysCreated = MAX(self.keysCreated, index + 1); + NSDictionary *keyDictionary = @{ + @(DSIdentityKeyDictionary_Key): [NSValue valueWithPointer:key], + @(DSIdentityKeyDictionary_KeyType): [NSValue valueWithPointer:key], + @(DSIdentityKeyDictionary_KeyStatus): @(status) + }; + [self.keyInfoDictionaries setObject:keyDictionary forKey:@(index)]; +} + +// MARK: From Remote/Network +// TODO: make sure we determine 'legacy' correctly here ++ (DMaybeOpaqueKey *)keyFromKeyDictionary:(NSDictionary *)dictionary + rType:(uint32_t *)rType + rIndex:(uint32_t *)rIndex { + NSData *keyData = dictionary[@"data"]; + NSNumber *keyId = dictionary[@"id"]; + NSNumber *type = dictionary[@"type"]; + if (keyData && keyId && type) { + DKeyKind *kind = dash_spv_crypto_keys_key_key_kind_from_index(type.intValue); + DMaybeOpaqueKey *key = [DSKeyManager keyWithPublicKeyData:keyData ofType:kind]; + *rIndex = [keyId unsignedIntValue]; + *rType = [type unsignedIntValue]; + return key; + } + return nil; +} + +- (void)addKeyFromKeyDictionary:(NSDictionary *)dictionary + save:(BOOL)save + inContext:(NSManagedObjectContext *_Nullable)context { + uint32_t index = 0; + uint32_t type = 0; + DKeyKind *kind = dash_spv_crypto_keys_key_key_kind_from_index(index); + DMaybeOpaqueKey *key = [DSIdentity keyFromKeyDictionary:dictionary rType:&type rIndex:&index]; + if (key && key->ok) { + [self addKey:key atIndex:index ofType:kind withStatus:DSIdentityKeyStatus_Registered save:save inContext:context]; + } +} + +// MARK: - Funding + +- (NSString *)registrationFundingAddress { + if (self.registrationAssetLockTransaction) { + return [DSKeyManager addressFromHash160:self.registrationAssetLockTransaction.creditBurnPublicKeyHash forChain:self.chain]; + } else { + DSAssetLockDerivationPath *derivationPathRegistrationFunding = self.isOutgoingInvitation + ? [[DSDerivationPathFactory sharedInstance] identityInvitationFundingDerivationPathForWallet:self.wallet] + : [[DSDerivationPathFactory sharedInstance] identityRegistrationFundingDerivationPathForWallet:self.wallet]; + return [derivationPathRegistrationFunding addressAtIndex:self.index]; + } +} + + +// MARK: Helpers + +- (BOOL)isRegistered { + return self.registrationStatus == DSIdentityRegistrationStatus_Registered; +} + +- (NSString *)localizedRegistrationStatusString { + switch (self.registrationStatus) { + case DSIdentityRegistrationStatus_Registered: + return DSLocalizedString(@"Registered", @"The Dash Identity is registered"); + case DSIdentityRegistrationStatus_Unknown: + return DSLocalizedString(@"Unknown", @"It is Unknown if the Dash Identity is registered"); + case DSIdentityRegistrationStatus_Registering: + return DSLocalizedString(@"Registering", @"The Dash Identity is being registered"); + case DSIdentityRegistrationStatus_NotRegistered: + return DSLocalizedString(@"Not Registered", @"The Dash Identity is not registered"); + default: + break; + } + return @""; +} + +- (void)applyIdentityDictionary:(NSDictionary *)identityDictionary + version:(uint32_t)version + save:(BOOL)save + inContext:(NSManagedObjectContext *_Nullable)context { + if (identityDictionary[@"balance"]) { + uint64_t creditBalance = (uint64_t)[identityDictionary[@"balance"] longLongValue]; + _creditBalance = creditBalance; + } + if (identityDictionary[@"publicKeys"]) { + for (NSDictionary *dictionary in identityDictionary[@"publicKeys"]) { + [self addKeyFromKeyDictionary:dictionary save:save inContext:context]; + } + } +} + ++ (DMaybeOpaqueKey *)firstKeyInIdentityDictionary:(NSDictionary *)identityDictionary { + if (identityDictionary[@"publicKeys"]) { + for (NSDictionary *dictionary in identityDictionary[@"publicKeys"]) { + uint32_t index = 0; + uint32_t type = 0; + DMaybeOpaqueKey *key = [DSIdentity keyFromKeyDictionary:dictionary rType:&type rIndex:&index]; + if (index == 0) return key; + } + } + return nil; +} + +// MARK: Transition + +//- (DSIdentityRegistrationTransition *)registrationTransitionSignedByPrivateKey:(DMaybeOpaqueKey *)privateKey +// registeringPublicKeys:(NSDictionary *)publicKeys +// usingAssetLockTransaction:(DSAssetLockTransaction *)transaction { +// DSIdentityRegistrationTransition *identityRegistrationTransition = [[DSIdentityRegistrationTransition alloc] initWithVersion:1 +// registeringPublicKeys:publicKeys +// usingAssetLockTransaction:transaction +// onChain:self.chain]; +// [identityRegistrationTransition signWithKey:privateKey atIndex:UINT32_MAX fromIdentity:self]; +// return identityRegistrationTransition; +//} +// +//- (void)registrationTransitionWithCompletion:(void (^_Nullable)(DSIdentityRegistrationTransition *_Nullable identityRegistrationTransaction, NSError *_Nullable error))completion { +// if (!self.internalRegistrationFundingPrivateKey) { +// if (completion) completion(nil, ERROR_NO_FUNDING_PRV_KEY); +// return; +// } +// uint32_t index = [self firstIndexOfKeyOfType:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() createIfNotPresent:YES saveKey:!self.wallet.isTransient]; +// DMaybeOpaqueKey *publicKey = [self keyAtIndex:index]; +// NSAssert((index & ~(BIP32_HARD)) == 0, @"The index should be 0 here"); +// NSAssert(self.registrationAssetLockTransaction, @"The registration credit funding transaction must be known"); +// if (!self.registrationAssetLockTransaction.instantSendLockAwaitingProcessing && self.registrationAssetLockTransaction.blockHeight == BLOCK_UNKNOWN_HEIGHT) { +// if (completion) completion(nil, ERROR_FUNDING_TX_NOT_MINED); +// return; +// } +// DSIdentityRegistrationTransition *transition = [self registrationTransitionSignedByPrivateKey:self.internalRegistrationFundingPrivateKey +// registeringPublicKeys:@{@(index): [NSValue valueWithPointer:publicKey]} +// usingAssetLockTransaction:self.registrationAssetLockTransaction]; +// completion(transition, nil); +//} + +// MARK: Registering + +- (void)createAndPublishRegistrationTransitionWithCompletion:(void (^)(BOOL, NSError *))completion { + if (!self.internalRegistrationFundingPrivateKey) { + if (completion) completion(nil, ERROR_NO_FUNDING_PRV_KEY); + return; + } + uint32_t index = [self firstIndexOfKeyOfType:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() createIfNotPresent:YES saveKey:!self.wallet.isTransient]; + DMaybeOpaqueKey *publicKey = [self keyAtIndex:index]; + NSAssert((index & ~(BIP32_HARD)) == 0, @"The index should be 0 here"); + NSAssert(self.registrationAssetLockTransaction, @"The registration credit funding transaction must be known"); + if (!self.registrationAssetLockTransaction.instantSendLockAwaitingProcessing && self.registrationAssetLockTransaction.blockHeight == BLOCK_UNKNOWN_HEIGHT) { + if (completion) completion(nil, ERROR_FUNDING_TX_NOT_MINED); + return; + } + +// platformKeyDictionary[@"id"] = @([indexIdentifier unsignedIntValue]); +// platformKeyDictionary[@"purpose"] = @(DWIdentityPublicKeyPurposeAuthentication); +// platformKeyDictionary[@"securityLevel"] = @(DWIdentityPublicKeySecurityLevelMaster); +// platformKeyDictionary[@"readOnly"] = @NO; +// platformKeyDictionary[@"type"] = @(key->ok->tag); +// platformKeyDictionary[@"data"] = [DSKeyManager publicKeyData:key->ok]; +// - (DSMutableStringValueDictionary *)assetLockProofDictionary { +// DSMutableStringValueDictionary *assetLockDictionary = [DSMutableStringValueDictionary dictionary]; +// if (self.assetLockTransaction.instantSendLockAwaitingProcessing) { +// assetLockDictionary[@"type"] = @(0); +// assetLockDictionary[@"instantLock"] = self.assetLockTransaction.instantSendLockAwaitingProcessing.toData; +// assetLockDictionary[@"outputIndex"] = @(self.assetLockTransaction.lockedOutpoint.n); +// assetLockDictionary[@"transaction"] = [self.assetLockTransaction toData]; +// } else { +// assetLockDictionary[@"type"] = @(1); +// assetLockDictionary[@"coreChainLockedHeight"] = @(self.assetLockTransaction.blockHeight); +// assetLockDictionary[@"outPoint"] = dsutxo_data(self.assetLockTransaction.lockedOutpoint); +// } +// +// return assetLockDictionary; +// } + + dpp_identity_identity_public_key_IdentityPublicKey *public_key = dash_spv_platform_transition_identity_registration_public_key(index, publicKey->ok); + + dpp_identity_state_transition_asset_lock_proof_AssetLockProof *proof; + DSInstantSendTransactionLock *isLock = self.registrationAssetLockTransaction.instantSendLockAwaitingProcessing; + if (isLock) { + uint8_t version = isLock.version; + NSArray *outpoints = isLock.inputOutpoints; + Arr_u8_36 **values = malloc(sizeof(Arr_u8_36 *) * outpoints.count); + for (int i = 0; i < outpoints.count; i++) { + NSData *o = outpoints[i]; + values[i] = Arr_u8_36_ctor(o.length, (uint8_t *) o.bytes); + } + Vec_u8_36 *lock_inputs = Vec_u8_36_ctor(isLock.inputOutpoints.count, values); + u256 *txid = u256_ctor_u(isLock.transactionHash); + u256 *cycle_hash = u256_ctor_u(isLock.cycleHash); + u768 *signature = u768_ctor_u(isLock.signature); + uint16_t tx_version = self.registrationAssetLockTransaction.version; + uint32_t lock_time = self.registrationAssetLockTransaction.lockTime; + NSArray *inputs = self.registrationAssetLockTransaction.inputs; + NSUInteger inputsCount = inputs.count; + dash_spv_crypto_tx_input_TransactionInput **tx_inputs = malloc(sizeof(dash_spv_crypto_tx_input_TransactionInput *) * inputsCount); + for (int i = 0; i < inputs.count; i++) { + DSTransactionInput *o = inputs[i]; + u256 *input_hash = u256_ctor_u(o.inputHash); + Vec_u8 *script = o.inScript ? Vec_u8_ctor(o.inScript.length, (uint8_t *) o.inScript.bytes) : NULL; + Vec_u8 *signature = Vec_u8_ctor(o.signature.length, (uint8_t *) o.signature.bytes); + tx_inputs[i] = dash_spv_crypto_tx_input_TransactionInput_ctor(input_hash, o.index, script, signature, o.sequence); + } + + NSArray *outputs = self.registrationAssetLockTransaction.outputs; + NSUInteger outputsCount = outputs.count; + dash_spv_crypto_tx_output_TransactionOutput **tx_outputs = malloc(sizeof(dash_spv_crypto_tx_output_TransactionOutput *) * outputsCount); + for (int i = 0; i < outputs.count; i++) { + DSTransactionOutput *o = outputs[i]; + Vec_u8 *script = o.outScript ? Vec_u8_ctor(o.outScript.length, (uint8_t *) o.outScript.bytes) : NULL; + tx_outputs[i] = dash_spv_crypto_tx_output_TransactionOutput_ctor(o.amount, script, NULL); + } + uint8_t asset_lock_payload_version = self.registrationAssetLockTransaction.specialTransactionVersion; + + NSArray *creditOutputs = self.registrationAssetLockTransaction.creditOutputs; + NSUInteger creditOutputsCount = creditOutputs.count; + dash_spv_crypto_tx_output_TransactionOutput **credit_outputs = malloc(sizeof(dash_spv_crypto_tx_output_TransactionOutput *) * creditOutputsCount); + for (int i = 0; i < creditOutputs.count; i++) { + DSTransactionOutput *o = creditOutputs[i]; + Vec_u8 *script = o.outScript ? Vec_u8_ctor(o.outScript.length, (uint8_t *) o.outScript.bytes) : NULL; + tx_outputs[i] = dash_spv_crypto_tx_output_TransactionOutput_ctor(o.amount, script, NULL); + } + + Vec_dash_spv_crypto_tx_input_TransactionInput *input = Vec_dash_spv_crypto_tx_input_TransactionInput_ctor(inputsCount, tx_inputs); + Vec_dash_spv_crypto_tx_output_TransactionOutput *output = Vec_dash_spv_crypto_tx_output_TransactionOutput_ctor(outputsCount, tx_outputs); + Vec_dash_spv_crypto_tx_output_TransactionOutput *credit_output = Vec_dash_spv_crypto_tx_output_TransactionOutput_ctor(creditOutputsCount, credit_outputs); + uint32_t output_index = (uint32_t ) self.registrationAssetLockTransaction.lockedOutpoint.n; + + proof = + dash_spv_platform_transition_instant_proof(output_index, version, lock_inputs, txid, cycle_hash, signature, tx_version, lock_time, input, output, asset_lock_payload_version, credit_output); + } else { + DSUTXO lockedOutpoint = self.registrationAssetLockTransaction.lockedOutpoint; + u256 *txid = u256_ctor_u(lockedOutpoint.hash); + uint32_t vout = (uint32_t) lockedOutpoint.n; + proof = dash_spv_platform_transition_chain_proof(self.registrationAssetLockTransaction.blockHeight, txid, vout); + } + + Result_ok_dpp_state_transition_proof_result_StateTransitionProofResult_err_dash_spv_platform_error_Error *state_transition_result = dash_spv_platform_PlatformSDK_identity_register_using_public_key_at_index(self.chain.shareCore.runtime, self.chain.shareCore.platform->obj, public_key, index, proof, self.internalRegistrationFundingPrivateKey->ok); + if (!state_transition_result) { + completion(NO, ERROR_REG_TRANSITION); + return; + } + if (state_transition_result->error) { + if (completion) completion(nil, ERROR_REG_TRANSITION_CREATION); + Result_ok_dpp_state_transition_proof_result_StateTransitionProofResult_err_dash_spv_platform_error_Error_destroy(state_transition_result); + return; + } + [self processStateTransitionResult:state_transition_result]; + + Result_ok_Option_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error *result = dash_spv_platform_identity_manager_IdentitiesManager_monitor_with_delay(self.chain.shareCore.runtime, self.chain.shareCore.identitiesManager->obj, u256_ctor(self.uniqueIDData), dash_spv_platform_util_RetryStrategy_Linear_ctor(5), dash_spv_platform_identity_manager_IdentityValidator_None_ctor(), 4); + +// BOOL unsuccess = result->error; + if (result->error) { + NSError *error = [NSError ffi_from_platform_error:result->error]; + Result_ok_Option_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error_destroy(result); + completion(NO, error); + } else if (result->ok) { + Result_ok_Option_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error_destroy(result); + completion(YES, NULL); + } else { + Result_ok_Option_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error_destroy(result); + completion(NO, ERROR_REG_TRANSITION); + } + +// if (unsuccess) { +// Result_ok_Option_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error *result_after_error = dash_spv_platform_identity_manager_IdentitiesManager_identity_monitor_with_delay(self.chain.shareCore.runtime, self.chain.shareCore.identitiesManager->obj, u256_ctor(self.uniqueIDData), dash_spv_platform_util_RetryStrategy_Linear_ctor(1), dash_spv_platform_identity_manager_IdentityMonitorValidator_None_ctor(), 4); +// NSError *err = result_after_error->ok ? nil : ERROR_REG_TRANSITION; +// Result_ok_Option_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error_destroy(result_after_error); +// completion(NO, err); +// } else { +// completion(YES, nil); +// } + +// DSIdentityRegistrationTransition *transition = [[DSIdentityRegistrationTransition alloc] initWithVersion:1 +// registeringPublicKeys:@{@(index): [NSValue valueWithPointer:publicKey]} +// usingAssetLockTransaction:self.registrationAssetLockTransaction +// onChain:self.chain]; +// +// transition.signatureData = [DSKeyManager signMesasageDigest:self.internalRegistrationFundingPrivateKey->ok digest:[transition serializedBaseDataHash].UInt256]; +// transition.signaturePublicKeyId = UINT32_MAX; +// transition.transitionHash = transition.data.SHA256; + + +// [transition signWithKey:self.internalRegistrationFundingPrivateKey atIndex:UINT32_MAX fromIdentity:self]; + + + +// [self.DAPIClient publishTransition:transition +// completionQueue:self.identityQueue +// success:^(NSDictionary *_Nonnull successDictionary, BOOL added) { +// +// Result_ok_Option_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error *result = dash_spv_platform_identity_manager_IdentitiesManager_identity_monitor_with_delay(self.chain.shareCore.runtime, self.chain.shareCore.identitiesManager->obj, u256_ctor(self.uniqueIDData), dash_spv_platform_util_RetryStrategy_Linear_ctor(5), dash_spv_platform_identity_manager_IdentityMonitorValidator_None_ctor(), 4); +// +// +//// [self monitorForIdentityWithRetryCount:5 +//// retryAbsentCount:5 +//// delay:4 +//// retryDelayType:DSIdentityRetryDelayType_Linear +//// options:DSIdentityMonitorOptions_None +//// inContext:self.platformContext +//// completion:^(BOOL success, BOOL found, NSError *error) { +//// if (completion) completion(successDictionary, error); +//// }]; +// } +// failure:^(NSError *_Nonnull error) { +// if (error) { +// Result_ok_Option_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error *result = dash_spv_platform_identity_manager_IdentitiesManager_identity_monitor_with_delay(self.chain.shareCore.runtime, self.chain.shareCore.identitiesManager->obj, u256_ctor(self.uniqueIDData), dash_spv_platform_util_RetryStrategy_Linear_ctor(1), dash_spv_platform_identity_manager_IdentityMonitorValidator_None_ctor(), 4); +// +//// [self monitorForIdentityWithRetryCount:1 +//// retryAbsentCount:1 +//// delay:4 +//// retryDelayType:DSIdentityRetryDelayType_Linear +//// options:DSIdentityMonitorOptions_None +//// inContext:self.platformContext +//// completion:^(BOOL success, BOOL found, NSError *error) { +//// if (completion) completion(nil, found ? nil : error); +//// }]; +// } else if (completion) { +// completion(nil, ERROR_REG_TRANSITION); +// } +// }]; + +// completion(transition, nil); + + + +// [self registrationTransitionWithCompletion:^(DSIdentityRegistrationTransition *transition, NSError *transitionError) { +// if (transition) { +// [self.DAPIClient publishTransition:transition +// completionQueue:self.identityQueue +// success:^(NSDictionary *_Nonnull successDictionary, BOOL added) { +// [self monitorForIdentityWithRetryCount:5 +// retryAbsentCount:5 +// delay:4 +// retryDelayType:DSIdentityRetryDelayType_Linear +// options:DSIdentityMonitorOptions_None +// inContext:self.platformContext +// completion:^(BOOL success, BOOL found, NSError *error) { +// if (completion) completion(successDictionary, error); +// }]; +// } +// failure:^(NSError *_Nonnull error) { +// if (error) { +// [self monitorForIdentityWithRetryCount:1 +// retryAbsentCount:1 +// delay:4 +// retryDelayType:DSIdentityRetryDelayType_Linear +// options:DSIdentityMonitorOptions_None +// inContext:self.platformContext +// completion:^(BOOL success, BOOL found, NSError *error) { +// if (completion) completion(nil, found ? nil : error); +// }]; +// } else if (completion) { +// completion(nil, ERROR_REG_TRANSITION); +// } +// }]; +// } else if (completion) { +// completion(nil, transitionError ? transitionError : ERROR_REG_TRANSITION_CREATION); +// } +// }]; +} + +// MARK: Retrieval + +- (void)fetchIdentityNetworkStateInformationWithCompletion:(void (^)(BOOL success, BOOL found, NSError *error))completion { + dispatch_async(self.identityQueue, ^{ + Result_ok_Option_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error *result = dash_spv_platform_identity_manager_IdentitiesManager_monitor_for_id_bytes(self.chain.shareCore.runtime, self.chain.shareCore.identitiesManager->obj, u256_ctor(self.uniqueIDData), dash_spv_platform_util_RetryStrategy_SlowingDown50Percent_ctor(DEFAULT_FETCH_IDENTITY_RETRY_COUNT), self.isLocal ? dash_spv_platform_identity_manager_IdentityValidator_AcceptNotFoundAsNotAnError_ctor() : dash_spv_platform_identity_manager_IdentityValidator_None_ctor()); + if (!result) { + completion(NO, NO, [NSError errorWithCode:0 localizedDescriptionKey:@"Unknown Error"]); + return; + } + if (result->error) { + NSError *error = [NSError ffi_from_platform_error:result->error]; + Result_ok_Option_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error_destroy(result); + completion(NO, NO, error); + return; + } + if (!result->ok) { + completion(YES, NO, nil); + return; + } + dpp_identity_v0_IdentityV0 *versioned_identity = result->ok->v0; + self->_creditBalance = versioned_identity->balance; + std_collections_Map_keys_dpp_identity_identity_public_key_KeyID_values_dpp_identity_identity_public_key_IdentityPublicKey *public_keys = versioned_identity->public_keys; + for (int i = 0; i < public_keys->count; i++) { + dpp_identity_identity_public_key_KeyID *key_id = public_keys->keys[i]; + dpp_identity_identity_public_key_IdentityPublicKey *key = public_keys->values[i]; + DMaybeOpaqueKey *maybe_key = dash_spv_platform_identity_manager_opaque_key_from_identity_public_key(key); + DKeyKind *kind = dash_spv_crypto_keys_key_OpaqueKey_kind(maybe_key->ok); + [self addKey:maybe_key atIndex:i ofType:kind withStatus:DSIdentityKeyStatus_Registered save:!self.isTransient inContext:self.platformContext]; + } + self.registrationStatus = DSIdentityRegistrationStatus_Registered; + completion(YES, YES, nil); + + }); +} + +//- (void)fetchIdentityNetworkStateInformationInContext:(NSManagedObjectContext *)context +// withCompletion:(void (^)(BOOL success, BOOL found, NSError *error))completion { +// //a local identity might not have been published yet +// //todo retryabsentcount should be 0 if it can be proved to be absent +// +// Result_ok_Option_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error *result = dash_spv_platform_identity_manager_IdentitiesManager_identity_monitor_for_id_bytes(self.chain.shareCore.runtime, self.chain.shareCore.identitiesManager->obj, u256_ctor(self.uniqueIDData), dash_spv_platform_util_RetryStrategy_SlowingDown50Percent_ctor(DEFAULT_FETCH_IDENTITY_RETRY_COUNT), self.isLocal ? dash_spv_platform_identity_manager_IdentityMonitorValidator_AcceptNotFoundAsNotAnError_ctor() : dash_spv_platform_identity_manager_IdentityMonitorValidator_None_ctor()); +// if (!result) { +// completion(NO, NO, [NSError errorWithCode:0 localizedDescriptionKey:@"Unknown Error"]); +// return; +// } +// if (result->error) { +// NSError *error; +// switch (result->error->tag) { +// case dash_spv_platform_error_Error_DashSDKError: { +// error = [NSError errorWithCode:0 localizedDescriptionKey:[NSString stringWithCString:result->error->dash_sdk_error encoding:NSUTF8StringEncoding]]; +// break; +// } +// default: +// break; +// } +// Result_ok_Option_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error_destroy(result); +// completion(NO, NO, error); +// return; +// } +// if (!result->ok) { +// completion(YES, NO, nil); +// return; +// } +// dpp_identity_v0_IdentityV0 *versioned_identity = result->ok->v0; +// _creditBalance = versioned_identity->balance; +// std_collections_Map_keys_dpp_identity_identity_public_key_KeyID_values_dpp_identity_identity_public_key_IdentityPublicKey *public_keys = versioned_identity->public_keys; +// for (int i = 0; i < public_keys->count; i++) { +// dpp_identity_identity_public_key_KeyID *key_id = public_keys->keys[i]; +// dpp_identity_identity_public_key_IdentityPublicKey *key = public_keys->values[i]; +// DMaybeOpaqueKey *maybe_key = dash_spv_platform_identity_manager_opaque_key_from_identity_public_key(key); +// DKeyKind *kind = dash_spv_crypto_keys_key_OpaqueKey_kind(maybe_key->ok); +// [self addKey:maybe_key atIndex:i ofType:kind withStatus:DSIdentityKeyStatus_Registered save:!self.isTransient inContext:context]; +// } +// self.registrationStatus = DSIdentityRegistrationStatus_Registered; +// completion(YES, YES, nil); +//// [self monitorForIdentityWithRetryCount:DEFAULT_FETCH_IDENTITY_RETRY_COUNT +//// retryAbsentCount:DEFAULT_FETCH_IDENTITY_RETRY_COUNT +//// delay:3 +//// retryDelayType:DSIdentityRetryDelayType_SlowingDown50Percent +//// options:self.isLocal ? DSIdentityMonitorOptions_AcceptNotFoundAsNotAnError : DSIdentityMonitorOptions_None +//// inContext:context +//// completion:completion]; +//} + +- (void)fetchAllNetworkStateInformationWithCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion { + dispatch_async(self.identityQueue, ^{ + [self fetchAllNetworkStateInformationInContext:self.platformContext + withCompletion:completion + onCompletionQueue:dispatch_get_main_queue()]; + }); +} + +- (void)fetchAllNetworkStateInformationInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + dispatch_async(self.identityQueue, ^{ + DSIdentityQueryStep query = DSIdentityQueryStep_None; + if ([DSOptionsManager sharedInstance].syncType & DSSyncType_Identities) + query |= DSIdentityQueryStep_Identity; + if ([DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) + query |= DSIdentityQueryStep_Username; + if ([DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) { + query |= DSIdentityQueryStep_Profile; + if (self.isLocal) + query |= DSIdentityQueryStep_ContactRequests; + } + [self fetchNetworkStateInformation:query + inContext:context + withCompletion:completion + onCompletionQueue:completionQueue]; + }); +} + +- (void)fetchL3NetworkStateInformation:(DSIdentityQueryStep)queryStep + withCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion { + [self fetchL3NetworkStateInformation:queryStep + inContext:self.platformContext + withCompletion:completion + onCompletionQueue:dispatch_get_main_queue()]; +} + +- (void)fetchL3NetworkStateInformation:(DSIdentityQueryStep)queryStep + inContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + 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; + } + __block DSIdentityQueryStep failureStep = DSIdentityQueryStep_None; + __block NSMutableArray *groupedErrors = [NSMutableArray array]; + dispatch_group_t dispatchGroup = dispatch_group_create(); + if (queryStep & DSIdentityQueryStep_Username) { + dispatch_group_enter(dispatchGroup); + [self fetchUsernamesInContext:context withCompletion:^(BOOL success, NSError *error) { + failureStep |= success & DSIdentityQueryStep_Username; + if (error) [groupedErrors addObject:error]; + dispatch_group_leave(dispatchGroup); + } + onCompletionQueue:self.identityQueue]; + } + + if (queryStep & DSIdentityQueryStep_Profile) { + dispatch_group_enter(dispatchGroup); + [self fetchProfileInContext:context withCompletion:^(BOOL success, NSError *error) { + failureStep |= success & DSIdentityQueryStep_Profile; + if (error) [groupedErrors addObject:error]; + dispatch_group_leave(dispatchGroup); + } + onCompletionQueue:self.identityQueue]; + } + + if (queryStep & DSIdentityQueryStep_OutgoingContactRequests) { + dispatch_group_enter(dispatchGroup); + [self fetchOutgoingContactRequestsInContext:context + withCompletion:^(BOOL success, NSArray *errors) { + failureStep |= success & DSIdentityQueryStep_OutgoingContactRequests; + if ([errors count]) { + [groupedErrors addObjectsFromArray:errors]; + dispatch_group_leave(dispatchGroup); + } else { + if (queryStep & DSIdentityQueryStep_IncomingContactRequests) { + [self fetchIncomingContactRequestsInContext:context + withCompletion:^(BOOL success, NSArray *errors) { + failureStep |= success & DSIdentityQueryStep_IncomingContactRequests; + if ([errors count]) [groupedErrors addObjectsFromArray:errors]; + dispatch_group_leave(dispatchGroup); + } + onCompletionQueue:self.identityQueue]; + } else { + dispatch_group_leave(dispatchGroup); + } + } + } + onCompletionQueue:self.identityQueue]; + } else if (queryStep & DSIdentityQueryStep_IncomingContactRequests) { + dispatch_group_enter(dispatchGroup); + [self fetchIncomingContactRequestsInContext:context + withCompletion:^(BOOL success, NSArray *errors) { + failureStep |= success & DSIdentityQueryStep_IncomingContactRequests; + if ([errors count]) [groupedErrors addObjectsFromArray:errors]; + dispatch_group_leave(dispatchGroup); + } + onCompletionQueue:self.identityQueue]; + } + + __weak typeof(self) weakSelf = self; + if (completion) { + dispatch_group_notify(dispatchGroup, self.identityQueue, ^{ +#if DEBUG + DSLogPrivate(@"Completed fetching of identity information for user %@ (query %lu - failures %lu)", + self.currentDashpayUsername ? self.currentDashpayUsername : self.uniqueIdString, (unsigned long)queryStep, failureStep); +#else + DSLog(@"Completed fetching of identity information for user %@ (query %lu - failures %lu)", + @"", (unsigned long)queryStep, failureStep); +#endif /* DEBUG */ + if (!(failureStep & DSIdentityQueryStep_ContactRequests)) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) return; + //todo This needs to be eventually set with the blockchain returned by platform. + strongSelf.dashpaySyncronizationBlockHash = strongSelf.chain.lastTerminalBlock.blockHash; + } + dispatch_async(completionQueue, ^{ completion(failureStep, [groupedErrors copy]); }); + }); + } +} + +- (void)fetchNetworkStateInformation:(DSIdentityQueryStep)querySteps + withCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion { + [self fetchNetworkStateInformation:querySteps + inContext:self.platformContext + withCompletion:completion + onCompletionQueue:dispatch_get_main_queue()]; +} + +- (void)fetchNetworkStateInformation:(DSIdentityQueryStep)querySteps + inContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + if (querySteps & DSIdentityQueryStep_Identity) { + [self fetchIdentityNetworkStateInformationWithCompletion:^(BOOL success, BOOL found, NSError *error) { + if (!success) { + if (completion) dispatch_async(completionQueue, ^{ completion(DSIdentityQueryStep_Identity, error ? @[error] : @[]); }); + return; + } + if (!found) { + if (completion) dispatch_async(completionQueue, ^{ completion(DSIdentityQueryStep_NoIdentity, @[]); }); + return; + } + [self fetchL3NetworkStateInformation:querySteps + inContext:context + withCompletion:completion + onCompletionQueue:completionQueue]; + }]; + } else { + NSAssert([self identityEntityInContext:context], @"Blockchain identity entity should be known"); + [self fetchL3NetworkStateInformation:querySteps + inContext:context + withCompletion:completion + onCompletionQueue:completionQueue]; + } +} + +- (void)fetchIfNeededNetworkStateInformation:(DSIdentityQueryStep)querySteps + inContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + dispatch_async(self.identityQueue, ^{ + if (!self.activeKeyCount) { + if (self.isLocal) { + [self fetchNetworkStateInformation:querySteps + inContext:context + withCompletion:completion + onCompletionQueue:completionQueue]; + } else { + DSIdentityQueryStep stepsNeeded = DSIdentityQueryStep_None; + if ([DSOptionsManager sharedInstance].syncType & DSSyncType_Identities) + stepsNeeded |= DSIdentityQueryStep_Identity; + if (![self.dashpayUsernameFullPaths count] && self.lastCheckedUsernamesTimestamp == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) + stepsNeeded |= DSIdentityQueryStep_Username; + if ((self.lastCheckedProfileTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + stepsNeeded |= DSIdentityQueryStep_Profile; + if (stepsNeeded != DSIdentityQueryStep_None) { + [self fetchNetworkStateInformation:stepsNeeded & querySteps inContext:context withCompletion:completion onCompletionQueue:completionQueue]; + } else if (completion) { + completion(DSIdentityQueryStep_None, @[]); + } + } + } else { + DSIdentityQueryStep stepsNeeded = DSIdentityQueryStep_None; + if (![self.dashpayUsernameFullPaths count] && self.lastCheckedUsernamesTimestamp == 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 date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + stepsNeeded |= DSIdentityQueryStep_Profile; + if (self.isLocal && (self.lastCheckedIncomingContactsTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + stepsNeeded |= DSIdentityQueryStep_IncomingContactRequests; + if (self.isLocal && (self.lastCheckedOutgoingContactsTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + stepsNeeded |= DSIdentityQueryStep_OutgoingContactRequests; + if (stepsNeeded != DSIdentityQueryStep_None) { + [self fetchNetworkStateInformation:stepsNeeded & querySteps inContext:context withCompletion:completion onCompletionQueue:completionQueue]; + } else if (completion) { + completion(DSIdentityQueryStep_None, @[]); + } + } + }); +} + +- (void)fetchNeededNetworkStateInformationWithCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion { + [self fetchNeededNetworkStateInformationInContext:self.platformContext + withCompletion:completion + onCompletionQueue:dispatch_get_main_queue()]; +} + +- (void)fetchNeededNetworkStateInformationInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(DSIdentityQueryStep failureStep, NSArray *errors))completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + dispatch_async(self.identityQueue, ^{ + if (!self.activeKeyCount) { + if (self.isLocal) { + [self fetchAllNetworkStateInformationWithCompletion:completion]; + } else { + DSIdentityQueryStep stepsNeeded = DSIdentityQueryStep_None; + if ([DSOptionsManager sharedInstance].syncType & DSSyncType_Identities) + stepsNeeded |= DSIdentityQueryStep_Identity; + if (![self.dashpayUsernameFullPaths count] && self.lastCheckedUsernamesTimestamp == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) + stepsNeeded |= DSIdentityQueryStep_Username; + if ((self.lastCheckedProfileTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + stepsNeeded |= DSIdentityQueryStep_Profile; + if (stepsNeeded != DSIdentityQueryStep_None) { + [self fetchNetworkStateInformation:stepsNeeded inContext:context withCompletion:completion onCompletionQueue:completionQueue]; + } else if (completion) { + completion(DSIdentityQueryStep_None, @[]); + } + } + } else { + DSIdentityQueryStep stepsNeeded = DSIdentityQueryStep_None; + if (![self.dashpayUsernameFullPaths count] && self.lastCheckedUsernamesTimestamp == 0 && [DSOptionsManager sharedInstance].syncType & DSSyncType_DPNS) + stepsNeeded |= DSIdentityQueryStep_Username; + if (![[self matchingDashpayUserInContext:context] createdAt] && (self.lastCheckedProfileTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + stepsNeeded |= DSIdentityQueryStep_Profile; + if (self.isLocal && (self.lastCheckedIncomingContactsTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + stepsNeeded |= DSIdentityQueryStep_IncomingContactRequests; + if (self.isLocal && (self.lastCheckedOutgoingContactsTimestamp < [[NSDate date] timeIntervalSince1970] - HOUR_TIME_INTERVAL) && [DSOptionsManager sharedInstance].syncType & DSSyncType_Dashpay) + stepsNeeded |= DSIdentityQueryStep_OutgoingContactRequests; + if (stepsNeeded != DSIdentityQueryStep_None) { + [self fetchNetworkStateInformation:stepsNeeded + inContext:context + withCompletion:completion + onCompletionQueue:completionQueue]; + } else if (completion) { + completion(DSIdentityQueryStep_None, @[]); + } + } + }); +} + +// MARK: - Platform Helpers + +- (DPDocumentFactory *)dashpayDocumentFactory { + if (!_dashpayDocumentFactory) { + DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; + NSAssert(contract, @"Contract must be defined"); + self.dashpayDocumentFactory = [[DPDocumentFactory alloc] initWithIdentity:self contract:contract onChain:self.chain]; + } + return _dashpayDocumentFactory; +} + +- (DPDocumentFactory *)dpnsDocumentFactory { + if (!_dpnsDocumentFactory) { + DPContract *contract = [DSDashPlatform sharedInstanceForChain:self.chain].dpnsContract; + NSAssert(contract, @"Contract must be defined"); + self.dpnsDocumentFactory = [[DPDocumentFactory alloc] initWithIdentity:self contract:contract onChain:self.chain]; + } + return _dpnsDocumentFactory; +} + +- (DSDAPIClient *)DAPIClient { + return self.chain.chainManager.DAPIClient; +} + +- (DSDAPIPlatformNetworkService *)DAPINetworkService { + return self.DAPIClient.DAPIPlatformNetworkService; +} + +// MARK: - Signing and Encryption + +- (BOOL)signStateTransition:(DSTransition *)transition + forKeyIndex:(uint32_t)keyIndex + ofType:(DKeyKind *)signingAlgorithm { + NSParameterAssert(transition); + DMaybeOpaqueKey *privateKey = [self privateKeyAtIndex:keyIndex ofType:signingAlgorithm]; + NSAssert(privateKey && privateKey->ok, @"The private key should exist"); + DMaybeOpaqueKey *publicKey = [self publicKeyAtIndex:keyIndex ofType:signingAlgorithm]; + NSAssert([DSKeyManager keysPublicKeyDataIsEqual:privateKey->ok key2:publicKey->ok], @"These should be equal"); + // NSLog(@"%@",uint160_hex(self.identityRegistrationTransition.pubkeyHash)); + // NSAssert(uint160_eq(privateKey.publicKeyData.hash160,self.identityRegistrationTransition.pubkeyHash),@"Keys aren't ok"); + [transition signWithKey:privateKey atIndex:keyIndex fromIdentity:self]; + return YES; +} + +- (BOOL)signStateTransition:(DSTransition *)transition { + if (!self.keysCreated) { + uint32_t index; + [self createNewKeyOfType:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() saveKey:!self.wallet.isTransient returnIndex:&index]; + } + return [self signStateTransition:transition forKeyIndex:self.currentMainKeyIndex ofType:self.currentMainKeyType]; +} + +//- (void)signMessageDigest:(UInt256)digest +// forKeyIndex:(uint32_t)keyIndex +// ofType:(DKeyKind *)signingAlgorithm +// completion:(void (^_Nullable)(BOOL success, NSData *signature))completion { +// NSParameterAssert(completion); +// DMaybeOpaqueKey *privateKey = [self privateKeyAtIndex:keyIndex ofType:signingAlgorithm]; +// NSAssert(privateKey, @"The private key should exist"); +// DMaybeOpaqueKey *publicKey = [self publicKeyAtIndex:keyIndex ofType:signingAlgorithm]; +// NSAssert(publicKey, @"The public key should exist"); +// NSAssert([DSKeyManager keysPublicKeyDataIsEqual:privateKey->ok key2:publicKey->ok], @"These should be equal"); +// +// DSLogPrivate(@"Signing %@ with key %@", uint256_hex(digest), [DSKeyManager publicKeyData:privateKey->ok].hexString); +// BYTES *sig = dash_spv_crypto_keys_key_OpaqueKey_sign(privateKey->ok, slice_u256_ctor_u(digest)); +// NSData *signature = [DSKeyManager NSDataFrom:sig]; +// DMaybeOpaqueKeyDtor(privateKey); +// DMaybeOpaqueKeyDtor(publicKey); +// completion(!signature.isZeroBytes, signature); +//} + +- (BOOL)verifySignature:(NSData *)signature + ofType:(DKeyKind *)signingAlgorithm + forMessageDigest:(UInt256)messageDigest { + for (NSValue *publicKey in [self activeKeysForKeyType:signingAlgorithm]) { + SLICE *message_digest = slice_u256_ctor_u(messageDigest); + SLICE *sig = slice_ctor(signature); + DMaybeOpaqueKey *maybe_key = publicKey.pointerValue; + Result_ok_bool_err_dash_spv_crypto_keys_KeyError *result = dash_spv_crypto_keys_key_OpaqueKey_verify(maybe_key->ok, message_digest, sig); + // TODO: check if correct + BOOL verified = result && result->ok && result->ok[0] == YES; + +// BOOL verified = key_verify_message_digest(publicKey.pointerValue, messageDigest.u8, signature.bytes, signature.length); + if (verified) { + return TRUE; + } + } + return FALSE; +} + +- (BOOL)verifySignature:(NSData *)signature + forKeyIndex:(uint32_t)keyIndex + ofType:(DKeyKind *)signingAlgorithm + forMessageDigest:(UInt256)messageDigest { + DMaybeOpaqueKey *publicKey = [self publicKeyAtIndex:keyIndex ofType:signingAlgorithm]; + BOOL verified = [DSKeyManager verifyMessageDigest:publicKey->ok digest:messageDigest signature:signature]; + // TODO: check we need to destroy here + DMaybeOpaqueKeyDtor(publicKey); + return verified; +} + +- (void)encryptData:(NSData *)data + withKeyAtIndex:(uint32_t)index + forRecipientKey:(DOpaqueKey *)recipientPublicKey + completion:(void (^_Nullable)(NSData *encryptedData))completion { + NSParameterAssert(data); + NSParameterAssert(recipientPublicKey); + + DKeyKind *kind = dash_spv_crypto_keys_key_OpaqueKey_kind(recipientPublicKey); + + DMaybeOpaqueKey *privateKey = [self privateKeyAtIndex:index ofType:kind]; + NSData *encryptedData = [data encryptWithSecretKey:privateKey->ok forPublicKey:recipientPublicKey]; + // TODO: destroy opaque pointer here? + DMaybeOpaqueKeyDtor(privateKey); + if (completion) { + completion(encryptedData); + } +} +// +//- (void)decryptData:(NSData *)encryptedData +// withKeyAtIndex:(uint32_t)index +// fromSenderKey:(DOpaqueKey *)senderPublicKey +// completion:(void (^_Nullable)(NSData *decryptedData))completion { +// +//// senderPublicKey->tag +//// senderPublicKey->tag +// DOpaqueKey *privateKey = [self privateKeyAtIndex:index ofType:(KeyKind)senderPublicKey->tag]; +// // TODO: destroy pointers here? +// NSData *data = [encryptedData decryptWithSecretKey:privateKey fromPublicKey:senderPublicKey]; +// if (completion) { +// completion(data); +// } +//} +// + +- (BOOL)processStateTransitionResult:(Result_ok_dpp_state_transition_proof_result_StateTransitionProofResult_err_dash_spv_platform_error_Error *)result { +#if (!defined(TEST) && defined(DPP_STATE_TRANSITIONS)) + dpp_state_transition_proof_result_StateTransitionProofResult *proof_result = result->ok; + switch (proof_result->tag) { + case dpp_state_transition_proof_result_StateTransitionProofResult_VerifiedDataContract: { + NSData *identifier = NSDataFromPtr(proof_result->verified_data_contract->v0->id->_0->_0); + DSLog(@"VerifiedDataContract: %@", identifier.hexString); + break; + } + case dpp_state_transition_proof_result_StateTransitionProofResult_VerifiedIdentity: { + NSData *identifier = NSDataFromPtr(proof_result->verified_identity->v0->id->_0->_0); + DSLog(@"VerifiedIdentity: %@", identifier.hexString); + break; + } + case dpp_state_transition_proof_result_StateTransitionProofResult_VerifiedPartialIdentity: { + NSData *identifier = NSDataFromPtr(proof_result->verified_partial_identity>id->_0->_0); + DSLog(@"VerifiedPartialIdentity: %@", identifier.hexString); + break; + } + case dpp_state_transition_proof_result_StateTransitionProofResult_VerifiedBalanceTransfer: { + dpp_state_transition_proof_result_StateTransitionProofResult_VerifiedBalanceTransfer_Body *transfer = proof_result->verified_balance_transfer; + NSData *from_identifier = NSDataFromPtr(transfer->_0->id->_0->_0); + NSData *to_identifier = NSDataFromPtr(transfer->_1->id->_0->_0); + DSLog(@"VerifiedBalanceTransfer: %@ --> %@", from_identifier.hexString, to_identifier.hexString); + break; + } + case dpp_state_transition_proof_result_StateTransitionProofResult_VerifiedDocuments: { + std_collections_Map_keys_platform_value_types_identifier_Identifier_values_Option_dpp_document_Document *verified_documents = proof_result->verified_documents; + DSLog(@"VerifiedDocuments: %u", verified_documents->count); + break; + } + case dpp_state_transition_proof_result_StateTransitionProofResult_VerifiedMasternodeVote: { + dpp_voting_votes_Vote *verified_masternode_vote = proof_result->verified_masternode_vote; + DSLog(@"VerifiedMasternodeVote: %u", verified_masternode_vote->tag); + break; + } + default: + break; + } + +#endif + return YES; +} + +// MARK: - Contracts + +- (void)fetchAndUpdateContract:(DPContract *)contract { + NSManagedObjectContext *context = [NSManagedObjectContext platformContext]; + __weak typeof(contract) weakContract = contract; + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //this is so we don't get DAPINetworkService immediately + BOOL isDPNSEmpty = [contract.name isEqual:@"DPNS"] && uint256_is_zero(self.chain.dpnsContractID); + BOOL isDashpayEmpty = [contract.name isEqual:@"DashPay"] && uint256_is_zero(self.chain.dashpayContractID); + BOOL isOtherContract = !([contract.name isEqual:@"DashPay"] || [contract.name isEqual:@"DPNS"]); + if (((isDPNSEmpty || isDashpayEmpty || isOtherContract) && uint256_is_zero(contract.registeredIdentityUniqueID)) || contract.contractState == DPContractState_NotRegistered) { + [contract registerCreator:self]; + [contract saveAndWaitInContext:context]; + + if (!self.keysCreated) { + uint32_t index; + [self createNewKeyOfType:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() saveKey:!self.wallet.isTransient returnIndex:&index]; + } + DMaybeOpaqueKey *privateKey = [self privateKeyAtIndex:self.currentMainKeyIndex ofType:self.currentMainKeyType]; + Result_ok_dpp_state_transition_proof_result_StateTransitionProofResult_err_dash_spv_platform_error_Error *state_transition_result = dash_spv_platform_PlatformSDK_data_contract_update(self.chain.shareCore.runtime, self.chain.shareCore.platform->obj, contract.raw_contract, 0, privateKey->ok); + + if (!state_transition_result) { + return; + } + if (state_transition_result->error) { + Result_ok_dpp_state_transition_proof_result_StateTransitionProofResult_err_dash_spv_platform_error_Error_destroy(state_transition_result); + return; + } + if ([self processStateTransitionResult:state_transition_result]) { + contract.contractState = DPContractState_Registering; + } else { + contract.contractState = DPContractState_Unknown; + } + [contract saveAndWaitInContext:context]; + + Result_ok_Option_dpp_data_contract_DataContract_err_dash_spv_platform_error_Error *monitor_result = dash_spv_platform_contract_manager_ContractsManager_monitor_for_id_bytes(self.chain.shareCore.runtime, self.chain.shareCore.contractsManager->obj, u256_ctor_u(contract.contractId), dash_spv_platform_util_RetryStrategy_Linear_ctor(2), dash_spv_platform_contract_manager_ContractValidator_None_ctor()); + + if (!monitor_result) { + return; + } + if (monitor_result->error) { + Result_ok_Option_dpp_data_contract_DataContract_err_dash_spv_platform_error_Error_destroy(monitor_result); + DSLog(@"Contract Monitoring Error: %@", [NSError ffi_from_platform_error:monitor_result->error]); + return; + } + if (monitor_result->ok) { + NSData *identifier = NSDataFromPtr(monitor_result->ok->v0->id->_0->_0); + if ([identifier isEqualToData:uint256_data(contract.contractId)]) { + DSLog(@"Contract Monitoring OK"); + contract.contractState = DPContractState_Registered; + [contract saveAndWaitInContext:context]; + } else { + DSLog(@"Contract Monitoring Error: Ids dont match"); + } + } + DSLog(@"Contract Monitoring Error"); + + } else if (contract.contractState == DPContractState_Registered || contract.contractState == DPContractState_Registering) { + DSLog(@"Fetching contract for verification %@", contract.base58ContractId); + DIdentifier *identifier = platform_value_types_identifier_Identifier_ctor(platform_value_types_identifier_IdentifierBytes32_ctor(u256_ctor_u(contract.contractId))); + Result_ok_Option_dpp_data_contract_DataContract_err_dash_spv_platform_error_Error *result = dash_spv_platform_contract_manager_ContractsManager_fetch_contract_by_id(self.chain.shareCore.runtime, self.chain.shareCore.contractsManager->obj, identifier); + if (!result) return; + if (result->error || !result->ok->v0->document_types) { + DSLog(@"Fetch contract error %u", result->error->tag); + contract.contractState = DPContractState_NotRegistered; + [contract saveAndWaitInContext:context]; + Result_ok_Option_dpp_data_contract_DataContract_err_dash_spv_platform_error_Error_destroy(result); + return; + } + + Result_ok_Option_dpp_data_contract_DataContract_err_dash_spv_platform_error_Error *contract_result = dash_spv_platform_contract_manager_ContractsManager_fetch_contract_by_id_bytes(self.chain.shareCore.runtime, self.chain.shareCore.contractsManager->obj, u256_ctor_u(contract.contractId)); + + dispatch_async(self.identityQueue, ^{ + __strong typeof(weakContract) strongContract = weakContract; + if (!weakContract || !contract_result) return; + if (!contract_result->ok) { + strongContract.contractState = DPContractState_NotRegistered; + [strongContract saveAndWaitInContext:context]; + Result_ok_Option_dpp_data_contract_DataContract_err_dash_spv_platform_error_Error_destroy(result); + return; + } + if (strongContract.contractState == DPContractState_Registered && !dash_spv_platform_contract_manager_has_equal_document_type_keys(contract_result->ok, strongContract.raw_contract)) { + strongContract.contractState = DPContractState_NotRegistered; + [strongContract saveAndWaitInContext:context]; + //DSLog(@"Contract dictionary is %@", contractDictionary); + } + }); +// +// [self.DAPINetworkService fetchContractForId:uint256_data(contract.contractId) +// completionQueue:self.identityQueue +// success:^(NSDictionary *_Nonnull contractDictionary) { +// __strong typeof(weakContract) strongContract = weakContract; +// if (!weakContract) return; +// if (!contractDictionary[@"documents"]) { +// strongContract.contractState = DPContractState_NotRegistered; +// [strongContract saveAndWaitInContext:context]; +// return; +// } +// if (strongContract.contractState == DPContractState_Registered) { +// NSSet *set1 = [NSSet setWithArray:[contractDictionary[@"documents"] allKeys]]; +// NSSet *set2 = [NSSet setWithArray:[strongContract.documents allKeys]]; +// if (![set1 isEqualToSet:set2]) { +// strongContract.contractState = DPContractState_NotRegistered; +// [strongContract saveAndWaitInContext:context]; +// } +// DSLog(@"Contract dictionary is %@", contractDictionary); +// } +// } +// failure:^(NSError *_Nonnull error) { +// NSString *debugDescription1 = [error.userInfo objectForKey:@"NSDebugDescription"]; +// NSError *jsonError; +// NSData *objectData = [debugDescription1 dataUsingEncoding:NSUTF8StringEncoding]; +// NSDictionary *debugDescription = [NSJSONSerialization JSONObjectWithData:objectData options:0 error:&jsonError]; +// //NSDictionary * debugDescription = +// __unused NSString *errorMessage = debugDescription[@"grpc_message"]; //!OCLINT +// if (TRUE) { //[errorMessage isEqualToString:@"Invalid argument: Contract not found"]) { +// __strong typeof(weakContract) strongContract = weakContract; +// if (!strongContract) return; +// __strong typeof(weakSelf) strongSelf = weakSelf; +// if (!strongSelf) return; +// strongContract.contractState = DPContractState_NotRegistered; +// [strongContract saveAndWaitInContext:context]; +// } +// }]; + } + }); +} +// +//- (void)fetchAndUpdateContractWithBase58Identifier:(NSString *)base58Identifier { +// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //this is so we don't get DAPINetworkService immediately +// [self.DAPINetworkService fetchContractForId:base58Identifier.base58ToData +// completionQueue:self.identityQueue +// success:^(NSDictionary *_Nonnull contract) {} +// failure:^(NSError *_Nonnull error) {}]; +// }); +//} + +// MARK: - Monitoring + +- (void)updateCreditBalance { + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //this is so we don't get DAPINetworkService immediately + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) return; + Result_ok_Option_u64_err_dash_spv_platform_error_Error *result = dash_spv_platform_identity_manager_IdentitiesManager_fetch_balance_by_id_bytes(strongSelf.chain.shareCore.runtime, strongSelf.chain.shareCore.identitiesManager->obj, u256_ctor(self.uniqueIDData)); + if (!result) { + DSLog(@"updateCreditBalance (%@): NULL RESULT", self.uniqueIDData.hexString); + return; + } + if (!result->ok) { + DSLog(@"updateCreditBalance (%@): ERROR RESULT: %u", self.uniqueIDData.hexString, result->error->tag); + Result_ok_Option_u64_err_dash_spv_platform_error_Error_destroy(result); + return; + } + dispatch_async(self.identityQueue, ^{ + DSLog(@"updateCreditBalance (%@): OK: %llu", self.uniqueIDData.hexString, result->ok[0]); + strongSelf.creditBalance = result->ok[0]; + }); + + +// DSDAPIPlatformNetworkService *dapiNetworkService = self.DAPINetworkService; +// [dapiNetworkService getIdentityById:self.uniqueIDData +// completionQueue:self.identityQueue +// success:^(NSDictionary *_Nullable profileDictionary) { +// __strong typeof(weakSelf) strongSelf = weakSelf; +// if (!strongSelf) return; +// dispatch_async(self.identityQueue, ^{ +// strongSelf.creditBalance = (uint64_t)[profileDictionary[@"balance"] longLongValue]; +// }); +// } +// failure:^(NSError *_Nonnull error) { +// if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node +// [self.DAPIClient removeDAPINodeByAddress:dapiNetworkService.ipAddress]; +// } +// }]; + }); +} + +//- (void)monitorForIdentityWithRetryCount:(uint32_t)retryCount +// retryAbsentCount:(uint32_t)retryAbsentCount +// delay:(NSTimeInterval)delay +// retryDelayType:(DSIdentityRetryDelayType)retryDelayType +// options:(DSIdentityMonitorOptions)options +// inContext:(NSManagedObjectContext *)context +// completion:(void (^)(BOOL success, BOOL found, NSError *error))completion { +// __weak typeof(self) weakSelf = self; +// DSDAPIPlatformNetworkService *dapiNetworkService = self.DAPINetworkService; +// void (^completionSuccess)(BOOL found) = ^(BOOL found) { if (completion) completion(YES, found, nil); }; +// void (^completionError)(BOOL success, BOOL found, NSError *error) = ^(BOOL success, BOOL found, NSError *error) { if (completion) completion(success, found, error); }; +// +// void (^notFoundError)(NSError *error) = ^(NSError *error) { +// BOOL acceptNotFound = options & DSIdentityMonitorOptions_AcceptNotFoundAsNotAnError; +// completionError(acceptNotFound, NO, acceptNotFound ? nil : error ?: ERROR_NO_IDENTITY); +// }; +// +// +// [dapiNetworkService getIdentityById:self.uniqueIDData +// completionQueue:self.identityQueue +// success:^(NSDictionary *_Nullable versionedIdentityDictionary) { +// +// __strong typeof(weakSelf) strongSelf = weakSelf; +// if (!strongSelf) return; +// if (!versionedIdentityDictionary) notFoundError(nil); +// if (![versionedIdentityDictionary respondsToSelector:@selector(objectForKey:)]) { +// completionSuccess(NO); +// return; +// } +// NSNumber *version = [versionedIdentityDictionary objectForKey:@(DSPlatformStoredMessage_Version)]; +// NSDictionary *identityDictionary = [versionedIdentityDictionary objectForKey:@(DSPlatformStoredMessage_Item)]; +// if (!identityDictionary) { +// notFoundError(nil); +// } else { +// if (identityDictionary.count) { +// [strongSelf applyIdentityDictionary:identityDictionary +// version:[version intValue] +// save:!self.isTransient +// inContext:context]; +// strongSelf.registrationStatus = DSIdentityRegistrationStatus_Registered; +// [self saveInContext:context]; +// } +// completionSuccess(YES); +// } +// } +// +// failure:^(NSError *_Nonnull error) { +// if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node +// [self.DAPIClient removeDAPINodeByAddress:dapiNetworkService.ipAddress]; +// } +// uint32_t nextRetryAbsentCount = retryAbsentCount; +// if ([[error localizedDescription] isEqualToString:@"Identity not found"]) { +// if (!retryAbsentCount) { +// notFoundError(error); +// return; +// } +// nextRetryAbsentCount--; +// } +// if (retryCount > 0) { +// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ +// NSTimeInterval nextDelay = delay; +// switch (retryDelayType) { +// case DSIdentityRetryDelayType_SlowingDown20Percent: +// nextDelay = delay * 1.2; +// break; +// case DSIdentityRetryDelayType_SlowingDown50Percent: +// nextDelay = delay * 1.5; +// break; +// default: +// break; +// } +// [self monitorForIdentityWithRetryCount:retryCount - 1 +// retryAbsentCount:nextRetryAbsentCount +// delay:nextDelay +// retryDelayType:retryDelayType +// options:options +// inContext:context +// completion:completion]; +// }); +// } else { +// completion(NO, NO, error); +// } +// }]; +//} + +- (void)monitorForContract:(DPContract *)contract + withRetryCount:(uint32_t)retryCount + inContext:(NSManagedObjectContext *)context + completion:(void (^)(BOOL success, NSError *error))completion { + __weak typeof(self) weakSelf = self; + NSParameterAssert(contract); + if (!contract) return; + DSDAPIPlatformNetworkService *dapiNetworkService = self.DAPINetworkService; + [dapiNetworkService fetchContractForId:uint256_data(contract.contractId) + completionQueue:self.identityQueue + success:^(id _Nonnull contractDictionary) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + if (completion) completion(NO, ERROR_MEM_ALLOC); + return; + } + DSLog(@"Contract dictionary is %@", contractDictionary); + if ([contractDictionary isKindOfClass:[NSDictionary class]] && [contractDictionary[@"$id"] isEqualToData:uint256_data(contract.contractId)]) { + contract.contractState = DPContractState_Registered; + [contract saveAndWaitInContext:context]; + if (completion) completion(TRUE, nil); + } else if (retryCount > 0) { + [strongSelf monitorForContract:contract withRetryCount:retryCount - 1 inContext:context completion:completion]; + } else if (completion) { + completion(NO, ERROR_MALFORMED_RESPONSE); + } + } + failure:^(NSError *_Nonnull error) { + if (error.code == 12) { //UNIMPLEMENTED, this would mean that we are connecting to an old node + [self.DAPIClient removeDAPINodeByAddress:dapiNetworkService.ipAddress]; + } + if (retryCount > 0) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + if (completion) completion(NO, ERROR_MEM_ALLOC); + return; + } + [strongSelf monitorForContract:contract + withRetryCount:retryCount - 1 + inContext:context + completion:completion]; + }); + } else if (completion) { + completion(FALSE, error); + } + }]; +} + +// MARK: - Dashpay + +// MARK: Helpers + +- (BOOL)isDashpayReady { + return self.activeKeyCount > 0 && self.isRegistered; +} + + + +// MARK: - Persistence + +// MARK: Saving + +- (void)saveInitial { + [self saveInitialInContext:self.platformContext]; +} + +- (DSBlockchainIdentityEntity *)initialEntityInContext:(NSManagedObjectContext *)context { + DSChainEntity *chainEntity = [self.chain chainEntityInContext:context]; + DSBlockchainIdentityEntity *entity = [DSBlockchainIdentityEntity managedObjectInBlockedContext:context]; + entity.uniqueID = uint256_data(self.uniqueID); + entity.isLocal = self.isLocal; + entity.registrationStatus = self.registrationStatus; + if (self.isLocal) { + entity.registrationFundingTransaction = (DSAssetLockTransactionEntity *)[DSTransactionEntity anyObjectInContext:context matching:@"transactionHash.txHash == %@", uint256_data(self.registrationAssetLockTransaction.txHash)]; + } + entity.chain = chainEntity; + [self collectUsernameEntitiesIntoIdentityEntityInContext:entity context:context]; + for (NSNumber *index in self.keyInfoDictionaries) { + NSDictionary *keyDictionary = self.keyInfoDictionaries[index]; + DSIdentityKeyStatus status = [keyDictionary[@(DSIdentityKeyDictionary_KeyStatus)] unsignedIntValue]; + DKeyKind *keyType = [keyDictionary[@(DSIdentityKeyDictionary_KeyType)] pointerValue]; + DMaybeOpaqueKey *key = [keyDictionary[@(DSIdentityKeyDictionary_Key)] pointerValue]; + DSAuthenticationKeysDerivationPath *derivationPath = [self derivationPathForType:keyType]; + const NSUInteger indexes[] = {_index, index.unsignedIntegerValue}; + [self createNewKey:key + forIdentityEntity:entity + atPath:[NSIndexPath indexPathWithIndexes:indexes length:2] + withStatus:status + fromDerivationPath:derivationPath + inContext:context]; + } + DSDashpayUserEntity *dashpayUserEntity = [DSDashpayUserEntity managedObjectInBlockedContext:context]; + dashpayUserEntity.chain = chainEntity; + entity.matchingDashpayUser = dashpayUserEntity; + if (self.isOutgoingInvitation) { + DSBlockchainInvitationEntity *invitationEntity = [DSBlockchainInvitationEntity managedObjectInBlockedContext:context]; + invitationEntity.chain = chainEntity; + entity.associatedInvitation = invitationEntity; + } + return entity; +} + +- (void)saveInitialInContext:(NSManagedObjectContext *)context { + if (self.isTransient) return; + //no need for active check, in fact it will cause an infinite loop + [context performBlockAndWait:^{ + DSBlockchainIdentityEntity *entity = [self initialEntityInContext:context]; + DSDashpayUserEntity *dashpayUserEntity = entity.matchingDashpayUser; + [context ds_saveInBlockAndWait]; + [[NSManagedObjectContext viewContext] performBlockAndWait:^{ + self.matchingDashpayUserInViewContext = [[NSManagedObjectContext viewContext] objectWithID:dashpayUserEntity.objectID]; + }]; + [[NSManagedObjectContext platformContext] performBlockAndWait:^{ + self.matchingDashpayUserInPlatformContext = [[NSManagedObjectContext platformContext] objectWithID:dashpayUserEntity.objectID]; + }]; + if ([self isLocal]) + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSIdentityDidUpdateNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSIdentityKey: self + }]; + }); + }]; +} + +- (void)saveInContext:(NSManagedObjectContext *)context { + if (self.isTransient) return; + if (!self.isActive) return; + [context performBlockAndWait:^{ + BOOL changeOccured = NO; + NSMutableArray *updateEvents = [NSMutableArray array]; + DSBlockchainIdentityEntity *entity = [self identityEntityInContext:context]; + if (entity.creditBalance != self.creditBalance) { + entity.creditBalance = self.creditBalance; + changeOccured = YES; + [updateEvents addObject:DSIdentityUpdateEventCreditBalance]; + } + if (entity.registrationStatus != self.registrationStatus) { + entity.registrationStatus = self.registrationStatus; + changeOccured = YES; + [updateEvents addObject:DSIdentityUpdateEventRegistration]; + } + + if (!uint256_eq(entity.dashpaySyncronizationBlockHash.UInt256, self.dashpaySyncronizationBlockHash)) { + entity.dashpaySyncronizationBlockHash = uint256_data(self.dashpaySyncronizationBlockHash); + changeOccured = YES; + [updateEvents addObject:DSIdentityUpdateEventDashpaySyncronizationBlockHash]; + } + + if (entity.lastCheckedUsernamesTimestamp != self.lastCheckedUsernamesTimestamp) { + entity.lastCheckedUsernamesTimestamp = self.lastCheckedUsernamesTimestamp; + changeOccured = YES; + } + + if (entity.lastCheckedProfileTimestamp != self.lastCheckedProfileTimestamp) { + entity.lastCheckedProfileTimestamp = self.lastCheckedProfileTimestamp; + changeOccured = YES; + } + + if (entity.lastCheckedIncomingContactsTimestamp != self.lastCheckedIncomingContactsTimestamp) { + entity.lastCheckedIncomingContactsTimestamp = self.lastCheckedIncomingContactsTimestamp; + changeOccured = YES; + } + + if (entity.lastCheckedOutgoingContactsTimestamp != self.lastCheckedOutgoingContactsTimestamp) { + entity.lastCheckedOutgoingContactsTimestamp = self.lastCheckedOutgoingContactsTimestamp; + changeOccured = YES; + } + + if (changeOccured) { + [context ds_save]; + if (updateEvents.count) + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSIdentityDidUpdateNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSIdentityKey: self, + DSIdentityUpdateEvents: updateEvents + }]; + }); + } + }]; +} + +- (NSString *)identifierForKeyAtPath:(NSIndexPath *)path + fromDerivationPath:(DSDerivationPath *)derivationPath { + return [NSString stringWithFormat:@"%@-%@-%@", self.uniqueIdString, derivationPath.standaloneExtendedPublicKeyUniqueID, [[path softenAllItems] indexPathString]]; +} + +- (BOOL)createNewKey:(DMaybeOpaqueKey *)key + forIdentityEntity:(DSBlockchainIdentityEntity *)identityEntity + atPath:(NSIndexPath *)path + withStatus:(DSIdentityKeyStatus)status + 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 *blockchainIdentityKeyPathEntity = [DSBlockchainIdentityKeyPathEntity managedObjectInBlockedContext:context]; + blockchainIdentityKeyPathEntity.derivationPath = derivationPathEntity; + blockchainIdentityKeyPathEntity.keyType = key->ok->tag; + blockchainIdentityKeyPathEntity.keyStatus = status; + NSData *privateKeyData = [DSKeyManager privateKeyData:key->ok]; + if (privateKeyData) { + setKeychainData(privateKeyData, [self identifierForKeyAtPath:path fromDerivationPath:derivationPath], YES); +#if DEBUG + DSLogPrivate(@"Saving key at %@ for user %@", [self identifierForKeyAtPath:path fromDerivationPath:derivationPath], self.currentDashpayUsername); +#else + DSLog(@"Saving key at %@ for user %@", @"", @""); +#endif + } else { + DKeyKind *kind = dash_spv_crypto_keys_key_OpaqueKey_kind(key->ok); + DMaybeOpaqueKey *privateKey = [self derivePrivateKeyAtIndexPath:path ofType:kind]; + NSAssert([DSKeyManager keysPublicKeyDataIsEqual:privateKey->ok key2:key->ok], @"The keys don't seem to match up"); + NSData *privateKeyData = [DSKeyManager privateKeyData:privateKey->ok]; + NSAssert(privateKeyData, @"Private key data should exist"); + setKeychainData(privateKeyData, [self identifierForKeyAtPath:path fromDerivationPath:derivationPath], YES); +#if DEBUG + DSLogPrivate(@"Saving key after rederivation %@ for user %@", [self identifierForKeyAtPath:path fromDerivationPath:derivationPath], self.currentDashpayUsername ? self.currentDashpayUsername : self.uniqueIdString); +#else + DSLog(@"Saving key after rederivation %@ for user %@", @"", @""); +#endif + } + + blockchainIdentityKeyPathEntity.path = path; + blockchainIdentityKeyPathEntity.publicKeyData = [DSKeyManager publicKeyData:key->ok]; + blockchainIdentityKeyPathEntity.keyID = (uint32_t)[path indexAtPosition:path.length - 1]; + [identityEntity addKeyPathsObject:blockchainIdentityKeyPathEntity]; + return YES; + } else { +#if DEBUG + DSLogPrivate(@"Already had saved this key %@", path); +#else + DSLog(@"Already had saved this key %@", @""); +#endif + return NO; //no need to save the context + } +} + +- (void)saveNewKey:(DMaybeOpaqueKey *)key + atPath:(NSIndexPath *)path + withStatus:(DSIdentityKeyStatus)status +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 fromDerivationPath:derivationPath inContext:context]) + [context ds_save]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSIdentityDidUpdateNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSIdentityKey: self, + DSIdentityUpdateEvents: @[DSIdentityUpdateEventKeyUpdate] + }]; + }); + }]; +} + +- (void)saveNewRemoteIdentityKey:(DMaybeOpaqueKey *)key + forKeyWithIndexID:(uint32_t)keyID + withStatus:(DSIdentityKeyStatus)status + inContext:(NSManagedObjectContext *)context { + NSAssert(self.isLocal == FALSE, @"This should only be called on non local blockchain 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 *blockchainIdentityKeyPathEntity = [DSBlockchainIdentityKeyPathEntity managedObjectInBlockedContext:context]; + blockchainIdentityKeyPathEntity.keyType = key->ok->tag; + blockchainIdentityKeyPathEntity.keyStatus = status; + blockchainIdentityKeyPathEntity.keyID = keyID; + blockchainIdentityKeyPathEntity.publicKeyData = [DSKeyManager publicKeyData:key->ok]; + [identityEntity addKeyPathsObject:blockchainIdentityKeyPathEntity]; + [context ds_save]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSIdentityDidUpdateNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSIdentityKey: self, + DSIdentityUpdateEvents: @[DSIdentityUpdateEventKeyUpdate] + }]; + }); + }]; +} + + +- (void)updateStatus:(DSIdentityKeyStatus)status + forKeyAtPath:(NSIndexPath *)path + 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]; + DSDerivationPathEntity *derivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:derivationPath inContext:context]; + DSBlockchainIdentityKeyPathEntity *blockchainIdentityKeyPathEntity = [[DSBlockchainIdentityKeyPathEntity objectsInContext:context matching:@"blockchainIdentity == %@ && derivationPath == %@ && path == %@", identityEntity, derivationPathEntity, path] firstObject]; + if (blockchainIdentityKeyPathEntity && (blockchainIdentityKeyPathEntity.keyStatus != status)) { + blockchainIdentityKeyPathEntity.keyStatus = status; + [context ds_save]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSIdentityDidUpdateNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSIdentityKey: self, + DSIdentityUpdateEvents: @[DSIdentityUpdateEventKeyUpdate] + }]; + }); + }]; +} + +- (void)updateStatus:(DSIdentityKeyStatus)status + forKeyWithIndexID:(uint32_t)keyID + inContext:(NSManagedObjectContext *)context { + NSAssert(self.isLocal == FALSE, @"This should only be called on non local blockchain identities"); + if (self.isLocal || self.isTransient || !self.isActive) return; + [context performBlockAndWait:^{ + DSBlockchainIdentityEntity *identityEntity = [self identityEntityInContext:context]; + DSBlockchainIdentityKeyPathEntity *identityKeyPathEntity = [[DSBlockchainIdentityKeyPathEntity objectsInContext:context matching:@"blockchainIdentity == %@ && derivationPath == NULL && keyID == %@", identityEntity, @(keyID)] firstObject]; + if (identityKeyPathEntity) { + DSBlockchainIdentityKeyPathEntity *identityKeyPathEntity = [DSBlockchainIdentityKeyPathEntity managedObjectInBlockedContext:context]; + identityKeyPathEntity.keyStatus = status; + [context ds_save]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSIdentityDidUpdateNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSIdentityKey: self, + DSIdentityUpdateEvents: @[DSIdentityUpdateEventKeyUpdate] + }]; + }); + }]; +} + +// MARK: Deletion + +- (void)deletePersistentObjectAndSave:(BOOL)save + inContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + DSBlockchainIdentityEntity *identityEntity = [self identityEntityInContext:context]; + if (identityEntity) { + NSSet *friendRequests = [identityEntity.matchingDashpayUser outgoingRequests]; + for (DSFriendRequestEntity *friendRequest in friendRequests) { + uint32_t accountNumber = friendRequest.account.index; + DSAccount *account = [self.wallet accountWithNumber:accountNumber]; + [account removeIncomingDerivationPathForFriendshipWithIdentifier:friendRequest.friendshipIdentifier]; + } + [identityEntity deleteObjectAndWait]; + if (save) { + [context ds_save]; + } + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSIdentityDidUpdateNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSIdentityKey: self + }]; + }); + }]; +} + +// MARK: Entity + +- (DSBlockchainIdentityEntity *)identityEntity { + return [self identityEntityInContext:[NSManagedObjectContext viewContext]]; +} + +- (DSBlockchainIdentityEntity *)identityEntityInContext:(NSManagedObjectContext *)context { + __block DSBlockchainIdentityEntity *entity = nil; + [context performBlockAndWait:^{ + entity = [DSBlockchainIdentityEntity anyObjectInContext:context matching:@"uniqueID == %@", self.uniqueIDData]; + }]; + NSAssert(entity, @"An entity should always be found"); + return entity; +} + +- (DSDashpayUserEntity *)matchingDashpayUserInViewContext { + if (!_matchingDashpayUserInViewContext) { + _matchingDashpayUserInViewContext = [self matchingDashpayUserInContext:[NSManagedObjectContext viewContext]]; + } + return _matchingDashpayUserInViewContext; +} + +- (DSDashpayUserEntity *)matchingDashpayUserInPlatformContext { + if (!_matchingDashpayUserInPlatformContext) { + _matchingDashpayUserInPlatformContext = [self matchingDashpayUserInContext:[NSManagedObjectContext platformContext]]; + } + return _matchingDashpayUserInPlatformContext; +} + +- (DSDashpayUserEntity *)matchingDashpayUserInContext:(NSManagedObjectContext *)context { + if (_matchingDashpayUserInViewContext || _matchingDashpayUserInPlatformContext) { + if (context == [_matchingDashpayUserInPlatformContext managedObjectContext]) return _matchingDashpayUserInPlatformContext; + if (context == [_matchingDashpayUserInViewContext managedObjectContext]) return _matchingDashpayUserInViewContext; + if (_matchingDashpayUserInPlatformContext) { + __block NSManagedObjectID *managedId; + [[NSManagedObjectContext platformContext] performBlockAndWait:^{ + managedId = _matchingDashpayUserInPlatformContext.objectID; + }]; + return [context objectWithID:managedId]; + } else { + __block NSManagedObjectID *managedId; + [[NSManagedObjectContext viewContext] performBlockAndWait:^{ + managedId = _matchingDashpayUserInViewContext.objectID; + }]; + return [context objectWithID:managedId]; + } + } else { + __block DSDashpayUserEntity *dashpayUserEntity = nil; + [context performBlockAndWait:^{ + dashpayUserEntity = [DSDashpayUserEntity anyObjectInContext:context matching:@"associatedBlockchainIdentity.uniqueID == %@", uint256_data(self.uniqueID)]; + }]; + return dashpayUserEntity; + } +} + +//-(DSIdentityRegistrationTransition*)identityRegistrationTransition { +// if (!_identityRegistrationTransition) { +// _identityRegistrationTransition = (DSIdentityRegistrationTransition*)[self.wallet.specialTransactionsHolder transactionForHash:self.registrationTransitionHash]; +// } +// return _identityRegistrationTransition; +//} + +//-(UInt256)lastTransitionHash { +// //this is not effective, do this locally in the future +// return [[self allTransitions] lastObject].transitionHash; +//} + + +- (NSString *)debugDescription { + return [[super debugDescription] stringByAppendingString:[NSString stringWithFormat:@" {%@-%@}", self.currentDashpayUsername, self.uniqueIdString]]; +} + +@end diff --git a/DashSync/shared/Models/Identity/DSInvitation+Protected.h b/DashSync/shared/Models/Identity/DSInvitation+Protected.h new file mode 100644 index 000000000..e45cd6ee4 --- /dev/null +++ b/DashSync/shared/Models/Identity/DSInvitation+Protected.h @@ -0,0 +1,55 @@ +// +// Created by Samuel Westrich +// Copyright © 2564 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "BigIntTypes.h" +#import "DSInvitation.h" +#import "DSBlockchainInvitationEntity+CoreDataClass.h" + +NS_ASSUME_NONNULL_BEGIN + +@class DSChain; + +@interface DSInvitation (Protected) + +- (instancetype)initAtIndex:(uint32_t)index + withLockedOutpoint:(DSUTXO)lockedOutpoint + inWallet:(DSWallet *)wallet; + +- (instancetype)initAtIndex:(uint32_t)index + withLockedOutpoint:(DSUTXO)lockedOutpoint + inWallet:(DSWallet *)wallet + withInvitationEntity:(DSBlockchainInvitationEntity *)invitationEntity; + +- (instancetype)initWithUniqueId:(UInt256)uniqueId + isTransient:(BOOL)isTransient + onChain:(DSChain *)chain; + +- (instancetype)initAtIndex:(uint32_t)index + inWallet:(DSWallet *)wallet; +- (instancetype)initAtIndex:(uint32_t)index + withAssetLockTransaction:(DSAssetLockTransaction *)transaction + inWallet:(DSWallet *)wallet; + +- (void)registerInWalletForIdentityUniqueId:(UInt256)identityUniqueId; +- (void)registerInWalletForAssetLockTransaction:(DSAssetLockTransaction *)transaction; + +- (void)deletePersistentObjectAndSave:(BOOL)save + inContext:(NSManagedObjectContext *)context; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Identity/DSBlockchainInvitation.h b/DashSync/shared/Models/Identity/DSInvitation.h similarity index 73% rename from DashSync/shared/Models/Identity/DSBlockchainInvitation.h rename to DashSync/shared/Models/Identity/DSInvitation.h index 111592b25..aa1030160 100644 --- a/DashSync/shared/Models/Identity/DSBlockchainInvitation.h +++ b/DashSync/shared/Models/Identity/DSInvitation.h @@ -15,26 +15,27 @@ // limitations under the License. // -#import "DSBlockchainIdentity.h" +#import "DSAssetLockTransaction.h" +#import "DSIdentity.h" #import -@class DSBlockchainIdentity, DSWallet, DSCreditFundingTransaction; +@class DSIdentity, DSWallet; NS_ASSUME_NONNULL_BEGIN -FOUNDATION_EXPORT NSString *const DSBlockchainInvitationDidUpdateNotification; -FOUNDATION_EXPORT NSString *const DSBlockchainInvitationKey; -FOUNDATION_EXPORT NSString *const DSBlockchainInvitationUpdateEvents; -FOUNDATION_EXPORT NSString *const DSBlockchainInvitationUpdateEventLink; +FOUNDATION_EXPORT NSString *const DSInvitationDidUpdateNotification; +FOUNDATION_EXPORT NSString *const DSInvitationKey; +FOUNDATION_EXPORT NSString *const DSInvitationUpdateEvents; +FOUNDATION_EXPORT NSString *const DSInvitationUpdateEventLink; -@interface DSBlockchainInvitation : NSObject +@interface DSInvitation : NSObject /*! @brief Initialized with an invitation link. The wallet must be on a chain that supports platform features. */ - (instancetype)initWithInvitationLink:(NSString *)invitationLink inWallet:(DSWallet *)wallet; /*! @brief This is the identity that was made from the invitation. There should always be an identity associated to a blockchain invitation. This identity might not yet be registered on Dash Platform. */ -@property (nonatomic, readonly) DSBlockchainIdentity *identity; +@property (nonatomic, readonly) DSIdentity *identity; /*! @brief This is an invitation that was created locally. */ @property (nonatomic, readonly) BOOL createdLocally; @@ -62,10 +63,10 @@ FOUNDATION_EXPORT NSString *const DSBlockchainInvitationUpdateEventLink; + (void)verifyInvitationLink:(NSString *)invitationLink onChain:(DSChain *)chain completion:(void (^_Nullable)(DSTransaction *transaction, bool spent, NSError *error))completion completionQueue:(dispatch_queue_t)completionQueue; /*! @brief Registers the blockchain identity if the invitation was created with an invitation link. The blockchain identity is then associated with the invitation. */ -- (void)acceptInvitationUsingWalletIndex:(uint32_t)index setDashpayUsername:(NSString *)dashpayUsername authenticationPrompt:(NSString *)authenticationMessage identityRegistrationSteps:(DSBlockchainIdentityRegistrationStep)identityRegistrationSteps stepCompletion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepCompleted))stepCompletion completion:(void (^_Nullable)(DSBlockchainIdentityRegistrationStep stepsCompleted, NSError *error))completion completionQueue:(dispatch_queue_t)completionQueue; +- (void)acceptInvitationUsingWalletIndex:(uint32_t)index setDashpayUsername:(NSString *)dashpayUsername authenticationPrompt:(NSString *)authenticationMessage identityRegistrationSteps:(DSIdentityRegistrationStep)identityRegistrationSteps stepCompletion:(void (^_Nullable)(DSIdentityRegistrationStep stepCompleted))stepCompletion completion:(void (^_Nullable)(DSIdentityRegistrationStep stepsCompleted, NSError *error))completion completionQueue:(dispatch_queue_t)completionQueue; /*! @brief Generates blockchain invitations' extended public keys by asking the user to authentication with the prompt. */ -- (void)generateBlockchainInvitationsExtendedPublicKeysWithPrompt:(NSString *)prompt completion:(void (^_Nullable)(BOOL registered))completion; +- (void)generateInvitationsExtendedPublicKeysWithPrompt:(NSString *)prompt completion:(void (^_Nullable)(BOOL registered))completion; /*! @brief Register the blockchain identity to its wallet. This should only be done once on the creation of the blockchain identity. */ @@ -80,15 +81,15 @@ FOUNDATION_EXPORT NSString *const DSBlockchainInvitationUpdateEventLink; */ - (BOOL)unregisterLocally; -/*! @brief Register the blockchain invitation to its wallet from a credit funding registration transaction. This should only be done once on the creation of the blockchain invitation. - @param fundingTransaction The funding transaction used to initially fund the blockchain identity. +/*! @brief Register the blockchain invitation to its wallet from a asset lock registration transaction. This should only be done once on the creation of the blockchain invitation. + @param transaction The asset lock transaction used to initially fund the blockchain identity. */ -- (void)registerInWalletForRegistrationFundingTransaction:(DSCreditFundingTransaction *)fundingTransaction; +- (void)registerInWalletForAssetLockTransaction:(DSAssetLockTransaction *)transaction; /*! @brief Create the invitation full link and mark the "fromIdentity" as the source of the invitation. @param identity The source of the invitation. */ -- (void)createInvitationFullLinkFromIdentity:(DSBlockchainIdentity *)identity completion:(void (^_Nullable)(BOOL cancelled, NSString *invitationFullLink))completion; +- (void)createInvitationFullLinkFromIdentity:(DSIdentity *)identity completion:(void (^_Nullable)(BOOL cancelled, NSString *invitationFullLink))completion; @end diff --git a/DashSync/shared/Models/Identity/DSInvitation.m b/DashSync/shared/Models/Identity/DSInvitation.m new file mode 100644 index 000000000..8836e64fe --- /dev/null +++ b/DashSync/shared/Models/Identity/DSInvitation.m @@ -0,0 +1,465 @@ +// +// Created by Samuel Westrich +// Copyright © 2564 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DSInvitation.h" +#import "DSAuthenticationManager.h" +#import "DSIdentity+Profile.h" +#import "DSIdentity+Protected.h" +#import "DSIdentity+Username.h" +#import "DSBlockchainInvitationEntity+CoreDataClass.h" +#import "DSChain+Params.h" +#import "DSChainManager.h" +#import "DSAssetLockDerivationPath.h" +#import "DSDAPICoreNetworkService.h" +#import "DSDerivationPathFactory.h" +#import "DSIdentitiesManager+Protected.h" +#import "DSInstantSendTransactionLock.h" +#import "DSWallet.h" +#import "DSWallet+Identity.h" +#import "DSWallet+Invitation.h" +#import "NSData+DSHash.h" +#import "NSError+Dash.h" +#import "NSManagedObject+Sugar.h" +#import "NSManagedObjectContext+DSSugar.h" +#import "NSString+Dash.h" + +#define ERROR_INVITATION_FORMAT [NSError errorWithCode:400 localizedDescriptionKey:@"Invitation format is not valid"] +#define ERROR_SETTING_EXT_PRV_KEY [NSError errorWithCode:500 localizedDescriptionKey:@"Error setting the external funding private key"] +#define ERROR_GEN_IDENTITY_KEYS [NSError errorWithCode:500 localizedDescriptionKey:@"Error generating Identity keys"] +#define ERROR_INVALID_FUNDING_PRV_KEY [NSError errorWithCode:400 localizedDescriptionKey:@"Funding private key is not valid"] +#define ERROR_INVALID_INV_TX [NSError errorWithCode:400 localizedDescriptionKey:@"Invitation transaction is not valid"] + +@interface DSInvitation () + +@property (nonatomic, weak) DSWallet *wallet; +@property (nonatomic, strong) DSChain *chain; +@property (nonatomic, copy) NSString *link; +@property (nonatomic, strong) DSIdentity *identity; +@property (nonatomic, assign) BOOL isTransient; +@property (nonatomic, assign) BOOL needsIdentityRetrieval; +@property (nonatomic, assign) BOOL createdLocally; + +@end + +@implementation DSInvitation + +- (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.isTransient = FALSE; + self.createdLocally = YES; + self.identity = [[DSIdentity alloc] initAtIndex:index inWallet:wallet]; + [self.identity setAssociatedInvitation:self]; + self.chain = wallet.chain; + self.needsIdentityRetrieval = NO; + return self; +} + +- (instancetype)initAtIndex:(uint32_t)index + withAssetLockTransaction:(DSAssetLockTransaction *)transaction + inWallet:(DSWallet *)wallet { + NSParameterAssert(wallet); + NSAssert(index != UINT32_MAX, @"index must be found"); + if (!(self = [super init])) return nil; + self.wallet = wallet; + self.isTransient = FALSE; + self.createdLocally = YES; + self.identity = [[DSIdentity alloc] initAtIndex:index withAssetLockTransaction:transaction withUsernameDictionary:nil inWallet:wallet]; + [self.identity setAssociatedInvitation:self]; + self.chain = wallet.chain; + self.needsIdentityRetrieval = NO; + return self; +} + +- (instancetype)initAtIndex:(uint32_t)index + withLockedOutpoint:(DSUTXO)lockedOutpoint + inWallet:(DSWallet *)wallet { + NSParameterAssert(wallet); + NSAssert(index != UINT32_MAX, @"index must be found"); + if (!(self = [super init])) return nil; + self.wallet = wallet; + self.isTransient = FALSE; + self.createdLocally = YES; + self.identity = [[DSIdentity alloc] initAtIndex:index withLockedOutpoint:lockedOutpoint inWallet:wallet]; + [self.identity setAssociatedInvitation:self]; + self.chain = wallet.chain; + self.needsIdentityRetrieval = NO; + return self; +} + +- (instancetype)initAtIndex:(uint32_t)index + withLockedOutpoint:(DSUTXO)lockedOutpoint + inWallet:(DSWallet *)wallet + withInvitationEntity:(DSBlockchainInvitationEntity *)invitationEntity { + if (!(self = [super init])) return nil; + self.wallet = wallet; + self.isTransient = FALSE; + self.createdLocally = YES; + self.identity = [[DSIdentity alloc] initAtIndex:index withLockedOutpoint:lockedOutpoint inWallet:wallet withIdentityEntity:invitationEntity.blockchainIdentity associatedToInvitation:self]; + self.link = invitationEntity.link; + self.name = invitationEntity.name; + self.tag = invitationEntity.tag; + self.chain = wallet.chain; + self.needsIdentityRetrieval = NO; + return self; +} + +- (instancetype)initWithInvitationLink:(NSString *)invitationLink + inWallet:(DSWallet *)wallet { + if (!(self = [super init])) return nil; + self.link = invitationLink; + self.wallet = wallet; + self.chain = wallet.chain; + self.needsIdentityRetrieval = YES; + self.createdLocally = NO; + return self; +} + +- (void)generateInvitationsExtendedPublicKeysWithPrompt:(NSString *)prompt + completion:(void (^_Nullable)(BOOL registered))completion { + __block DSAssetLockDerivationPath *derivationPathInvitationFunding = [[DSDerivationPathFactory sharedInstance] identityInvitationFundingDerivationPathForWallet:self.wallet]; + if ([derivationPathInvitationFunding hasExtendedPublicKey]) { + completion(YES); + return; + } + [[DSAuthenticationManager sharedInstance] seedWithPrompt:prompt + forWallet:self.wallet + forAmount:0 + forceAuthentication:NO + completion:^(NSData *_Nullable seed, BOOL cancelled) { + if (!seed) { + completion(NO); + return; + } + [derivationPathInvitationFunding generateExtendedPublicKeyFromSeed:seed + storeUnderWalletUniqueId:self.wallet.uniqueIDString]; + completion(YES); + }]; +} + +- (void)registerInWalletForAssetLockTransaction:(DSAssetLockTransaction *)transaction { + NSAssert(self.identity != nil, @"The identity must already exist"); + [self.identity setInvitationAssetLockTransaction:transaction]; + [self registerInWalletForIdentityUniqueId:transaction.creditBurnIdentityIdentifier]; + //we need to also set the address of the funding transaction to being used so future identities past the initial gap limit are found + [transaction markInvitationAddressAsUsedInWallet:self.wallet]; +} + +- (void)registerInWalletForIdentityUniqueId:(UInt256)identityUniqueId { + [self.identity setInvitationUniqueId:identityUniqueId]; + [self registerInWallet]; +} + +- (BOOL)isRegisteredInWallet { + if (!self.wallet) return FALSE; + return [self.wallet containsInvitation:self]; +} + +- (void)registerInWallet { + NSAssert(self.identity.isOutgoingInvitation, @"The underlying identity is not from an invitation"); + if (!self.identity.isOutgoingInvitation) return; + [self.wallet registerInvitation:self]; + [self.identity saveInitial]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSInvitationDidUpdateNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSInvitationKey: self + }]; + }); +} + +- (void)updateInWallet { + [self saveInContext:[NSManagedObjectContext platformContext]]; +} + +- (BOOL)unregisterLocally { + NSAssert(self.identity.isOutgoingInvitation, @"The underlying identity is not from an invitation"); + if (!self.identity.isOutgoingInvitation) return FALSE; + if (self.identity.isRegistered) return FALSE; //if the invitation has already been used we can not unregister it + [self.wallet unregisterInvitation:self]; + [self deletePersistentObjectAndSave:YES inContext:[NSManagedObjectContext platformContext]]; + return TRUE; +} + +- (void)verifyInvitationLinkWithCompletion:(void (^_Nullable)(DSTransaction *transaction, bool spent, NSError *error))completion + completionQueue:(dispatch_queue_t)completionQueue { + [DSInvitation verifyInvitationLink:self.link + onChain:self.wallet.chain + completion:completion + completionQueue:completionQueue]; +} + ++ (void)verifyInvitationLink:(NSString *)invitationLink + onChain:(DSChain *)chain + completion:(void (^_Nullable)(DSTransaction *transaction, bool spent, NSError *error))completion + completionQueue:(dispatch_queue_t)completionQueue { + DSDAPICoreNetworkService *coreNetworkService = chain.chainManager.DAPIClient.DAPICoreNetworkService; + NSURLComponents *components = [NSURLComponents componentsWithString:invitationLink]; + NSArray *queryItems = components.queryItems; + UInt256 assetLockTransactionHash = UINT256_ZERO; + BOOL isEmptyFundingPrivateKey = true; + for (NSURLQueryItem *queryItem in queryItems) { + if ([queryItem.name isEqualToString:@"assetlocktx"]) { + assetLockTransactionHash = queryItem.value.hexToData.UInt256; + } else if ([queryItem.name isEqualToString:@"pk"]) { +// isEmptyFundingPrivateKey = key_ecdsa_secret_key_is_empty([queryItem.value UTF8String], chain.chainType); + isEmptyFundingPrivateKey = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_contains_secret_key((char *)[queryItem.value UTF8String], chain.chainType); + } + } + if (uint256_is_zero(assetLockTransactionHash)) { + if (completion) completion(nil, NO, ERROR_INVITATION_FORMAT); + return; + } + if (isEmptyFundingPrivateKey) { + if (completion) completion(nil, NO, ERROR_INVALID_FUNDING_PRV_KEY); + return; + } + + [coreNetworkService getTransactionWithHash:assetLockTransactionHash + completionQueue:completionQueue + success:^(DSTransaction *_Nonnull transaction) { + NSAssert(transaction, @"transaction must not be null"); + if (!transaction || ![transaction isKindOfClass:[DSAssetLockTransaction class]]) { + if (completion) completion(nil, NO, ERROR_INVALID_INV_TX); + return; + } + if (completion) completion(transaction, NO, nil); + } + failure:^(NSError *_Nonnull error) { + if (completion) completion(nil, NO, ERROR_INVITATION_FORMAT); + }]; +} + +- (void)acceptInvitationUsingWalletIndex:(uint32_t)index + setDashpayUsername:(NSString *)dashpayUsername + authenticationPrompt:(NSString *)authenticationMessage + identityRegistrationSteps:(DSIdentityRegistrationStep)identityRegistrationSteps + stepCompletion:(void (^_Nullable)(DSIdentityRegistrationStep stepCompleted))stepCompletion + completion:(void (^_Nullable)(DSIdentityRegistrationStep stepsCompleted, NSError *error))completion + completionQueue:(dispatch_queue_t)completionQueue { + DSDAPICoreNetworkService *coreNetworkService = self.chain.chainManager.DAPIClient.DAPICoreNetworkService; + NSURLComponents *components = [NSURLComponents componentsWithString:self.link]; + NSArray *queryItems = components.queryItems; + UInt256 assetLockTransactionHash = UINT256_ZERO; + DMaybeOpaqueKey *fundingPrivateKey = nil; + for (NSURLQueryItem *queryItem in queryItems) { + if ([queryItem.name isEqualToString:@"assetlocktx"]) { + assetLockTransactionHash = queryItem.value.hexToData.UInt256; + } else if ([queryItem.name isEqualToString:@"pk"]) { + fundingPrivateKey = dash_spv_crypto_keys_key_KeyKind_key_with_private_key(dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor(), (char *)[queryItem.value UTF8String], self.chain.chainType); +// fundingPrivateKey = [DSKeyManager keyWithPrivateKeyString:queryItem.value ofKeyType:KeyKind_ECDSA forChainType:self.chain.chainType]; + } + } + if (uint256_is_zero(assetLockTransactionHash)) { + if (completion) completion(DSIdentityRegistrationStep_None, ERROR_INVITATION_FORMAT); + return; + } + if (!fundingPrivateKey || !dash_spv_crypto_keys_key_OpaqueKey_has_private_key(fundingPrivateKey->ok)) { + if (completion) completion(DSIdentityRegistrationStep_None, ERROR_INVALID_FUNDING_PRV_KEY); + return; + } + + [coreNetworkService getTransactionWithHash:assetLockTransactionHash + completionQueue:self.chain.chainManager.identitiesManager.identityQueue + success:^(DSTransaction *_Nonnull transaction) { + NSAssert(transaction, @"transaction must not be null"); + if (!transaction || ![transaction isKindOfClass:[DSAssetLockTransaction class]]) { + if (completion) completion(DSIdentityRegistrationStep_None, ERROR_INVALID_INV_TX); + return; + } + self.identity = [[DSIdentity alloc] initAtIndex:index + withAssetLockTransaction:(DSAssetLockTransaction *)transaction + withUsernameDictionary:nil + inWallet:self.wallet]; + [self.identity setAssociatedInvitation:self]; + [self.identity addDashpayUsername:dashpayUsername save:NO]; + [self.identity registerInWalletForAssetLockTransaction: (DSAssetLockTransaction *)transaction]; + BOOL success = [self.identity setExternalFundingPrivateKey:fundingPrivateKey]; + if (!success && fundingPrivateKey != NULL) + DMaybeOpaqueKeyDtor(fundingPrivateKey); + + NSAssert(success, @"We must be able to set the external funding private key"); + if (success) { + [self.identity generateIdentityExtendedPublicKeysWithPrompt:authenticationMessage + completion:^(BOOL registered) { + if (registered) { + [self.identity continueRegisteringIdentityOnNetwork:identityRegistrationSteps + stepsCompleted:DSIdentityRegistrationStep_L1Steps + stepCompletion:stepCompletion + completion:completion]; + } else if (completion) { + completion(DSIdentityRegistrationStep_None, ERROR_GEN_IDENTITY_KEYS); + } + }]; + } else if (completion) { + completion(DSIdentityRegistrationStep_None, ERROR_SETTING_EXT_PRV_KEY); + } + } + failure:^(NSError *_Nonnull error) { + if (completion) completion(DSIdentityRegistrationStep_None, ERROR_INVITATION_FORMAT); + }]; +} + +- (void)createInvitationFullLinkFromIdentity:(DSIdentity *)identity + completion:(void (^_Nullable)(BOOL cancelled, NSString *invitationFullLink))completion { + if (!self.identity.registrationAssetLockTransaction.instantSendLockAwaitingProcessing) { + if (completion) completion(NO, nil); + return; + } + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSString *senderUsername = identity.currentDashpayUsername; + NSString *senderDisplayName = identity.displayName; + NSString *senderAvatarPath = identity.avatarPath; + NSString *fundingTransactionHexString = uint256_reverse_hex(self.identity.registrationAssetLockTransaction.txHash); + __block DMaybeOpaqueKey *registrationFundingPrivateKey = self.identity.registrationFundingPrivateKey; + __block BOOL rCancelled = FALSE; + + if (!registrationFundingPrivateKey) { + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + dispatch_async(dispatch_get_main_queue(), ^{ + [[DSAuthenticationManager sharedInstance] seedWithPrompt:DSLocalizedString(@"Would you like to share this invitation?", nil) + forWallet:self.wallet + forAmount:0 + forceAuthentication:NO + completion:^(NSData *_Nullable seed, BOOL cancelled) { + rCancelled = cancelled; + if (seed) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + DSAssetLockDerivationPath *derivationPathRegistrationFunding = [[DSDerivationPathFactory sharedInstance] identityInvitationFundingDerivationPathForWallet:self.wallet]; + // TODO: cleanup? + registrationFundingPrivateKey = [derivationPathRegistrationFunding privateKeyAtIndexPath:[NSIndexPath indexPathWithIndex:self.identity.index] + fromSeed:seed]; + dispatch_semaphore_signal(sem); + }); + } else { + dispatch_semaphore_signal(sem); + } + }]; + }); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + } + if (!registrationFundingPrivateKey) { + dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(rCancelled, nil); }); + return; + } + //in WIF format + NSString *registrationFundingPrivateKeyString = [DSKeyManager serializedPrivateKey:registrationFundingPrivateKey->ok chainType:self.chain.chainType]; + NSString *serializedISLock = [self.identity.registrationAssetLockTransaction.instantSendLockAwaitingProcessing.toData hexString]; + NSURLComponents *components = [NSURLComponents componentsWithString:@"https://invitations.dashpay.io/applink"]; + NSMutableArray *queryItems = [NSMutableArray array]; + if (senderUsername) + [queryItems addObject:[NSURLQueryItem queryItemWithName:@"du" value:senderUsername]]; + if (senderDisplayName) + [queryItems addObject:[NSURLQueryItem queryItemWithName:@"display-name" value:senderDisplayName]]; + if (senderAvatarPath) + [queryItems addObject:[NSURLQueryItem queryItemWithName:@"avatar-url" value:senderAvatarPath]]; + [queryItems addObject:[NSURLQueryItem queryItemWithName:@"assetlocktx" value:fundingTransactionHexString.lowercaseString]]; + [queryItems addObject:[NSURLQueryItem queryItemWithName:@"pk" value:registrationFundingPrivateKeyString]]; + [queryItems addObject:[NSURLQueryItem queryItemWithName:@"islock" value:serializedISLock.lowercaseString]]; + components.queryItems = queryItems; + dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(NO, components.URL.absoluteString); }); + }); +} + +// MARK: Saving + +- (void)saveInContext:(NSManagedObjectContext *)context { + if (self.isTransient) return; + [context performBlockAndWait:^{ + BOOL changeOccured = NO; + NSMutableArray *updateEvents = [NSMutableArray array]; + DSBlockchainInvitationEntity *entity = [self invitationEntityInContext:context]; + if (entity.tag != self.tag) { + entity.tag = self.tag; + changeOccured = YES; + [updateEvents addObject:DSInvitationUpdateEvents]; + } + if (entity.name != self.name) { + entity.name = self.name; + changeOccured = YES; + [updateEvents addObject:DSInvitationUpdateEvents]; + } + if (entity.link != self.link) { + entity.link = self.link; + changeOccured = YES; + [updateEvents addObject:DSInvitationUpdateEventLink]; + } + if (changeOccured) { + [context ds_save]; + if (updateEvents.count) { + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSInvitationDidUpdateNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSInvitationKey: self, + DSInvitationUpdateEvents: updateEvents + }]; + }); + } + } + }]; +} + +// MARK: Deletion + +- (void)deletePersistentObjectAndSave:(BOOL)save inContext:(NSManagedObjectContext *)context { + [context performBlockAndWait:^{ + DSBlockchainInvitationEntity *invitationEntity = [self invitationEntityInContext:context]; + if (invitationEntity) { + [invitationEntity deleteObjectAndWait]; + if (save) [context ds_save]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSInvitationDidUpdateNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSInvitationKey: self + }]; + }); + }]; +} + +// MARK: Entity + +- (DSBlockchainInvitationEntity *)invitationEntity { + return [self invitationEntityInContext:[NSManagedObjectContext viewContext]]; +} + +- (DSBlockchainInvitationEntity *)invitationEntityInContext:(NSManagedObjectContext *)context { + __block DSBlockchainInvitationEntity *entity = nil; + [context performBlockAndWait:^{ + entity = [DSBlockchainInvitationEntity anyObjectInContext:context matching:@"blockchainIdentity.uniqueID == %@", self.identity.uniqueIDData]; + }]; + NSAssert(entity, @"An entity should always be found"); + return entity; +} + +- (NSString *)debugDescription { + return [[super debugDescription] stringByAppendingString:[NSString stringWithFormat:@" {%d-%@-%@}", self.identity.index, self.identity.currentDashpayUsername, self.identity.uniqueIdString]]; +} + + +@end diff --git a/DashSync/shared/Models/Identity/DSPotentialContact.h b/DashSync/shared/Models/Identity/DSPotentialContact.h index 39e618cb5..31607f34a 100644 --- a/DashSync/shared/Models/Identity/DSPotentialContact.h +++ b/DashSync/shared/Models/Identity/DSPotentialContact.h @@ -28,16 +28,14 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, copy) NSString *displayName; @property (nonatomic, copy) NSString *avatarPath; @property (nonatomic, copy) NSString *publicMessage; -@property (nonatomic, assign) UInt256 associatedBlockchainIdentityUniqueId; +@property (nonatomic, assign) UInt256 associatedIdentityUniqueId; - (instancetype)initWithUsername:(NSString *)username; - -- (instancetype)initWithUsername:(NSString *)username avatarPath:(NSString *)avatarPath publicMessage:(NSString *)publicMessage; - +- (instancetype)initWithUsername:(NSString *)username + avatarPath:(NSString *)avatarPath + publicMessage:(NSString *)publicMessage; - (instancetype)initWithDashpayUser:(DSDashpayUserEntity *)contactEntity; - - (void)addPublicKey:(NSValue *)key atIndex:(NSUInteger)index; - - (NSValue *)publicKeyAtIndex:(NSUInteger)index; @end diff --git a/DashSync/shared/Models/Identity/DSPotentialContact.m b/DashSync/shared/Models/Identity/DSPotentialContact.m index 0db48b80e..ffd1fd7bf 100644 --- a/DashSync/shared/Models/Identity/DSPotentialContact.m +++ b/DashSync/shared/Models/Identity/DSPotentialContact.m @@ -17,7 +17,7 @@ #import "DSPotentialContact.h" #import "BigIntTypes.h" -#import "DSBlockchainIdentity.h" +#import "DSIdentity.h" #import "DSBlockchainIdentityEntity+CoreDataClass.h" #import "DSBlockchainIdentityUsernameEntity+CoreDataClass.h" #import "DSDashpayUserEntity+CoreDataClass.h" @@ -35,13 +35,15 @@ - (instancetype)initWithUsername:(NSString *)username { self = [super init]; if (self) { _username = username; - _associatedBlockchainIdentityUniqueId = UINT256_ZERO; + _associatedIdentityUniqueId = UINT256_ZERO; self.keyDictionary = [NSMutableDictionary dictionary]; } return self; } -- (instancetype)initWithUsername:(NSString *)username avatarPath:(NSString *)avatarPath publicMessage:(NSString *)publicMessage { +- (instancetype)initWithUsername:(NSString *)username + avatarPath:(NSString *)avatarPath + publicMessage:(NSString *)publicMessage { self = [self initWithUsername:username]; if (self) { _avatarPath = avatarPath; @@ -52,15 +54,17 @@ - (instancetype)initWithUsername:(NSString *)username avatarPath:(NSString *)ava - (instancetype)initWithDashpayUser:(DSDashpayUserEntity *)dashpayUserEntity { DSBlockchainIdentityUsernameEntity *usernameEntity = [dashpayUserEntity.associatedBlockchainIdentity.usernames anyObject]; - self = [self initWithUsername:usernameEntity.stringValue avatarPath:dashpayUserEntity.avatarPath publicMessage:dashpayUserEntity.publicMessage]; + self = [self initWithUsername:usernameEntity.stringValue + avatarPath:dashpayUserEntity.avatarPath + publicMessage:dashpayUserEntity.publicMessage]; if (self) { - _associatedBlockchainIdentityUniqueId = dashpayUserEntity.associatedBlockchainIdentity.uniqueID.UInt256; + _associatedIdentityUniqueId = dashpayUserEntity.associatedBlockchainIdentity.uniqueID.UInt256; } return self; } - (NSString *)debugDescription { - return [NSString stringWithFormat:@"%@ - %@ - %@", [super debugDescription], self.username, uint256_hex(self.associatedBlockchainIdentityUniqueId)]; + return [NSString stringWithFormat:@"%@ - %@ - %@", [super debugDescription], self.username, uint256_hex(self.associatedIdentityUniqueId)]; } - (void)addPublicKey:(NSValue *)key atIndex:(NSUInteger)index { diff --git a/DashSync/shared/Models/Identity/DSPotentialOneWayFriendship.h b/DashSync/shared/Models/Identity/DSPotentialOneWayFriendship.h index be1dcc43e..77d8ed06e 100644 --- a/DashSync/shared/Models/Identity/DSPotentialOneWayFriendship.h +++ b/DashSync/shared/Models/Identity/DSPotentialOneWayFriendship.h @@ -21,28 +21,39 @@ NS_ASSUME_NONNULL_BEGIN -@class DSBlockchainIdentity, DSAccount, DSBlockchainIdentityRegistrationTransition, DSFriendRequestEntity, DSPotentialContact, DSDashpayUserEntity, DSIncomingFundsDerivationPath, DSDerivationPathEntity; +@class DPDocument, DSIdentity, DSAccount, DSIdentityRegistrationTransition, DSFriendRequestEntity, DSPotentialContact, DSDashpayUserEntity, DSIncomingFundsDerivationPath, DSDerivationPathEntity; @interface DSPotentialOneWayFriendship : NSObject @property (nonatomic, readonly) DSAccount *account; -@property (nonatomic, readonly) DSBlockchainIdentity *destinationBlockchainIdentity; -@property (nonatomic, readonly) DSBlockchainIdentity *sourceBlockchainIdentity; //this is the holder of the contacts, not the destination +@property (nonatomic, readonly) DSIdentity *destinationIdentity; +@property (nonatomic, readonly) DSIdentity *sourceIdentity; //this is the holder of the contacts, not the destination @property (nonatomic, readonly) NSTimeInterval createdAt; @property (nonatomic, readonly) DSIncomingFundsDerivationPath *derivationPath; @property (nonatomic, readonly) uint32_t sourceKeyIndex; @property (nonatomic, readonly) uint32_t destinationKeyIndex; -- (instancetype)initWithDestinationBlockchainIdentity:(DSBlockchainIdentity *)destinationBlockchainIdentity destinationKeyIndex:(uint32_t)destinationKeyIndex sourceBlockchainIdentity:(DSBlockchainIdentity *)sourceBlockchainIdentity sourceKeyIndex:(uint32_t)sourceKeyIndex account:(DSAccount *)account; +- (instancetype)initWithDestinationIdentity:(DSIdentity *)destinationIdentity + destinationKeyIndex:(uint32_t)destinationKeyIndex + sourceIdentity:(DSIdentity *)sourceIdentity + sourceKeyIndex:(uint32_t)sourceKeyIndex + account:(DSAccount *)account; -- (instancetype)initWithDestinationBlockchainIdentity:(DSBlockchainIdentity *)destinationBlockchainIdentity destinationKeyIndex:(uint32_t)destinationKeyIndex sourceBlockchainIdentity:(DSBlockchainIdentity *)sourceBlockchainIdentity sourceKeyIndex:(uint32_t)sourceKeyIndex account:(DSAccount *)account createdAt:(NSTimeInterval)createdAt; +- (instancetype)initWithDestinationIdentity:(DSIdentity *)destinationIdentity + destinationKeyIndex:(uint32_t)destinationKeyIndex + sourceIdentity:(DSIdentity *)sourceIdentity + sourceKeyIndex:(uint32_t)sourceKeyIndex + account:(DSAccount *)account + createdAt:(NSTimeInterval)createdAt; //-(DSFriendRequestEntity*)outgoingFriendRequest; -- (DSFriendRequestEntity *)outgoingFriendRequestForDashpayUserEntity:(DSDashpayUserEntity *)dashpayUserEntity atTimestamp:(NSTimeInterval)timestamp; +- (DSFriendRequestEntity *)outgoingFriendRequestForDashpayUserEntity:(DSDashpayUserEntity *)dashpayUserEntity + atTimestamp:(NSTimeInterval)timestamp; -- (DSDerivationPathEntity *)storeExtendedPublicKeyAssociatedWithFriendRequest:(DSFriendRequestEntity *)friendRequestEntity inContext:(NSManagedObjectContext *)context; -- (DSDerivationPathEntity *)storeExtendedPublicKeyAssociatedWithFriendRequest:(DSFriendRequestEntity *)friendRequestEntity; +- (DSDerivationPathEntity *)storeExtendedPublicKeyAssociatedWithFriendRequest:(DSFriendRequestEntity *)entity + inContext:(NSManagedObjectContext *)context; +- (DSDerivationPathEntity *)storeExtendedPublicKeyAssociatedWithFriendRequest:(DSFriendRequestEntity *)entity; - (void)createDerivationPathAndSaveExtendedPublicKeyWithCompletion:(void (^)(BOOL success, DSIncomingFundsDerivationPath *incomingFundsDerivationPath))completion; diff --git a/DashSync/shared/Models/Identity/DSPotentialOneWayFriendship.m b/DashSync/shared/Models/Identity/DSPotentialOneWayFriendship.m index ecd9b15ec..c2b6a2527 100644 --- a/DashSync/shared/Models/Identity/DSPotentialOneWayFriendship.m +++ b/DashSync/shared/Models/Identity/DSPotentialOneWayFriendship.m @@ -16,12 +16,14 @@ // #import "dash_shared_core.h" +#import "DPDocumentFactory.h" #import "DSPotentialOneWayFriendship.h" #import "DSAccount.h" -#import "DSBlockchainIdentity+Protected.h" +#import "DSIdentity+Protected.h" #import "DSBlockchainIdentityEntity+CoreDataClass.h" #import "DSDashPlatform.h" #import "DSDashpayUserEntity+CoreDataClass.h" +#import "DSDerivationPath+Protected.h" #import "DSDerivationPathEntity+CoreDataClass.h" #import "DSDerivationPathFactory.h" #import "DSFriendRequestEntity+CoreDataClass.h" @@ -36,11 +38,11 @@ @interface DSPotentialOneWayFriendship () @property (nonatomic, strong) DSAccount *account; -@property (nonatomic, strong) DSBlockchainIdentity *sourceBlockchainIdentity; -@property (nonatomic, strong) DSBlockchainIdentity *destinationBlockchainIdentity; +@property (nonatomic, strong) DSIdentity *sourceIdentity; +@property (nonatomic, strong) DSIdentity *destinationIdentity; @property (nonatomic, strong) DSPotentialContact *destinationContact; @property (nonatomic, strong) DSIncomingFundsDerivationPath *fundsDerivationPathForContact; -@property (nonatomic, assign) OpaqueKey *extendedPublicKey; +@property (nonatomic, assign) DMaybeOpaqueKey *extendedPublicKey; @property (nonatomic, strong) NSData *encryptedExtendedPublicKeyData; @property (nonatomic, assign) uint32_t sourceKeyIndex; @property (nonatomic, assign) uint32_t destinationKeyIndex; @@ -50,15 +52,29 @@ @interface DSPotentialOneWayFriendship () @implementation DSPotentialOneWayFriendship -- (instancetype)initWithDestinationBlockchainIdentity:(DSBlockchainIdentity *)destinationBlockchainIdentity destinationKeyIndex:(uint32_t)destinationKeyIndex sourceBlockchainIdentity:(DSBlockchainIdentity *)sourceBlockchainIdentity sourceKeyIndex:(uint32_t)sourceKeyIndex account:(DSAccount *)account { - return [self initWithDestinationBlockchainIdentity:destinationBlockchainIdentity destinationKeyIndex:destinationKeyIndex sourceBlockchainIdentity:sourceBlockchainIdentity sourceKeyIndex:sourceKeyIndex account:account createdAt:[[NSDate date] timeIntervalSince1970]]; -} - -- (instancetype)initWithDestinationBlockchainIdentity:(DSBlockchainIdentity *)destinationBlockchainIdentity destinationKeyIndex:(uint32_t)destinationKeyIndex sourceBlockchainIdentity:(DSBlockchainIdentity *)sourceBlockchainIdentity sourceKeyIndex:(uint32_t)sourceKeyIndex account:(DSAccount *)account createdAt:(NSTimeInterval)createdAt { +- (instancetype)initWithDestinationIdentity:(DSIdentity *)destinationIdentity + destinationKeyIndex:(uint32_t)destinationKeyIndex + sourceIdentity:(DSIdentity *)sourceIdentity + sourceKeyIndex:(uint32_t)sourceKeyIndex + account:(DSAccount *)account { + return [self initWithDestinationIdentity:destinationIdentity + destinationKeyIndex:destinationKeyIndex + sourceIdentity:sourceIdentity + sourceKeyIndex:sourceKeyIndex + account:account + createdAt:[[NSDate date] timeIntervalSince1970]]; +} + +- (instancetype)initWithDestinationIdentity:(DSIdentity *)destinationIdentity + destinationKeyIndex:(uint32_t)destinationKeyIndex + sourceIdentity:(DSIdentity *)sourceIdentity + sourceKeyIndex:(uint32_t)sourceKeyIndex + account:(DSAccount *)account + createdAt:(NSTimeInterval)createdAt { if (!(self = [super init])) return nil; - self.destinationBlockchainIdentity = destinationBlockchainIdentity; + self.destinationIdentity = destinationIdentity; self.account = account; - self.sourceBlockchainIdentity = sourceBlockchainIdentity; + self.sourceIdentity = sourceIdentity; self.sourceKeyIndex = sourceKeyIndex; self.destinationKeyIndex = destinationKeyIndex; self.createdAt = createdAt; @@ -66,23 +82,23 @@ - (instancetype)initWithDestinationBlockchainIdentity:(DSBlockchainIdentity *)de return self; } -- (UInt256)destinationBlockchainIdentityUniqueId { - if (self.destinationBlockchainIdentity) { - return self.destinationBlockchainIdentity.uniqueID; +- (UInt256)destinationIdentityUniqueId { + if (self.destinationIdentity) { + return self.destinationIdentity.uniqueID; } else if (self.destinationContact) { - return self.destinationContact.associatedBlockchainIdentityUniqueId; + return self.destinationContact.associatedIdentityUniqueId; } return UINT256_ZERO; } -- (OpaqueKey *)sourceKeyAtIndex { - NSAssert(self.sourceBlockchainIdentity != nil, @"The source identity should be present"); - return [self.sourceBlockchainIdentity keyAtIndex:self.sourceKeyIndex]; +- (DMaybeOpaqueKey *)sourceKeyAtIndex { + NSAssert(self.sourceIdentity != nil, @"The source identity should be present"); + return [self.sourceIdentity keyAtIndex:self.sourceKeyIndex]; } -- (OpaqueKey *)destinationKeyAtIndex { - if (self.destinationBlockchainIdentity) { - return [self.destinationBlockchainIdentity keyAtIndex:self.destinationKeyIndex]; +- (DMaybeOpaqueKey *)destinationKeyAtIndex { + if (self.destinationIdentity) { + return [self.destinationIdentity keyAtIndex:self.destinationKeyIndex]; } else if (self.destinationContact) { return [self.destinationContact publicKeyAtIndex:self.destinationKeyIndex].pointerValue; } @@ -90,57 +106,46 @@ - (OpaqueKey *)destinationKeyAtIndex { } - (DSIncomingFundsDerivationPath *)derivationPath { - NSAssert(uint256_is_not_zero([self destinationBlockchainIdentityUniqueId]), @"destinationBlockchainIdentityUniqueId must not be null"); - DSIncomingFundsDerivationPath *fundsDerivationPathForContact = [DSIncomingFundsDerivationPath - contactBasedDerivationPathWithDestinationBlockchainIdentityUniqueId:[self destinationBlockchainIdentityUniqueId] - sourceBlockchainIdentityUniqueId:self.sourceBlockchainIdentity.uniqueID - forAccountNumber:self.account.accountNumber - onChain:self.sourceBlockchainIdentity.wallet.chain]; - fundsDerivationPathForContact.account = self.account; - return fundsDerivationPathForContact; + NSAssert(uint256_is_not_zero([self destinationIdentityUniqueId]), @"destinationBlockchainIdentityUniqueId must not be null"); + return [DSIncomingFundsDerivationPath contactBasedDerivationPathWithDestinationIdentityUniqueId:[self destinationIdentityUniqueId] + sourceIdentityUniqueId:self.sourceIdentity.uniqueID + forAccount:self.account + onChain:self.sourceIdentity.wallet.chain]; } - (void)createDerivationPathAndSaveExtendedPublicKeyWithCompletion:(void (^)(BOOL success, DSIncomingFundsDerivationPath *incomingFundsDerivationPath))completion { - NSAssert(uint256_is_not_zero([self destinationBlockchainIdentityUniqueId]), @"destinationBlockchainIdentityUniqueId must not be null"); + NSAssert(uint256_is_not_zero([self destinationIdentityUniqueId]), @"destinationBlockchainIdentityUniqueId must not be null"); self.fundsDerivationPathForContact = [self derivationPath]; DSDerivationPath *masterContactsDerivationPath = [self.account masterContactsDerivationPath]; - self.extendedPublicKey = [self.fundsDerivationPathForContact generateExtendedPublicKeyFromParentDerivationPath:masterContactsDerivationPath storeUnderWalletUniqueId:nil]; - if (completion) { - completion(YES, self.fundsDerivationPathForContact); - } + if (completion) completion(YES, self.fundsDerivationPathForContact); } - (void)encryptExtendedPublicKeyWithCompletion:(void (^)(BOOL success))completion { - NSAssert(self.extendedPublicKey, @"Problem creating extended public key for potential contact?"); + NSAssert(self.extendedPublicKey && self.extendedPublicKey->ok, @"Problem creating extended public key for potential contact?"); __weak typeof(self) weakSelf = self; - OpaqueKey *recipientKey = [self destinationKeyAtIndex]; - [self.sourceBlockchainIdentity encryptData:[DSKeyManager extendedPublicKeyData:self.extendedPublicKey] - withKeyAtIndex:self.sourceKeyIndex - forRecipientKey:recipientKey - completion:^(NSData *_Nonnull encryptedData) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - if (completion) { - completion(NO); - } - return; - } - strongSelf.encryptedExtendedPublicKeyData = encryptedData; - if (completion) { - completion(YES); - } - }]; + DMaybeOpaqueKey *recipientKey = [self destinationKeyAtIndex]; + [self.sourceIdentity encryptData:[DSKeyManager extendedPublicKeyData:self.extendedPublicKey->ok] + withKeyAtIndex:self.sourceKeyIndex + forRecipientKey:recipientKey->ok + completion:^(NSData *_Nonnull encryptedData) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + if (completion) completion(NO); + return; + } + strongSelf.encryptedExtendedPublicKeyData = encryptedData; + if (completion) completion(YES); + }]; } - (uint32_t)createAccountReference { // TODO: make test - OpaqueKey *key = [self sourceKeyAtIndex]; - return key_create_account_reference(key, self.extendedPublicKey, self.account.accountNumber); + return dash_spv_crypto_keys_key_OpaqueKey_create_account_reference([self sourceKeyAtIndex]->ok, self.extendedPublicKey->ok, self.account.accountNumber); } - (DPDocument *)contactRequestDocumentWithEntropy:(NSData *)entropyData { - NSAssert(uint256_is_not_zero([self destinationBlockchainIdentityUniqueId]), @"the destination contact's associatedBlockchainIdentityUniqueId must be set before making a friend request"); + NSAssert(uint256_is_not_zero([self destinationIdentityUniqueId]), @"the destination contact's associatedIdentityUniqueId must be set before making a friend request"); NSAssert([self.encryptedExtendedPublicKeyData length] > 0, @"The encrypted extended public key must exist"); NSAssert(self.extendedPublicKey, @"Problem creating extended public key for potential contact?"); NSError *error = nil; @@ -148,56 +153,60 @@ - (DPDocument *)contactRequestDocumentWithEntropy:(NSData *)entropyData { uint64_t createAtMs = (self.createdAt) * 1000; DSStringValueDictionary *data = @{ @"$createdAt": @(createAtMs), - @"toUserId": uint256_data([self destinationBlockchainIdentityUniqueId]), + @"toUserId": uint256_data([self destinationIdentityUniqueId]), @"encryptedPublicKey": self.encryptedExtendedPublicKeyData, @"senderKeyIndex": @(self.sourceKeyIndex), @"recipientKeyIndex": @(self.destinationKeyIndex), @"accountReference": @([self createAccountReference]) }; - - - DPDocument *contact = [self.sourceBlockchainIdentity.dashpayDocumentFactory documentOnTable:@"contactRequest" withDataDictionary:data usingEntropy:entropyData error:&error]; + DPDocument *contact = [self.sourceIdentity.dashpayDocumentFactory documentOnTable:@"contactRequest" withDataDictionary:data usingEntropy:entropyData error:&error]; NSAssert(error == nil, @"Failed to build a contact"); return contact; } -- (DSDerivationPathEntity *)storeExtendedPublicKeyAssociatedWithFriendRequest:(DSFriendRequestEntity *)friendRequestEntity inContext:(NSManagedObjectContext *)context { - [self.fundsDerivationPathForContact storeExtendedPublicKeyUnderWalletUniqueId:self.account.wallet.uniqueIDString]; +- (DSDerivationPathEntity *)storeExtendedPublicKeyAssociatedWithFriendRequest:(DSFriendRequestEntity *)entity + inContext:(NSManagedObjectContext *)context { +// [self.fundsDerivationPathForContact storeExtendedPublicKeyUnderWalletUniqueId:self.account.wallet.uniqueIDString]; + + NSData *data = [DSKeyManager extendedPublicKeyData:self.fundsDerivationPathForContact.extendedPublicKey->ok]; + setKeychainData(data, [self.fundsDerivationPathForContact walletBasedExtendedPublicKeyLocationStringForWalletUniqueID:self.account.wallet.uniqueIDString], NO); __block DSDerivationPathEntity *fundsDerivationPathEntity = nil; [context performBlockAndWait:^{ - fundsDerivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self.fundsDerivationPathForContact associateWithFriendRequest:friendRequestEntity - inContext:context]; + fundsDerivationPathEntity = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self.fundsDerivationPathForContact + associateWithFriendRequest:entity + inContext:context]; }]; return fundsDerivationPathEntity; } -- (DSDerivationPathEntity *)storeExtendedPublicKeyAssociatedWithFriendRequest:(DSFriendRequestEntity *)friendRequestEntity { - return [self storeExtendedPublicKeyAssociatedWithFriendRequest:friendRequestEntity inContext:friendRequestEntity.managedObjectContext]; +- (DSDerivationPathEntity *)storeExtendedPublicKeyAssociatedWithFriendRequest:(DSFriendRequestEntity *)entity { + return [self storeExtendedPublicKeyAssociatedWithFriendRequest:entity + inContext:entity.managedObjectContext]; } -- (DSFriendRequestEntity *)outgoingFriendRequestForDashpayUserEntity:(DSDashpayUserEntity *)dashpayUserEntity atTimestamp:(NSTimeInterval)timestamp { +- (DSFriendRequestEntity *)outgoingFriendRequestForDashpayUserEntity:(DSDashpayUserEntity *)dashpayUserEntity + atTimestamp:(NSTimeInterval)timestamp { NSParameterAssert(dashpayUserEntity); - NSAssert(uint256_eq(dashpayUserEntity.associatedBlockchainIdentity.uniqueID.UInt256, [self destinationBlockchainIdentityUniqueId]), @"contact entity must match"); - NSAssert(self.sourceBlockchainIdentity.matchingDashpayUserInViewContext, @"The own contact of the source Identity must be set"); + NSAssert(uint256_eq(dashpayUserEntity.associatedBlockchainIdentity.uniqueID.UInt256, [self destinationIdentityUniqueId]), @"contact entity must match"); + NSAssert(self.sourceIdentity.matchingDashpayUserInViewContext, @"The own contact of the source Identity must be set"); DSFriendRequestEntity *friendRequestEntity = [DSFriendRequestEntity managedObjectInBlockedContext:dashpayUserEntity.managedObjectContext]; - friendRequestEntity.sourceContact = [self.sourceBlockchainIdentity matchingDashpayUserInContext:friendRequestEntity.managedObjectContext]; + friendRequestEntity.sourceContact = [self.sourceIdentity matchingDashpayUserInContext:friendRequestEntity.managedObjectContext]; friendRequestEntity.destinationContact = dashpayUserEntity; NSAssert(friendRequestEntity.sourceContact != friendRequestEntity.destinationContact, @"This must be different contacts"); friendRequestEntity.derivationPath = [DSDerivationPathEntity derivationPathEntityMatchingDerivationPath:self.fundsDerivationPathForContact inContext:dashpayUserEntity.managedObjectContext]; NSAssert(friendRequestEntity.derivationPath, @"There must be a derivation path"); friendRequestEntity.account = friendRequestEntity.derivationPath.account; friendRequestEntity.timestamp = timestamp; - [friendRequestEntity finalizeWithFriendshipIdentifier]; return friendRequestEntity; } //-(DSFriendRequestEntity*)outgoingFriendRequest { -// NSAssert(uint256_is_not_zero(self.destinationContact.associatedBlockchainIdentityUniqueId), @"destination contact must be known"); -// DSDashpayUserEntity * dashpayUserEntity = [DSDashpayUserEntity anyObjectInContext:context matching:@"associatedBlockchainIdentityUniqueId == %@",uint256_data(self.destinationContact.associatedBlockchainIdentityUniqueId)]; +// NSAssert(uint256_is_not_zero(self.destinationContact.associatedIdentityUniqueId), @"destination contact must be known"); +// DSDashpayUserEntity * dashpayUserEntity = [DSDashpayUserEntity anyObjectInContext:context matching:@"associatedBlockchainIdentityUniqueId == %@",uint256_data(self.destinationContact.associatedIdentityUniqueId)]; // if (!dashpayUserEntity) { // dashpayUserEntity = [DSDashpayUserEntity managedObject]; // dashpayUserEntity.avatarPath = self.destinationContact.avatarPath; @@ -210,28 +219,21 @@ - (DSFriendRequestEntity *)outgoingFriendRequestForDashpayUserEntity:(DSDashpayU //} - (BOOL)isEqual:(id)object { - if (self == object) { - return TRUE; - } - - if (![object isKindOfClass:[self class]]) { - return FALSE; - } - - if (uint256_eq(self.destinationBlockchainIdentity.uniqueID, ((DSPotentialOneWayFriendship *)object).destinationBlockchainIdentity.uniqueID) && uint256_eq(self.sourceBlockchainIdentity.uniqueID, ((DSPotentialOneWayFriendship *)object).sourceBlockchainIdentity.uniqueID) && + if (self == object) return TRUE; + if (![object isKindOfClass:[self class]]) return FALSE; + if (uint256_eq(self.destinationIdentity.uniqueID, ((DSPotentialOneWayFriendship *)object).destinationIdentity.uniqueID) && uint256_eq(self.sourceIdentity.uniqueID, ((DSPotentialOneWayFriendship *)object).sourceIdentity.uniqueID) && self.account.accountNumber == ((DSPotentialOneWayFriendship *)object).account.accountNumber) { return TRUE; } - return FALSE; } - (NSUInteger)hash { - return self.destinationBlockchainIdentity.hash ^ self.sourceBlockchainIdentity.hash ^ self.account.accountNumber; + return self.destinationIdentity.hash ^ self.sourceIdentity.hash ^ self.account.accountNumber; } - (NSString *)debugDescription { - return [NSString stringWithFormat:@"%@ - s:%@ d:%@", [super debugDescription], self.sourceBlockchainIdentity.currentDashpayUsername, self.destinationBlockchainIdentity.currentDashpayUsername]; + return [NSString stringWithFormat:@"%@ - s:%@ d:%@", [super debugDescription], self.sourceIdentity.currentDashpayUsername, self.destinationIdentity.currentDashpayUsername]; } @end diff --git a/DashSync/shared/Models/Keys/NSData+Encryption.h b/DashSync/shared/Models/Keys/NSData+Encryption.h index 2a209a043..45e640410 100644 --- a/DashSync/shared/Models/Keys/NSData+Encryption.h +++ b/DashSync/shared/Models/Keys/NSData+Encryption.h @@ -22,10 +22,10 @@ NS_ASSUME_NONNULL_BEGIN @interface NSData (Encryption) -- (nullable NSData *)encryptWithSecretKey:(OpaqueKey *)secretKey forPublicKey:(OpaqueKey *)peerPubKey; -- (nullable NSData *)encryptWithSecretKey:(OpaqueKey *)secretKey forPublicKey:(OpaqueKey *)peerPubKey usingInitializationVector:(NSData *)initializationVector; -- (nullable NSData *)decryptWithSecretKey:(OpaqueKey *)secretKey fromPublicKey:(OpaqueKey *)peerPubKey; -- (nullable NSData *)encryptWithDHKey:(OpaqueKey *)dhKey; +- (nullable NSData *)encryptWithSecretKey:(DOpaqueKey *)secretKey forPublicKey:(DOpaqueKey *)peerPubKey; +- (nullable NSData *)encryptWithSecretKey:(DOpaqueKey *)secretKey forPublicKey:(DOpaqueKey *)peerPubKey usingInitializationVector:(NSData *)initializationVector; +- (nullable NSData *)decryptWithSecretKey:(DOpaqueKey *)secretKey fromPublicKey:(DOpaqueKey *)peerPubKey; +- (nullable NSData *)encryptWithDHKey:(DOpaqueKey *)dhKey; - (nullable NSData *)encapsulatedDHEncryptionWithKeys:(NSArray *)keys; - (nullable NSData *)encapsulatedDHDecryptionWithKeys:(NSArray *)keys; - (nullable NSData *)encapsulatedDHEncryptionWithKeys:(NSArray *)keys usingInitializationVector:(NSData *)initializationVector; diff --git a/DashSync/shared/Models/Keys/NSData+Encryption.mm b/DashSync/shared/Models/Keys/NSData+Encryption.mm index 9bd36d597..d41510443 100644 --- a/DashSync/shared/Models/Keys/NSData+Encryption.mm +++ b/DashSync/shared/Models/Keys/NSData+Encryption.mm @@ -208,7 +208,7 @@ @implementation NSData (Encryption) // return resultData; //} // -- (nullable NSData *)encryptWithSecretKey:(OpaqueKey *)secretKey forPublicKey:(OpaqueKey *)peerPubKey { +- (nullable NSData *)encryptWithSecretKey:(DOpaqueKey *)secretKey forPublicKey:(DOpaqueKey *)peerPubKey { return [DSKeyManager encryptData:self secretKey:secretKey publicKey:peerPubKey]; // if ([secretKey isMemberOfClass:[DSBLSKey class]] && [peerPubKey isMemberOfClass:[DSBLSKey class]]) { // return [self encryptWithBLSSecretKey:(DSBLSKey *)secretKey forPublicKey:(DSBLSKey *)peerPubKey]; @@ -220,7 +220,7 @@ - (nullable NSData *)encryptWithSecretKey:(OpaqueKey *)secretKey forPublicKey:(O // return nil; } -- (nullable NSData *)encryptWithSecretKey:(OpaqueKey *)secretKey forPublicKey:(OpaqueKey *)peerPubKey usingInitializationVector:(NSData *)initializationVector { +- (nullable NSData *)encryptWithSecretKey:(DOpaqueKey *)secretKey forPublicKey:(DOpaqueKey *)peerPubKey usingInitializationVector:(NSData *)initializationVector { return [DSKeyManager encryptData:self secretKey:secretKey publicKey:peerPubKey usingIV:initializationVector]; // if ([secretKey isMemberOfClass:[DSBLSKey class]] && [peerPubKey isMemberOfClass:[DSBLSKey class]]) { // return [self encryptWithBLSSecretKey:(DSBLSKey *)secretKey forPublicKey:(DSBLSKey *)peerPubKey usingInitializationVector:initializationVector]; @@ -232,11 +232,11 @@ - (nullable NSData *)encryptWithSecretKey:(OpaqueKey *)secretKey forPublicKey:(O // return nil; } -- (nullable NSData *)decryptWithSecretKey:(OpaqueKey *)secretKey fromPublicKey:(OpaqueKey *)peerPubKey { +- (nullable NSData *)decryptWithSecretKey:(DOpaqueKey *)secretKey fromPublicKey:(DOpaqueKey *)peerPubKey { return [DSKeyManager decryptData:self secretKey:secretKey publicKey:peerPubKey]; } -- (nullable NSData *)decryptWithSecretKey:(OpaqueKey *)secretKey fromPublicKey:(OpaqueKey *)peerPubKey usingIVSize:(NSUInteger)ivSize { +- (nullable NSData *)decryptWithSecretKey:(DOpaqueKey *)secretKey fromPublicKey:(DOpaqueKey *)peerPubKey usingIVSize:(NSUInteger)ivSize { return [DSKeyManager decryptData:self secretKey:secretKey publicKey:peerPubKey usingIVSize:ivSize]; // if ([secretKey isMemberOfClass:[DSBLSKey class]] && [peerPubKey isMemberOfClass:[DSBLSKey class]]) { // return [self decryptWithBLSSecretKey:(DSBLSKey *)secretKey fromPublicKey:(DSBLSKey *)peerPubKey usingIVSize:ivSize]; @@ -248,7 +248,7 @@ - (nullable NSData *)decryptWithSecretKey:(OpaqueKey *)secretKey fromPublicKey:( // return nil; } -- (nullable NSData *)encryptWithDHKey:(OpaqueKey *)dhKey { +- (nullable NSData *)encryptWithDHKey:(DOpaqueKey *)dhKey { return [DSKeyManager encryptData:self withDHKey:dhKey]; // if ([dhKey isMemberOfClass:[DSBLSKey class]]) { // return [self encryptWithDHBLSKey:(DSBLSKey *)dhKey]; @@ -260,7 +260,7 @@ - (nullable NSData *)encryptWithDHKey:(OpaqueKey *)dhKey { // return nil; } -- (nullable NSData *)decryptWithDHKey:(OpaqueKey *)dhKey { +- (nullable NSData *)decryptWithDHKey:(DOpaqueKey *)dhKey { return [DSKeyManager decryptData:self withDHKey:dhKey]; // if ([dhKey isMemberOfClass:[DSBLSKey class]]) { // return [self decryptWithDHBLSKey:(DSBLSKey *)dhKey]; @@ -276,9 +276,11 @@ - (nullable NSData *)encapsulatedDHDecryptionWithKeys:(NSArray *)keys NSAssert(keys.count > 1, @"There should be at least two key (first pair)"); if ([keys count] < 2) return self; - OpaqueKey *firstKey = (OpaqueKey *)[keys firstObject].pointerValue; - OpaqueKey *secondKey = (OpaqueKey *)[keys objectAtIndex:1].pointerValue; - NSData *encryptedData = [self decryptWithSecretKey:secondKey fromPublicKey:firstKey usingIVSize:ivSize]; + DMaybeOpaqueKey *firstKey = (DMaybeOpaqueKey *)[keys firstObject].pointerValue; + DMaybeOpaqueKey *secondKey = (DMaybeOpaqueKey *)[keys objectAtIndex:1].pointerValue; + NSAssert(firstKey->ok, @"First key should be ok"); + NSAssert(secondKey->ok, @"Second key should be ok"); + NSData *encryptedData = [self decryptWithSecretKey:secondKey->ok fromPublicKey:firstKey->ok usingIVSize:ivSize]; if (keys.count == 2) { //not really necessary but easier to read return encryptedData; } else { @@ -289,8 +291,9 @@ - (nullable NSData *)encapsulatedDHDecryptionWithKeys:(NSArray *)keys - (nullable NSData *)encapsulatedDHDecryptionWithKeys:(NSArray *)keys { NSAssert(keys.count > 0, @"There should be at least one key"); if (![keys count]) return self; - OpaqueKey *firstKey = (OpaqueKey *) [keys firstObject].pointerValue; - NSData *encryptedData = [self decryptWithDHKey:firstKey]; + DMaybeOpaqueKey *firstKey = (DMaybeOpaqueKey *) [keys firstObject].pointerValue; + NSAssert(firstKey->ok, @"First key should be ok"); + NSData *encryptedData = [self decryptWithDHKey:firstKey->ok]; if (keys.count == 1) { //not really necessary but easier to read return encryptedData; } else { @@ -301,8 +304,9 @@ - (nullable NSData *)encapsulatedDHDecryptionWithKeys:(NSArray *)keys - (nullable NSData *)encapsulatedDHEncryptionWithKeys:(NSArray *)keys { NSAssert(keys.count > 0, @"There should be at least one key"); if (![keys count]) return self; - OpaqueKey *firstKey = (OpaqueKey *) [keys firstObject].pointerValue; - NSData *encryptedData = [self encryptWithDHKey:firstKey]; + DMaybeOpaqueKey *firstKey = (DMaybeOpaqueKey *) [keys firstObject].pointerValue; + NSAssert(firstKey->ok, @"First key should be ok"); + NSData *encryptedData = [self encryptWithDHKey:firstKey->ok]; if (keys.count == 1) { //not really necessary but easier to read return encryptedData; } else { @@ -314,10 +318,12 @@ - (nullable NSData *)encapsulatedDHEncryptionWithKeys:(NSArray *)keys NSAssert(keys.count > 1, @"There should be at least two key (first pair)"); if ([keys count] < 2) return self; - OpaqueKey *firstKey = (OpaqueKey *) [keys firstObject].pointerValue; - OpaqueKey *secondKey = (OpaqueKey *) [keys objectAtIndex:1].pointerValue; - - NSData *encryptedData = [self encryptWithSecretKey:firstKey forPublicKey:secondKey usingInitializationVector:initializationVector]; + DMaybeOpaqueKey *firstKey = (DMaybeOpaqueKey *) [keys firstObject].pointerValue; + DMaybeOpaqueKey *secondKey = (DMaybeOpaqueKey *) [keys objectAtIndex:1].pointerValue; + NSAssert(firstKey->ok, @"First key should be ok"); + NSAssert(secondKey->ok, @"Second key should be ok"); + + NSData *encryptedData = [self encryptWithSecretKey:firstKey->ok forPublicKey:secondKey->ok usingInitializationVector:initializationVector]; if (keys.count == 2) { //not really necessary but easier to read return encryptedData; } else { diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Protected.h b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Protected.h index e24ddb3fc..f9e69de09 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Protected.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Protected.h @@ -35,6 +35,7 @@ typedef NS_ENUM(uint16_t, DSChainNotificationType) { - (void)notify:(NSNotificationName)name userInfo:(NSDictionary *_Nullable)userInfo; - (void)notifySyncStateChanged; +- (void)notifyMasternodeSyncStateChange:(uint32_t)lastBlockHeihgt storedCount:(uintptr_t)storedCount; @end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Transactions.m b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Transactions.m index 0ef4efefd..a563b53fd 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Transactions.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Transactions.m @@ -15,6 +15,7 @@ // limitations under the License. // +#import "DSChain+Params.h" #import "DSChainManager+Protected.h" #import "DSChainManager+Transactions.h" #import "DSWallet+Protected.h" diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.h index 0d0ba4bbe..a21a432bc 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.h @@ -25,6 +25,7 @@ #import "DSBackgroundManager.h" #import "DSChain.h" +#import "DSDAPIClient.h" #import "DSKeyManager.h" #import "DSPeer.h" #import "DSSyncState.h" diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m index 2672588d6..959d9862d 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m @@ -24,7 +24,9 @@ // THE SOFTWARE. #import "DSBloomFilter.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" +#import "DSChain+Wallet.h" #import "DSChainEntity+CoreDataClass.h" #import "DSChainManager+Mining.h" #import "DSChainManager+Protected.h" @@ -219,7 +221,7 @@ - (void)chain:(DSChain *)chain didSetBlockHeight:(int32_t)height andTimestamp:(N [self.transactionManager chain:chain didSetBlockHeight:height andTimestamp:timestamp forTransactionHashes:txHashes updatedTransactions:updatedTransactions]; } -- (void)chain:(DSChain *)chain didFinishInChainSyncPhaseFetchingBlockchainIdentityDAPInformation:(DSBlockchainIdentity *)blockchainIdentity { +- (void)chain:(DSChain *)chain didFinishInChainSyncPhaseFetchingIdentityDAPInformation:(DSIdentity *)identity { dispatch_async(chain.networkingQueue, ^{ [self.peerManager resumeBlockchainSynchronizationOnPeers]; }); @@ -321,7 +323,7 @@ - (void)syncBlockchain { - (void)chainFinishedSyncingMasternodeListsAndQuorums:(DSChain *)chain { DSLog(@"[%@] finished syncing masternode list and quorums, it should start syncing chain", self.chain.name); if (chain.isEvolutionEnabled) { - [self.identitiesManager syncBlockchainIdentitiesWithCompletion:^(NSArray *_Nullable blockchainIdentities) { + [self.identitiesManager syncIdentitiesWithCompletion:^(NSArray *_Nullable identities) { [self syncBlockchain]; }]; } else { @@ -485,6 +487,14 @@ - (void)setupNotificationTimer:(void (^ __nullable)(void))completion { } } +- (void)notifyMasternodeSyncStateChange:(uint32_t)lastBlockHeihgt storedCount:(uintptr_t)storedCount { + @synchronized (self.syncState) { + self.syncState.masternodeListSyncInfo.lastBlockHeight = lastBlockHeihgt; + self.syncState.masternodeListSyncInfo.storedCount = storedCount; + [self notifySyncStateChanged]; + } +} + - (void)notifySyncStateChanged { [self setupNotificationTimer:^{ @synchronized (self) { diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainsManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSChainsManager.h index d3dadb65b..3132fb55d 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSChainsManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainsManager.h @@ -55,7 +55,7 @@ FOUNDATION_EXPORT NSString *const DSChainsDidChangeNotification; sporkAddress:(NSString *)sporkAddress sporkPrivateKey:(NSString *)sporkPrivateKey; -- (DSChain *_Nullable)registerDevnetChainWithIdentifier:(DevnetType)devnetType +- (DSChain *_Nullable)registerDevnetChainWithIdentifier:(dash_spv_crypto_network_chain_type_DevnetType *)devnetType forServiceLocations:(NSOrderedSet *)serviceLocations withMinimumDifficultyBlocks:(uint32_t)minimumDifficultyBlocks standardPort:(uint32_t)standardPort diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainsManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSChainsManager.m index 9dee6f6b6..ef19edd13 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSChainsManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainsManager.m @@ -23,8 +23,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#import "DPContract+Protected.h" #import "DSChainsManager.h" +#import "DSChain+Checkpoint.h" +#import "DSChain+Identity.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" +#import "DSChain+Wallet.h" #import "DSChainEntity+CoreDataClass.h" #import "DSChainManager+Protected.h" #import "DSDashPlatform.h" @@ -188,24 +193,29 @@ - (void)updateDevnetChain:(DSChain *)chain chain.dpnsContractID = dpnsContractID; DPContract *contract = [DSDashPlatform sharedInstanceForChain:chain].dpnsContract; if (uint256_is_not_zero(dpnsContractID)) { - DSBlockchainIdentity *blockchainIdentity = [chain blockchainIdentityThatCreatedContract:[DPContract localDPNSContractForChain:chain] withContractId:dpnsContractID foundInWallet:nil]; - if (blockchainIdentity) { - [contract registerCreator:blockchainIdentity inContext:[NSManagedObjectContext platformContext]]; + DSIdentity *identity = [chain identityThatCreatedContract:[DPContract localDPNSContractForChain:chain] withContractId:dpnsContractID foundInWallet:nil]; + if (identity) { + [contract registerCreator:identity]; + [contract saveAndWaitInContext:[NSManagedObjectContext platformContext]]; } } else { - [contract unregisterCreatorInContext:[NSManagedObjectContext platformContext]]; + [contract unregisterCreator]; + [contract saveAndWaitInContext:[NSManagedObjectContext platformContext]]; } } if (!uint256_eq(dashpayContractID, chain.dashpayContractID)) { chain.dashpayContractID = dashpayContractID; DPContract *contract = [DSDashPlatform sharedInstanceForChain:chain].dashPayContract; if (uint256_is_not_zero(dashpayContractID)) { - DSBlockchainIdentity *blockchainIdentity = [chain blockchainIdentityThatCreatedContract:[DPContract localDashpayContractForChain:chain] withContractId:dashpayContractID foundInWallet:nil]; - if (blockchainIdentity) { - [contract registerCreator:blockchainIdentity inContext:[NSManagedObjectContext platformContext]]; + DSIdentity *identity = [chain identityThatCreatedContract:[DPContract localDashpayContractForChain:chain] withContractId:dashpayContractID foundInWallet:nil]; + if (identity) { + [contract registerCreator:identity]; + [contract saveAndWaitInContext:[NSManagedObjectContext platformContext]]; } } else { - [contract unregisterCreatorInContext:[NSManagedObjectContext platformContext]]; + [contract unregisterCreator]; + [contract saveAndWaitInContext:[NSManagedObjectContext platformContext]]; + } } for (NSString *serviceLocation in serviceLocations) { @@ -233,7 +243,7 @@ - (void)updateDevnetChain:(DSChain *)chain } } -- (DSChain *_Nullable)registerDevnetChainWithIdentifier:(DevnetType)devnetType +- (DSChain *_Nullable)registerDevnetChainWithIdentifier:(dash_spv_crypto_network_chain_type_DevnetType *)devnetType forServiceLocations:(NSOrderedSet *)serviceLocations withMinimumDifficultyBlocks:(uint32_t)minimumDifficultyBlocks standardPort:(uint32_t)standardPort @@ -287,7 +297,8 @@ - (DSChain *_Nullable)registerDevnetChainWithIdentifier:(DevnetType)devnetType NSMutableDictionary *registeredDevnetsDictionary = [getKeychainDict(DEVNET_CHAINS_KEY, @[[NSString class], [NSArray class], [DSCheckpoint class]], &error) mutableCopy]; if (!registeredDevnetsDictionary) registeredDevnetsDictionary = [NSMutableDictionary dictionary]; - NSString *devnetIdentifier = [DSKeyManager NSStringFrom:chain_devnet_identifier(devnetType)]; + char *devnet_id = dash_spv_crypto_network_chain_type_ChainType_devnet_identifier(chain.chainType); + NSString *devnetIdentifier = [DSKeyManager NSStringFrom:devnet_id]; if (![[registeredDevnetsDictionary allKeys] containsObject:devnetIdentifier]) { [registeredDevnetsDictionary setObject:chain.checkpoints forKey:devnetIdentifier]; setKeychainDict(registeredDevnetsDictionary, DEVNET_CHAINS_KEY, NO); @@ -309,8 +320,6 @@ - (void)removeDevnetChain:(DSChain *)chain { NSError *error = nil; DSChainManager *chainManager = [self chainManagerForChain:chain]; DSPeerManager *peerManager = chainManager.peerManager; - DSMasternodeManager *masternodeManager = chainManager.masternodeManager; - [masternodeManager destroyProcessors]; [peerManager clearRegisteredPeers]; NSMutableDictionary *registeredDevnetsDictionary = [getKeychainDict(DEVNET_CHAINS_KEY, @[[NSString class], [NSArray class], [DSCheckpoint class]], &error) mutableCopy]; diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSGovernanceSyncManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSGovernanceSyncManager.m index 6d004a78e..97d62a173 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSGovernanceSyncManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSGovernanceSyncManager.m @@ -25,6 +25,7 @@ #import "DSGovernanceSyncManager.h" #import "DSAccount.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" #import "DSChainEntity+CoreDataProperties.h" #import "DSChainsManager.h" diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager+CoreData.h b/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager+CoreData.h new file mode 100644 index 000000000..44c40e424 --- /dev/null +++ b/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager+CoreData.h @@ -0,0 +1,35 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "DSIdentity.h" +#import "DSIdentitiesManager.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSIdentitiesManager (CoreData) +- (void)setup; +- (void)loadExternalIdentities; +- (void)registerForeignIdentity:(DSIdentity *)identity; +- (DSIdentity *)foreignIdentityWithUniqueId:(UInt256)uniqueId; +- (DSIdentity *)foreignIdentityWithUniqueId:(UInt256)uniqueId + createIfMissing:(BOOL)addIfMissing + inContext:(NSManagedObjectContext *_Nullable)context; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager+CoreData.m b/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager+CoreData.m new file mode 100644 index 000000000..b11377347 --- /dev/null +++ b/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager+CoreData.m @@ -0,0 +1,87 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DSIdentitiesManager+CoreData.h" +#import "DSIdentity+Protected.h" +#import "DSBlockchainIdentityEntity+CoreDataClass.h" +#import "NSManagedObject+Sugar.h" +#import "NSManagedObjectContext+DSSugar.h" + +@interface DSIdentitiesManager () +@property (nonatomic, strong) NSMutableDictionary *foreignIdentities; +@end + +@implementation DSIdentitiesManager (CoreData) + +- (void)clearExternalIdentities { + self.foreignIdentities = [NSMutableDictionary dictionary]; +} + +- (void)setup { + [self clearExternalIdentities]; + [self loadExternalIdentities]; +} + + +- (void)loadExternalIdentities { + NSManagedObjectContext *context = [NSManagedObjectContext chainContext]; //shouldn't matter what context is used + + [context performBlockAndWait:^{ + NSArray *externalIdentityEntities = [DSBlockchainIdentityEntity objectsInContext:context matching:@"chain == %@ && isLocal == FALSE", [self.chain chainEntityInContext:context]]; + for (DSBlockchainIdentityEntity *entity in externalIdentityEntities) { + DSIdentity *identity = [[DSIdentity alloc] initWithIdentityEntity:entity]; + if (identity) { + self.foreignIdentities[uint256_data(identity.uniqueID)] = identity; + } + } + }]; +} + +- (void)registerForeignIdentity:(DSIdentity *)identity { + NSAssert(!identity.isTransient, @"Dash Identity should no longer be transient"); + @synchronized(self.foreignIdentities) { + if (!self.foreignIdentities[uint256_data(identity.uniqueID)]) { + [identity saveInitial]; + self.foreignIdentities[uint256_data(identity.uniqueID)] = identity; + } + } +} +- (DSIdentity *)foreignIdentityWithUniqueId:(UInt256)uniqueId { + return [self foreignIdentityWithUniqueId:uniqueId createIfMissing:NO inContext:nil]; +} + +- (DSIdentity *)foreignIdentityWithUniqueId:(UInt256)uniqueId + createIfMissing:(BOOL)addIfMissing + inContext:(NSManagedObjectContext *_Nullable)context { + //foreign blockchain identities are for local blockchain identies' contacts, not for search. + @synchronized(self.foreignIdentities) { + DSIdentity *foreignIdentity = self.foreignIdentities[uint256_data(uniqueId)]; + if (foreignIdentity) { + NSAssert(context ? [foreignIdentity identityEntityInContext:context] : foreignIdentity.identityEntity, @"Blockchain identity entity should exist"); + return foreignIdentity; + } else if (addIfMissing) { + foreignIdentity = [[DSIdentity alloc] initWithUniqueId:uniqueId isTransient:FALSE onChain:self.chain]; + [foreignIdentity saveInitialInContext:context]; + self.foreignIdentities[uint256_data(uniqueId)] = foreignIdentity; + return self.foreignIdentities[uint256_data(uniqueId)]; + } + return nil; + } +} + + +@end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager+Protected.h b/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager+Protected.h index 3ae4db917..4db9f984e 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager+Protected.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager+Protected.h @@ -29,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN @interface DSIdentitiesManager (Protected) -- (void)clearExternalBlockchainIdentities; +- (void)clearExternalIdentities; @property (nonatomic, readonly) dispatch_queue_t identityQueue; diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.h index 1d94284a9..e3920227c 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.h @@ -20,15 +20,15 @@ NS_ASSUME_NONNULL_BEGIN -@class DSChain, DSBlockchainIdentity, DSCreditFundingTransaction, DSTransientDashpayUser; +@class DSChain, DSIdentity, DSAssetLockTransaction, DSTransientDashpayUser; @protocol DSDAPINetworkServiceRequest; -typedef void (^IdentitiesSuccessCompletionBlock)(NSArray *_Nullable blockchainIdentities); -typedef void (^IdentitiesCompletionBlock)(BOOL success, NSArray *_Nullable blockchainIdentities, NSArray *errors); -typedef void (^IdentityCompletionBlock)(BOOL success, DSBlockchainIdentity *_Nullable blockchainIdentity, NSError *_Nullable error); +typedef void (^IdentitiesSuccessCompletionBlock)(NSArray *_Nullable identities); +typedef void (^IdentitiesCompletionBlock)(BOOL success, NSArray *_Nullable identities, NSArray *errors); +typedef void (^IdentityCompletionBlock)(BOOL success, DSIdentity *_Nullable identity, NSError *_Nullable error); typedef void (^DashpayUserInfoCompletionBlock)(BOOL success, DSTransientDashpayUser *_Nullable dashpayUserInfo, NSError *_Nullable error); -typedef void (^DashpayUserInfosCompletionBlock)(BOOL success, NSDictionary *_Nullable dashpayUserInfosByBlockchainIdentityUniqueId, NSError *_Nullable error); +typedef void (^DashpayUserInfosCompletionBlock)(BOOL success, NSDictionary *_Nullable dashpayUserInfosByIdentityUniqueId, NSError *_Nullable error); @interface DSIdentitiesManager : NSObject @@ -42,48 +42,67 @@ typedef void (^DashpayUserInfosCompletionBlock)(BOOL success, NSDictionary)searchIdentityByDashpayUsername:(NSString *)name withCompletion:(IdentityCompletionBlock)completion; +- (id)searchIdentityByDashpayUsername:(NSString *)name + withCompletion:(IdentityCompletionBlock)completion; -- (id)searchIdentityByName:(NSString *)namePrefix inDomain:(NSString *)domain withCompletion:(IdentityCompletionBlock)completion; +- (id)searchIdentityByName:(NSString *)namePrefix + inDomain:(NSString *)domain + withCompletion:(IdentityCompletionBlock)completion; -- (id)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo withCompletion:(IdentitiesCompletionBlock)completion; +- (id)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix + queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo + withCompletion:(IdentitiesCompletionBlock)completion; -- (id)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix startAfter:(NSData* _Nullable)startAfter limit:(uint32_t)limit queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo withCompletion:(IdentitiesCompletionBlock)completion; +- (id)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix + startAfter:(NSData* _Nullable)startAfter + limit:(uint32_t)limit + queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo + withCompletion:(IdentitiesCompletionBlock)completion; -- (id)searchIdentitiesByNamePrefix:(NSString *)namePrefix inDomain:(NSString *)domain startAfter:(NSData* _Nullable)startAfter limit:(uint32_t)limit withCompletion:(IdentitiesCompletionBlock)completion; +- (id)searchIdentitiesByNamePrefix:(NSString *)namePrefix + inDomain:(NSString *)domain + startAfter:(NSData* _Nullable)startAfter + limit:(uint32_t)limit + withCompletion:(IdentitiesCompletionBlock)completion; -- (id)fetchProfileForBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity withCompletion:(DashpayUserInfoCompletionBlock)completion onCompletionQueue:(dispatch_queue_t)completionQueue; +- (id)fetchProfileForIdentity:(DSIdentity *)identity withCompletion:(DashpayUserInfoCompletionBlock)completion onCompletionQueue:(dispatch_queue_t)completionQueue; -- (id)fetchProfileForBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity +- (id)fetchProfileForIdentity:(DSIdentity *)identity retryCount:(uint32_t)retryCount delay:(uint32_t)delay delayIncrease:(uint32_t)delayIncrease withCompletion:(DashpayUserInfoCompletionBlock)completion onCompletionQueue:(dispatch_queue_t)completionQueue; -- (id)fetchProfilesForBlockchainIdentities:(NSArray *)blockchainIdentities withCompletion:(DashpayUserInfosCompletionBlock)completion onCompletionQueue:(dispatch_queue_t)completionQueue; +- (id)fetchProfilesForIdentities:(NSArray *)identityUserIds + withCompletion:(DashpayUserInfosCompletionBlock)completion + onCompletionQueue:(dispatch_queue_t)completionQueue; -- (void)searchIdentitiesByDPNSRegisteredBlockchainIdentityUniqueID:(NSData *)userID withCompletion:(IdentitiesCompletionBlock)completion; +- (void)searchIdentitiesByDPNSRegisteredIdentityUniqueID:(NSData *)userID + withCompletion:(IdentitiesCompletionBlock)completion; - (void)retrieveIdentitiesByKeysUntilSuccessWithCompletion:(IdentitiesSuccessCompletionBlock)completion completionQueue:(dispatch_queue_t)completionQueue; -- (void)retrieveIdentitiesByKeysWithCompletion:(IdentitiesCompletionBlock)completion completionQueue:(dispatch_queue_t)completionQueue; +- (void)retrieveIdentitiesByKeysWithCompletion:(IdentitiesCompletionBlock)completion + completionQueue:(dispatch_queue_t)completionQueue; -- (void)fetchNeededNetworkStateInformationForBlockchainIdentities:(NSArray *)blockchainIdentities withCompletion:(IdentitiesCompletionBlock)completion completionQueue:(dispatch_queue_t)completionQueue; +- (void)fetchNeededNetworkStateInformationForIdentities:(NSArray *)identities + withCompletion:(IdentitiesCompletionBlock)completion + completionQueue:(dispatch_queue_t)completionQueue; @end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.m index 3a3171009..6e113cde6 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSIdentitiesManager.m @@ -15,13 +15,19 @@ // limitations under the License. // +#import "DPContract.h" #import "DSIdentitiesManager.h" +#import "DSIdentitiesManager+CoreData.h" +#import "DSAssetLockTransaction.h" #import "DSAuthenticationKeysDerivationPath.h" -#import "DSBlockchainIdentity+Protected.h" +#import "DSIdentity+Protected.h" +#import "DSIdentity+Username.h" #import "DSBlockchainIdentityEntity+CoreDataClass.h" +#import "DSChain+Identity.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" +#import "DSChain+Wallet.h" #import "DSChainManager.h" -#import "DSCreditFundingTransaction.h" #import "DSDAPIClient.h" #import "DSDAPIPlatformNetworkService.h" #import "DSDashPlatform.h" @@ -30,17 +36,21 @@ #import "DSPeerManager.h" #import "DSTransientDashpayUser+Protected.h" #import "DSWallet.h" +#import "DSWallet+Identity.h" #import "NSError+Dash.h" #import "NSManagedObject+Sugar.h" #import "NSManagedObjectContext+DSSugar.h" #import "NSString+Dash.h" #import "dash_shared_core.h" +#define ERROR_UNKNOWN_KEYS [NSError errorWithCode:500 localizedDescriptionKey:@"Identity has unknown keys"] +#define ERROR_CONTRACT_SETUP [NSError errorWithCode:500 localizedDescriptionKey:@"The Dashpay contract is not properly set up"] + @interface DSIdentitiesManager () @property (nonatomic, strong) DSChain *chain; @property (nonatomic, strong) dispatch_queue_t identityQueue; -@property (nonatomic, strong) NSMutableDictionary *foreignBlockchainIdentities; +@property (nonatomic, strong) NSMutableDictionary *foreignIdentities; @property (nonatomic, assign) NSTimeInterval lastSyncedIndentitiesTimestamp; @property (nonatomic, assign) BOOL hasRecentIdentitiesSync; @@ -55,27 +65,15 @@ - (instancetype)initWithChain:(DSChain *)chain { self.chain = chain; _identityQueue = dispatch_queue_create([@"org.dashcore.dashsync.identity" UTF8String], DISPATCH_QUEUE_SERIAL); - self.foreignBlockchainIdentities = [NSMutableDictionary dictionary]; - [self loadExternalBlockchainIdentities]; + [self setup]; +// self.foreignIdentities = [NSMutableDictionary dictionary]; +// [self loadExternalIdentities]; return self; } // MARK: - Loading -- (void)loadExternalBlockchainIdentities { - NSManagedObjectContext *context = [NSManagedObjectContext chainContext]; //shouldn't matter what context is used - - [context performBlockAndWait:^{ - NSArray *externalIdentityEntities = [DSBlockchainIdentityEntity objectsInContext:context matching:@"chain == %@ && isLocal == FALSE", [self.chain chainEntityInContext:context]]; - for (DSBlockchainIdentityEntity *entity in externalIdentityEntities) { - DSBlockchainIdentity *identity = [[DSBlockchainIdentity alloc] initWithBlockchainIdentityEntity:entity]; - if (identity) { - self.foreignBlockchainIdentities[uint256_data(identity.uniqueID)] = identity; - } - } - }]; -} - (BOOL)hasRecentIdentitiesSync { return ([[NSDate date] timeIntervalSince1970] - self.lastSyncedIndentitiesTimestamp < 30); @@ -83,139 +81,119 @@ - (BOOL)hasRecentIdentitiesSync { // MARK: - Wiping -- (void)clearExternalBlockchainIdentities { - self.foreignBlockchainIdentities = [NSMutableDictionary dictionary]; -} +//- (void)clearExternalIdentities { +// self.foreignIdentities = [NSMutableDictionary dictionary]; +//} // MARK: - Identities -- (void)registerForeignBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity { - NSAssert(!blockchainIdentity.isTransient, @"Dash Identity should no longer be transient"); - @synchronized(self.foreignBlockchainIdentities) { - if (!self.foreignBlockchainIdentities[uint256_data(blockchainIdentity.uniqueID)]) { - [blockchainIdentity saveInitial]; - self.foreignBlockchainIdentities[uint256_data(blockchainIdentity.uniqueID)] = blockchainIdentity; - } - } -} - -- (DSBlockchainIdentity *)foreignBlockchainIdentityWithUniqueId:(UInt256)uniqueId { - return [self foreignBlockchainIdentityWithUniqueId:uniqueId createIfMissing:NO inContext:nil]; -} - -- (DSBlockchainIdentity *)foreignBlockchainIdentityWithUniqueId:(UInt256)uniqueId createIfMissing:(BOOL)addIfMissing inContext:(NSManagedObjectContext *)context { - //foreign blockchain identities are for local blockchain identies' contacts, not for search. - @synchronized(self.foreignBlockchainIdentities) { - DSBlockchainIdentity *foreignBlockchainIdentity = self.foreignBlockchainIdentities[uint256_data(uniqueId)]; - if (foreignBlockchainIdentity) { - NSAssert(context ? [foreignBlockchainIdentity blockchainIdentityEntityInContext:context] : foreignBlockchainIdentity.blockchainIdentityEntity, @"Blockchain identity entity should exist"); - return foreignBlockchainIdentity; - } else if (addIfMissing) { - foreignBlockchainIdentity = [[DSBlockchainIdentity alloc] initWithUniqueId:uniqueId isTransient:FALSE onChain:self.chain]; - [foreignBlockchainIdentity saveInitialInContext:context]; - self.foreignBlockchainIdentities[uint256_data(uniqueId)] = foreignBlockchainIdentity; - return self.foreignBlockchainIdentities[uint256_data(uniqueId)]; - } - return nil; - } -} - -- (NSArray *)unsyncedBlockchainIdentities { - NSMutableArray *unsyncedBlockchainIdentities = [NSMutableArray array]; - for (DSBlockchainIdentity *blockchainIdentity in [self.chain localBlockchainIdentities]) { - if (!blockchainIdentity.registrationCreditFundingTransaction || (blockchainIdentity.registrationCreditFundingTransaction.blockHeight == BLOCK_UNKNOWN_HEIGHT)) { - [unsyncedBlockchainIdentities addObject:blockchainIdentity]; - } else if (self.chain.lastSyncBlockHeight > blockchainIdentity.dashpaySyncronizationBlockHeight) { +//- (void)registerForeignIdentity:(DSIdentity *)identity { +// NSAssert(!identity.isTransient, @"Dash Identity should no longer be transient"); +// @synchronized(self.foreignIdentities) { +// if (!self.foreignIdentities[uint256_data(identity.uniqueID)]) { +// [identity saveInitial]; +// self.foreignIdentities[uint256_data(identity.uniqueID)] = identity; +// } +// } +//} + +//- (DSIdentity *)foreignIdentityWithUniqueId:(UInt256)uniqueId { +// return [self foreignIdentityWithUniqueId:uniqueId createIfMissing:NO inContext:nil]; +//} +// +//- (DSIdentity *)foreignIdentityWithUniqueId:(UInt256)uniqueId +// createIfMissing:(BOOL)addIfMissing +// inContext:(NSManagedObjectContext *)context { +// //foreign identities are for local blockchain identies' contacts, not for search. +// @synchronized(self.foreignIdentities) { +// DSIdentity *foreignIdentity = self.foreignIdentities[uint256_data(uniqueId)]; +// if (foreignIdentity) { +// NSAssert(context ? [foreignIdentity identityEntityInContext:context] : foreignIdentity.identityEntity, @"Identity entity should exist"); +// return foreignIdentity; +// } else if (addIfMissing) { +// foreignIdentity = [[DSIdentity alloc] initWithUniqueId:uniqueId isTransient:FALSE onChain:self.chain]; +// [foreignIdentity saveInitialInContext:context]; +// self.foreignIdentities[uint256_data(uniqueId)] = foreignIdentity; +// return self.foreignIdentities[uint256_data(uniqueId)]; +// } +// return nil; +// } +//} + +- (NSArray *)unsyncedIdentities { + NSMutableArray *unsyncedIdentities = [NSMutableArray array]; + for (DSIdentity *identity in [self.chain localIdentities]) { + if (!identity.registrationAssetLockTransaction || (identity.registrationAssetLockTransaction.blockHeight == BLOCK_UNKNOWN_HEIGHT)) { + [unsyncedIdentities addObject:identity]; + } else if (self.chain.lastSyncBlockHeight > identity.dashpaySyncronizationBlockHeight) { //If they are equal then the blockchain identity is synced //This is because the dashpaySyncronizationBlock represents the last block for the bloom filter used in L1 should be considered valid //That's because it is set at the time with the hash of the last - [unsyncedBlockchainIdentities addObject:blockchainIdentity]; + [unsyncedIdentities addObject:identity]; } } - return unsyncedBlockchainIdentities; + return unsyncedIdentities; } //TODO: if we get an error or identity not found, better stop the process and start syncing chain -- (void)syncBlockchainIdentitiesWithCompletion:(IdentitiesSuccessCompletionBlock)completion { - [self - retrieveIdentitiesByKeysUntilSuccessWithCompletion:^(NSArray *_Nullable retrievedBlockchainIdentities) { - NSArray *blockchainIdentities = [self unsyncedBlockchainIdentities]; - [self fetchNeededNetworkStateInformationForBlockchainIdentities:blockchainIdentities - withCompletion:^(BOOL success, NSArray *_Nullable blockchainIdentities, NSArray *_Nonnull errors) { +- (void)syncIdentitiesWithCompletion:(IdentitiesSuccessCompletionBlock)completion { + [self retrieveIdentitiesByKeysUntilSuccessWithCompletion:^(NSArray *_Nullable retrievedIdentities) { + [self fetchNeededNetworkStateInformationForIdentities:[self unsyncedIdentities] + withCompletion:^(BOOL success, NSArray *_Nullable identities, NSArray *_Nonnull errors) { self.lastSyncedIndentitiesTimestamp = [[NSDate date] timeIntervalSince1970]; - if (success) { - if (completion) { - completion(blockchainIdentities); - } - } + if (success) if (completion) completion(identities); } completionQueue:self.chain.networkingQueue]; } completionQueue:self.chain.networkingQueue]; } -- (void)retrieveAllBlockchainIdentitiesChainStates { +- (void)retrieveAllIdentitiesChainStates { for (DSWallet *wallet in self.chain.wallets) { - [self retrieveAllBlockchainIdentitiesChainStatesForWallet:wallet]; + [self retrieveAllIdentitiesChainStatesForWallet:wallet]; } } -- (void)retrieveAllBlockchainIdentitiesChainStatesForWallet:(DSWallet *)wallet { - for (DSBlockchainIdentity *identity in [wallet.blockchainIdentities allValues]) { - if (identity.registrationStatus == DSBlockchainIdentityRegistrationStatus_Unknown) { +- (void)retrieveAllIdentitiesChainStatesForWallet:(DSWallet *)wallet { + for (DSIdentity *identity in [wallet.identities allValues]) { + if (identity.registrationStatus == DSIdentityRegistrationStatus_Unknown) { [identity fetchIdentityNetworkStateInformationWithCompletion:^(BOOL success, BOOL found, NSError *error) { - if (success && found) { - //now lets get dpns info - if (([[DSOptionsManager sharedInstance] syncType] & DSSyncType_DPNS)) { - [identity fetchUsernamesWithCompletion:^(BOOL success, NSError *error){ - - }]; - } - } + //now lets get dpns info + if (success && found && ([[DSOptionsManager sharedInstance] syncType] & DSSyncType_DPNS)) + [identity fetchUsernamesWithCompletion:^(BOOL success, NSError *error) {}]; }]; - } else if (identity.registrationStatus == DSBlockchainIdentityRegistrationStatus_Registered) { - if (!identity.currentDashpayUsername) { - if (([[DSOptionsManager sharedInstance] syncType] & DSSyncType_DPNS)) { - [identity fetchUsernamesWithCompletion:^(BOOL success, NSError *error){ - - }]; - } - } + } else if (identity.registrationStatus == DSIdentityRegistrationStatus_Registered && !identity.currentDashpayUsername && ([[DSOptionsManager sharedInstance] syncType] & DSSyncType_DPNS)) { + [identity fetchUsernamesWithCompletion:^(BOOL success, NSError *error) {}]; } } } -- (id)searchIdentityByDashpayUsername:(NSString *)name withCompletion:(IdentityCompletionBlock)completion { +- (id)searchIdentityByDashpayUsername:(NSString *)name + withCompletion:(IdentityCompletionBlock)completion { return [self searchIdentityByName:name inDomain:@"dash" withCompletion:completion]; } -- (id)searchIdentityByName:(NSString *)name inDomain:(NSString *)domain withCompletion:(IdentityCompletionBlock)completion { +- (id)searchIdentityByName:(NSString *)name + inDomain:(NSString *)domain + withCompletion:(IdentityCompletionBlock)completion { DSDAPIClient *client = self.chain.chainManager.DAPIClient; id call = [client.DAPIPlatformNetworkService getDPNSDocumentsForUsernames:@[name] inDomain:domain completionQueue:self.identityQueue success:^(NSArray *_Nonnull documents) { - __block NSMutableArray *rBlockchainIdentities = [NSMutableArray array]; + __block NSMutableArray *rIdentities = [NSMutableArray array]; for (NSDictionary *document in documents) { NSData *userIdData = document[@"$ownerId"]; NSString *normalizedLabel = document[@"normalizedLabel"]; NSString *domain = document[@"normalizedParentDomainName"]; - DSBlockchainIdentity *identity = [[DSBlockchainIdentity alloc] initWithUniqueId:userIdData.UInt256 isTransient:TRUE onChain:self.chain]; - [identity addUsername:normalizedLabel inDomain:domain status:DSBlockchainIdentityUsernameStatus_Confirmed save:NO registerOnNetwork:NO]; - [rBlockchainIdentities addObject:identity]; - } - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(YES, [rBlockchainIdentities firstObject], nil); - }); + DSIdentity *identity = [[DSIdentity alloc] initWithUniqueId:userIdData.UInt256 isTransient:TRUE onChain:self.chain]; + [identity addUsername:normalizedLabel inDomain:domain status:DSIdentityUsernameStatus_Confirmed save:NO registerOnNetwork:NO]; + [rIdentities addObject:identity]; } + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(YES, [rIdentities firstObject], nil); }); } failure:^(NSError *_Nonnull error) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(NO, nil, error); - }); - } + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(NO, nil, error); }); #if DEBUG DSLogPrivate(@"Failure in searchIdentityByName %@", error); #else @@ -225,89 +203,73 @@ - (void)retrieveAllBlockchainIdentitiesChainStatesForWallet:(DSWallet *)wallet { return call; } -- (id)fetchProfileForBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity withCompletion:(DashpayUserInfoCompletionBlock)completion onCompletionQueue:(dispatch_queue_t)completionQueue { - return [self fetchProfileForBlockchainIdentity:blockchainIdentity retryCount:5 delay:2 delayIncrease:1 withCompletion:completion onCompletionQueue:completionQueue]; +- (id)fetchProfileForIdentity:(DSIdentity *)identity + withCompletion:(DashpayUserInfoCompletionBlock)completion + onCompletionQueue:(dispatch_queue_t)completionQueue { + return [self fetchProfileForIdentity:identity retryCount:5 delay:2 delayIncrease:1 withCompletion:completion onCompletionQueue:completionQueue]; } -- (id)fetchProfileForBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity - retryCount:(uint32_t)retryCount - delay:(uint32_t)delay - delayIncrease:(uint32_t)delayIncrease - withCompletion:(DashpayUserInfoCompletionBlock)completion - onCompletionQueue:(dispatch_queue_t)completionQueue { +- (id)fetchProfileForIdentity:(DSIdentity *)identity + retryCount:(uint32_t)retryCount + delay:(uint32_t)delay + delayIncrease:(uint32_t)delayIncrease + withCompletion:(DashpayUserInfoCompletionBlock)completion + onCompletionQueue:(dispatch_queue_t)completionQueue { DPContract *dashpayContract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; if ([dashpayContract contractState] != DPContractState_Registered) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, nil, [NSError errorWithCode:500 localizedDescriptionKey:@"The Dashpay contract is not properly set up"]); - }); - } + if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, ERROR_CONTRACT_SETUP); }); return nil; } DSDAPIClient *client = self.chain.chainManager.DAPIClient; - id call = [client.DAPIPlatformNetworkService getDashpayProfileForUserId:blockchainIdentity.uniqueIDData + id call = [client.DAPIPlatformNetworkService getDashpayProfileForUserId:identity.uniqueIDData completionQueue:self.identityQueue success:^(NSArray *_Nonnull documents) { if (documents.count == 0) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(YES, nil, nil); - }); - } + if (completion) dispatch_async(completionQueue, ^{ completion(YES, nil, nil); }); return; } NSDictionary *contactDictionary = documents.firstObject; - DSTransientDashpayUser *transientDashpayUser = [[DSTransientDashpayUser alloc] initWithDashpayProfileDocument:contactDictionary]; - if (completion) { - dispatch_async(completionQueue, ^{ - completion(YES, transientDashpayUser, nil); - }); - } + if (completion) dispatch_async(completionQueue, ^{ completion(YES, transientDashpayUser, nil); }); } failure:^(NSError *_Nonnull error) { if (retryCount > 0) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), self.identityQueue, ^{ - [self fetchProfileForBlockchainIdentity:blockchainIdentity retryCount:retryCount - 1 delay:delay + delayIncrease delayIncrease:delayIncrease withCompletion:completion onCompletionQueue:completionQueue]; + [self fetchProfileForIdentity:identity + retryCount:retryCount - 1 + delay:delay + delayIncrease + delayIncrease:delayIncrease + withCompletion:completion + onCompletionQueue:completionQueue]; }); - } else { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, nil, error); - }); - } + } else if (completion) { + dispatch_async(completionQueue, ^{ completion(NO, nil, error); }); } }]; return call; } -- (id)fetchProfilesForBlockchainIdentities:(NSArray *)blockchainIdentities withCompletion:(DashpayUserInfosCompletionBlock)completion onCompletionQueue:(dispatch_queue_t)completionQueue { +- (id)fetchProfilesForIdentities:(NSArray *)identityUserIds + withCompletion:(DashpayUserInfosCompletionBlock)completion + onCompletionQueue:(dispatch_queue_t)completionQueue { __weak typeof(self) weakSelf = self; DPContract *dashpayContract = [DSDashPlatform sharedInstanceForChain:self.chain].dashPayContract; if ([dashpayContract contractState] != DPContractState_Registered) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, nil, [NSError errorWithCode:500 localizedDescriptionKey:@"The Dashpay contract is not properly set up"]); - }); - } + if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, ERROR_CONTRACT_SETUP); }); return nil; } - NSMutableArray *blockchainIdentityUserIds = [NSMutableArray array]; - for (DSBlockchainIdentity *blockchainIdentity in blockchainIdentities) { - [blockchainIdentityUserIds addObject:blockchainIdentity.uniqueIDData]; - } +// NSMutableArray *identityUserIds = [NSMutableArray array]; +// for (DSIdentity *identity in identities) { +// [identityUserIds addObject:identity.uniqueIDData]; +// } DSDAPIClient *client = self.chain.chainManager.DAPIClient; - id call = [client.DAPIPlatformNetworkService getDashpayProfilesForUserIds:blockchainIdentityUserIds + id call = [client.DAPIPlatformNetworkService getDashpayProfilesForUserIds:identityUserIds completionQueue:self.identityQueue success:^(NSArray *_Nonnull documents) { __strong typeof(weakSelf) strongSelf = weakSelf; if (!strongSelf) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, nil, [NSError errorWithCode:500 localizedDescriptionKey:@"Internal memory allocation error"]); - }); - } + if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, ERROR_MEM_ALLOC); }); return; } @@ -329,55 +291,49 @@ - (void)retrieveAllBlockchainIdentitiesChainStatesForWallet:(DSWallet *)wallet { } } failure:^(NSError *_Nonnull error) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(NO, nil, error); - }); - } + if (completion) dispatch_async(completionQueue, ^{ completion(NO, nil, error); }); }]; return call; } -- (id)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo withCompletion:(IdentitiesCompletionBlock)completion { - return [self searchIdentitiesByDashpayUsernamePrefix:namePrefix startAfter:nil limit:100 queryDashpayProfileInfo:queryDashpayProfileInfo withCompletion:completion]; +- (id)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix + queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo + withCompletion:(IdentitiesCompletionBlock)completion { + return [self searchIdentitiesByDashpayUsernamePrefix:namePrefix + startAfter:nil + limit:100 + queryDashpayProfileInfo:queryDashpayProfileInfo + withCompletion:completion]; } -- (id)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix startAfter:(NSData* _Nullable)startAfter limit:(uint32_t)limit queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo withCompletion:(IdentitiesCompletionBlock)completion { +- (id)searchIdentitiesByDashpayUsernamePrefix:(NSString *)namePrefix + startAfter:(NSData* _Nullable)startAfter + limit:(uint32_t)limit + queryDashpayProfileInfo:(BOOL)queryDashpayProfileInfo + withCompletion:(IdentitiesCompletionBlock)completion { return [self searchIdentitiesByNamePrefix:namePrefix inDomain:@"dash" startAfter:startAfter limit:limit - withCompletion:^(BOOL success, NSArray *_Nullable blockchainIdentities, NSArray *_Nonnull errors) { + withCompletion:^(BOOL success, NSArray *_Nullable identities, NSArray *_Nonnull errors) { if (errors.count) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(success, blockchainIdentities, errors); - }); - } - } else if (queryDashpayProfileInfo && blockchainIdentities.count) { - __block NSMutableDictionary *blockchainIdentityDictionary = [NSMutableDictionary dictionary]; - for (DSBlockchainIdentity *blockchainIdentity in blockchainIdentities) { - [blockchainIdentityDictionary setObject:blockchainIdentity forKey:blockchainIdentity.uniqueIDData]; + 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 fetchProfilesForBlockchainIdentities:blockchainIdentities - withCompletion:^(BOOL success, NSDictionary *_Nullable dashpayUserInfosByBlockchainIdentityUniqueId, NSError *_Nullable error) { - for (NSData *blockchainIdentityUniqueIdData in dashpayUserInfosByBlockchainIdentityUniqueId) { - DSBlockchainIdentity *blockchainIdentity = blockchainIdentityDictionary[blockchainIdentityUniqueIdData]; - blockchainIdentity.transientDashpayUser = dashpayUserInfosByBlockchainIdentityUniqueId[blockchainIdentityUniqueIdData]; - } - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(success, blockchainIdentities, errors); - }); + [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, blockchainIdentities, errors); - }); - } + } else if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ completion(success, identities, errors); }); } }]; } @@ -390,39 +346,27 @@ - (void)retrieveAllBlockchainIdentitiesChainStatesForWallet:(DSWallet *)wallet { limit:limit completionQueue:self.identityQueue success:^(NSArray *_Nonnull documents) { - __block NSMutableDictionary *rBlockchainIdentities = [NSMutableDictionary dictionary]; + __block NSMutableDictionary *rIdentities = [NSMutableDictionary dictionary]; for (NSDictionary *document in documents) { NSData *userIdData = document[@"$ownerId"]; - DSBlockchainIdentity *identity = [rBlockchainIdentities objectForKey:userIdData]; + DSIdentity *identity = [rIdentities objectForKey:userIdData]; UInt256 uniqueId = userIdData.UInt256; - if (!identity) { - identity = [self.chain blockchainIdentityForUniqueId:uniqueId foundInWallet:nil includeForeignBlockchainIdentities:YES]; - } + if (!identity) + identity = [self.chain identityForUniqueId:uniqueId foundInWallet:nil includeForeignIdentities:YES]; NSString *label = document[@"label"]; NSString *domain = document[@"normalizedParentDomainName"]; if (!identity) { - identity = [[DSBlockchainIdentity alloc] initWithUniqueId:uniqueId isTransient:TRUE onChain:self.chain]; - [identity addUsername:label inDomain:domain status:DSBlockchainIdentityUsernameStatus_Confirmed save:NO registerOnNetwork:NO]; - } else { - if (![identity.dashpayUsernames containsObject:label]) { - [identity addUsername:label inDomain:domain status:DSBlockchainIdentityUsernameStatus_Confirmed save:YES registerOnNetwork:NO]; - } + identity = [[DSIdentity alloc] initWithUniqueId:uniqueId isTransient:TRUE onChain:self.chain]; + [identity addUsername:label inDomain:domain status:DSIdentityUsernameStatus_Confirmed save:NO registerOnNetwork:NO]; + } else if (![identity.dashpayUsernames containsObject:label]) { + [identity addUsername:label inDomain:domain status:DSIdentityUsernameStatus_Confirmed save:YES registerOnNetwork:NO]; } - - [rBlockchainIdentities setObject:identity forKey:userIdData]; - } - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(YES, [[rBlockchainIdentities allValues] copy], @[]); - }); + [rIdentities setObject:identity forKey:userIdData]; } + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(YES, [[rIdentities allValues] copy], @[]); }); } failure:^(NSError *_Nonnull error) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(NO, nil, @[error]); - }); - } + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(NO, nil, @[error]); }); #if DEBUG DSLogPrivate(@"Failure in searchIdentitiesByNamePrefix %@", error); #else @@ -432,89 +376,73 @@ - (void)retrieveAllBlockchainIdentitiesChainStatesForWallet:(DSWallet *)wallet { return call; } -- (void)searchIdentitiesByDPNSRegisteredBlockchainIdentityUniqueID:(NSData *)userID withCompletion:(IdentitiesCompletionBlock)completion { - DSDAPIClient *client = self.chain.chainManager.DAPIClient; - [client.DAPIPlatformNetworkService getDPNSDocumentsForIdentityWithUserId:userID - completionQueue:self.identityQueue - success:^(NSArray *_Nonnull documents) { - __block NSMutableArray *rBlockchainIdentities = [NSMutableArray array]; +- (void)searchIdentitiesByDPNSRegisteredIdentityUniqueID:(NSData *)userID + withCompletion:(IdentitiesCompletionBlock)completion { + [self.chain.chainManager.DAPIClient.DAPIPlatformNetworkService getDPNSDocumentsForIdentityWithUserId:userID + completionQueue:self.identityQueue + success:^(NSArray *_Nonnull documents) { + __block NSMutableArray *rIdentities = [NSMutableArray array]; for (NSDictionary *document in documents) { NSData *userIdData = document[@"$ownerId"]; NSString *normalizedLabel = document[@"normalizedLabel"]; NSString *domain = document[@"normalizedParentDomainName"]; - DSBlockchainIdentity *identity = [[DSBlockchainIdentity alloc] initWithUniqueId:userIdData.UInt256 isTransient:TRUE onChain:self.chain]; - [identity addUsername:normalizedLabel inDomain:domain status:DSBlockchainIdentityUsernameStatus_Confirmed save:NO registerOnNetwork:NO]; - [identity fetchIdentityNetworkStateInformationWithCompletion:^(BOOL success, BOOL found, NSError *error){ - - }]; - [rBlockchainIdentities addObject:identity]; - } - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(YES, [rBlockchainIdentities copy], @[]); - }); + DSIdentity *identity = [[DSIdentity alloc] initWithUniqueId:userIdData.UInt256 isTransient:TRUE onChain:self.chain]; + [identity addUsername:normalizedLabel inDomain:domain status:DSIdentityUsernameStatus_Confirmed save:NO registerOnNetwork:NO]; + [identity fetchIdentityNetworkStateInformationWithCompletion:^(BOOL success, BOOL found, NSError *error) {}]; + [rIdentities addObject:identity]; } + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(YES, [rIdentities copy], @[]); }); } - failure:^(NSError *_Nonnull error) { - if (completion) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(NO, nil, @[error]); - }); - } + failure:^(NSError *_Nonnull error) { + if (completion) dispatch_async(dispatch_get_main_queue(), ^{ completion(NO, nil, @[error]); }); #if DEBUG - DSLogPrivate(@"Failure in searchIdentitiesByDPNSRegisteredBlockchainIdentityUniqueID %@", error); + DSLogPrivate(@"Failure in searchIdentitiesByDPNSRegisteredIdentityUniqueID %@", error); #else - DSLog(@"Failure in searchIdentitiesByDPNSRegisteredBlockchainIdentityUniqueID %@", @""); + DSLog(@"Failure in searchIdentitiesByDPNSRegisteredIdentityUniqueID %@", @""); #endif }]; } -- (void)checkCreditFundingTransactionForPossibleNewIdentity:(DSCreditFundingTransaction *)creditFundingTransaction { +- (void)checkAssetLockTransactionForPossibleNewIdentity:(DSAssetLockTransaction *)transaction { uint32_t index; - DSWallet *wallet = [self.chain walletHavingBlockchainIdentityCreditFundingRegistrationHash:creditFundingTransaction.creditBurnPublicKeyHash foundAtIndex:&index]; - + DSWallet *wallet = [self.chain walletHavingIdentityAssetLockRegistrationHash:transaction.creditBurnPublicKeyHash foundAtIndex:&index]; if (!wallet) return; //it's a topup or we are funding an external identity - - DSBlockchainIdentity *blockchainIdentity = [wallet blockchainIdentityForUniqueId:creditFundingTransaction.creditBurnIdentityIdentifier]; - - NSAssert(blockchainIdentity, @"We should have already created the blockchain identity at this point in the transaction manager by calling triggerUpdatesForLocalReferences"); - - - //DSLogPrivate(@"Paused Sync at block %d to gather identity information on %@",block.height,blockchainIdentity.uniqueIdString); - [self fetchNeededNetworkStateInformationForBlockchainIdentity:blockchainIdentity - withCompletion:^(BOOL success, DSBlockchainIdentity *_Nullable blockchainIdentity, NSError *_Nullable error) { - if (success && blockchainIdentity != nil) { - [self chain:self.chain didFinishInChainSyncPhaseFetchingBlockchainIdentityDAPInformation:blockchainIdentity]; - } + DSIdentity *identity = [wallet identityForUniqueId:transaction.creditBurnIdentityIdentifier]; + NSAssert(identity, @"We should have already created the blockchain identity at this point in the transaction manager by calling triggerUpdatesForLocalReferences"); + //DSLogPrivate(@"Paused Sync at block %d to gather identity information on %@", block.height, identity.uniqueIdString); + [self fetchNeededNetworkStateInformationForIdentity:identity + withCompletion:^(BOOL success, DSIdentity *_Nullable identity, NSError *_Nullable error) { + if (success && identity != nil) + [self chain:self.chain didFinishInChainSyncPhaseFetchingIdentityDAPInformation:identity]; } completionQueue:self.chain.networkingQueue]; } -- (void)fetchNeededNetworkStateInformationForBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity withCompletion:(IdentityCompletionBlock)completion completionQueue:(dispatch_queue_t)completionQueue { - [blockchainIdentity fetchNeededNetworkStateInformationWithCompletion:^(DSBlockchainIdentityQueryStep failureStep, NSArray *_Nullable errors) { - if (!failureStep || failureStep == DSBlockchainIdentityQueryStep_NoIdentity) { +- (void)fetchNeededNetworkStateInformationForIdentity:(DSIdentity *)identity + withCompletion:(IdentityCompletionBlock)completion + completionQueue:(dispatch_queue_t)completionQueue { + [identity fetchNeededNetworkStateInformationWithCompletion:^(DSIdentityQueryStep failureStep, NSArray *_Nullable errors) { + if (!failureStep || failureStep == DSIdentityQueryStep_NoIdentity) { //if this was never registered no need to retry - if (completion) { - dispatch_async(completionQueue, ^{ - completion(YES, blockchainIdentity, nil); - }); - } + if (completion) dispatch_async(completionQueue, ^{ completion(YES, identity, nil); }); } else { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), completionQueue, ^{ - [self fetchNeededNetworkStateInformationForBlockchainIdentity:blockchainIdentity withCompletion:completion completionQueue:completionQueue]; + [self fetchNeededNetworkStateInformationForIdentity:identity withCompletion:completion completionQueue:completionQueue]; }); } }]; } -- (void)fetchNeededNetworkStateInformationForBlockchainIdentities:(NSArray *)blockchainIdentities withCompletion:(IdentitiesCompletionBlock)completion completionQueue:(dispatch_queue_t)completionQueue { +- (void)fetchNeededNetworkStateInformationForIdentities:(NSArray *)identities + withCompletion:(IdentitiesCompletionBlock)completion + completionQueue:(dispatch_queue_t)completionQueue { dispatch_group_t dispatchGroup = dispatch_group_create(); __block NSMutableArray *errors = [NSMutableArray array]; - for (DSBlockchainIdentity *blockchainIdentity in blockchainIdentities) { + for (DSIdentity *identity in identities) { dispatch_group_enter(dispatchGroup); - [self fetchNeededNetworkStateInformationForBlockchainIdentity:blockchainIdentity - withCompletion:^(BOOL success, DSBlockchainIdentity *_Nullable blockchainIdentity, NSError *_Nullable error) { - if (success && blockchainIdentity != nil) { + [self fetchNeededNetworkStateInformationForIdentity:identity + withCompletion:^(BOOL success, DSIdentity *_Nullable identity, NSError *_Nullable error) { + if (success && identity != nil) { dispatch_group_leave(dispatchGroup); } else { [errors addObject:error]; @@ -523,108 +451,119 @@ - (void)fetchNeededNetworkStateInformationForBlockchainIdentities:(NSArray *)identitiesFromIdentityDictionaries:(NSArray *)identityDictionaries keyIndexes:(NSDictionary *)keyIndexes forWallet:(DSWallet *)wallet { - NSMutableArray *identities = [NSMutableArray array]; - for (NSDictionary *versionedIdentityDictionary in identityDictionaries) { - NSNumber *version = [versionedIdentityDictionary objectForKey:@(DSPlatformStoredMessage_Version)]; - NSDictionary *identityDictionary = [versionedIdentityDictionary objectForKey:@(DSPlatformStoredMessage_Item)]; - OpaqueKey *key = [DSBlockchainIdentity firstKeyInIdentityDictionary:identityDictionary]; - NSNumber *index = [keyIndexes objectForKey:[DSKeyManager publicKeyData:key]]; - if (index) { - DSBlockchainIdentity *blockchainIdentity = [[DSBlockchainIdentity alloc] initAtIndex:index.intValue withIdentityDictionary:identityDictionary version:[version intValue] inWallet:wallet]; - [identities addObject:blockchainIdentity]; - } - } - return identities; -} +//- (NSArray *)identitiesFromIdentityDictionaries:(NSArray *)identityDictionaries keyIndexes:(NSDictionary *)keyIndexes forWallet:(DSWallet *)wallet { +// NSMutableArray *identities = [NSMutableArray array]; +// for (NSDictionary *versionedIdentityDictionary in identityDictionaries) { +// NSNumber *version = [versionedIdentityDictionary objectForKey:@(DSPlatformStoredMessage_Version)]; +// NSDictionary *identityDictionary = [versionedIdentityDictionary objectForKey:@(DSPlatformStoredMessage_Item)]; +// DOpaqueKey *key = [DSIdentity firstKeyInIdentityDictionary:identityDictionary]; +// NSNumber *index = [keyIndexes objectForKey:[DSKeyManager publicKeyData:key]]; +// if (index) { +// DSIdentity *identity = [[DSIdentity alloc] initAtIndex:index.intValue withIdentityDictionary:identityDictionary version:[version intValue] inWallet:wallet]; +// [identities addObject:identity]; +// } +// } +// return identities; +//} #define RETRIEVE_IDENTITIES_DELAY_INCREMENT 2 -- (void)retrieveIdentitiesByKeysUntilSuccessWithCompletion:(IdentitiesSuccessCompletionBlock)completion completionQueue:(dispatch_queue_t)completionQueue { +- (void)retrieveIdentitiesByKeysUntilSuccessWithCompletion:(IdentitiesSuccessCompletionBlock)completion + completionQueue:(dispatch_queue_t)completionQueue { [self internalRetrieveIdentitiesByKeysUntilSuccessWithDelay:0 withCompletion:completion completionQueue:completionQueue]; } -- (void)internalRetrieveIdentitiesByKeysUntilSuccessWithDelay:(uint32_t)delay withCompletion:(IdentitiesSuccessCompletionBlock)completion completionQueue:(dispatch_queue_t)completionQueue { +- (void)internalRetrieveIdentitiesByKeysUntilSuccessWithDelay:(uint32_t)delay + withCompletion:(IdentitiesSuccessCompletionBlock)completion + completionQueue:(dispatch_queue_t)completionQueue { [self - retrieveIdentitiesByKeysWithCompletion:^(BOOL success, NSArray *_Nullable blockchainIdentities, NSArray *_Nonnull errors) { + retrieveIdentitiesByKeysWithCompletion:^(BOOL success, NSArray *_Nullable identities, NSArray *_Nonnull errors) { if (!success) { dispatch_after(delay, self.identityQueue, ^{ - [self internalRetrieveIdentitiesByKeysUntilSuccessWithDelay:delay + RETRIEVE_IDENTITIES_DELAY_INCREMENT withCompletion:completion completionQueue:completionQueue]; + [self internalRetrieveIdentitiesByKeysUntilSuccessWithDelay:delay + RETRIEVE_IDENTITIES_DELAY_INCREMENT + withCompletion:completion + completionQueue:completionQueue]; }); } else if (completion) { - completion(blockchainIdentities); + completion(identities); } } completionQueue:completionQueue]; } -- (void)retrieveIdentitiesByKeysWithCompletion:(IdentitiesCompletionBlock)completion completionQueue:(dispatch_queue_t)completionQueue { +- (void)retrieveIdentitiesByKeysWithCompletion:(IdentitiesCompletionBlock)completion + completionQueue:(dispatch_queue_t)completionQueue { if (!self.chain.isEvolutionEnabled) { - if (completion) { - dispatch_async(completionQueue, ^{ - completion(YES, @[], @[]); - }); - } + if (completion) dispatch_async(completionQueue, ^{ completion(YES, @[], @[]); }); return; } dispatch_async(self.identityQueue, ^{ NSArray *wallets = self.chain.wallets; - DSDAPIClient *client = self.chain.chainManager.DAPIClient; + __block dispatch_group_t dispatch_group = dispatch_group_create(); __block NSMutableArray *errors = [NSMutableArray array]; __block NSMutableArray *allIdentities = [NSMutableArray array]; for (DSWallet *wallet in wallets) { - NSMutableArray *keyHashes = [NSMutableArray array]; - uint32_t unusedIndex = [wallet unusedBlockchainIdentityIndex]; - DSAuthenticationKeysDerivationPath *derivationPath = [DSBlockchainIdentity derivationPathForType:KeyKind_ECDSA forWallet:wallet]; + uint32_t unusedIndex = [wallet unusedIdentityIndex]; + DSAuthenticationKeysDerivationPath *derivationPath = [DSIdentity derivationPathForType:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() forWallet:wallet]; const int keysToCheck = 5; - NSMutableDictionary *keyIndexes = [NSMutableDictionary dictionary]; + NSMutableDictionary *keyIndexes = [NSMutableDictionary dictionaryWithCapacity:keysToCheck]; + u160 **key_hashes = malloc(keysToCheck * sizeof(u160 *)); for (int i = 0; i < keysToCheck; i++) { const NSUInteger indexes[] = {(unusedIndex + i) | BIP32_HARD, 0 | BIP32_HARD}; NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; - OpaqueKey *key = [derivationPath publicKeyAtIndexPath:indexPath]; - NSData *publicKeyData = [DSKeyManager publicKeyData:key]; - [keyHashes addObject:uint160_data(publicKeyData.hash160)]; + NSData *publicKeyData = [derivationPath publicKeyDataAtIndexPath:indexPath]; + key_hashes[i] = u160_ctor_u(publicKeyData.hash160); [keyIndexes setObject:@(unusedIndex + i) forKey:publicKeyData]; } dispatch_group_enter(dispatch_group); - [client.DAPIPlatformNetworkService fetchIdentitiesByKeyHashes:keyHashes - completionQueue:self.identityQueue - success:^(NSArray *_Nonnull identityDictionaries) { - NSArray *identities = [self identitiesFromIdentityDictionaries:identityDictionaries keyIndexes:keyIndexes forWallet:wallet]; - BOOL success = [wallet registerBlockchainIdentities:identities verify:YES]; - if (success) { - [allIdentities addObjectsFromArray:identities]; - NSManagedObjectContext *platformContext = [NSManagedObjectContext platformContext]; - [platformContext performBlockAndWait:^{ - for (DSBlockchainIdentity *identity in identities) { - [identity saveInitialInContext:platformContext]; - } - dispatch_group_leave(dispatch_group); - }]; - } else { - [errors addObject:[NSError errorWithDomain:@"DashPlatform" - code:500 - userInfo:@{NSLocalizedDescriptionKey: - DSLocalizedString(@"Identity has unknown keys", nil)}]]; - dispatch_group_leave(dispatch_group); - } + NSString *walletID = wallet.uniqueIDString; + 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_get_identities_for_key_hashes(self.chain.shareCore.runtime, self.chain.shareCore.identitiesManager->obj, (char *) [walletID UTF8String], Vec_u8_20_ctor(keysToCheck, key_hashes)); + + if (!result) { + DSLog(@"get_identities_for_key_hashes: NULL result "); + [errors addObject:ERROR_UNKNOWN_KEYS]; + dispatch_group_leave(dispatch_group); + return; } - failure:^(NSError *_Nonnull error) { - if (error) { - [errors addObject:error]; - } + if (!result->ok) { + DSLog(@"get_identities_for_key_hashes: Error "); + [errors addObject:ERROR_UNKNOWN_KEYS]; + Result_ok_std_collections_Map_keys_u8_arr_20_values_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error_destroy(result); dispatch_group_leave(dispatch_group); - }]; + return; + } + std_collections_Map_keys_u8_arr_20_values_dpp_identity_identity_Identity *ok = result->ok; + NSMutableArray *identities = [NSMutableArray array]; + + for (int j = 0; j < ok->count; j++) { + dpp_identity_v0_IdentityV0 *identity_v0 = ok->values[j]->v0; + DMaybeOpaqueKey *maybe_opaque_key = dash_spv_platform_identity_manager_opaque_key_from_identity_public_key(identity_v0->public_keys->values[0]); + NSData *publicKeyData = [DSKeyManager publicKeyData:maybe_opaque_key->ok]; + NSNumber *index = [keyIndexes objectForKey:publicKeyData]; + DSIdentity *identity = [[DSIdentity alloc] initAtIndex:index.intValue uniqueId:u256_cast(identity_v0->id->_0->_0) balance:identity_v0->balance public_keys:identity_v0->public_keys inWallet:wallet]; + [identities addObject:identity]; + } + BOOL success = [wallet registerIdentities:identities verify:YES]; + DSLog(@"get_identities_for_key_hashes: Success: %u ", success); + if (success) { + [allIdentities addObjectsFromArray:identities]; + NSManagedObjectContext *platformContext = [NSManagedObjectContext platformContext]; + [platformContext performBlockAndWait:^{ + for (DSIdentity *identity in identities) { + [identity saveInitialInContext:platformContext]; + } + }]; + } else { + [errors addObject:ERROR_UNKNOWN_KEYS]; + } + Result_ok_std_collections_Map_keys_u8_arr_20_values_dpp_identity_identity_Identity_err_dash_spv_platform_error_Error_destroy(result); } - dispatch_group_notify(dispatch_group, completionQueue, ^{ completion(!errors.count, allIdentities, errors); }); @@ -633,8 +572,8 @@ - (void)retrieveIdentitiesByKeysWithCompletion:(IdentitiesCompletionBlock)comple // MARK: - DSChainIdentitiesDelegate -- (void)chain:(DSChain *)chain didFinishInChainSyncPhaseFetchingBlockchainIdentityDAPInformation:(DSBlockchainIdentity *)blockchainIdentity { - [self.chain.chainManager chain:chain didFinishInChainSyncPhaseFetchingBlockchainIdentityDAPInformation:blockchainIdentity]; +- (void)chain:(DSChain *)chain didFinishInChainSyncPhaseFetchingIdentityDAPInformation:(DSIdentity *)identity { + [self.chain.chainManager chain:chain didFinishInChainSyncPhaseFetchingIdentityDAPInformation:identity]; } @end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.h index fed7b41db..fd082114a 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.h @@ -18,7 +18,200 @@ #import #import "dash_shared_core.h" #import "DSChain.h" -#import "DSDerivationPath.h" +#import "NSIndexPath+FFI.h" + +#define u128 Arr_u8_16 +#define u160 Arr_u8_20 +#define u256 Arr_u8_32 +#define u264 Arr_u8_33 +#define u384 Arr_u8_48 +#define u512 Arr_u8_64 +#define u768 Arr_u8_96 + +#define SLICE Slice_u8 +#define BYTES Vec_u8 +#define BITSET dash_spv_crypto_llmq_bitset_Bitset + +#define u128_ctor(data) Arr_u8_16_ctor(data.length, (uint8_t *) data.bytes) +#define u160_ctor(data) Arr_u8_20_ctor(data.length, (uint8_t *) data.bytes) +#define u256_ctor(data) Arr_u8_32_ctor(data.length, (uint8_t *) data.bytes) +#define u264_ctor(data) Arr_u8_33_ctor(data.length, (uint8_t *) data.bytes) +#define u384_ctor(data) Arr_u8_48_ctor(data.length, (uint8_t *) data.bytes) +#define u512_ctor(data) Arr_u8_64_ctor(data.length, (uint8_t *) data.bytes) +#define u768_ctor(data) Arr_u8_96_ctor(data.length, (uint8_t *) data.bytes) + +#define u128_cast(u) *((UInt128 *)u->values) +#define u160_cast(u) *((UInt160 *)u->values) +#define u256_cast(u) *((UInt256 *)u->values) +#define u384_cast(u) *((UInt384 *)u->values) +#define u512_cast(u) *((UInt512 *)u->values) +#define u768_cast(u) *((UInt768 *)u->values) + + +#define u128_ctor_u(u) (^{ \ + uint8_t *hash = malloc(16 * sizeof(uint8_t)); \ + memcpy(hash, u.u8, 16); \ + return Arr_u8_16_ctor(16, hash); \ +}()) + +#define u160_ctor_u(u) (^{ \ + uint8_t *ffi = malloc(20 * sizeof(uint8_t)); \ + memcpy(ffi, u.u8, 20); \ + return Arr_u8_20_ctor(20, ffi); \ +}()) +#define u256_ctor_u(u) (^{ \ + uint8_t *ffi_ref = malloc(32 * sizeof(uint8_t)); \ + memcpy(ffi_ref, u.u8, 32); \ + return Arr_u8_32_ctor(32, ffi_ref); \ +}()) + +#define u384_ctor_u(u) (^{ \ + uint8_t *ffi_ref = malloc(48 * sizeof(uint8_t)); \ + memcpy(ffi_ref, u.u8, 48); \ + return Arr_u8_48_ctor(48, ffi_ref); \ +}()) + +#define u512_ctor_u(u) (^{ \ + uint8_t *ffi_ref = malloc(64 * sizeof(uint8_t)); \ + memcpy(ffi_ref, u.u8, 64); \ + return Arr_u8_64_ctor(64, ffi_ref); \ +}()) + +#define u768_ctor_u(u) (^{ \ + uint8_t *ffi_ref = malloc(96 * sizeof(uint8_t)); \ + memcpy(ffi_ref, u.u8, 96); \ + return Arr_u8_96_ctor(96, ffi_ref); \ +}()) + + +#define u128_dtor(ptr) Arr_u8_16_destroy(ptr) +#define u160_dtor(ptr) Arr_u8_20_destroy(ptr) +#define u256_dtor(ptr) Arr_u8_32_destroy(ptr) +#define u264_dtor(ptr) Arr_u8_33_destroy(ptr) +#define u384_dtor(ptr) Arr_u8_48_destroy(ptr) +#define u512_dtor(ptr) Arr_u8_64_destroy(ptr) +#define u768_dtor(ptr) Arr_u8_96_destroy(ptr) + +#define u_is_zero(ptr) ({ \ + BOOL result = YES; \ + for (uintptr_t i = 0; i < ptr->count; i++) { \ + if (ptr->values[i] != 0) { \ + result = NO; \ + break; \ + } \ + } \ + result; \ +}) + +#define slice_ctor(data) Slice_u8_ctor(data.length, (uint8_t *) data.bytes) +#define slice_u128_ctor_u(u) Slice_u8_ctor(16, u.u8) +#define slice_u160_ctor_u(u) Slice_u8_ctor(20, u.u8) +#define slice_u256_ctor_u(u) Slice_u8_ctor(32, u.u8) +#define slice_u384_ctor_u(u) Slice_u8_ctor(48, u.u8) +#define slice_u512_ctor_u(u) Slice_u8_ctor(64, u.u8) +#define slice_u768_ctor_u(u) Slice_u8_ctor(96, u.u8) + +#define slice_dtor(ptr) Slice_u8_destroy(ptr) + +#define bytes_ctor(data) Vec_u8_ctor(data.length, (uint8_t *)data.bytes) +#define bytes_dtor(ptr) Vec_u8_destroy(ptr) + +#define bitset_ctor(data, count) dash_spv_crypto_llmq_bitset_Bitset_ctor(data.length, (uint8_t *)data.bytes) +#define bitset_dtor(ptr) dash_spv_crypto_llmq_bitset_Bitset_destroy(ptr) + + +#define MaybePubKey Result_ok_u8_arr_48_err_drive_proof_verifier_error_ContextProviderError +#define MaybePubKeyDtor Result_ok_u8_arr_48_err_drive_proof_verifier_error_ContextProviderError_destroy +#define MaybeDataContract Result_ok_Option_std_sync_Arc_dpp_data_contract_DataContract_err_drive_proof_verifier_error_ContextProviderError +#define MaybeDataContractDtor Result_ok_Option_std_sync_Arc_dpp_data_contract_DataContract_err_drive_proof_verifier_error_ContextProviderError_destroy +#define MaybeSignedData Result_ok_platform_value_types_binary_data_BinaryData_err_dpp_errors_protocol_error_ProtocolError +#define MaybeSignedDataDtor Result_ok_platform_value_types_binary_data_BinaryData_err_dpp_errors_protocol_error_ProtocolError_destroy +#define MaybePlatformActivationHeight Result_ok_dpp_prelude_CoreBlockHeight_err_drive_proof_verifier_error_ContextProviderError + +#define DCoreProviderError dash_spv_masternode_processor_processing_core_provider_CoreProviderError +#define DCoreProviderErrorNullResultCtor() dash_spv_masternode_processor_processing_core_provider_CoreProviderError_NullResult_ctor() +#define MaybeBool Result_ok_bool_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError +#define MaybeLLMQSnapshot Result_ok_dash_spv_masternode_processor_models_snapshot_LLMQSnapshot_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError + +#define DBlock dash_spv_masternode_processor_common_block_Block +#define DBlockCtor(height, hash) dash_spv_masternode_processor_common_block_Block_ctor(height, hash) + +#define DMBlock dash_spv_masternode_processor_common_block_MBlock +#define DMBlockCtor(height, hash, merkle_root) dash_spv_masternode_processor_common_block_MBlock_ctor(height, hash, merkle_root) + +#define DMaybeBlock Result_ok_dash_spv_masternode_processor_common_block_Block_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError +#define DMaybeBlockCtor(ok, err) Result_ok_dash_spv_masternode_processor_common_block_Block_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError_ctor(ok, err) +#define DMaybeBlockDtor(ptr) Result_ok_dash_spv_masternode_processor_common_block_Block_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError_destroy(ptr) + +#define DMaybeMBlock Result_ok_dash_spv_masternode_processor_common_block_MBlock_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError +#define DMaybeMBlockCtor(ok, err) Result_ok_dash_spv_masternode_processor_common_block_MBlock_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError_ctor(ok, err) + +#define DMasternodeList dash_spv_masternode_processor_models_masternode_list_MasternodeList +#define DArcMasternodeList std_sync_Arc_dash_spv_masternode_processor_models_masternode_list_MasternodeList +#define DArcMasternodeListDtor(ptr) std_sync_Arc_dash_spv_masternode_processor_models_masternode_list_MasternodeList_destroy(ptr) +#define DMaybeArcMasternodeList Result_ok_std_sync_Arc_dash_spv_masternode_processor_models_masternode_list_MasternodeList_err_dash_spv_masternode_processor_processing_core_provider_CoreProviderError +#define DMasternodeEntry dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry +#define DMasternodeEntryList Vec_dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry +#define DMasternodeEntryListCtor(count, list) Vec_dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_ctor(count, list) +#define DMasternodeEntryListDtor(ptr) Vec_dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_destroy(ptr) +#define DMasternodeEntryMap std_collections_Map_keys_u8_arr_32_values_dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry +#define DLLMQMap std_collections_Map_keys_dash_spv_crypto_network_llmq_type_LLMQType_values_std_collections_Map_keys_u8_arr_32_values_dash_spv_crypto_llmq_entry_LLMQEntry +#define DLLMQEntry dash_spv_crypto_llmq_entry_LLMQEntry +#define DLLMQEntrySignID(ptr, req_id, hash) dash_spv_crypto_llmq_entry_LLMQEntry_sign_id(ptr, req_id, hash) +#define DLLMQEntryVerifySignature(ptr, sign_id, sig) dash_spv_crypto_llmq_entry_LLMQEntry_verify_signature(ptr, sign_id, sig) +#define DLLMQEntryHashHex(ptr) dash_spv_crypto_llmq_entry_LLMQEntry_llmq_hash_hex(ptr) + +#define DLLMQEntryList Vec_dash_spv_crypto_llmq_entry_LLMQEntry +#define DLLMQEntryListCtor(count, list) Vec_dash_spv_crypto_llmq_entry_LLMQEntry_ctor(count, list) + +#define DLLMQType dash_spv_crypto_network_llmq_type_LLMQType +#define DLLMQSnapshot dash_spv_masternode_processor_models_snapshot_LLMQSnapshot +#define DKeyKind dash_spv_crypto_keys_key_KeyKind +#define DOpaqueKey dash_spv_crypto_keys_key_OpaqueKey +#define DMaybeOpaqueKey Result_ok_dash_spv_crypto_keys_key_OpaqueKey_err_dash_spv_crypto_keys_KeyError +#define DMaybeOpaqueKeys Result_ok_Vec_dash_spv_crypto_keys_key_OpaqueKey_err_dash_spv_crypto_keys_KeyError +#define DMaybeKeyData Result_ok_Vec_u8_err_dash_spv_crypto_keys_KeyError +#define DMaybeKeyString Result_ok_String_err_dash_spv_crypto_keys_KeyError +#define DChainType dash_spv_crypto_network_chain_type_ChainType +#define DDevnetType dash_spv_crypto_network_chain_type_DevnetType +#define DIndexPathU256 dash_spv_crypto_keys_key_IndexPathU256 +#define DMaybeOpaqueKeyDtor(ptr) Result_ok_dash_spv_crypto_keys_key_OpaqueKey_err_dash_spv_crypto_keys_KeyError_destroy(ptr) +#define DMaybeKeyDataDtor(ptr) Result_ok_Vec_u8_err_dash_spv_crypto_keys_KeyError_destroy(ptr) +#define DMaybeKeyStringDtor(ptr) Result_ok_String_err_dash_spv_crypto_keys_KeyError_destroy(ptr) + +#define DIdentityPublicKey dpp_identity_identity_public_key_IdentityPublicKey +#define DIdentifier platform_value_types_identifier_Identifier + +#define DQRInfoResult Result_Tuple_Arr_u8_32_Arr_u8_32_err_dash_spv_masternode_processor_processing_processing_error_ProcessingError +#define DQRInfoResultDtor(ptr) Result_Tuple_Arr_u8_32_Arr_u8_32_err_dash_spv_masternode_processor_processing_processing_error_ProcessingError_destroy(ptr) + +#define DMnDiffResult Result_Tuple_Arr_u8_32_Arr_u8_32_bool_err_dash_spv_masternode_processor_processing_processing_error_ProcessingError +#define DMnDiffResultDtor(ptr) Result_Tuple_Arr_u8_32_Arr_u8_32_bool_err_dash_spv_masternode_processor_processing_processing_error_ProcessingError_destroy(ptr) + +#define DProcessor MasternodeProcessor +#define DCache MasternodeProcessorCache +#define DMNListDiffResult dash_spv_masternode_processor_processing_mn_listdiff_result_MNListDiffResult + +#define NSDataFromPtr(ptr) ptr ? [NSData dataWithBytes:(const void *)ptr->values length:ptr->count] : nil + +#define DStoredMasternodeListsCount(proc) dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_stored_masternode_lists_count(proc) +#define DKnownMasternodeListsCount(cache) dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_known_masternode_lists_count(cache) +#define DLastMasternodeListBlockHeight(proc) dash_spv_masternode_processor_processing_processor_MasternodeProcessor_last_masternode_list_block_height(proc) +#define DHeightForBlockHash(proc, hash) dash_spv_masternode_processor_processing_processor_MasternodeProcessor_height_for_block_hash(proc, hash) + +#define DMasternodeEntryVotingAddress(entry, chain_type) dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_voting_address(entry, chain_type) +#define DMasternodeEntryOperatorPublicKeyAddress(entry, chain_type) dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_operator_public_key_address(entry, chain_type) +#define DMasternodeEntryEvoNodeAddress(entry, chain_type) dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_evo_node_address(entry, chain_type) +#define DAddMasternodeList(cache, hash, list) dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_add_masternode_list(cache, hash, list) +#define DRemoveMasternodeList(cache, hash) dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_remove_masternode_list(cache, hash) +#define DRemoveMasternodeListsBefore(cache, height) dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_remove_masternode_lists_before_height(cache, height) +#define DMasternodeListLoaded(cache, hash, list) dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_masternode_list_loaded(cache, hash, list) +#define DCacheBlockHeight(cache, hash, height) dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_cache_block_height_for_hash(cache, hash, height) +#define DAddMasternodeListStub(cache, hash) dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_add_stub_for_masternode_list(cache, hash) +#define DProcessingErrorIndex(ptr) dash_spv_masternode_processor_processing_processing_error_ProcessingError_index(ptr) + + +#define DDocumentTypes std_collections_Map_keys_dpp_data_contract_DocumentName_values_dpp_data_contract_document_type_DocumentType NS_ASSUME_NONNULL_BEGIN @@ -28,78 +221,110 @@ NS_ASSUME_NONNULL_BEGIN @interface DSKeyManager : NSObject - (instancetype)initWithChain:(DSChain *)chain; +//+ (DKeyKind *)keyKindFromIndex:(uint16_t)index; + ++ (BOOL)hasPrivateKey:(DOpaqueKey *)key; ++ (NSString *)secretKeyHexString:(DOpaqueKey *)key; ++ (DMaybeOpaqueKey *_Nullable)keyWithPrivateKeyString:(NSString *)key + ofKeyType:(DKeyKind *)keyType + forChainType:(DChainType *)chainType; ++ (DMaybeOpaqueKey *_Nullable)keyWithPrivateKeyData:(NSData *)data ofType:(DKeyKind *)keyType; ++ (DMaybeOpaqueKey *_Nullable)keyWithPublicKeyData:(NSData *)data ofType:(DKeyKind *)keyType; ++ (DMaybeOpaqueKey *_Nullable)keyWithExtendedPublicKeyData:(NSData *)data ofType:(DKeyKind *)keyType; ++ (BOOL)keysPublicKeyDataIsEqual:(DOpaqueKey *)key1 key2:(DOpaqueKey *)key2; ++ (NSData *)signMesasageDigest:(DOpaqueKey *)key digest:(UInt256)digest; ++ (BOOL)verifyMessageDigest:(DOpaqueKey *)key digest:(UInt256)digest signature:(NSData *)signature; + +//+ (DMaybeOpaqueKey *_Nullable)privateKeyAtIndexPath:(DKeyKind *)keyType +// path:(DIndexPathU256 *)path +// index_path:(Vec_u32 *)index_path +// seed:(NSData *)seed; +//+ (DMaybeOpaqueKey *_Nullable)privateKeyAtIndexPath:(DKeyKind *)keyType +// indexes:(UInt256 *)indexes +// hardened:(BOOL *)hardened +// length:(NSUInteger)length +// indexPath:(NSIndexPath *)indexPath +// fromSeed:(NSData *)seed; ++ (DMaybeOpaqueKey *_Nullable)publicKeyAtIndexPath:(DOpaqueKey *)key indexPath:(NSIndexPath *)indexPath; ++ (NSData *_Nullable)publicKeyDataAtIndexPath:(DOpaqueKey *)key indexPath:(NSIndexPath *)indexPath; + ++ (NSData *)privateKeyData:(DOpaqueKey *)key; ++ (NSData *)publicKeyData:(DOpaqueKey *)key; ++ (NSData *)extendedPrivateKeyData:(DOpaqueKey *)key; ++ (NSData *)extendedPublicKeyData:(DOpaqueKey *)key; -+ (BOOL)hasPrivateKey:(OpaqueKey *)key; -+ (NSString *)secretKeyHexString:(OpaqueKey *)key; -+ (OpaqueKey *_Nullable)keyWithPrivateKeyString:(NSString *)key ofKeyType:(KeyKind)keyType forChainType:(ChainType)chainType; -+ (OpaqueKey *_Nullable)keyWithPrivateKeyData:(NSData *)data ofType:(KeyKind)keyType; -+ (OpaqueKey *_Nullable)keyWithPublicKeyData:(NSData *)data ofType:(KeyKind)keyType; -+ (OpaqueKey *_Nullable)keyWithExtendedPublicKeyData:(NSData *)data ofType:(KeyKind)keyType; -+ (BOOL)keysPublicKeyDataIsEqual:(OpaqueKey *)key1 key2:(OpaqueKey *)key2; -+ (NSData *)signMesasageDigest:(OpaqueKey *)key digest:(UInt256)digest; -+ (BOOL)verifyMessageDigest:(OpaqueKey *)key digest:(UInt256)digest signature:(NSData *)signature; - -+ (OpaqueKey *_Nullable)privateKeyAtIndexPath:(KeyKind)keyType indexes:(UInt256 *)indexes hardened:(BOOL *)hardened length:(NSUInteger)length indexPath:(NSIndexPath *)indexPath fromSeed:(NSData *)seed; -+ (OpaqueKey *_Nullable)publicKeyAtIndexPath:(OpaqueKey *)key indexPath:(NSIndexPath *)indexPath; -+ (NSData *_Nullable)publicKeyDataAtIndexPath:(OpaqueKey *)key indexPath:(NSIndexPath *)indexPath; - -+ (NSData *)privateKeyData:(OpaqueKey *)key; -+ (NSData *)publicKeyData:(OpaqueKey *)key; -+ (NSData *)extendedPrivateKeyData:(OpaqueKey *)key; -+ (NSData *)extendedPublicKeyData:(OpaqueKey *)key; - -+ (OpaqueKey *_Nullable)deriveKeyFromExtenedPrivateKeyDataAtIndexPath:(NSData *_Nullable)data indexPath:(NSIndexPath *)indexPath forKeyType:(KeyKind)keyType; -+ (OpaqueKey *_Nullable)keyPublicDeriveTo256Bit:(DSDerivationPath *)parentPath childIndexes:(UInt256 *)childIndexes childHardened:(BOOL *)childHardened length:(NSUInteger)length; - -+ (NSString *)serializedPrivateKey:(OpaqueKey *)key chainType:(ChainType)chainType; - -+ (NSString *)addressForKey:(OpaqueKey *)key forChainType:(ChainType)chainType; -+ (NSString *)addressWithPublicKeyData:(NSData *)data forChain:(nonnull DSChain *)chain; -+ (NSString *_Nullable)addressWithScriptPubKey:(NSData *)script forChain:(nonnull DSChain *)chain; -+ (NSString *_Nullable)addressWithScriptSig:(NSData *)script forChain:(nonnull DSChain *)chain; -+ (NSString *)addressFromHash160:(UInt160)hash forChain:(nonnull DSChain *)chain; -+ (BOOL)isValidDashAddress:(NSString *)address forChain:(nonnull DSChain *)chain; -+ (NSData *)scriptPubKeyForAddress:(NSString *)address forChain:(nonnull DSChain *)chain; - -+ (UInt160)ecdsaKeyPublicKeyHashFromSecret:(NSString *)secret forChainType:(ChainType)chainType; - -+ (NSString *_Nullable)ecdsaKeyAddressFromPublicKeyData:(NSData *)data forChainType:(ChainType)chainType; -- (NSString *)ecdsaKeyPublicKeyUniqueIDFromDerivedKeyData:(UInt256)secret forChainType:(ChainType)chainType; -- (NSString *)keyRecoveredFromCompactSig:(NSData *)signature andMessageDigest:(UInt256)md; -+ (NSData *_Nullable)compactSign:(DSDerivationPath *)derivationPath fromSeed:(NSData *)seed atIndexPath:(NSIndexPath *)indexPath digest:(UInt256)digest; -+ (ECDSAKey *)ecdsaKeyWithPrivateKey:(NSString *)key forChainType:(ChainType)chainType; -+ (NSString *)blsPublicKeySerialize:(OpaqueKey *)key legacy:(BOOL)legacy; -+ (NSString *_Nullable)ecdsaKeyWithBIP38Key:(NSString *)key passphrase:(NSString *)passphrase forChainType:(ChainType)chainType; ++ (DMaybeOpaqueKey *_Nullable)deriveKeyFromExtenedPrivateKeyDataAtIndexPath:(NSData *_Nullable)data + indexPath:(NSIndexPath *)indexPath + forKeyType:(DKeyKind *)keyType; +//+ (DMaybeOpaqueKey *_Nullable)keyPublicDeriveTo256Bit:(DSDerivationPath *)parentPath +// childIndexes:(UInt256 *)childIndexes +// childHardened:(BOOL *)childHardened +// length:(NSUInteger)length; + ++ (NSString *)serializedPrivateKey:(DOpaqueKey *)key + chainType:(DChainType *)chainType; + ++ (NSString *)addressForKey:(DOpaqueKey *)key + forChainType:(DChainType *)chainType; ++ (NSString *)addressWithPublicKeyData:(NSData *)data + forChain:(nonnull DSChain *)chain; ++ (NSString *_Nullable)addressWithScriptPubKey:(NSData *)script + forChain:(nonnull DSChain *)chain; ++ (NSString *_Nullable)addressWithScriptSig:(NSData *)script + forChain:(nonnull DSChain *)chain; ++ (NSString *)addressFromHash160:(UInt160)hash + forChain:(nonnull DSChain *)chain; ++ (BOOL)isValidDashAddress:(NSString *)address + forChain:(nonnull DSChain *)chain; ++ (NSData *)scriptPubKeyForAddress:(NSString *)address + forChain:(nonnull DSChain *)chain; + +//+ (UInt160)ecdsaKeyPublicKeyHashFromSecret:(NSString *)secret forChainType:(DChainType *)chainType; + ++ (NSString *_Nullable)ecdsaKeyAddressFromPublicKeyData:(NSData *)data + forChainType:(DChainType *)chainType; +- (NSString *)ecdsaKeyPublicKeyUniqueIDFromDerivedKeyData:(UInt256)secret + forChainType:(DChainType *)chainType; +- (NSString *)keyRecoveredFromCompactSig:(NSData *)signature + andMessageDigest:(UInt256)md; ++ (NSData *_Nullable)compactSign:(DSDerivationPath *)derivationPath + fromSeed:(NSData *)seed + atIndexPath:(NSIndexPath *)indexPath + digest:(UInt256)digest; +//+ (struct ECDSAKey *)ecdsaKeyWithPrivateKey:(NSString *)key forChainType:(DChainType *)chainType; ++ (NSString *)blsPublicKeySerialize:(DOpaqueKey *)key + legacy:(BOOL)legacy; ++ (NSString *_Nullable)ecdsaKeyWithBIP38Key:(NSString *)key + passphrase:(NSString *)passphrase + forChainType:(DChainType *)chainType; + (BOOL)isValidDashBIP38Key:(NSString *)key; -+ (OpaqueKey *_Nullable)keyDeprecatedExtendedPublicKeyFromSeed:(NSData *)seed indexes:(UInt256 *)indexes hardened:(BOOL *)hardened length:(NSUInteger)length; +//+ (DOpaqueKey *_Nullable)keyDeprecatedExtendedPublicKeyFromSeed:(NSData *)seed +// indexes:(UInt256 *)indexes +// hardened:(BOOL *)hardened +// length:(NSUInteger)length; + (NSString *)NSStringFrom:(char *)c_string; -+ (NSData *)NSDataFrom:(ByteArray)byte_array; -+ (NSString *)localizedKeyType:(OpaqueKey *)key; ++ (NSData *)NSDataFrom:(BYTES *)byte_array; ++ (NSData *)NSDataFromArr_u8_32:(u256 *)byte_array; ++ (NSString *)localizedKeyType:(DOpaqueKey *)key; + (UInt256)x11:(NSData *)data; + (UInt256)blake3:(NSData *)data; -+ (NSData *)encryptData:(NSData *)data secretKey:(OpaqueKey *)secretKey publicKey:(OpaqueKey *)publicKey; -+ (NSData *)encryptData:(NSData *)data secretKey:(OpaqueKey *)secretKey publicKey:(OpaqueKey *)publicKey usingIV:(NSData *)iv; -+ (NSData *)decryptData:(NSData *)data secretKey:(OpaqueKey *)secretKey publicKey:(OpaqueKey *)publicKey; -+ (NSData *)decryptData:(NSData *)data secretKey:(OpaqueKey *)secretKey publicKey:(OpaqueKey *)publicKey usingIVSize:(NSUInteger)ivSize; ++ (NSData *)encryptData:(NSData *)data secretKey:(DOpaqueKey *)secretKey publicKey:(DOpaqueKey *)publicKey; ++ (NSData *)encryptData:(NSData *)data secretKey:(DOpaqueKey *)secretKey publicKey:(DOpaqueKey *)publicKey usingIV:(NSData *)iv; ++ (NSData *)decryptData:(NSData *)data secretKey:(DOpaqueKey *)secretKey publicKey:(DOpaqueKey *)publicKey; ++ (NSData *)decryptData:(NSData *)data secretKey:(DOpaqueKey *)secretKey publicKey:(DOpaqueKey *)publicKey usingIVSize:(NSUInteger)ivSize; -+ (NSData *)encryptData:(NSData *)data withDHKey:(OpaqueKey *)dhKey; -+ (NSData *)decryptData:(NSData *)data withDHKey:(OpaqueKey *)dhKey; ++ (NSData *)encryptData:(NSData *)data withDHKey:(DOpaqueKey *)dhKey; ++ (NSData *)decryptData:(NSData *)data withDHKey:(DOpaqueKey *)dhKey; -+ (NSString *)keyStoragePrefix:(KeyKind)keyType; +//+ (NSString *)keyStoragePrefix:(DKeyKind *)keyType; /// Transactions + (BOOL)verifyProRegTXPayloadSignature:(NSData *)signature payload:(NSData *)payload ownerKeyHash:(UInt160)ownerKeyHash; -+ (NSData *)proRegTXPayloadCollateralDigest:(NSData *)payload - scriptPayout:(NSData *)scriptPayout - reward:(uint16_t)reward - ownerKeyHash:(UInt160)ownerKeyHash - voterKeyHash:(UInt160)voterKeyHash - chainType:(ChainType)chainType; - -+ (NSString *_Nullable)devnetIdentifierFor:(ChainType)chainType; + ++ (NSString *_Nullable)devnetIdentifierFor:(DChainType *)chainType; @end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.m index de71bac40..5f5bbb4f1 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.m @@ -15,18 +15,19 @@ // limitations under the License. // -#import "dash_shared_core.h" -#import "DSChain.h" +#import "DSChain+Params.h" #import "DSDerivationPath+Protected.h" #import "DSKeyManager.h" #import "NSIndexPath+FFI.h" +#import "NSString+Dash.h" + // Main purpose of this class is to organize work with rust bindings for keys and internal cache @interface DSKeyManager () @property (nonatomic, strong) DSChain *chain; -@property (nonatomic, assign, nullable) KeysCache *keysCache; +//@property (nonatomic, assign, nullable) KeysCache *keysCache; @end @@ -36,309 +37,552 @@ @implementation DSKeyManager - (instancetype)initWithChain:(DSChain *)chain { NSParameterAssert(chain); if (!(self = [super init])) return nil; - _keysCache = [DSKeyManager createKeysCache]; +// _keysCache = [DSKeyManager createKeysCache]; DSLog(@"[%@] DSKeyManager.initWithChain: %@: ", chain.name, chain); return self; } -- (void)dealloc { - [DSKeyManager destroyKeysCache:self.keysCache]; -} - -+ (KeysCache *)createKeysCache { - return keys_create_cache(); -} - -+ (void)destroyKeysCache:(KeysCache *)cache { - keys_destroy_cache(cache); -} +//- (void)dealloc { +// [DSKeyManager destroyKeysCache:self.keysCache]; +//} +// +//+ (KeysCache *)createKeysCache { +// return keys_create_cache(); +//} +// +//+ (void)destroyKeysCache:(KeysCache *)cache { +// keys_destroy_cache(cache); +//} -+ (BOOL)hasPrivateKey:(OpaqueKey *)key { - return key_has_private_key(key); ++ (BOOL)hasPrivateKey:(DOpaqueKey *)key { + return dash_spv_crypto_keys_key_OpaqueKey_has_private_key(key); } -+ (BOOL)keysPublicKeyDataIsEqual:(OpaqueKey *)key1 key2:(OpaqueKey *)key2 { ++ (BOOL)keysPublicKeyDataIsEqual:(DOpaqueKey *)key1 key2:(DOpaqueKey *)key2 { if (key1 == NULL || key2 == NULL) return false; - return keys_public_key_data_is_equal(key1, key2); -} - -+ (NSString *)secretKeyHexString:(OpaqueKey *)key { - return [DSKeyManager NSStringFrom:key_secret_key_string(key)]; -} - -+ (OpaqueKey *_Nullable)keyWithPrivateKeyData:(NSData *)data ofType:(KeyKind)keyType { - return key_create_with_private_key_data(data.bytes, data.length, (int16_t) keyType); -} - -+ (OpaqueKey *_Nullable)keyWithPublicKeyData:(NSData *)data ofType:(KeyKind)keyType { - return key_create_with_public_key_data(data.bytes, data.length, (int16_t) keyType); -} - -+ (OpaqueKey *_Nullable)keyWithExtendedPublicKeyData:(NSData *)data ofType:(KeyKind)keyType { - return key_create_from_extended_public_key_data(data.bytes, data.length, (int16_t) keyType); -} - -+ (NSData *)signMesasageDigest:(OpaqueKey *)key digest:(UInt256)digest { - return [DSKeyManager NSDataFrom:key_sign_message_digest(key, digest.u8)]; -} - -+ (BOOL)verifyMessageDigest:(OpaqueKey *)key digest:(UInt256)digest signature:(NSData *)signature { - return key_verify_message_digest(key, digest.u8, signature.bytes, signature.length); -} - -+ (OpaqueKey *_Nullable)privateKeyAtIndexPath:(KeyKind)keyType indexes:(UInt256 *)indexes hardened:(BOOL *)hardened length:(NSUInteger)length indexPath:(NSIndexPath *)indexPath fromSeed:(NSData *)seed { - NSParameterAssert(indexPath); - NSParameterAssert(seed); - if (!seed || !indexPath) return nil; - if (!length) return nil; //there needs to be at least 1 length - IndexPathData *index_path = [indexPath ffi_malloc]; - OpaqueKey *key = key_private_key_at_index_path(seed.bytes, seed.length, (int16_t) keyType, index_path, (const uint8_t *) indexes, hardened, length); - [NSIndexPath ffi_free:index_path]; - return key; -} - -+ (OpaqueKey *_Nullable)keyPublicDeriveTo256Bit:(DSDerivationPath *)parentPath childIndexes:(UInt256 *)childIndexes childHardened:(BOOL *)childHardened length:(NSUInteger)length { - OpaqueKey *key = key_public_derive_to_256bit(parentPath.extendedPublicKey, (const uint8_t *) childIndexes, childHardened, length, parentPath.length); - return key; -} - -+ (OpaqueKey *_Nullable)publicKeyAtIndexPath:(OpaqueKey *)key indexPath:(NSIndexPath *)indexPath { + BYTES *public_key_data2 = dash_spv_crypto_keys_key_OpaqueKey_public_key_data(key2); + BOOL is_equal = dash_spv_crypto_keys_key_OpaqueKey_public_key_data_equal_to(key1, public_key_data2); +// bytes_dtor(public_key_data2); + return is_equal; +} + ++ (NSString *)secretKeyHexString:(DOpaqueKey *)key { + return [DSKeyManager NSStringFrom:dash_spv_crypto_keys_key_OpaqueKey_secret_key_string(key)]; +} + ++ (DMaybeOpaqueKey *_Nullable)keyWithPrivateKeyData:(NSData *)data ofType:(DKeyKind *)keyType { + // TODO: memory + SLICE *slice = slice_ctor(data); + DMaybeOpaqueKey *result = dash_spv_crypto_keys_key_KeyKind_key_with_private_key_data(keyType, slice); +// slice_dtor(slice); + return result; +// return key_create_with_private_key_data(data.bytes, data.length, (int16_t) keyType); +} + ++ (DMaybeOpaqueKey *_Nullable)keyWithPublicKeyData:(NSData *)data ofType:(DKeyKind *)keyType { + SLICE *slice = slice_ctor(data); + DMaybeOpaqueKey *result = dash_spv_crypto_keys_key_KeyKind_key_with_public_key_data(keyType, slice); +// slice_dtor(slice); + return result; +// return key_create_with_public_key_data(data.bytes, data.length, (int16_t) keyType); +} + ++ (DMaybeOpaqueKey *_Nullable)keyWithExtendedPublicKeyData:(NSData *)data ofType:(DKeyKind *)keyType { + SLICE *slice = slice_ctor(data); + DMaybeOpaqueKey *result = dash_spv_crypto_keys_key_KeyKind_key_with_extended_private_key_data(keyType, slice); + return result; +// return key_create_from_extended_public_key_data(data.bytes, data.length, (int16_t) keyType); +} + ++ (NSData *)signMesasageDigest:(DOpaqueKey *)key digest:(UInt256)digest { + SLICE *digest_slice = slice_u256_ctor_u(digest); + NSData *signature = [DSKeyManager NSDataFrom:dash_spv_crypto_keys_key_OpaqueKey_sign(key, digest_slice)]; + return signature; +} + ++ (BOOL)verifyMessageDigest:(DOpaqueKey *)key digest:(UInt256)digest signature:(NSData *)signature { + SLICE *message_digest = slice_u256_ctor_u(digest); + SLICE *sig = slice_ctor(signature); + Result_ok_bool_err_dash_spv_crypto_keys_KeyError *result = dash_spv_crypto_keys_key_OpaqueKey_verify(key, message_digest, sig); + BOOL verified = result && result->ok && result->ok[0]; + Result_ok_bool_err_dash_spv_crypto_keys_KeyError_destroy(result); + return verified; +// return key_verify_message_digest(key, digest.u8, signature.bytes, signature.length); +} +//+ (DMaybeOpaqueKey *_Nullable)privateKeyAtIndexPath:(DKeyKind *)keyType +// path:(DIndexPathU256 *)path +// index_path:(Vec_u32 *)index_path +// seed:(NSData *)seed { +// SLICE *seed_slice = slice_ctor(seed); +// DMaybeOpaqueKey *result = dash_spv_crypto_keys_key_KeyKind_private_key_at_index_path_wrapped(keyType, seed_slice, index_path, path); +// return result; +//} + +//+ (DMaybeOpaqueKey *_Nullable)privateKeyAtIndexPath:(DKeyKind *)keyType +// indexes:(UInt256 *)indexes +// hardened:(BOOL *)hardened +// length:(NSUInteger)length +// indexPath:(NSIndexPath *)indexPath +// fromSeed:(NSData *)seed { +// NSParameterAssert(indexPath); +// NSParameterAssert(seed); +// if (!seed || !indexPath) return nil; +// if (!length) return nil; //there needs to be at least 1 length +// SLICE *seed_slice = slice_ctor(seed); +// Vec_u32 *index_path = [NSIndexPath ffi_to:indexPath]; +// u256 **ind = malloc(length * sizeof(u256 *)); +// bool *hard = malloc(length * sizeof(bool)); +// +// for (NSUInteger i = 0; i < length; i++) { +// ind[i] = u256_ctor_u(indexes[i]); +// hard[i] = hardened[i]; +// } +// Vec_u8_32 *i = Vec_u8_32_ctor(length, ind); +// Vec_bool *h = Vec_bool_ctor(length, hard); +// +// DIndexPathU256 *path = dash_spv_crypto_keys_key_IndexPathU256_ctor(i, h); +// DMaybeOpaqueKey *result = dash_spv_crypto_keys_key_KeyKind_private_key_at_index_path_wrapped(keyType, seed_slice, index_path, path); +// +// [NSIndexPath ffi_destroy:index_path]; +// slice_dtor(seed_slice); +// for (NSUInteger i = 0; i < length; i++) { +// u256_dtor(ind[i]); +// } +// free(ind); +// free(hard); +// Vec_u8_32_destroy(i); +// Vec_bool_destroy(h); +// dash_spv_crypto_keys_key_IndexPathU256_destroy(path); +// return result; +// +// +// +//// if (!result) return nil; +//// if (!result->ok) { +//// DMaybeOpaqueKeyDtor(result); +//// return nil; +//// } +//// return key; +//// IndexPathData *index_path = [indexPath ffi_malloc]; +//// DOpaqueKey *key = key_private_key_at_index_path(seed.bytes, seed.length, (int16_t) keyType, index_path, (const uint8_t *) indexes, hardened, length); +//// [NSIndexPath ffi_free:index_path]; +//// return key; +//} + +//+ (DMaybeOpaqueKey *_Nullable)keyPublicDeriveTo256Bit:(DSDerivationPath *)parentPath +// childIndexes:(UInt256 *)childIndexes +// childHardened:(BOOL *)childHardened +// length:(NSUInteger)length { +// +// u256 **indexes = malloc(length * sizeof(u256)); +// bool *hardened = malloc(length * sizeof(bool)); +// for (NSUInteger i = 0; i < length; i++) { +// indexes[i] = Arr_u8_32_ctor(32, childIndexes[i].u8); +// hardened[i] = childHardened[i]; +// } +// Vec_u8_32 *i = Vec_u8_32_ctor(length, indexes); +// Vec_bool *h = Vec_bool_ctor(length, hardened); +// DIndexPathU256 *path = dash_spv_crypto_keys_key_IndexPathU256_ctor(i, h); +// DMaybeOpaqueKey *result = dash_spv_crypto_keys_key_OpaqueKey_public_derive_to_256_path_with_offset(parentPath.extendedPublicKey->ok, path, parentPath.length); +//// DOpaqueKey *key = key_public_derive_to_256bit(parentPath.extendedPublicKey, (const uint8_t *) childIndexes, childHardened, length, parentPath.length); +// return result; +//} +// ++ (DMaybeOpaqueKey *_Nullable)publicKeyAtIndexPath:(DOpaqueKey *)key indexPath:(NSIndexPath *)indexPath { if (key == NULL) return nil; - IndexPathData *index_path = [indexPath ffi_malloc]; - OpaqueKey *key_at_index_path = key_public_key_at_index_path(key, index_path); - [NSIndexPath ffi_free:index_path]; - return key_at_index_path; + Vec_u32 *index_path = [NSIndexPath ffi_to:indexPath]; + DMaybeOpaqueKey *maybe_key = dash_spv_crypto_keys_key_OpaqueKey_public_key_from_extended_public_key_data_at_index_path(key, index_path); + return maybe_key; } -+ (NSData *_Nullable)publicKeyDataAtIndexPath:(OpaqueKey *)key indexPath:(NSIndexPath *)indexPath { ++ (NSData *_Nullable)publicKeyDataAtIndexPath:(DOpaqueKey *)key indexPath:(NSIndexPath *)indexPath { if (key == NULL) return nil; - IndexPathData *index_path = [indexPath ffi_malloc]; - ByteArray byte_array = key_public_key_data_at_index_path(key, index_path); - [NSIndexPath ffi_free:index_path]; - return [DSKeyManager NSDataFrom:byte_array]; + Vec_u32 *index_path = [NSIndexPath ffi_to:indexPath]; + DMaybeKeyData *maybe_data = dash_spv_crypto_keys_key_OpaqueKey_public_key_data_at_index_path(key, index_path); + NSData *data = NULL; + if (maybe_data) { + if (maybe_data->ok) + data = NSDataFromPtr(maybe_data->ok); + DMaybeKeyDataDtor(maybe_data); + } +// NSLog(@"[DSKeyManager] publicKeyDataAtIndexPath: %@ = %@", indexPath, data.hexString); + + return data; } -+ (NSString *)serializedPrivateKey:(OpaqueKey *)key chainType:(ChainType)chainType { - char *c_string = key_serialized_private_key_for_chain(key, chainType); ++ (NSString *)serializedPrivateKey:(DOpaqueKey *)key chainType:(DChainType *)chainType { + uint8_t priv_key = dash_spv_crypto_network_chain_type_ChainType_script_priv_key(chainType); + char *c_string = dash_spv_crypto_keys_key_OpaqueKey_serialized_private_key_for_script(key, priv_key); return [DSKeyManager NSStringFrom:c_string]; } -+ (NSString *)addressForKey:(OpaqueKey *)key forChainType:(ChainType)chainType { - char *c_string = key_address_for_key(key, chainType); ++ (NSString *)addressForKey:(DOpaqueKey *)key forChainType:(DChainType *)chainType { + char *c_string = dash_spv_crypto_keys_key_OpaqueKey_address_with_public_key_data(key, chainType); return [DSKeyManager NSStringFrom:c_string]; } + (NSString *)addressWithPublicKeyData:(NSData *)data forChain:(nonnull DSChain *)chain { - char *c_string = key_address_with_public_key_data(data.bytes, data.length, chain.chainType); + SLICE *slice = slice_ctor(data); + char *c_string = dash_spv_crypto_util_address_address_with_public_key_data(slice, chain.chainType); +// slice_dtor(slice); return [DSKeyManager NSStringFrom:c_string]; } + (NSString *)addressFromHash160:(UInt160)hash forChain:(nonnull DSChain *)chain { - char *c_string = address_from_hash160(hash.u8, chain.chainType); + u160 *h = u160_ctor_u(hash); + char *c_string = dash_spv_apple_bindings_address_addresses_address_from_hash160(h, chain.chainType); +// u160_dtor(h); return [DSKeyManager NSStringFrom:c_string]; } + (NSString *_Nullable)addressWithScriptPubKey:(NSData *)script forChain:(nonnull DSChain *)chain { - char *c_string = address_with_script_pubkey(script.bytes, script.length, chain.chainType); + BYTES *vec = bytes_ctor(script); + char *c_string = dash_spv_apple_bindings_address_addresses_address_with_script_pubkey(vec, chain.chainType); +// char *c_string = address_with_script_pubkey(script.bytes, script.length, chain.chainType); return [DSKeyManager NSStringFrom:c_string]; } + (NSString *_Nullable)addressWithScriptSig:(NSData *)script forChain:(nonnull DSChain *)chain { - char *c_string = address_with_script_sig(script.bytes, script.length, chain.chainType); + BYTES *vec = bytes_ctor(script); + char *c_string = dash_spv_apple_bindings_address_addresses_address_with_script_sig(vec, chain.chainType); +// char *c_string = address_with_script_sig(script.bytes, script.length, chain.chainType); return [DSKeyManager NSStringFrom:c_string]; } + (BOOL)isValidDashAddress:(NSString *)address forChain:(nonnull DSChain *)chain { - return is_valid_dash_address_for_chain([address UTF8String], chain.chainType); + return dash_spv_apple_bindings_address_addresses_is_valid_dash_address_for_chain((char *) [address UTF8String], chain.chainType); } + (NSData *)scriptPubKeyForAddress:(NSString *)address forChain:(nonnull DSChain *)chain { - return [DSKeyManager NSDataFrom:script_pubkey_for_address([address UTF8String], chain.chainType)]; + BYTES *vec = dash_spv_apple_bindings_address_addresses_script_pubkey_for_address((char *) [address UTF8String], chain.chainType); + return [DSKeyManager NSDataFrom:vec]; } -+ (NSData *)privateKeyData:(OpaqueKey *)key { - ByteArray arr = key_private_key_data(key); - return [DSKeyManager NSDataFrom:arr]; ++ (NSData *)privateKeyData:(DOpaqueKey *)key { + DMaybeKeyData *result = dash_spv_crypto_keys_key_OpaqueKey_private_key_data(key); +// ByteArray arr = key_private_key_data(key); + if (result->error) { + return NULL; + } + NSData *data = nil; + if (result->ok) { + data = [NSData dataWithBytes:(const void *)result->ok->values length:result->ok->count]; + } + DMaybeKeyDataDtor(result); + return data; } -+ (NSData *)publicKeyData:(OpaqueKey *)key { - ByteArray arr = key_public_key_data(key); - return [DSKeyManager NSDataFrom:arr]; ++ (NSData *)publicKeyData:(DOpaqueKey *)key { + BYTES *vec = dash_spv_crypto_keys_key_OpaqueKey_public_key_data(key); + NSData *data = [DSKeyManager NSDataFrom:vec]; +// ByteArray arr = key_public_key_data(key); + return data; } -+ (NSData *)extendedPrivateKeyData:(OpaqueKey *)key { - ByteArray arr = key_extended_private_key_data(key); - return [DSKeyManager NSDataFrom:arr]; ++ (NSData *)extendedPrivateKeyData:(DOpaqueKey *)key { +// ByteArray arr = key_extended_private_key_data(key); +// return [DSKeyManager NSDataFrom:arr]; + + Result_ok_dash_spv_crypto_util_sec_vec_SecVec_err_dash_spv_crypto_keys_KeyError *result = dash_spv_crypto_keys_key_OpaqueKey_extended_private_key_data(key); + if (result->error) { + return NULL; + } + BYTES *bytes = dash_spv_crypto_util_sec_vec_SecVec_to_vec(result->ok); + NSData *data = NSDataFromPtr(bytes); + Result_ok_dash_spv_crypto_util_sec_vec_SecVec_err_dash_spv_crypto_keys_KeyError_destroy(result); + return data; } -+ (NSData *)extendedPublicKeyData:(OpaqueKey *)key { - ByteArray arr = key_extended_public_key_data(key); - return [DSKeyManager NSDataFrom:arr]; ++ (NSData *)extendedPublicKeyData:(DOpaqueKey *)key { + DMaybeKeyData *result = dash_spv_crypto_keys_key_OpaqueKey_extended_public_key_data(key); + NSData *data = NULL; + if (result) { + if (result->ok) + data = NSDataFromPtr(result->ok); + DMaybeKeyDataDtor(result); + } + return data; +// ByteArray arr = key_extended_public_key_data(key); +// return [DSKeyManager NSDataFrom:arr]; } -+ (OpaqueKey *_Nullable)deriveKeyFromExtenedPrivateKeyDataAtIndexPath:(NSData *_Nullable)data ++ (DMaybeOpaqueKey *_Nullable)deriveKeyFromExtenedPrivateKeyDataAtIndexPath:(NSData *_Nullable)data indexPath:(NSIndexPath *)indexPath - forKeyType:(KeyKind)keyType { + forKeyType:(DKeyKind *)keyType { if (!data) return nil; - NSUInteger idxs[[indexPath length]]; - [indexPath getIndexes:idxs]; - OpaqueKey *key = key_derive_key_from_extened_private_key_data_for_index_path(data.bytes, data.length, (int16_t) keyType, idxs, indexPath.length); - return key; +// NSUInteger idxs[[indexPath length]]; +// [indexPath getIndexes:idxs]; + SLICE *slice = slice_ctor(data); + Vec_u32 *index_path = [NSIndexPath ffi_to:indexPath]; +// NSLog(@"[kind: %u] deriveKeyFromExtenedPrivateKeyDataAtIndexPath: %@ %@", dash_spv_crypto_keys_key_KeyKind_index(keyType), data.hexString, indexPath); + DMaybeOpaqueKey *maybe_key = dash_spv_crypto_keys_key_KeyKind_derive_key_from_extended_private_key_data_for_index_path(keyType, slice, index_path); + return maybe_key; } -+ (UInt160)ecdsaKeyPublicKeyHashFromSecret:(NSString *)secret forChainType:(ChainType)chainType { - return [DSKeyManager NSDataFrom:ecdsa_public_key_hash_from_secret([secret UTF8String], chainType)].UInt160; -} +//+ (UInt160)ecdsaKeyPublicKeyHashFromSecret:(NSString *)secret forChainType:(DChainType *)chainType { +// key_with_private_key +// key_hash +// return [DSKeyManager NSDataFrom:ecdsa_public_key_hash_from_secret([secret UTF8String], chainType)].UInt160; +//} -+ (NSString *_Nullable)ecdsaKeyAddressFromPublicKeyData:(NSData *)data forChainType:(ChainType)chainType { - return [DSKeyManager NSStringFrom:ecdsa_address_from_public_key_data(data.bytes, data.length, chainType)]; ++ (NSString *_Nullable)ecdsaKeyAddressFromPublicKeyData:(NSData *)data forChainType:(DChainType *)chainType { + SLICE *slice = slice_ctor(data); + char *addr = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_address_from_public_key_data(slice, chainType); +// slice_dtor(slice); + return [DSKeyManager NSStringFrom:addr]; } -- (NSString *)ecdsaKeyPublicKeyUniqueIDFromDerivedKeyData:(UInt256)secret forChainType:(ChainType)chainType { - uint64_t unque_id = ecdsa_public_key_unique_id_from_derived_key_data(secret.u8, 32, chainType); - return [NSString stringWithFormat:@"%0llx", unque_id]; +- (NSString *)ecdsaKeyPublicKeyUniqueIDFromDerivedKeyData:(UInt256)secret forChainType:(DChainType *)chainType { + SLICE *slice = slice_u256_ctor_u(secret); + uint64_t unique_id = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_public_key_unique_id_from_derived_key_data(slice, chainType); + return [NSString stringWithFormat:@"%0llx", unique_id]; } - (NSString *)keyRecoveredFromCompactSig:(NSData *)signature andMessageDigest:(UInt256)md { - return [DSKeyManager NSStringFrom:address_for_ecdsa_key_recovered_from_compact_sig(signature.bytes, signature.length, md.u8, self.chain.chainType)]; + SLICE *slice = slice_ctor(signature); + u256 *digest = u256_ctor_u(md); + DMaybeKeyString *result = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_address_from_recovered_compact_sig(slice, digest, self.chain.chainType); + NSString *addr = NULL; + if (result) { + if (result->ok) + addr = [NSString stringWithCString:(const char *)result->ok encoding:NSUTF8StringEncoding]; + DMaybeKeyStringDtor(result); + } + return addr; } + (NSData *_Nullable)compactSign:(DSDerivationPath *)derivationPath fromSeed:(NSData *)seed atIndexPath:(NSIndexPath *)indexPath digest:(UInt256)digest { - OpaqueKey *key = [derivationPath privateKeyAtIndexPath:indexPath fromSeed:seed]; - // TODO: wrong need to sign opaque? - NSData *data = [DSKeyManager NSDataFrom:key_ecdsa_compact_sign(key->ecdsa, digest.u8)]; - processor_destroy_opaque_key(key); + DMaybeOpaqueKey *key = [derivationPath privateKeyAtIndexPath:indexPath fromSeed:seed]; + NSData *data = NULL; + if (key) { + if (key->ok) { + SLICE *slice = slice_u256_ctor_u(digest); + BYTES *bytes = dash_spv_crypto_keys_key_OpaqueKey_sign(key->ok, slice); + data = NSDataFromPtr(bytes); + bytes_dtor(bytes); + } + DMaybeOpaqueKeyDtor(key); + } return data; } -+ (ECDSAKey *)ecdsaKeyWithPrivateKey:(NSString *)key forChainType:(ChainType)chainType { - return key_ecdsa_with_private_key([key UTF8String], chainType); -} - -+ (NSString *)blsPublicKeySerialize:(OpaqueKey *)key legacy:(BOOL)legacy { - BLSKey *bls; - if (key->tag == OpaqueKey_BLSBasic) - bls = key->bls_basic; - else - bls = key->bls_legacy; - return uint384_hex([DSKeyManager NSDataFrom:key_bls_serialize(bls, legacy)].UInt384); -} +//+ (struct ECDSAKey *)ecdsaKeyWithPrivateKey:(NSString *)key forChainType:(DChainType *)chainType { +// struct Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError *result = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_key_with_private_key([key UTF8String], chainType); +// return key_ecdsa_with_private_key([key UTF8String], chainType); +//} -+ (NSString *_Nullable)ecdsaKeyWithBIP38Key:(NSString *)key passphrase:(NSString *)passphrase forChainType:(ChainType)chainType { - return [DSKeyManager NSStringFrom:key_ecdsa_with_bip38_key([key UTF8String], [passphrase UTF8String], chainType)]; ++ (NSString *)blsPublicKeySerialize:(DOpaqueKey *)key legacy:(BOOL)legacy { + DMaybeKeyString *result = dash_spv_crypto_keys_bls_key_BLSKey_public_key_serialized(key->bls, legacy); + NSString *keySerialized = NULL; + if (result) { + if (result->ok) + keySerialized = [NSString stringWithCString:(const char *)result->ok encoding:NSUTF8StringEncoding]; + DMaybeKeyStringDtor(result); + } + return keySerialized; +} + ++ (NSString *_Nullable)ecdsaKeyWithBIP38Key:(NSString *)key + passphrase:(NSString *)passphrase + forChainType:(DChainType *)chainType { + DMaybeKeyString *result = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_serialized_from_bip38_key((char *) [key UTF8String], (char *) [passphrase UTF8String], chainType); + NSString *keySerialized = NULL; + if (result) { + if (result->ok) + keySerialized = [NSString stringWithCString:(const char *)result->ok encoding:NSUTF8StringEncoding]; + DMaybeKeyStringDtor(result); + } + return keySerialized; } + (BOOL)isValidDashBIP38Key:(NSString *)key { - return key_is_valid_bip38_key([key UTF8String]); + return dash_spv_crypto_keys_ecdsa_key_ECDSAKey_is_valid_bip38_key((char *) [key UTF8String]); } -+ (OpaqueKey *_Nullable)keyWithPrivateKeyString:(NSString *)key ofKeyType:(KeyKind)keyType forChainType:(ChainType)chainType { - return key_with_private_key([key UTF8String], keyType, chainType); ++ (DMaybeOpaqueKey *_Nullable)keyWithPrivateKeyString:(NSString *)key + ofKeyType:(DKeyKind *)keyType + forChainType:(DChainType *)chainType { + return dash_spv_crypto_keys_key_KeyKind_key_with_private_key(keyType, (char *) [key UTF8String], chainType); } -+ (OpaqueKey *_Nullable)keyDeprecatedExtendedPublicKeyFromSeed:(NSData *)seed indexes:(UInt256 *)indexes hardened:(BOOL *)hardened length:(NSUInteger)length { - OpaqueKey *key = deprecated_incorrect_extended_public_key_from_seed(seed.bytes, seed.length, (const uint8_t *) indexes, hardened, length); - return key; -} +//+ (DOpaqueKey *_Nullable)keyDeprecatedExtendedPublicKeyFromSeed:(NSData *)seed indexes:(UInt256 *)indexes hardened:(BOOL *)hardened length:(NSUInteger)length { +// SLICE *secret = slice_ctor(seed); +// +// dash_spv_crypto_keys_ecdsa_key_ECDSAKey_deprecated_incorrect_extended_public_key_from_seed_as_opaque(<#struct Slice_u8 *secret#>, <#struct Slice_u8 *chaincode#>, <#struct Slice_u8 *hashes#>, <#uintptr_t derivation_len#>) +// +// DOpaqueKey *key = deprecated_incorrect_extended_public_key_from_seed(seed.bytes, seed.length, (const uint8_t *) indexes, hardened, length); +// return key; +//} + (NSString *)NSStringFrom:(char *)c_string { if (c_string == NULL) { return nil; } else { NSString *address = [NSString stringWithUTF8String:c_string]; - processor_destroy_string(c_string); + str_destroy(c_string); +// processor_destroy_string(c_string); return address; } } -+ (NSData *)NSDataFrom:(ByteArray)byte_array { - if (byte_array.ptr == NULL && byte_array.len == 0) { ++ (NSData *)NSDataFrom:(BYTES *)byte_array { + if (byte_array->values == NULL && byte_array->count == 0) { return nil; } else { - NSData *data = [NSData dataWithBytes:(const void *)byte_array.ptr length:byte_array.len]; - processor_destroy_byte_array(byte_array.ptr, byte_array.len); + NSData *data = NSDataFromPtr(byte_array); + bytes_dtor(byte_array); return data; } } - -+ (NSString *)keyStoragePrefix:(KeyKind)keyType { - switch (keyType) { - case KeyKind_ECDSA: return @""; - case KeyKind_BLS: return @"_BLS_"; - case KeyKind_BLSBasic: return @"_BLS_B_"; - case KeyKind_ED25519: return @"_ED25519_"; ++ (NSData *)NSDataFromArr_u8_32:(u256 *)byte_array { + if (byte_array->values == NULL && byte_array->count == 0) { + return nil; + } else { + NSData *data = NSDataFromPtr(byte_array); + u256_dtor(byte_array); + return data; } } -+ (NSString *)localizedKeyType:(OpaqueKey *)key { +//+ (NSString *)keyStoragePrefix:(DKeyKind *)keyType { +// switch (&keyType) { +// case dash_spv_crypto_keys_key_KeyKind_ECDSA: return @""; +// case dash_spv_crypto_keys_key_KeyKind_BLS: return @"_BLS_"; +// case dash_spv_crypto_keys_key_KeyKind_BLSBasic: return @"_BLS_B_"; +// case dash_spv_crypto_keys_key_KeyKind_ED25519: return @"_ED25519_"; +// } +//} + ++ (NSString *)localizedKeyType:(DOpaqueKey *)key { switch (key->tag) { - case OpaqueKey_ECDSA: return DSLocalizedString(@"ECDSA", nil); - case OpaqueKey_BLSLegacy: return DSLocalizedString(@"BLS (Legacy)", nil); - case OpaqueKey_BLSBasic: return DSLocalizedString(@"BLS (Basic)", nil); - case OpaqueKey_ED25519: return DSLocalizedString(@"ED25519", nil); + case dash_spv_crypto_keys_key_OpaqueKey_ECDSA: return DSLocalizedString(@"ECDSA", nil); +// case dash_spv_crypto_keys_key_OpaqueKey_BLSLegacy: return DSLocalizedString(@"BLS (Legacy)", nil); + case dash_spv_crypto_keys_key_OpaqueKey_BLS: return DSLocalizedString(@"BLS", nil); +// case dash_spv_crypto_keys_key_OpaqueKey_BLSBasic: return DSLocalizedString(@"BLS (Basic)", nil); + case dash_spv_crypto_keys_key_OpaqueKey_ED25519: return DSLocalizedString(@"ED25519", nil); default: return DSLocalizedString(@"Unknown Key Type", nil); } } +//+ (DKeyKind *)keyKindFromIndex:(uint16_t)index { +// switch (index) { +// case dash_spv_crypto_keys_key_KeyKind_ECDSA: +// return dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor(); +// case dash_spv_crypto_keys_key_KeyKind_BLS: +// return dash_spv_crypto_keys_key_KeyKind_BLS_ctor(); +// case dash_spv_crypto_keys_key_KeyKind_BLSBasic: +// return dash_spv_crypto_keys_key_KeyKind_BLSBasic_ctor(); +// case dash_spv_crypto_keys_key_KeyKind_ED25519: +// return dash_spv_crypto_keys_key_KeyKind_ED25519_ctor(); +// } +// +//} + /// Crypto + (UInt256)x11:(NSData *)data { - return [DSKeyManager NSDataFrom:processor_x11(data.bytes, data.length)].UInt256; + SLICE *slice = slice_ctor(data); + u256 *result = dash_spv_crypto_x11(slice); +// slice_dtor(slice); + NSData *hash = NSDataFromPtr(result); +// u256_dtor(result); + return hash.UInt256; } + (UInt256)blake3:(NSData *)data { - return [DSKeyManager NSDataFrom:processor_blake3(data.bytes, data.length)].UInt256; -} - -+ (NSData *)encryptData:(NSData *)data secretKey:(OpaqueKey *)secretKey publicKey:(OpaqueKey *)publicKey { - ByteArray result = key_encrypt_data(data.bytes, data.length, secretKey, publicKey); - return [DSKeyManager NSDataFrom:result]; -} - -+ (NSData *)encryptData:(NSData *)data secretKey:(OpaqueKey *)secretKey publicKey:(OpaqueKey *)publicKey usingIV:(NSData *)iv { - ByteArray result = key_encrypt_data_using_iv(data.bytes, data.length, secretKey, publicKey, iv.bytes, iv.length); - return [DSKeyManager NSDataFrom:result]; -} - -+ (NSData *)decryptData:(NSData *)data secretKey:(OpaqueKey *)secretKey publicKey:(OpaqueKey *)publicKey { - ByteArray result = key_decrypt_data(data.bytes, data.length, secretKey, publicKey); - return [DSKeyManager NSDataFrom:result]; + SLICE *slice = slice_ctor(data); + u256 *result = dash_spv_crypto_blake3(slice); +// slice_dtor(slice); + NSData *hash = NSDataFromPtr(result); +// u256_dtor(result); + return hash.UInt256; +} + ++ (NSData *)encryptData:(NSData *)data secretKey:(DOpaqueKey *)secretKey publicKey:(DOpaqueKey *)publicKey { + SLICE *slice = slice_ctor(data); + DMaybeKeyData *result = dash_spv_crypto_keys_key_OpaqueKey_encrypt_data(secretKey, publicKey, slice); + NSData *encrypted = NULL; + if (result) { + if (result->ok) + encrypted = NSDataFromPtr(result->ok); + DMaybeKeyDataDtor(result); + } + return encrypted; +} + ++ (NSData *)encryptData:(NSData *)data secretKey:(DOpaqueKey *)secretKey publicKey:(DOpaqueKey *)publicKey usingIV:(NSData *)iv { + SLICE *slice = slice_ctor(data); + BYTES *iv_slice = bytes_ctor(iv); +// NSLog(@"[DSKeyManager] encryptData --> %@ -- %p -- %p -- %@", data.hexString, secretKey, publicKey, iv.hexString); + DMaybeKeyData *result = dash_spv_crypto_keys_key_OpaqueKey_encrypt_data_using_iv(secretKey, publicKey, slice, iv_slice); + NSData *encrypted = NULL; + if (result) { + if (result->ok) + encrypted = NSDataFromPtr(result->ok); + DMaybeKeyDataDtor(result); + } +// NSLog(@"[DSKeyManager] encryptData <-- %@", encrypted.hexString); + return encrypted; +} + ++ (NSData *)decryptData:(NSData *)data secretKey:(DOpaqueKey *)secretKey publicKey:(DOpaqueKey *)publicKey { + SLICE *slice = slice_ctor(data); + DMaybeKeyData *result = dash_spv_crypto_keys_key_OpaqueKey_decrypt_data(secretKey, publicKey, slice); + NSData *decrypted = NULL; + if (result) { + if (result->ok) + decrypted = NSDataFromPtr(result->ok); + DMaybeKeyDataDtor(result); + } + return decrypted; } -+ (NSData *)decryptData:(NSData *)data secretKey:(OpaqueKey *)secretKey publicKey:(OpaqueKey *)publicKey usingIVSize:(NSUInteger)ivSize { - ByteArray result = key_decrypt_data_using_iv_size(data.bytes, data.length, secretKey, publicKey, ivSize); - return [DSKeyManager NSDataFrom:result]; ++ (NSData *)decryptData:(NSData *)data secretKey:(DOpaqueKey *)secretKey publicKey:(DOpaqueKey *)publicKey usingIVSize:(NSUInteger)ivSize { + SLICE *slice = slice_ctor(data); + DMaybeKeyData *result = dash_spv_crypto_keys_key_OpaqueKey_decrypt_data_using_iv_size(secretKey, publicKey, slice, ivSize); + NSData *decrypted = NULL; + if (result) { + if (result->ok) + decrypted = NSDataFromPtr(result->ok); + DMaybeKeyDataDtor(result); + } + return decrypted; } -+ (NSData *)encryptData:(NSData *)data withDHKey:(OpaqueKey *)dhKey { - ByteArray result = key_encrypt_data_with_dh_key(data.bytes, data.length, dhKey); - return [DSKeyManager NSDataFrom:result]; ++ (NSData *)encryptData:(NSData *)data withDHKey:(DOpaqueKey *)dhKey { + BYTES *bytes = bytes_ctor(data); + DMaybeKeyData *result = dash_spv_crypto_keys_key_OpaqueKey_encrypt_data_with_dh_key(dhKey, bytes); + NSData *encrypted = NULL; + if (result) { + if (result->ok) + encrypted = NSDataFromPtr(result->ok); + DMaybeKeyDataDtor(result); + } + return encrypted; } -+ (NSData *)decryptData:(NSData *)data withDHKey:(OpaqueKey *)dhKey { - ByteArray result = key_decrypt_data_with_dh_key(data.bytes, data.length, dhKey); - return [DSKeyManager NSDataFrom:result]; ++ (NSData *)decryptData:(NSData *)data withDHKey:(DOpaqueKey *)dhKey { + BYTES *bytes = bytes_ctor(data); + DMaybeKeyData *result = dash_spv_crypto_keys_key_OpaqueKey_decrypt_data_with_dh_key(dhKey, bytes); + NSData *decrypted = NULL; + if (result) { + if (result->ok) + decrypted = NSDataFromPtr(result->ok); + DMaybeKeyDataDtor(result); + } + return decrypted; } - - - + (BOOL)verifyProRegTXPayloadSignature:(NSData *)signature payload:(NSData *)payload ownerKeyHash:(UInt160)ownerKeyHash { - return pro_reg_tx_verify_payload_signature(signature.bytes, signature.length, payload.bytes, payload.length, ownerKeyHash.u8); -} - -+ (NSData *)proRegTXPayloadCollateralDigest:(NSData *)payload - scriptPayout:(NSData *)scriptPayout - reward:(uint16_t)reward - ownerKeyHash:(UInt160)ownerKeyHash - voterKeyHash:(UInt160)voterKeyHash - chainType:(ChainType)chainType { - ByteArray result = pro_reg_tx_payload_collateral_digest(payload.bytes, payload.length, scriptPayout.bytes, scriptPayout.length, reward, ownerKeyHash.u8, voterKeyHash.u8, chainType); - return [DSKeyManager NSDataFrom:result]; + SLICE *sig = slice_ctor(signature); + SLICE *pld = slice_ctor(payload); + u160 *hash = u160_ctor_u(ownerKeyHash); + BOOL verified = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_pro_reg_tx_verify_payload_signature(sig, pld, hash); + return verified; } -+ (NSString *_Nullable)devnetIdentifierFor:(ChainType)chainType { - return [DSKeyManager NSStringFrom:devnet_identifier_for_chain_type(chainType)]; ++ (NSString *_Nullable)devnetIdentifierFor:(DChainType *)chainType { + return [DSKeyManager NSStringFrom:dash_spv_crypto_network_chain_type_ChainType_devnet_identifier(chainType)]; } @end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+LocalMasternode.h b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+LocalMasternode.h index 4ceb012a7..30e292757 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+LocalMasternode.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+LocalMasternode.h @@ -57,17 +57,20 @@ NS_ASSUME_NONNULL_BEGIN fundsWalletIndex:(uint32_t)fundsWalletIndex inOperatorWallet:(DSWallet *_Nullable)operatorWallet operatorWalletIndex:(uint32_t)operatorWalletIndex - operatorPublicKey:(OpaqueKey *)operatorPublicKey + operatorPublicKey:(DOpaqueKey *)operatorPublicKey inOwnerWallet:(DSWallet *_Nullable)ownerWallet ownerWalletIndex:(uint32_t)ownerWalletIndex - ownerPrivateKey:(OpaqueKey *)ownerPrivateKey + ownerPrivateKey:(DOpaqueKey *)ownerPrivateKey inVotingWallet:(DSWallet *_Nullable)votingWallet votingWalletIndex:(uint32_t)votingWalletIndex - votingKey:(OpaqueKey *)votingKey + votingKey:(DOpaqueKey *)votingKey inPlatformNodeWallet:(DSWallet *_Nullable)platformNodeWallet platformNodeWalletIndex:(uint32_t)platformNodeWalletIndex - platformNodeKey:(OpaqueKey *)platformNodeKey; + platformNodeKey:(DOpaqueKey *)platformNodeKey; - (DSLocalMasternode *_Nullable)localMasternodeFromProviderRegistrationTransaction:(DSProviderRegistrationTransaction *)providerRegistrationTransaction save:(BOOL)save; -- (DSLocalMasternode *)localMasternodeFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry claimedWithOwnerWallet:(DSWallet *)wallet ownerKeyIndex:(uint32_t)ownerKeyIndex; +- (DSLocalMasternode *)localMasternodeFromSimplifiedMasternodeEntry:(DMasternodeEntry *)simplifiedMasternodeEntry + claimedWithOwnerWallet:(DSWallet *)wallet + ownerKeyIndex:(uint32_t)ownerKeyIndex + onChain:(DSChain *)chain; - (DSLocalMasternode *_Nullable)localMasternodeHavingProviderRegistrationTransactionHash:(UInt256)providerRegistrationTransactionHash; - (DSLocalMasternode *_Nullable)localMasternodeUsingIndex:(uint32_t)index atDerivationPath:(DSDerivationPath *)derivationPath; - (NSArray *_Nullable)localMasternodesPreviouslyUsingIndex:(uint32_t)index atDerivationPath:(DSDerivationPath *)derivationPath; diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+LocalMasternode.m b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+LocalMasternode.m index c17220628..95e63f0d0 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+LocalMasternode.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+LocalMasternode.m @@ -17,22 +17,16 @@ #import "DSChain+Protected.h" #import "DSChain.h" +#import "DSChain+Wallet.h" #import "DSChainManager+Protected.h" #import "DSLocalMasternode+Protected.h" #import "DSMasternodeManager+LocalMasternode.h" -#import "DSSimplifiedMasternodeEntry.h" #import NSString const *localMasternodesDictionaryKey = @"localMasternodesDictionaryKey"; -@interface DSMasternodeManager () -@property (nonatomic, strong) NSMutableDictionary *localMasternodesDictionaryByRegistrationTransactionHash; -@end - @implementation DSMasternodeManager (LocalMasternode) -//@dynamic localMasternodesDictionaryByRegistrationTransactionHash; - - (void)setLocalMasternodesDictionaryByRegistrationTransactionHash:(NSMutableDictionary *)dictionary { objc_setAssociatedObject(self, &localMasternodesDictionaryKey, dictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @@ -49,7 +43,13 @@ - (void)setLocalMasternodesDictionaryByRegistrationTransactionHash:(NSMutableDic - (DSLocalMasternode *)createNewMasternodeWithIPAddress:(UInt128)ipAddress onPort:(uint32_t)port inWallet:(DSWallet *)wallet { NSParameterAssert(wallet); - return [self createNewMasternodeWithIPAddress:ipAddress onPort:port inFundsWallet:wallet inOperatorWallet:wallet inOwnerWallet:wallet inVotingWallet:wallet inPlatformNodeWallet:wallet]; + return [self createNewMasternodeWithIPAddress:ipAddress + onPort:port + inFundsWallet:wallet + inOperatorWallet:wallet + inOwnerWallet:wallet + inVotingWallet:wallet + inPlatformNodeWallet:wallet]; } - (DSLocalMasternode *)createNewMasternodeWithIPAddress:(UInt128)ipAddress @@ -59,8 +59,13 @@ - (DSLocalMasternode *)createNewMasternodeWithIPAddress:(UInt128)ipAddress inOwnerWallet:(DSWallet *)ownerWallet inVotingWallet:(DSWallet *)votingWallet inPlatformNodeWallet:(DSWallet *)platformNodeWallet { - DSLocalMasternode *localMasternode = [[DSLocalMasternode alloc] initWithIPAddress:ipAddress onPort:port inFundsWallet:fundsWallet inOperatorWallet:operatorWallet inOwnerWallet:ownerWallet inVotingWallet:votingWallet inPlatformNodeWallet:platformNodeWallet]; - return localMasternode; + return [[DSLocalMasternode alloc] initWithIPAddress:ipAddress + onPort:port + inFundsWallet:fundsWallet + inOperatorWallet:operatorWallet + inOwnerWallet:ownerWallet + inVotingWallet:votingWallet + inPlatformNodeWallet:platformNodeWallet]; } - (DSLocalMasternode *)createNewMasternodeWithIPAddress:(UInt128)ipAddress @@ -75,8 +80,18 @@ - (DSLocalMasternode *)createNewMasternodeWithIPAddress:(UInt128)ipAddress votingWalletIndex:(uint32_t)votingWalletIndex inPlatformNodeWallet:(DSWallet *_Nullable)platformNodeWallet platformNodeWalletIndex:(uint32_t)platformNodeWalletIndex { - DSLocalMasternode *localMasternode = [[DSLocalMasternode alloc] initWithIPAddress:ipAddress onPort:port inFundsWallet:fundsWallet fundsWalletIndex:fundsWalletIndex inOperatorWallet:operatorWallet operatorWalletIndex:operatorWalletIndex inOwnerWallet:ownerWallet ownerWalletIndex:ownerWalletIndex inVotingWallet:votingWallet votingWalletIndex:votingWalletIndex inPlatformNodeWallet:platformNodeWallet platformNodeWalletIndex:platformNodeWalletIndex]; - return localMasternode; + return [[DSLocalMasternode alloc] initWithIPAddress:ipAddress + onPort:port + inFundsWallet:fundsWallet + fundsWalletIndex:fundsWalletIndex + inOperatorWallet:operatorWallet + operatorWalletIndex:operatorWalletIndex + inOwnerWallet:ownerWallet + ownerWalletIndex:ownerWalletIndex + inVotingWallet:votingWallet + votingWalletIndex:votingWalletIndex + inPlatformNodeWallet:platformNodeWallet + platformNodeWalletIndex:platformNodeWalletIndex]; } - (DSLocalMasternode *)createNewMasternodeWithIPAddress:(UInt128)ipAddress @@ -85,62 +100,78 @@ - (DSLocalMasternode *)createNewMasternodeWithIPAddress:(UInt128)ipAddress fundsWalletIndex:(uint32_t)fundsWalletIndex inOperatorWallet:(DSWallet *_Nullable)operatorWallet operatorWalletIndex:(uint32_t)operatorWalletIndex - operatorPublicKey:(OpaqueKey *)operatorPublicKey + operatorPublicKey:(DOpaqueKey *)operatorPublicKey inOwnerWallet:(DSWallet *_Nullable)ownerWallet ownerWalletIndex:(uint32_t)ownerWalletIndex - ownerPrivateKey:(OpaqueKey *)ownerPrivateKey + ownerPrivateKey:(DOpaqueKey *)ownerPrivateKey inVotingWallet:(DSWallet *_Nullable)votingWallet votingWalletIndex:(uint32_t)votingWalletIndex - votingKey:(OpaqueKey *)votingKey + votingKey:(DOpaqueKey *)votingKey inPlatformNodeWallet:(DSWallet *_Nullable)platformNodeWallet platformNodeWalletIndex:(uint32_t)platformNodeWalletIndex - platformNodeKey:(OpaqueKey *)platformNodeKey { - DSLocalMasternode *localMasternode = [[DSLocalMasternode alloc] initWithIPAddress:ipAddress onPort:port inFundsWallet:fundsWallet fundsWalletIndex:fundsWalletIndex inOperatorWallet:operatorWallet operatorWalletIndex:operatorWalletIndex inOwnerWallet:ownerWallet ownerWalletIndex:ownerWalletIndex inVotingWallet:votingWallet votingWalletIndex:votingWalletIndex inPlatformNodeWallet:platformNodeWallet platformNodeWalletIndex:platformNodeWalletIndex]; - - if (operatorWalletIndex == UINT32_MAX && operatorPublicKey) { + platformNodeKey:(DOpaqueKey *)platformNodeKey { + DSLocalMasternode *localMasternode = [[DSLocalMasternode alloc] initWithIPAddress:ipAddress + onPort:port + inFundsWallet:fundsWallet + fundsWalletIndex:fundsWalletIndex + inOperatorWallet:operatorWallet + operatorWalletIndex:operatorWalletIndex + inOwnerWallet:ownerWallet + ownerWalletIndex:ownerWalletIndex + inVotingWallet:votingWallet + votingWalletIndex:votingWalletIndex + inPlatformNodeWallet:platformNodeWallet + platformNodeWalletIndex:platformNodeWalletIndex]; + + if (operatorWalletIndex == UINT32_MAX && operatorPublicKey) [localMasternode forceOperatorPublicKey:operatorPublicKey]; - } - - if (ownerWalletIndex == UINT32_MAX && ownerPrivateKey) { + if (ownerWalletIndex == UINT32_MAX && ownerPrivateKey) [localMasternode forceOwnerPrivateKey:ownerPrivateKey]; - } - - if (votingWalletIndex == UINT32_MAX && votingKey) { + if (votingWalletIndex == UINT32_MAX && votingKey) [localMasternode forceVotingKey:votingKey]; - } - - if (platformNodeWalletIndex == UINT32_MAX && platformNodeKey) { + if (platformNodeWalletIndex == UINT32_MAX && platformNodeKey) [localMasternode forcePlatformNodeKey:platformNodeKey]; - } - return localMasternode; } -- (DSLocalMasternode *)localMasternodeFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry claimedWithOwnerWallet:(DSWallet *)ownerWallet ownerKeyIndex:(uint32_t)ownerKeyIndex { +- (DSLocalMasternode *)localMasternodeFromSimplifiedMasternodeEntry:(DMasternodeEntry *)simplifiedMasternodeEntry + claimedWithOwnerWallet:(DSWallet *)ownerWallet + ownerKeyIndex:(uint32_t)ownerKeyIndex + onChain:(DSChain *)chain { NSParameterAssert(simplifiedMasternodeEntry); NSParameterAssert(ownerWallet); - - DSLocalMasternode *localMasternode = [self localMasternodeHavingProviderRegistrationTransactionHash:simplifiedMasternodeEntry.providerRegistrationTransactionHash]; - + UInt256 proRegTxHash = u256_cast(simplifiedMasternodeEntry->provider_registration_transaction_hash); + DSLocalMasternode *localMasternode = [self localMasternodeHavingProviderRegistrationTransactionHash:proRegTxHash]; if (localMasternode) return localMasternode; + UInt160 keyIDVoting = u160_cast(simplifiedMasternodeEntry->key_id_voting); uint32_t votingIndex; - DSWallet *votingWallet = [simplifiedMasternodeEntry.chain walletHavingProviderVotingAuthenticationHash:simplifiedMasternodeEntry.keyIDVoting foundAtIndex:&votingIndex]; - + DSWallet *votingWallet = [chain walletHavingProviderVotingAuthenticationHash:keyIDVoting foundAtIndex:&votingIndex]; + UInt384 operatorPublicKey = u384_cast(simplifiedMasternodeEntry->operator_public_key->data); uint32_t operatorIndex; - DSWallet *operatorWallet = [simplifiedMasternodeEntry.chain walletHavingProviderOperatorAuthenticationKey:simplifiedMasternodeEntry.operatorPublicKey foundAtIndex:&operatorIndex]; - + DSWallet *operatorWallet = [chain walletHavingProviderOperatorAuthenticationKey:operatorPublicKey foundAtIndex:&operatorIndex]; + UInt160 platformNodeID = u160_cast(simplifiedMasternodeEntry->platform_node_id); uint32_t platformNodeIndex; - DSWallet *platformNodeWallet = [simplifiedMasternodeEntry.chain walletHavingPlatformNodeAuthenticationHash:simplifiedMasternodeEntry.platformNodeID foundAtIndex:&platformNodeIndex]; - - if (votingWallet || operatorWallet) { - return [[DSLocalMasternode alloc] initWithIPAddress:simplifiedMasternodeEntry.address onPort:simplifiedMasternodeEntry.port inFundsWallet:nil fundsWalletIndex:0 inOperatorWallet:operatorWallet operatorWalletIndex:operatorIndex inOwnerWallet:ownerWallet ownerWalletIndex:ownerKeyIndex inVotingWallet:votingWallet votingWalletIndex:votingIndex inPlatformNodeWallet:platformNodeWallet platformNodeWalletIndex:platformNodeIndex]; - } else { - return nil; - } + DSWallet *platformNodeWallet = [chain walletHavingPlatformNodeAuthenticationHash:platformNodeID foundAtIndex:&platformNodeIndex]; + UInt128 ipAddress = u128_cast(simplifiedMasternodeEntry->socket_address->ip_address); + return votingWallet || operatorWallet + ? [[DSLocalMasternode alloc] initWithIPAddress:ipAddress + onPort:simplifiedMasternodeEntry->socket_address->port + inFundsWallet:nil + fundsWalletIndex:0 + inOperatorWallet:operatorWallet + operatorWalletIndex:operatorIndex + inOwnerWallet:ownerWallet + ownerWalletIndex:ownerKeyIndex + inVotingWallet:votingWallet + votingWalletIndex:votingIndex + inPlatformNodeWallet:platformNodeWallet + platformNodeWalletIndex:platformNodeIndex] + : nil; } -- (DSLocalMasternode *)localMasternodeFromProviderRegistrationTransaction:(DSProviderRegistrationTransaction *)providerRegistrationTransaction save:(BOOL)save { +- (DSLocalMasternode *)localMasternodeFromProviderRegistrationTransaction:(DSProviderRegistrationTransaction *)providerRegistrationTransaction + save:(BOOL)save { NSParameterAssert(providerRegistrationTransaction); //First check to see if we have a local masternode for this provider registration hash @@ -165,8 +196,7 @@ - (DSLocalMasternode *)localMasternodeFromProviderRegistrationTransaction:(DSPro } - (DSLocalMasternode *)localMasternodeHavingProviderRegistrationTransactionHash:(UInt256)providerRegistrationTransactionHash { - DSLocalMasternode *localMasternode = self.localMasternodesDictionaryByRegistrationTransactionHash[uint256_data(providerRegistrationTransactionHash)]; - return localMasternode; + return self.localMasternodesDictionaryByRegistrationTransactionHash[uint256_data(providerRegistrationTransactionHash)]; } - (DSLocalMasternode *)localMasternodeUsingIndex:(uint32_t)index atDerivationPath:(DSDerivationPath *)derivationPath { diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Mndiff.h b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Mndiff.h index e94622df3..898031579 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Mndiff.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Mndiff.h @@ -15,51 +15,51 @@ // limitations under the License. // -#import "dash_shared_core.h" -#import "DSChain.h" -#import "DSMasternodeProcessorContext.h" -#import "DSMasternodeList.h" -#import "DSMasternodeManager.h" -#import "DSMnDiffProcessingResult.h" -#import "DSQRInfoProcessingResult.h" -#import "DSQuorumEntry.h" -#import "DSSimplifiedMasternodeEntry.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface DSMasternodeManager (Mndiff) - +//#import "dash_shared_core.h" +//#import "DSChain.h" +//#import "DSMasternodeProcessorContext.h" +////#import "DSMasternodeList.h" +//#import "DSMasternodeManager.h" +//#import "DSMnDiffProcessingResult.h" +//#import "DSQRInfoProcessingResult.h" +//#import "DSQuorumEntry.h" +//#import "DSSimplifiedMasternodeEntry.h" +//#import +// +//NS_ASSUME_NONNULL_BEGIN +// +//@interface DSMasternodeManager (Mndiff) +// /// Rust FFI callbacks -MasternodeList *getMasternodeListByBlockHash(uint8_t (*block_hash)[32], const void *context); -bool saveMasternodeList(uint8_t (*block_hash)[32], MasternodeList *masternode_list, const void *context); -void destroyMasternodeList(MasternodeList *masternode_list); -void destroyU8(uint8_t *block_hash); -uint32_t getBlockHeightByHash(uint8_t (*block_hash)[32], const void *context); -uint8_t *getBlockHashByHeight(uint32_t block_height, const void *context); -uint8_t *getMerkleRootByHash(uint8_t (*block_hash)[32], const void *context); -LLMQSnapshot *getLLMQSnapshotByBlockHash(uint8_t (*block_hash)[32], const void *context); -bool saveLLMQSnapshot(uint8_t (*block_hash)[32], LLMQSnapshot *snapshot, const void *context); -void destroyLLMQSnapshot(LLMQSnapshot *snapshot); -void addInsightForBlockHash(uint8_t (*block_hash)[32], const void *context); -ProcessingError shouldProcessDiffWithRange(uint8_t (*base_block_hash)[32], uint8_t (*block_hash)[32], const void *context); - -+ (MasternodeProcessor *)registerProcessor; -+ (void)unregisterProcessor:(MasternodeProcessor *)processor; - -+ (MasternodeProcessorCache *)createProcessorCache; -+ (void)destroyProcessorCache:(MasternodeProcessorCache *)processorCache; - -- (DSMnDiffProcessingResult *)processMasternodeDiffFromFile:(NSData *)message protocolVersion:(uint32_t)protocolVersion withContext:(DSMasternodeProcessorContext *)context; - -- (void)processMasternodeDiffWith:(NSData *)message context:(DSMasternodeProcessorContext *)context completion:(void (^)(DSMnDiffProcessingResult *result))completion; -- (void)processQRInfoWith:(NSData *)message context:(DSMasternodeProcessorContext *)context completion:(void (^)(DSQRInfoProcessingResult *result))completion; - -- (void)clearProcessorCache; -- (void)removeMasternodeListFromCacheAtBlockHash:(UInt256)blockHash; -- (void)removeSnapshotFromCacheAtBlockHash:(UInt256)blockHash; - -@end - +//MasternodeList *getMasternodeListByBlockHash(uint8_t (*block_hash)[32], const void *context); +//bool saveMasternodeList(uint8_t (*block_hash)[32], MasternodeList *masternode_list, const void *context); +//void destroyMasternodeList(MasternodeList *masternode_list); +//void destroyU8(uint8_t *block_hash); +//uint32_t getBlockHeightByHash(uint8_t (*block_hash)[32], const void *context); +//uint8_t *getBlockHashByHeight(uint32_t block_height, const void *context); +//uint8_t *getMerkleRootByHash(uint8_t (*block_hash)[32], const void *context); +//LLMQSnapshot *getLLMQSnapshotByBlockHash(uint8_t (*block_hash)[32], const void *context); +//bool saveLLMQSnapshot(uint8_t (*block_hash)[32], LLMQSnapshot *snapshot, const void *context); +//void destroyLLMQSnapshot(LLMQSnapshot *snapshot); +//void addInsightForBlockHash(uint8_t (*block_hash)[32], const void *context); +//ProcessingError shouldProcessDiffWithRange(uint8_t (*base_block_hash)[32], uint8_t (*block_hash)[32], const void *context); +// +//+ (struct MasternodeProcessor *)registerProcessor; +//+ (void)unregisterProcessor:(struct MasternodeProcessor *)processor; +// +//+ (struct MasternodeProcessorCache *)createProcessorCache; +//+ (void)destroyProcessorCache:(struct MasternodeProcessorCache *)processorCache; +// +//- (DSMnDiffProcessingResult *)processMasternodeDiffFromFile:(NSData *)message protocolVersion:(uint32_t)protocolVersion withContext:(DSMasternodeProcessorContext *)context; +// +//- (void)processMasternodeDiffWith:(NSData *)message context:(DSMasternodeProcessorContext *)context completion:(void (^)(DSMnDiffProcessingResult *result))completion; +//- (void)processQRInfoWith:(NSData *)message context:(DSMasternodeProcessorContext *)context completion:(void (^)(DSQRInfoProcessingResult *result))completion; -NS_ASSUME_NONNULL_END +//- (void)clearProcessorCache; +//- (void)removeMasternodeListFromCacheAtBlockHash:(UInt256)blockHash; +//- (void)removeSnapshotFromCacheAtBlockHash:(UInt256)blockHash; +// +//@end +// +// +//NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Mndiff.m b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Mndiff.m index 1d1955269..4c31a4cb2 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Mndiff.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Mndiff.m @@ -15,283 +15,285 @@ // limitations under the License. // -#import "DSBlock.h" -#import "DSBlockOperation.h" -#import "DSChain+Protected.h" -#import "DSChainManager.h" -#import "DSInsightManager.h" -#import "DSMasternodeList+Mndiff.h" -#import "DSMasternodeManager+Mndiff.h" -#import "DSMasternodeManager+Protected.h" -#import "DSMerkleBlock.h" -#import "DSQuorumEntry+Mndiff.h" -#import "DSQuorumSnapshot+Mndiff.h" -#import "DSSimplifiedMasternodeEntry+Mndiff.h" -#import "NSData+Dash.h" - -#define AS_OBJC(context) ((__bridge DSMasternodeProcessorContext *)(context)) -#define AS_RUST(context) ((__bridge void *)(context)) - -@implementation DSMasternodeManager (Mndiff) - - -/// -/// MARK: Rust FFI callbacks -/// -MasternodeList *getMasternodeListByBlockHash(uint8_t (*block_hash)[32], const void *context) { - UInt256 blockHash = *((UInt256 *)block_hash); - MasternodeList *c_list = NULL; - @synchronized (context) { - DSMasternodeList *list = [AS_OBJC(context) masternodeListForBlockHash:blockHash]; - if (list) { - c_list = [list ffi_malloc]; - } - } - processor_destroy_block_hash(block_hash); - return c_list; -} - -bool saveMasternodeList(uint8_t (*block_hash)[32], MasternodeList *masternode_list, const void *context) { - UInt256 blockHash = *((UInt256 *)block_hash); - BOOL saved = NO; - @synchronized (context) { - DSMasternodeProcessorContext *processorContext = AS_OBJC(context); - DSMasternodeList *masternodeList = [DSMasternodeList masternodeListWith:masternode_list onChain:processorContext.chain]; - saved = [processorContext saveMasternodeList:masternodeList forBlockHash:blockHash]; - } - processor_destroy_block_hash(block_hash); - processor_destroy_masternode_list(masternode_list); - return saved; -} - -void destroyMasternodeList(MasternodeList *masternode_list) { - [DSMasternodeList ffi_free:masternode_list]; -} - -void destroyU8(uint8_t *block_hash) { // big uint - if (block_hash) { - free(block_hash); - } -} - -uint32_t getBlockHeightByHash(uint8_t (*block_hash)[32], const void *context) { - UInt256 blockHash = *((UInt256 *)block_hash); - uint32_t block_height = UINT32_MAX; - @synchronized (context) { - block_height = [AS_OBJC(context) blockHeightForBlockHash:blockHash]; - } - processor_destroy_block_hash(block_hash); - return block_height; -} - -uint8_t *getBlockHashByHeight(uint32_t block_height, const void *context) { - uint8_t (*block_hash)[32] = NULL; - @synchronized (context) { - DSBlock *block = [AS_OBJC(context) blockForBlockHeight:block_height]; - if (block) { - block_hash = uint256_malloc(block.blockHash); - } - } - return (uint8_t *)block_hash; -} - - -uint8_t *getMerkleRootByHash(uint8_t (*block_hash)[32], const void *context) { - UInt256 blockHash = *((UInt256 *)block_hash); - uint8_t (*merkle_root)[32] = NULL; - @synchronized (context) { - UInt256 merkleRoot = [AS_OBJC(context) merkleRootForBlockHash:blockHash]; - merkle_root = uint256_malloc(merkleRoot); - } - processor_destroy_block_hash(block_hash); - return (uint8_t *)merkle_root; -} - -LLMQSnapshot *getLLMQSnapshotByBlockHash(uint8_t (*block_hash)[32], const void *context) { - UInt256 blockHash = *((UInt256 *)block_hash); - LLMQSnapshot *c_snapshot = NULL; - @synchronized (context) { - DSQuorumSnapshot *snapshot = [AS_OBJC(context) quorumSnapshotForBlockHash:blockHash]; - if (snapshot) { - c_snapshot = [snapshot ffi_malloc]; - } - } - processor_destroy_block_hash(block_hash); - return c_snapshot; -} - - -bool saveLLMQSnapshot(uint8_t (*block_hash)[32], LLMQSnapshot *snapshot, const void *context) { - UInt256 blockHash = *((UInt256 *)block_hash); - BOOL saved = NO; - @synchronized (context) { - saved = [AS_OBJC(context) saveQuorumSnapshot:[DSQuorumSnapshot quorumSnapshotWith:snapshot forBlockHash:blockHash]]; - } - processor_destroy_block_hash(block_hash); - processor_destroy_llmq_snapshot(snapshot); - return saved; -} -void destroyLLMQSnapshot(LLMQSnapshot *snapshot) { - [DSQuorumSnapshot ffi_free:snapshot]; -} - -uint8_t *getCLSignatureByBlockHash(uint8_t (*block_hash)[32], const void *context) { - UInt256 blockHash = *((UInt256 *)block_hash); - uint8_t (*cl_signature)[96] = NULL; - @synchronized (context) { - NSData *signature = [AS_OBJC(context) CLSignatureForBlockHash:blockHash]; - if (signature) { - cl_signature = uint768_malloc(signature.UInt768); - } - } - processor_destroy_block_hash(block_hash); - return (uint8_t *)cl_signature; -} -bool saveCLSignature(uint8_t (*block_hash)[32], uint8_t (*cl_signature)[96], const void *context) { - UInt256 blockHash = *((UInt256 *)block_hash); - UInt768 clSignature = *((UInt768 *)cl_signature); - BOOL saved = NO; - @synchronized (context) { - saved = [AS_OBJC(context) saveCLSignature:blockHash signature:clSignature]; - } - processor_destroy_block_hash(block_hash); - processor_destroy_cl_signature(cl_signature); - return saved; -} - -void addInsightForBlockHash(uint8_t (*block_hash)[32], const void *context) { - UInt256 blockHash = *((UInt256 *)block_hash); - @synchronized (context) { - [AS_OBJC(context) blockUntilGetInsightForBlockHash:blockHash]; - } - processor_destroy_block_hash(block_hash); -} - -ProcessingError shouldProcessDiffWithRange(uint8_t (*base_block_hash)[32], uint8_t (*block_hash)[32], const void *context) { - UInt256 baseBlockHash = *((UInt256 *)base_block_hash); - UInt256 blockHash = *((UInt256 *)block_hash); - processor_destroy_block_hash(base_block_hash); - processor_destroy_block_hash(block_hash); - ProcessingError error = ProcessingError_None; - @synchronized (context) { - error = [AS_OBJC(context) shouldProcessDiffWithRange:baseBlockHash blockHash:blockHash]; - } - return error; -} - +//#import "DSBlock.h" +//#import "DSBlockOperation.h" +//#import "DSChain+Protected.h" +//#import "DSChainManager.h" +//#import "DSInsightManager.h" +////#import "DSMasternodeList+Mndiff.h" +//#import "DSMasternodeManager+Mndiff.h" +//#import "DSMasternodeManager+Protected.h" +//#import "DSMerkleBlock.h" +//#import "DSQuorumEntry+Mndiff.h" +//#import "DSQuorumSnapshot+Mndiff.h" +//#import "DSSimplifiedMasternodeEntry+Mndiff.h" +//#import "NSData+Dash.h" + +//#define AS_OBJC(context) ((__bridge DSMasternodeProcessorContext *)(context)) +//#define AS_RUST(context) ((__bridge void *)(context)) + +//@implementation DSMasternodeManager (Mndiff) + + +///// +///// MARK: Rust FFI callbacks +///// +//MasternodeList *getMasternodeListByBlockHash(uint8_t (*block_hash)[32], const void *context) { +// UInt256 blockHash = *((UInt256 *)block_hash); +// MasternodeList *c_list = NULL; +// @synchronized (context) { +// DSMasternodeList *list = [AS_OBJC(context) masternodeListForBlockHash:blockHash]; +// if (list) { +// c_list = [list ffi_malloc]; +// } +// } +// processor_destroy_block_hash(block_hash); +// return c_list; +//} +// +//bool saveMasternodeList(uint8_t (*block_hash)[32], MasternodeList *masternode_list, const void *context) { +// UInt256 blockHash = *((UInt256 *)block_hash); +// BOOL saved = NO; +// @synchronized (context) { +// DSMasternodeProcessorContext *processorContext = AS_OBJC(context); +// DSMasternodeList *masternodeList = [DSMasternodeList masternodeListWith:masternode_list onChain:processorContext.chain]; +// saved = [processorContext saveMasternodeList:masternodeList forBlockHash:blockHash]; +// } +// processor_destroy_block_hash(block_hash); +// processor_destroy_masternode_list(masternode_list); +// return saved; +//} +// +//void destroyMasternodeList(MasternodeList *masternode_list) { +// [DSMasternodeList ffi_free:masternode_list]; +//} +// +//void destroyU8(uint8_t *block_hash) { // big uint +// if (block_hash) { +// free(block_hash); +// } +//} +// +//uint32_t getBlockHeightByHash(uint8_t (*block_hash)[32], const void *context) { +// UInt256 blockHash = *((UInt256 *)block_hash); +// uint32_t block_height = UINT32_MAX; +// @synchronized (context) { +// block_height = [AS_OBJC(context) blockHeightForBlockHash:blockHash]; +// } +// processor_destroy_block_hash(block_hash); +// return block_height; +//} +// +//uint8_t *getBlockHashByHeight(uint32_t block_height, const void *context) { +// uint8_t (*block_hash)[32] = NULL; +// @synchronized (context) { +// DSBlock *block = [AS_OBJC(context) blockForBlockHeight:block_height]; +// if (block) { +// block_hash = uint256_malloc(block.blockHash); +// } +// } +// return (uint8_t *)block_hash; +//} +// +// +//uint8_t *getMerkleRootByHash(uint8_t (*block_hash)[32], const void *context) { +// UInt256 blockHash = *((UInt256 *)block_hash); +// uint8_t (*merkle_root)[32] = NULL; +// @synchronized (context) { +// UInt256 merkleRoot = [AS_OBJC(context) merkleRootForBlockHash:blockHash]; +// merkle_root = uint256_malloc(merkleRoot); +// } +// processor_destroy_block_hash(block_hash); +// return (uint8_t *)merkle_root; +//} +// +//LLMQSnapshot *getLLMQSnapshotByBlockHash(uint8_t (*block_hash)[32], const void *context) { +// UInt256 blockHash = *((UInt256 *)block_hash); +// LLMQSnapshot *c_snapshot = NULL; +// @synchronized (context) { +// DSQuorumSnapshot *snapshot = [AS_OBJC(context) quorumSnapshotForBlockHash:blockHash]; +// if (snapshot) { +// c_snapshot = [snapshot ffi_malloc]; +// } +// } +// processor_destroy_block_hash(block_hash); +// return c_snapshot; +//} +// +// +//bool saveLLMQSnapshot(uint8_t (*block_hash)[32], LLMQSnapshot *snapshot, const void *context) { +// UInt256 blockHash = *((UInt256 *)block_hash); +// BOOL saved = NO; +// @synchronized (context) { +// saved = [AS_OBJC(context) saveQuorumSnapshot:[DSQuorumSnapshot quorumSnapshotWith:snapshot forBlockHash:blockHash]]; +// } +// processor_destroy_block_hash(block_hash); +// processor_destroy_llmq_snapshot(snapshot); +// return saved; +//} +//void destroyLLMQSnapshot(LLMQSnapshot *snapshot) { +// [DSQuorumSnapshot ffi_free:snapshot]; +//} +// +//uint8_t *getCLSignatureByBlockHash(uint8_t (*block_hash)[32], const void *context) { +// UInt256 blockHash = *((UInt256 *)block_hash); +// uint8_t (*cl_signature)[96] = NULL; +// @synchronized (context) { +// NSData *signature = [AS_OBJC(context) CLSignatureForBlockHash:blockHash]; +// if (signature) { +// cl_signature = uint768_malloc(signature.UInt768); +// } +// } +// processor_destroy_block_hash(block_hash); +// return (uint8_t *)cl_signature; +//} +//bool saveCLSignature(uint8_t (*block_hash)[32], uint8_t (*cl_signature)[96], const void *context) { +// UInt256 blockHash = *((UInt256 *)block_hash); +// UInt768 clSignature = *((UInt768 *)cl_signature); +// BOOL saved = NO; +// @synchronized (context) { +// saved = [AS_OBJC(context) saveCLSignature:blockHash signature:clSignature]; +// } +// processor_destroy_block_hash(block_hash); +// processor_destroy_cl_signature(cl_signature); +// return saved; +//} +// +//void addInsightForBlockHash(uint8_t (*block_hash)[32], const void *context) { +// UInt256 blockHash = *((UInt256 *)block_hash); +// @synchronized (context) { +// [AS_OBJC(context) blockUntilGetInsightForBlockHash:blockHash]; +// } +// processor_destroy_block_hash(block_hash); +//} +// +//ProcessingError shouldProcessDiffWithRange(uint8_t (*base_block_hash)[32], uint8_t (*block_hash)[32], const void *context) { +// UInt256 baseBlockHash = *((UInt256 *)base_block_hash); +// UInt256 blockHash = *((UInt256 *)block_hash); +// processor_destroy_block_hash(base_block_hash); +// processor_destroy_block_hash(block_hash); +// ProcessingError error = ProcessingError_None; +// @synchronized (context) { +// error = [AS_OBJC(context) shouldProcessDiffWithRange:baseBlockHash blockHash:blockHash]; +// } +// return error; +//} +// /// /// MARK: Registering/unregistering processor (which is responsible for callback processing) /// - -+ (MasternodeProcessor *)registerProcessor { - return register_processor( - getMerkleRootByHash, - getBlockHeightByHash, - getBlockHashByHeight, - getLLMQSnapshotByBlockHash, - saveLLMQSnapshot, - getCLSignatureByBlockHash, - saveCLSignature, - getMasternodeListByBlockHash, - saveMasternodeList, - destroyMasternodeList, - addInsightForBlockHash, - destroyU8, - destroyLLMQSnapshot, - shouldProcessDiffWithRange); -} - -+ (void)unregisterProcessor:(MasternodeProcessor *)processor { - unregister_processor(processor); -} - -/// -/// MARK: Creating/destroying opaque cache (which is important for storing some of the results between processing sessions) -/// - -+ (MasternodeProcessorCache *)createProcessorCache { - return processor_create_cache(); -} - -+ (void)destroyProcessorCache:(MasternodeProcessorCache *)processorCache { - processor_destroy_cache(processorCache); -} - - +// +//+ (struct MasternodeProcessor *)registerProcessor { +// +// return register_processor( +// getMerkleRootByHash, +// getBlockHeightByHash, +// getBlockHashByHeight, +// getLLMQSnapshotByBlockHash, +// saveLLMQSnapshot, +// getCLSignatureByBlockHash, +// saveCLSignature, +// getMasternodeListByBlockHash, +// saveMasternodeList, +// destroyMasternodeList, +// addInsightForBlockHash, +// destroyU8, +// destroyLLMQSnapshot, +// shouldProcessDiffWithRange); +//} +// +//+ (void)unregisterProcessor:(struct MasternodeProcessor *)processor { +// unregister_processor(processor); +//} +// +///// +///// MARK: Creating/destroying opaque cache (which is important for storing some of the results between processing sessions) +///// +// +//+ (struct MasternodeProcessorCache *)createProcessorCache { +// return processor_create_cache(); +//} +// +//+ (void)destroyProcessorCache:(struct MasternodeProcessorCache *)processorCache { +// +// processor_destroy_cache(processorCache); +//} +// +// /// /// MARK: Call processing methods /// -- (void)processMasternodeDiffWith:(NSData *)message context:(DSMasternodeProcessorContext *)context completion:(void (^)(DSMnDiffProcessingResult *result))completion { - NSAssert(self.processor, @"processMasternodeDiffMessage: No processor created"); - DSLog(@"[%@] processMasternodeDiffWith: %@", context.chain.name, context); - MNListDiffResult *result = process_mnlistdiff_from_message(message.bytes, - message.length, - context.chain.chainType, - context.useInsightAsBackup, - context.isFromSnapshot, - context.peer ? context.peer.version : context.chain.protocolVersion, - self.processor, - self.processorCache, - AS_RUST(context)); - DSMnDiffProcessingResult *processingResult = [DSMnDiffProcessingResult processingResultWith:result onChain:context.chain]; - processor_destroy_mnlistdiff_result(result); - completion(processingResult); -} - -- (void)processQRInfoWith:(NSData *)message context:(DSMasternodeProcessorContext *)context completion:(void (^)(DSQRInfoProcessingResult *result))completion { - NSAssert(self.processor, @"processQRInfoMessage: No processor created"); - NSAssert(self.processorCache, @"processQRInfoMessage: No processorCache created"); - DSLog(@"[%@] processQRInfoWith: %@", context.chain.name, context); - QRInfoResult *result = process_qrinfo_from_message(message.bytes, - message.length, - context.chain.chainType, - context.useInsightAsBackup, - context.isFromSnapshot, - context.chain.isRotatedQuorumsPresented, - context.peer ? context.peer.version : context.chain.protocolVersion, - self.processor, - self.processorCache, - AS_RUST(context)); - DSQRInfoProcessingResult *processingResult = [DSQRInfoProcessingResult processingResultWith:result onChain:context.chain]; - processor_destroy_qr_info_result(result); - completion(processingResult); -} - -- (DSMnDiffProcessingResult *)processMasternodeDiffFromFile:(NSData *)message protocolVersion:(uint32_t)protocolVersion withContext:(DSMasternodeProcessorContext *)context { - NSAssert(self.processor, @"processMasternodeDiffMessage: No processor created"); - DSLog(@"[%@] processMasternodeDiffMessage: %@", context.chain.name, context); - MNListDiffResult *result = NULL; - @synchronized (context) { - result = process_mnlistdiff_from_message( - message.bytes, - message.length, - context.chain.chainType, - context.useInsightAsBackup, - context.isFromSnapshot, - protocolVersion, - self.processor, - self.processorCache, - AS_RUST(context)); - } - DSMnDiffProcessingResult *processingResult = [DSMnDiffProcessingResult processingResultWith:result onChain:context.chain]; - processor_destroy_mnlistdiff_result(result); - return processingResult; -} - -- (void)clearProcessorCache { - NSAssert(self.processorCache, @"clearProcessorCache: No processorCache created"); - processor_clear_cache(self.processorCache); -} - -- (void)removeMasternodeListFromCacheAtBlockHash:(UInt256)blockHash { - NSAssert(self.processorCache, @"removeMasternodeListFromCacheAtBlockHash: No processorCache created"); - processor_remove_masternode_list_from_cache_for_block_hash((const uint8_t *) blockHash.u8, self.processorCache); -} - -- (void)removeSnapshotFromCacheAtBlockHash:(UInt256)blockHash { - NSAssert(self.processorCache, @"removeSnapshotFromCacheAtBlockHash: No processorCache created"); - processor_remove_llmq_snapshot_from_cache_for_block_hash((const uint8_t *) blockHash.u8, self.processorCache); -} - - -@end +//- (void)processMasternodeDiffWith:(NSData *)message context:(DSMasternodeProcessorContext *)context completion:(void (^)(DSMnDiffProcessingResult *result))completion { +// NSAssert(self.processor, @"processMasternodeDiffMessage: No processor created"); +// DSLog(@"[%@] processMasternodeDiffWith: %@", context.chain.name, context); +// MNListDiffResult *result = process_mnlistdiff_from_message(message.bytes, +// message.length, +// context.chain.chainType, +// context.useInsightAsBackup, +// context.isFromSnapshot, +// context.peer ? context.peer.version : context.chain.protocolVersion, +// self.processor, +// self.processorCache, +// AS_RUST(context)); +// DSMnDiffProcessingResult *processingResult = [DSMnDiffProcessingResult processingResultWith:result onChain:context.chain]; +// processor_destroy_mnlistdiff_result(result); +// completion(processingResult); +//} +// +//- (void)processQRInfoWith:(NSData *)message context:(DSMasternodeProcessorContext *)context completion:(void (^)(DSQRInfoProcessingResult *result))completion { +// NSAssert(self.processor, @"processQRInfoMessage: No processor created"); +// NSAssert(self.processorCache, @"processQRInfoMessage: No processorCache created"); +// DSLog(@"[%@] processQRInfoWith: %@", context.chain.name, context); +// QRInfoResult *result = process_qrinfo_from_message(message.bytes, +// message.length, +// context.chain.chainType, +// context.useInsightAsBackup, +// context.isFromSnapshot, +// context.chain.isRotatedQuorumsPresented, +// context.peer ? context.peer.version : context.chain.protocolVersion, +// self.processor, +// self.processorCache, +// AS_RUST(context)); +// DSQRInfoProcessingResult *processingResult = [DSQRInfoProcessingResult processingResultWith:result onChain:context.chain]; +// processor_destroy_qr_info_result(result); +// completion(processingResult); +//} +// +//- (DSMnDiffProcessingResult *)processMasternodeDiffFromFile:(NSData *)message protocolVersion:(uint32_t)protocolVersion withContext:(DSMasternodeProcessorContext *)context { +// NSAssert(self.processor, @"processMasternodeDiffMessage: No processor created"); +// DSLog(@"[%@] processMasternodeDiffMessage: %@", context.chain.name, context); +// MNListDiffResult *result = NULL; +// @synchronized (context) { +// result = process_mnlistdiff_from_message( +// message.bytes, +// message.length, +// context.chain.chainType, +// context.useInsightAsBackup, +// context.isFromSnapshot, +// protocolVersion, +// self.processor, +// self.processorCache, +// AS_RUST(context)); +// } +// DSMnDiffProcessingResult *processingResult = [DSMnDiffProcessingResult processingResultWith:result onChain:context.chain]; +// processor_destroy_mnlistdiff_result(result); +// return processingResult; +//} +// +//- (void)clearProcessorCache { +// NSAssert(self.processorCache, @"clearProcessorCache: No processorCache created"); +// dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_clear(self.processorCache); +//} +// +//- (void)removeMasternodeListFromCacheAtBlockHash:(UInt256)blockHash { +// NSAssert(self.processorCache, @"removeMasternodeListFromCacheAtBlockHash: No processorCache created"); +// processor_remove_masternode_list_from_cache_for_block_hash((const uint8_t *) blockHash.u8, self.processorCache); +//} +// +//- (void)removeSnapshotFromCacheAtBlockHash:(UInt256)blockHash { +// NSAssert(self.processorCache, @"removeSnapshotFromCacheAtBlockHash: No processorCache created"); +// processor_remove_llmq_snapshot_from_cache_for_block_hash((const uint8_t *) blockHash.u8, self.processorCache); +//} +// +// +//@end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Protected.h b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Protected.h index e9ab3ff59..e791b2eda 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Protected.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Protected.h @@ -25,9 +25,9 @@ #import "DSMasternodeProcessorContext.h" #import "DSMasternodeManager.h" -#import "DSMnDiffProcessingResult.h" +//#import "DSMnDiffProcessingResult.h" #import "DSOperationQueue.h" -#import "DSQRInfoProcessingResult.h" +//#import "DSQRInfoProcessingResult.h" NS_ASSUME_NONNULL_BEGIN @@ -39,15 +39,23 @@ NS_ASSUME_NONNULL_BEGIN - (void)wipeMasternodeInfo; - (void)getRecentMasternodeList; - (void)getCurrentMasternodeListWithSafetyDelay:(uint32_t)safetyDelay; -- (void)getMasternodeListsForBlockHashes:(NSOrderedSet *)blockHashes; +//- (void)getMasternodeListsForBlockHashes:(NSOrderedSet *)blockHashes; - (void)peer:(DSPeer *)peer relayedMasternodeDiffMessage:(NSData *)message; - (void)peer:(DSPeer *)peer relayedQuorumRotationInfoMessage:(NSData *)message; -- (DSLocalMasternode *)localMasternodeFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry claimedWithOwnerWallet:(DSWallet *)wallet ownerKeyIndex:(uint32_t)ownerKeyIndex; +- (DSLocalMasternode *)localMasternodeFromSimplifiedMasternodeEntry:(DMasternodeEntry *)simplifiedMasternodeEntry + claimedWithOwnerWallet:(DSWallet *)wallet + ownerKeyIndex:(uint32_t)ownerKeyIndex + onChain:(DSChain *)chain; -+ (void)saveMasternodeList:(DSMasternodeList *)masternodeList toChain:(DSChain *)chain havingModifiedMasternodes:(NSDictionary *)modifiedMasternodes createUnknownBlocks:(BOOL)createUnknownBlocks inContext:(NSManagedObjectContext *)context completion:(void (^)(NSError *error))completion; ++ (nullable NSError *)saveMasternodeList:(DArcMasternodeList *)masternodeList + toChain:(DSChain *)chain + havingModifiedMasternodes:(DMasternodeEntryMap *)modifiedMasternodes + createUnknownBlocks:(BOOL)createUnknownBlocks + inContext:(NSManagedObjectContext *)context; - (BOOL)isMasternodeListOutdated; +- (BOOL)processRequestFromFileForBlockHash:(UInt256)blockHash; @end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.h index 6259fb70b..a3eeba5aa 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.h @@ -24,11 +24,8 @@ // THE SOFTWARE. #import "DSChain.h" -#import "DSMasternodeListService.h" +#import "DSKeyManager.h" #import "DSMasternodeListStore.h" -#import "DSMasternodeListDiffService.h" -#import "DSQuorumRotationService.h" -#import "DSQuorumSnapshot.h" #import "DSPeer.h" #import @@ -37,70 +34,64 @@ NS_ASSUME_NONNULL_BEGIN #define MASTERNODE_COST 100000000000 -@class DSPeer, DSChain, DSSimplifiedMasternodeEntry, DSWallet, DSLocalMasternode, DSProviderRegistrationTransaction, DSQuorumEntry, DSMasternodeList, DSInstantSendTransactionLock, DSMasternodeListService, DSQuorumRotationService, DSMasternodeListDiffService; +@class DSChain, DSWallet, DSLocalMasternode, DSProviderRegistrationTransaction, DSInstantSendTransactionLock, DSMasternodeListService, DSQuorumRotationService, DSMasternodeListDiffService, DSPeer; -@interface DSMasternodeManager : NSObject +@interface DSMasternodeManager : NSObject @property (nonatomic, readonly, nonnull) DSChain *chain; @property (nonatomic, readonly) NSUInteger simplifiedMasternodeEntryCount; @property (nonatomic, readonly) NSUInteger activeQuorumsCount; -@property (nonatomic, readonly) NSArray *recentMasternodeLists; @property (nonatomic, readonly) NSUInteger knownMasternodeListsCount; @property (nonatomic, readonly) uint32_t earliestMasternodeListBlockHeight; @property (nonatomic, readonly) uint32_t lastMasternodeListBlockHeight; -@property (nonatomic, readonly) DSMasternodeList *currentMasternodeList; +@property (nonatomic, readonly) DArcMasternodeList *currentMasternodeList; @property (nonatomic, readonly) NSUInteger masternodeListRetrievalQueueCount; @property (nonatomic, readonly) NSUInteger masternodeListRetrievalQueueMaxAmount; -@property (nonatomic, readonly) BOOL hasMasternodeListCurrentlyBeingSaved; @property (nonatomic, readonly) BOOL currentMasternodeListIsInLast24Hours; @property (nonatomic, readonly) DSMasternodeListStore *store; @property (nonatomic, readonly) DSMasternodeListDiffService *masternodeListDiffService; @property (nonatomic, readonly) DSQuorumRotationService *quorumRotationService; - -@property (nonatomic, readonly, nullable) MasternodeProcessor *processor; -@property (nonatomic, readonly, nullable) MasternodeProcessorCache *processorCache; @property (nonatomic, assign, readonly) uint32_t rotatedQuorumsActivationHeight; +@property (nonatomic, readonly) BOOL isSyncing; - (instancetype)init NS_UNAVAILABLE; - (uint32_t)heightForBlockHash:(UInt256)blockhash; - (BOOL)hasCurrentMasternodeListInLast30Days; -- (DSSimplifiedMasternodeEntry *)masternodeHavingProviderRegistrationTransactionHash:(NSData *)providerRegistrationTransactionHash; +- (DMasternodeEntry *)masternodeHavingProviderRegistrationTransactionHash:(NSData *)providerRegistrationTransactionHash; - (BOOL)hasMasternodeAtLocation:(UInt128)IPAddress port:(uint32_t)port; -- (DSQuorumEntry *_Nullable)quorumEntryForInstantSendRequestID:(UInt256)requestID withBlockHeightOffset:(uint32_t)blockHeightOffset; -- (DSQuorumEntry *_Nullable)quorumEntryForChainLockRequestID:(UInt256)requestID withBlockHeightOffset:(uint32_t)blockHeightOffset; -- (DSQuorumEntry *_Nullable)quorumEntryForChainLockRequestID:(UInt256)requestID forBlockHeight:(uint32_t)blockHeight; -- (DSQuorumEntry *_Nullable)quorumEntryForPlatformHavingQuorumHash:(UInt256)quorumHash forBlockHeight:(uint32_t)blockHeight; - -- (DSMasternodeList *_Nullable)masternodeListForBlockHash:(UInt256)blockHash withBlockHeightLookup:(uint32_t (^_Nullable)(UInt256 blockHash))blockHeightLookup; -- (DSMasternodeList *_Nullable)masternodeListForBlockHash:(UInt256)blockHash; +- (DLLMQEntry *_Nullable)quorumEntryForInstantSendRequestID:(UInt256)requestID + withBlockHeightOffset:(uint32_t)blockHeightOffset; +- (DLLMQEntry *_Nullable)quorumEntryForChainLockRequestID:(UInt256)requestID + withBlockHeightOffset:(uint32_t)blockHeightOffset; +- (DLLMQEntry *_Nullable)quorumEntryForChainLockRequestID:(UInt256)requestID + forBlockHeight:(uint32_t)blockHeight; +- (DLLMQEntry *_Nullable)quorumEntryForPlatformHavingQuorumHash:(UInt256)quorumHash + forBlockHeight:(uint32_t)blockHeight; -/// Rust helpers -- (DSQuorumSnapshot *_Nullable)quorumSnapshotForBlockHeight:(uint32_t)blockHeight; -- (DSQuorumSnapshot *_Nullable)quorumSnapshotForBlockHash:(UInt256)blockHash; -- (BOOL)saveQuorumSnapshot:(DSQuorumSnapshot *)snapshot; -- (BOOL)saveMasternodeList:(DSMasternodeList *)masternodeList forBlockHash:(UInt256)blockHash; - -- (NSData *_Nullable)CLSignatureForBlockHeight:(uint32_t)blockHeight; -- (NSData *_Nullable)CLSignatureForBlockHash:(UInt256)blockHash; -- (BOOL)saveCLSignature:(NSData *)blockHashData signatureData:(NSData *)signatureData; +- (DArcMasternodeList *_Nullable)masternodeListForBlockHash:(UInt256)blockHash + withBlockHeightLookup:(uint32_t (^_Nullable)(UInt256 blockHash))blockHeightLookup; +- (DArcMasternodeList *_Nullable)masternodeListForBlockHash:(UInt256)blockHash; - (void)startSync; - (void)stopSync; -- (BOOL)requestMasternodeListForBlockHeight:(uint32_t)blockHeight error:(NSError *_Nullable *_Nullable)error; -- (BOOL)requestMasternodeListForBlockHash:(UInt256)blockHash; +- (BOOL)requestMasternodeListForBlockHeight:(uint32_t)blockHeight + error:(NSError *_Nullable *_Nullable)error; /// Returns current masternode list -- (DSMasternodeList *_Nullable)reloadMasternodeLists; -- (DSMasternodeList *_Nullable)reloadMasternodeListsWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; -- (void)destroyProcessors; +- (DArcMasternodeList *_Nullable)reloadMasternodeLists; +- (DArcMasternodeList *_Nullable)reloadMasternodeListsWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; + +- (void)checkPingTimesForCurrentMasternodeListInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(NSMutableDictionary *pingTimes, NSMutableDictionary *errors))completion; -- (void)checkPingTimesForCurrentMasternodeListInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(NSMutableDictionary *pingTimes, NSMutableDictionary *errors))completion; -- (UInt256)buildLLMQHashFor:(DSQuorumEntry *)quorum; +- (BOOL)masternodeListServiceDidRequestFileFromBlockHash:(DSMasternodeListService *)service blockHash:(UInt256)blockHash; +//- (void)masternodeListServiceExceededMaxFailuresForMasternodeList:(DSMasternodeListService *)service blockHash:(UInt256)blockHash; +- (void)masternodeListServiceEmptiedRetrievalQueue:(DSMasternodeListService *)service; @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m index d63a89b47..1d5b45f10 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m @@ -24,27 +24,16 @@ // THE SOFTWARE. #import "DSMasternodeManager.h" +#import "DSChain+Checkpoint.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" -#import "DSChainLock.h" #import "DSChainManager+Protected.h" #import "DSCheckpoint.h" -#import "DSGetMNListDiffRequest.h" -#import "DSGetQRInfoRequest.h" -#import "DSMasternodeProcessorContext.h" #import "DSMasternodeListService+Protected.h" -#import "DSMasternodeListStore+Protected.h" -#import "DSMasternodeManager+LocalMasternode.h" -#import "DSMasternodeManager+Mndiff.h" -#import "DSMasternodeManager+Protected.h" +#import "DSMasternodeListDiffService.h" +#import "DSQuorumRotationService.h" #import "DSMerkleBlock.h" -#import "DSMnDiffProcessingResult.h" -#import "DSOperationQueue.h" #import "DSOptionsManager.h" -#import "DSPeer.h" -#import "DSPeerManager+Protected.h" -#import "DSQRInfoProcessingResult.h" -#import "DSSimplifiedMasternodeEntry.h" -#import "DSTransactionManager+Protected.h" #import "NSError+Dash.h" #define SAVE_MASTERNODE_DIFF_TO_FILE (0 && DEBUG) @@ -59,9 +48,6 @@ @interface DSMasternodeManager () @property (nonatomic, strong) DSQuorumRotationService *quorumRotationService; @property (nonatomic, assign) NSTimeInterval timeIntervalForMasternodeRetrievalSafetyDelay; -@property (nonatomic, assign, nullable) MasternodeProcessor *processor; -@property (nonatomic, assign, nullable) MasternodeProcessorCache *processorCache; - @property (nonatomic, assign) uint32_t rotatedQuorumsActivationHeight; @property (nonatomic, strong) dispatch_group_t processingGroup; @property (nonatomic, strong) dispatch_queue_t processingQueue; @@ -72,19 +58,11 @@ @interface DSMasternodeManager () @implementation DSMasternodeManager -- (void)dealloc { - [self destroyProcessors]; -} - -- (void)destroyProcessors { - [DSMasternodeManager unregisterProcessor:self.processor]; - [DSMasternodeManager destroyProcessorCache:self.processorCache]; - _processor = nil; - _processorCache = nil; -} - - (BOOL)hasCurrentMasternodeListInLast30Days { - return self.currentMasternodeList && [[NSDate date] timeIntervalSince1970] - [self.chain timestampForBlockHeight:self.currentMasternodeList.height] < DAY_TIME_INTERVAL * 30; + if (self.currentMasternodeList && (!self.currentMasternodeList->obj->known_height || self.currentMasternodeList->obj->known_height == UINT32_MAX)) { + self.currentMasternodeList->obj->known_height = [self.chain heightForBlockHash:u256_cast(self.currentMasternodeList->obj->block_hash)]; + } + return self.currentMasternodeList && [[NSDate date] timeIntervalSince1970] - [self.chain timestampForBlockHeight:self.currentMasternodeList->obj->known_height] < DAY_TIME_INTERVAL * 30; } - (instancetype)initWithChain:(DSChain *)chain { @@ -92,55 +70,61 @@ - (instancetype)initWithChain:(DSChain *)chain { if (!(self = [super init])) return nil; _chain = chain; _store = [[DSMasternodeListStore alloc] initWithChain:chain]; - self.masternodeListDiffService = [[DSMasternodeListDiffService alloc] initWithChain:chain store:_store delegate:self]; - self.quorumRotationService = [[DSQuorumRotationService alloc] initWithChain:chain store:_store delegate:self]; + self.masternodeListDiffService = [[DSMasternodeListDiffService alloc] initWithChain:chain store:_store]; + self.quorumRotationService = [[DSQuorumRotationService alloc] initWithChain:chain store:_store]; _rotatedQuorumsActivationHeight = UINT32_MAX; - _processor = [DSMasternodeManager registerProcessor]; - _processorCache = [DSMasternodeManager createProcessorCache]; _processingGroup = dispatch_group_create(); _processingQueue = dispatch_queue_create([[NSString stringWithFormat:@"org.dashcore.dashsync.processing.%@", uint256_data(self.chain.genesisHash).shortHexString] UTF8String], DISPATCH_QUEUE_SERIAL); return self; } +- (MasternodeProcessor *)processor { + return self.chain.shareCore.processor->obj; +} +- (MasternodeProcessorCache *)cache { + return self.chain.shareCore.cache->obj; +} + + #pragma mark - DSMasternodeListServiceDelegate -- (DSMasternodeList *__nullable)masternodeListServiceDidRequestFileFromBlockHash:(DSMasternodeListService *)service blockHash:(UInt256)blockHash { +- (BOOL)masternodeListServiceDidRequestFileFromBlockHash:(DSMasternodeListService *)service blockHash:(UInt256)blockHash { return [self processRequestFromFileForBlockHash:blockHash]; } - -- (void)masternodeListServiceExceededMaxFailuresForMasternodeList:(DSMasternodeListService *)service blockHash:(UInt256)blockHash { - [self removeOutdatedMasternodeListsBeforeBlockHash:blockHash]; -} - +// +//- (void)masternodeListServiceExceededMaxFailuresForMasternodeList:(DSMasternodeListService *)service blockHash:(UInt256)blockHash { +// [self.store removeOldMasternodeLists]; +//} +// - (void)masternodeListServiceEmptiedRetrievalQueue:(DSMasternodeListService *)service { if (![self.masternodeListDiffService retrievalQueueCount]) { - if (![self.quorumRotationService retrievalQueueCount]) - [self removeOutdatedMasternodeListsBeforeBlockHash:self.store.lastQueriedBlockHash]; - [self.chain.chainManager chainFinishedSyncingMasternodeListsAndQuorums:self.chain]; + if (![self.quorumRotationService retrievalQueueCount]) { + [self.store removeOldMasternodeLists]; + } + if (dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_has_last_queried_qr_masternode_list_at_tip(self.chain.shareCore.cache->obj)) { + [self.chain.chainManager chainFinishedSyncingMasternodeListsAndQuorums:self.chain]; + } } } // MARK: - Helpers -- (NSArray *)recentMasternodeLists { - return [self.store recentMasternodeLists]; -} - - (NSUInteger)knownMasternodeListsCount { - return [self.store knownMasternodeListsCount]; + return DKnownMasternodeListsCount(self.chain.shareCore.cache->obj); } - (uint32_t)earliestMasternodeListBlockHeight { - return [self.store earliestMasternodeListBlockHeight]; + return dash_spv_masternode_processor_processing_processor_MasternodeProcessor_earliest_masternode_list_block_height(self.processor); } - (uint32_t)lastMasternodeListBlockHeight { - return [self.store lastMasternodeListBlockHeight]; + return DLastMasternodeListBlockHeight(self.processor); } - (uint32_t)heightForBlockHash:(UInt256)blockhash { - return [self.store heightForBlockHash:blockhash]; + u256 *block_hash = u256_ctor_u(blockhash); + return DHeightForBlockHash(self.processor, block_hash); } - (BOOL)isMasternodeListOutdated { @@ -148,26 +132,25 @@ - (BOOL)isMasternodeListOutdated { return lastHeight == UINT32_MAX || lastHeight < self.chain.lastTerminalBlockHeight - 8; } -- (DSSimplifiedMasternodeEntry *)masternodeHavingProviderRegistrationTransactionHash:(NSData *)providerRegistrationTransactionHash { +- (DMasternodeEntry *)masternodeHavingProviderRegistrationTransactionHash:(NSData *)providerRegistrationTransactionHash { NSParameterAssert(providerRegistrationTransactionHash); - return [self.currentMasternodeList.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash objectForKey:providerRegistrationTransactionHash]; + u256 *pro_reg_tx_hash = u256_ctor(providerRegistrationTransactionHash); + DMasternodeEntry *entry = masternode_by_pro_reg_tx_hash(self.currentMasternodeList->obj->masternodes, pro_reg_tx_hash); + return entry; } - (NSUInteger)simplifiedMasternodeEntryCount { - return [self.currentMasternodeList masternodeCount]; + return self.currentMasternodeList ? dash_spv_masternode_processor_models_masternode_list_MasternodeList_masternode_count(self.currentMasternodeList->obj) : 0; } - (NSUInteger)activeQuorumsCount { - return [self.currentMasternodeList quorumsCount]; + return self.currentMasternodeList ? dash_spv_masternode_processor_models_masternode_list_MasternodeList_quorums_count(self.currentMasternodeList->obj) : 0; } - (BOOL)hasMasternodeAtLocation:(UInt128)IPAddress port:(uint32_t)port { - for (DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry in [self.currentMasternodeList.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash allValues]) { - if (uint128_eq(simplifiedMasternodeEntry.address, IPAddress) && simplifiedMasternodeEntry.port == port) { - return YES; - } - } - return NO; + u128 *addr = u128_ctor_u(IPAddress); + BOOL result = dash_spv_masternode_processor_models_masternode_list_MasternodeList_has_masternode_at_location(self.currentMasternodeList->obj, addr, (uint16_t) port); + return result; } - (NSUInteger)masternodeListRetrievalQueueCount { @@ -182,7 +165,7 @@ - (BOOL)currentMasternodeListIsInLast24Hours { if (!self.currentMasternodeList) { return NO; } - DSBlock *block = [self.chain blockForBlockHash:self.currentMasternodeList.blockHash]; + DSBlock *block = [self.chain blockForBlockHash:u256_cast(self.currentMasternodeList->obj->block_hash)]; if (!block) return FALSE; NSTimeInterval currentTimestamp = [[NSDate date] timeIntervalSince1970]; NSTimeInterval delta = currentTimestamp - block.timestamp; @@ -193,35 +176,25 @@ - (BOOL)currentMasternodeListIsInLast24Hours { // MARK: - Set Up and Tear Down - (void)setUp { - __weak typeof(self) weakSelf = self; - [self.store setUp:^(DSMasternodeList * _Nonnull masternodeList) { - __strong typeof(weakSelf) strongSelf = weakSelf; - if (!strongSelf) { - return; - } - strongSelf.masternodeListDiffService.currentMasternodeList = masternodeList; - }]; + [self.store setUp]; [self loadFileDistributedMasternodeLists]; } -- (DSMasternodeList *_Nullable)reloadMasternodeLists { - return [self reloadMasternodeListsWithBlockHeightLookup:nil]; +- (DArcMasternodeList *_Nullable)reloadMasternodeLists { + return [self reloadMasternodeListsWithBlockHeightLookup:^uint32_t(UInt256 blockHash) { + return [self.chain heightForBlockHash:blockHash]; + }]; } -- (DSMasternodeList *_Nullable)reloadMasternodeListsWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { - [self clearProcessorCache]; - [self.store removeAllMasternodeLists]; +- (DArcMasternodeList *)reloadMasternodeListsWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { + dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_clear(self.chain.shareCore.cache->obj); + [self.chain.chainManager notifyMasternodeSyncStateChange:UINT32_MAX storedCount:0]; + return [self.store loadMasternodeListsWithBlockHeightLookup:blockHeightLookup]; } -- (DSMasternodeList *)currentMasternodeList { - if (!self.chain.isRotatedQuorumsPresented) { - return self.masternodeListDiffService.currentMasternodeList; - } else { - UInt256 lastMnlistDiffBlockHash = self.masternodeListDiffService.currentMasternodeList.blockHash; - UInt256 lastQrInfoDiffBlockHash = self.quorumRotationService.currentMasternodeList.blockHash; - return [self heightForBlockHash:lastMnlistDiffBlockHash] > [self heightForBlockHash:lastQrInfoDiffBlockHash] ? self.masternodeListDiffService.currentMasternodeList : self.quorumRotationService.currentMasternodeList; - } +- (DArcMasternodeList *)currentMasternodeList { + return dash_spv_masternode_processor_processing_processor_MasternodeProcessor_current_masternode_list(self.chain.masternodeManager.processor, self.chain.isRotatedQuorumsPresented); } - (void)loadFileDistributedMasternodeLists { @@ -238,99 +211,53 @@ - (void)loadFileDistributedMasternodeLists { [self masternodeListForBlockHash:checkpoint.blockHash withBlockHeightLookup:nil]) { return; } - DSMasternodeList *masternodeList = [self processRequestFromFileForBlockHash:checkpoint.blockHash]; - if (masternodeList) { - self.masternodeListDiffService.currentMasternodeList = masternodeList; - } -} + BOOL exist = [self processRequestFromFileForBlockHash:checkpoint.blockHash]; + if (exist) { +// TODO: re-implement +// dispatch_async(dispatch_get_main_queue(), ^{ +// [[NSNotificationCenter defaultCenter] postNotificationName:DSCurrentMasternodeListDidChangeNotification +// object:nil +// userInfo:@{ +// DSChainManagerNotificationChainKey: self.chain, +// DSMasternodeManagerNotificationMasternodeListKey: self.currentMasternodeList +// ? [NSValue valueWithPointer:dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_get_last_queried_mn_masternode_list(self.processorCache)] +// : [NSNull null] +// }]; +// }); -- (DSMasternodeList *)loadMasternodeListAtBlockHash:(NSData *)blockHash withBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { - return [self.store loadMasternodeListAtBlockHash:blockHash withBlockHeightLookup:blockHeightLookup]; +// self.masternodeListDiffService.currentMasternodeList = result->ok->masternode_list; + } } - (void)wipeMasternodeInfo { DSLog(@"[%@] wipeMasternodeInfo", self.chain.name); - [self clearProcessorCache]; - [self.store removeAllMasternodeLists]; + dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_clear(self.chain.shareCore.cache->obj); [self.masternodeListDiffService cleanAllLists]; [self.quorumRotationService cleanAllLists]; + [self.chain.chainManager notifyMasternodeSyncStateChange:UINT32_MAX storedCount:0]; dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:DSMasternodeListDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain}]; [[NSNotificationCenter defaultCenter] postNotificationName:DSQuorumListDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain}]; }); } -// MARK: - LLMQ Snapshot List Helpers -- (DSQuorumSnapshot *_Nullable)quorumSnapshotForBlockHeight:(uint32_t)blockHeight { - DSBlock *block = [self.chain blockAtHeight:blockHeight]; - if (!block) { - DSLog(@"[%@] No block for snapshot at height: %ul: ", self.chain.name, blockHeight); - return nil; - } - return [self.store.cachedQuorumSnapshots objectForKey:uint256_data(block.blockHash)]; -} - -- (DSQuorumSnapshot *_Nullable)quorumSnapshotForBlockHash:(UInt256)blockHash { - return [self.store.cachedQuorumSnapshots objectForKey:uint256_data(blockHash)]; -} - -- (BOOL)saveQuorumSnapshot:(DSQuorumSnapshot *)snapshot { - [self.store.cachedQuorumSnapshots setObject:snapshot forKey:uint256_data(snapshot.blockHash)]; - return YES; -} - -// MARK: - ChainLock Signature List Helpers -- (NSData *_Nullable)CLSignatureForBlockHeight:(uint32_t)blockHeight { - DSBlock *block = [self.chain blockAtHeight:blockHeight]; - if (!block) { - DSLog(@"[%@] No block for snapshot at height: %ul: ", self.chain.name, blockHeight); - return nil; - } - return [self.store.cachedCLSignatures objectForKey:uint256_data(block.blockHash)]; -} - -- (NSData *_Nullable)CLSignatureForBlockHash:(UInt256)blockHash { - NSData *cachedSig = [self.store.cachedCLSignatures objectForKey:uint256_data(blockHash)]; - if (!cachedSig) { - DSChainLock *chainLock = [self.chain.chainManager chainLockForBlockHash:blockHash]; - if (chainLock) { - return uint768_data(chainLock.signature); - } - } - return cachedSig; -} - -- (BOOL)saveCLSignature:(NSData *)blockHashData signatureData:(NSData *)signatureData { - [self.store.cachedCLSignatures setObject:signatureData forKey:blockHashData]; - return YES; -} - // MARK: - Masternode List Helpers -- (BOOL)saveMasternodeList:(DSMasternodeList *)masternodeList forBlockHash:(UInt256)blockHash { - /// TODO: need to properly store in CoreData or wait for rust SQLite - //DSLog(@"[%@] ••• cache mnlist -> %@: %@", self.chain.name, uint256_hex(blockHash), masternodeList); - [self.store.masternodeListsByBlockHash setObject:masternodeList forKey:uint256_data(blockHash)]; - uint32_t lastHeight = self.lastMasternodeListBlockHeight; - @synchronized (self.chain.chainManager.syncState) { - self.chain.chainManager.syncState.masternodeListSyncInfo.lastBlockHeight = lastHeight; - self.chain.chainManager.syncState.masternodeListSyncInfo.storedCount = self.store.masternodeListsByBlockHash.count; - DSLog(@"[%@] [DSMasternodeManager] New List Stored: %u/%lu", self.chain.name, lastHeight, self.store.masternodeListsByBlockHash.count); - [self.chain.chainManager notifySyncStateChanged]; - } - return YES; -} - -- (DSMasternodeList *)masternodeListForBlockHash:(UInt256)blockHash { +- (DArcMasternodeList *)masternodeListForBlockHash:(UInt256)blockHash { return [self masternodeListForBlockHash:blockHash withBlockHeightLookup:nil]; } -- (DSMasternodeList *)masternodeListForBlockHash:(UInt256)blockHash withBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { - return [self.store masternodeListForBlockHash:blockHash withBlockHeightLookup:blockHeightLookup]; +- (DArcMasternodeList *)masternodeListForBlockHash:(UInt256)blockHash + withBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { + u256 *block_hash = u256_ctor_u(blockHash); + DArcMasternodeList *list = dash_spv_masternode_processor_processing_processor_MasternodeProcessor_masternode_list_for_block_hash(self.processor, block_hash); + return list; } -- (DSMasternodeList *)masternodeListBeforeBlockHash:(UInt256)blockHash { - return [self.store masternodeListBeforeBlockHash:blockHash]; +- (DArcMasternodeList *)masternodeListBeforeBlockHash:(UInt256)blockHash { + u256 *arr = u256_ctor_u(blockHash); + DArcMasternodeList *list = dash_spv_masternode_processor_processing_processor_MasternodeProcessor_masternode_list_before_block_hash(self.processor, arr); + return list; } // MARK: - Requesting Masternode List @@ -345,6 +272,7 @@ - (void)stopSync { DSLog(@"[%@] [DSMasternodeManager] stopSync", self.chain.name); self.isSyncing = NO; [self cancelMasternodeListTimer]; + dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_clear_current_lists(self.chain.shareCore.cache->obj); if (self.chain.isRotatedQuorumsPresented) { [self.quorumRotationService stop]; } @@ -386,9 +314,6 @@ - (void)cancelMasternodeListTimer { } } } -- (void)getMasternodeListsForBlockHashes:(NSOrderedSet *)blockHashes { - [self.masternodeListDiffService populateRetrievalQueueWithBlockHashes:blockHashes]; -} - (BOOL)requestMasternodeListForBlockHeight:(uint32_t)blockHeight error:(NSError **)error { DSMerkleBlock *merkleBlock = [self.chain blockAtHeight:blockHeight]; @@ -398,65 +323,43 @@ - (BOOL)requestMasternodeListForBlockHeight:(uint32_t)blockHeight error:(NSError } return FALSE; } - [self requestMasternodeListForBlockHash:merkleBlock.blockHash]; + UInt256 blockHash = merkleBlock.blockHash; + u256 *block_hash = u256_ctor_u(blockHash); + dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_set_last_queried_block_hash(self.chain.shareCore.cache->obj, block_hash); + dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_add_block_hash_for_list_needing_quorums_validated(self.chain.shareCore.cache->obj, block_hash); + dash_spv_masternode_processor_processing_processor_MasternodeProcessor_add_to_mn_list_retrieval_queue(self.chain.shareCore.processor->obj, block_hash); + [self.masternodeListDiffService dequeueMasternodeListRequest]; return TRUE; } -- (BOOL)requestMasternodeListForBlockHash:(UInt256)blockHash { - self.store.lastQueriedBlockHash = blockHash; - NSData *blockHashData = uint256_data(blockHash); - @synchronized (self.store.masternodeListQueriesNeedingQuorumsValidated) { - [self.store.masternodeListQueriesNeedingQuorumsValidated addObject:blockHashData]; - } - // this is safe - [self getMasternodeListsForBlockHashes:[NSOrderedSet orderedSetWithObject:blockHashData]]; - return TRUE; -} - -- (DSMasternodeList *__nullable)processRequestFromFileForBlockHash:(UInt256)blockHash { +- (BOOL)processRequestFromFileForBlockHash:(UInt256)blockHash { DSCheckpoint *checkpoint = [self.chain checkpointForBlockHash:blockHash]; if (!checkpoint || !checkpoint.masternodeListName || [checkpoint.masternodeListName isEqualToString:@""]) { - return nil; + return NO; } NSString *bundlePath = [[NSBundle bundleForClass:self.class] pathForResource:@"DashSync" ofType:@"bundle"]; NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; NSString *masternodeListName = checkpoint.masternodeListName; NSString *filePath = [bundle pathForResource:masternodeListName ofType:@"dat"]; if (!filePath) { - return nil; + return NO; } NSData *message = [NSData dataWithContentsOfFile:filePath]; if (!message) { - return NULL; - } - - MerkleBlockFinder blockFinder = ^DSMerkleBlock *(UInt256 blockHash) { - return [self.chain blockForBlockHash:blockHash]; - }; - DSMasternodeProcessorContext *context = [self createDiffMessageContext:NO isFromSnapshot:YES isDIP0024:NO peer:nil merkleRootLookup:^UInt256(UInt256 blockHash) { - return blockFinder(blockHash).merkleRoot; - }]; - DSMnDiffProcessingResult *result = [self processMasternodeDiffFromFile:message protocolVersion:[checkpoint protocolVersion] withContext:context]; - - __block DSMerkleBlock *block = blockFinder(blockHash); - if (![result isValid]) { - DSLog(@"[%@] Invalid File for block at height %u with merkleRoot %@ (foundCoinbase %@ | validQuorums %@ | rootMNListValid %@ | rootQuorumListValid %@)", self.chain.name, block.height, uint256_hex(block.merkleRoot), result.foundCoinbase?@"Yes":@"No", result.validQuorums?@"Yes":@"No", result.rootMNListValid?@"Yes":@"No", result.rootQuorumListValid?@"Yes":@"No"); - return NULL; + return NO; } - // valid Coinbase might be false if no merkle block - if (block && !result.validCoinbase) { - DSLog(@"[%@] Invalid Coinbase for block at height %u with merkleRoot %@", self.chain.name, block.height, uint256_hex(block.merkleRoot)); - return NULL; + Slice_u8 *message_slice = slice_ctor(message); + DMnDiffResult *result = dash_spv_masternode_processor_processing_processor_MasternodeProcessor_mn_list_diff_result_from_file(self.processor, message_slice, [checkpoint protocolVersion]); + + DSMerkleBlock *block = [self.chain blockForBlockHash:blockHash]; + if (result->error) { + DSLog(@"[%@] ProcessingError while reading %@ for block at height %u with merkleRoot %@", self.chain.name, masternodeListName, block.height, uint256_hex(block.merkleRoot)); + DMnDiffResultDtor(result); + return NO; } - DSMasternodeList *masternodeList = result.masternodeList; - [self.store saveMasternodeList:masternodeList - addedMasternodes:result.addedMasternodes - modifiedMasternodes:result.modifiedMasternodes - completion:^(NSError *_Nonnull error) { - DSLog(@"[%@] MNL Saved from file", self.chain.name); - }]; - return masternodeList; + DMnDiffResultDtor(result); + return YES; } @@ -481,260 +384,6 @@ - (DSBlock *)lastBlockForBlockHash:(UInt256)blockHash fromPeer:(DSPeer *)peer { return lastBlock; } -- (NSString *)logListSet:(NSOrderedSet *)list { - NSString *str = @"\n"; - for (NSData *blockHashData in list) { - str = [str stringByAppendingString:[NSString stringWithFormat:@"•••• -> %d: %@,\n", [self heightForBlockHash:blockHashData.UInt256], blockHashData.hexString]]; - } - return str; -} - -- (void)removeOutdatedMasternodeListsBeforeBlockHash:(UInt256)blockHash { - DSMasternodeList *qrinfoMasternodeList = self.quorumRotationService.masternodeListAtH4C; - if (!qrinfoMasternodeList) { - qrinfoMasternodeList = self.quorumRotationService.masternodeListAtH3C; - } - DSMasternodeList *diffMasternodeList = self.masternodeListDiffService.currentMasternodeList; - uint32_t heightToDelete = UINT32_MAX; - if (diffMasternodeList) { - heightToDelete = diffMasternodeList.height; - NSData *oldestHashInDiffQueue = [self.masternodeListDiffService.retrievalQueue firstObject]; - if (oldestHashInDiffQueue) { - uint32_t oldestHeight = [self heightForBlockHash:oldestHashInDiffQueue.UInt256]; - if (heightToDelete > oldestHeight) { - heightToDelete = oldestHeight; - } - } - } else { - // Don't remove if we didn't get updates from mnlistdiff - return; - } - if (qrinfoMasternodeList) { - if (heightToDelete > qrinfoMasternodeList.height) { - heightToDelete = qrinfoMasternodeList.height; - } - NSData *oldestHashInQRInfoQueue = [self.quorumRotationService.retrievalQueue firstObject]; - if (oldestHashInQRInfoQueue) { - uint32_t oldestHeight = [self heightForBlockHash:oldestHashInQRInfoQueue.UInt256]; - if (heightToDelete > oldestHeight) { - heightToDelete = oldestHeight; - } - } - } else { - // Don't remove if we didn't get updates from qrinfo - return; - } - if (heightToDelete > 0 && heightToDelete != UINT32_MAX) { - DSLog(@"[%@] --> removeOldMasternodeLists (removeOutdatedMasternodeListsBeforeBlockHash): %u (%u, %u)", self.chain.name, heightToDelete, diffMasternodeList.height, qrinfoMasternodeList.height); - uint32_t h = heightToDelete - 50; - NSDictionary *lists = [[self.store masternodeListsByBlockHash] copy]; - for (NSData *proRegTxHashData in lists) { - DSMasternodeList *list = lists[proRegTxHashData]; - if (list.height < h) { - [self removeMasternodeListFromCacheAtBlockHash:list.blockHash]; - } - } - [self.store removeOldMasternodeLists:heightToDelete]; - } -} - -- (void)processMasternodeListDiffResult:(DSMnDiffProcessingResult *)result forPeer:(DSPeer *)peer skipPresenceInRetrieval:(BOOL)skipPresenceInRetrieval completion:(void (^)(void))completion { - DSMasternodeList *masternodeList = result.masternodeList; - DSLog(@"[%@] •••• processMasternodeListDiffResult: isValid: %d validCoinbase: %d", self.chain.name, [result isValid], result.validCoinbase); - if ([self.masternodeListDiffService shouldProcessDiffResult:result skipPresenceInRetrieval:skipPresenceInRetrieval]) { - NSOrderedSet *neededMissingMasternodeLists = result.neededMissingMasternodeLists; - DSLog(@"[%@] •••• processMasternodeListDiffResult: missingMasternodeLists: %@", self.chain.name, [self logListSet:neededMissingMasternodeLists]); - UInt256 masternodeListBlockHash = masternodeList.blockHash; - NSData *masternodeListBlockHashData = uint256_data(masternodeListBlockHash); - BOOL hasAwaitingQuorumValidation; - @synchronized (self.store.masternodeListQueriesNeedingQuorumsValidated) { - hasAwaitingQuorumValidation = [self.store.masternodeListQueriesNeedingQuorumsValidated containsObject:masternodeListBlockHashData]; - } - - if ([neededMissingMasternodeLists count] && hasAwaitingQuorumValidation) { - [self.masternodeListDiffService removeFromRetrievalQueue:masternodeListBlockHashData]; - [self processMissingMasternodeLists:neededMissingMasternodeLists forMasternodeList:masternodeList]; - completion(); - } else { - if (uint256_eq(self.store.lastQueriedBlockHash, masternodeListBlockHash)) { - self.masternodeListDiffService.currentMasternodeList = masternodeList; - @synchronized (self.store.masternodeListQueriesNeedingQuorumsValidated) { - [self.store.masternodeListQueriesNeedingQuorumsValidated removeObject:masternodeListBlockHashData]; - } - } - DSLog(@"[%@] ••• updateStoreWithMasternodeList: %u: %@ (%@)", self.chain.name, masternodeList.height, uint256_hex(masternodeListBlockHash), uint256_reverse_hex(masternodeListBlockHash)); - [self updateStoreWithProcessingResult:masternodeList result:result completion:^(NSError *error) { - if ([result hasRotatedQuorumsForChain:self.chain] && !self.chain.isRotatedQuorumsPresented) { - uint32_t masternodeListBlockHeight = [self heightForBlockHash:masternodeListBlockHash]; - DSLog(@"[%@] •••• processMasternodeListDiffResult: rotated quorums are presented at height %u: %@, so we'll switch into consuming qrinfo", self.chain.name, masternodeListBlockHeight, uint256_hex(masternodeListBlockHash)); - self.chain.isRotatedQuorumsPresented = YES; - self.rotatedQuorumsActivationHeight = masternodeListBlockHeight; - if (self.isSyncing) { - [self.quorumRotationService addToRetrievalQueue:masternodeListBlockHashData]; - [self.quorumRotationService dequeueMasternodeListRequest]; - } - } - if (self.isSyncing) - [self.masternodeListDiffService updateAfterProcessingMasternodeListWithBlockHash:masternodeListBlockHashData fromPeer:peer]; - completion(); - }]; - } - } else { - [self.masternodeListDiffService issueWithMasternodeListFromPeer:peer]; - completion(); - } -} -- (void)processQRInfoResult:(DSQRInfoProcessingResult *)result forPeer:(DSPeer *)peer completion:(void (^)(void))completion { - - DSMnDiffProcessingResult *mnListDiffResultAtTip = result.mnListDiffResultAtTip; - DSMnDiffProcessingResult *mnListDiffResultAtH = result.mnListDiffResultAtH; - DSMnDiffProcessingResult *mnListDiffResultAtHC = result.mnListDiffResultAtHC; - DSMnDiffProcessingResult *mnListDiffResultAtH2C = result.mnListDiffResultAtH2C; - DSMnDiffProcessingResult *mnListDiffResultAtH3C = result.mnListDiffResultAtH3C; - DSMnDiffProcessingResult *mnListDiffResultAtH4C = result.mnListDiffResultAtH4C; - DSLog(@"[%@] •••• processQRInfoResult tip: %d", self.chain.name, [mnListDiffResultAtTip isValid]); - - NSOrderedSet *missingMasternodeListsAtTip = mnListDiffResultAtTip.neededMissingMasternodeLists; - NSOrderedSet *missingMasternodeListsAtH = mnListDiffResultAtH.neededMissingMasternodeLists; - NSOrderedSet *missingMasternodeListsAtHC = mnListDiffResultAtHC.neededMissingMasternodeLists; - NSOrderedSet *missingMasternodeListsAtH2C = mnListDiffResultAtH2C.neededMissingMasternodeLists; - NSOrderedSet *missingMasternodeListsAtH3C = mnListDiffResultAtH3C.neededMissingMasternodeLists; - NSOrderedSet *missingMasternodeListsAtH4C = mnListDiffResultAtH4C.neededMissingMasternodeLists; - - NSMutableOrderedSet *missingMasternodeLists = [NSMutableOrderedSet orderedSet]; - [missingMasternodeLists addObjectsFromArray:[missingMasternodeListsAtTip array]]; - [missingMasternodeLists addObjectsFromArray:[missingMasternodeListsAtH array]]; - [missingMasternodeLists addObjectsFromArray:[missingMasternodeListsAtHC array]]; - [missingMasternodeLists addObjectsFromArray:[missingMasternodeListsAtH2C array]]; - [missingMasternodeLists addObjectsFromArray:[missingMasternodeListsAtH3C array]]; - [missingMasternodeLists addObjectsFromArray:[missingMasternodeListsAtH4C array]]; - DSLog(@"[%@] •••• processQRInfoResult: missingMasternodeLists: %@", self.chain.name, [self logListSet:missingMasternodeLists]); - - DSMasternodeList *masternodeListAtTip = mnListDiffResultAtTip.masternodeList; - DSMasternodeList *masternodeListAtH = mnListDiffResultAtH.masternodeList; - DSMasternodeList *masternodeListAtHC = mnListDiffResultAtHC.masternodeList; - DSMasternodeList *masternodeListAtH2C = mnListDiffResultAtH2C.masternodeList; - DSMasternodeList *masternodeListAtH3C = mnListDiffResultAtH3C.masternodeList; - DSMasternodeList *masternodeListAtH4C = mnListDiffResultAtH4C.masternodeList; - self.quorumRotationService.masternodeListAtTip = masternodeListAtTip; - self.quorumRotationService.masternodeListAtH = masternodeListAtH; - self.quorumRotationService.masternodeListAtHC = masternodeListAtHC; - self.quorumRotationService.masternodeListAtH2C = masternodeListAtH2C; - self.quorumRotationService.masternodeListAtH3C = masternodeListAtH3C; - self.quorumRotationService.masternodeListAtH4C = masternodeListAtH4C; - UInt256 blockHashAtTip = masternodeListAtTip.blockHash; - UInt256 blockHashAtH = masternodeListAtH.blockHash; - UInt256 blockHashAtHC = masternodeListAtHC.blockHash; - UInt256 blockHashAtH2C = masternodeListAtH2C.blockHash; - UInt256 blockHashAtH3C = masternodeListAtH3C.blockHash; - UInt256 blockHashAtH4C = masternodeListAtH4C.blockHash; - NSData *blockHashDataAtTip = uint256_data(blockHashAtTip); - NSData *blockHashDataAtH = uint256_data(blockHashAtH); - NSData *blockHashDataAtHC = uint256_data(blockHashAtHC); - NSData *blockHashDataAtH2C = uint256_data(blockHashAtH2C); - NSData *blockHashDataAtH3C = uint256_data(blockHashAtH3C); - NSData *blockHashDataAtH4C = uint256_data(blockHashAtH4C); - NSMutableSet *masternodeListQueriesNeedingQuorumsValidated; - @synchronized (self.store.masternodeListQueriesNeedingQuorumsValidated) { - masternodeListQueriesNeedingQuorumsValidated = self.store.masternodeListQueriesNeedingQuorumsValidated; - } - if (![self.quorumRotationService shouldProcessDiffResult:mnListDiffResultAtH4C skipPresenceInRetrieval:YES]) { - [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; - } else if (![missingMasternodeListsAtH4C count] || ![masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtH4C]) { - [self updateStoreWithProcessingResult:masternodeListAtH4C result:mnListDiffResultAtH4C completion:^(NSError *error) {}]; - } - if (![self.quorumRotationService shouldProcessDiffResult:mnListDiffResultAtH3C skipPresenceInRetrieval:YES]) { - [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; - } else if (![missingMasternodeListsAtH3C count] || ![masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtH3C]) { - [self updateStoreWithProcessingResult:masternodeListAtH3C result:mnListDiffResultAtH3C completion:^(NSError *error) {}]; - } - if (![self.quorumRotationService shouldProcessDiffResult:mnListDiffResultAtH2C skipPresenceInRetrieval:YES]) { - [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; - } else if (![missingMasternodeListsAtH2C count] || ![masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtH2C]) { - [self updateStoreWithProcessingResult:masternodeListAtH2C result:mnListDiffResultAtH2C completion:^(NSError *error) {}]; - } - if (![self.quorumRotationService shouldProcessDiffResult:mnListDiffResultAtHC skipPresenceInRetrieval:YES]) { - [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; - } else if (![missingMasternodeListsAtHC count] || ![masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtHC]) { - [self updateStoreWithProcessingResult:masternodeListAtHC result:mnListDiffResultAtHC completion:^(NSError *error) {}]; - } - if (![self.quorumRotationService shouldProcessDiffResult:mnListDiffResultAtH skipPresenceInRetrieval:YES]) { - [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; - } else if (![missingMasternodeListsAtH count] || ![masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtH]) { - [self updateStoreWithProcessingResult:masternodeListAtH result:mnListDiffResultAtH completion:^(NSError *error) {}]; - } - - if (![self.quorumRotationService shouldProcessDiffResult:mnListDiffResultAtTip skipPresenceInRetrieval:YES]) { - [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; - } else { - if ([missingMasternodeListsAtTip count] && [masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtTip]) { - [self.quorumRotationService removeFromRetrievalQueue:blockHashDataAtTip]; - [self processMissingMasternodeLists:missingMasternodeLists forMasternodeList:masternodeListAtTip]; - } else { - if (uint256_eq(self.store.lastQueriedBlockHash, blockHashAtTip)) { - self.quorumRotationService.currentMasternodeList = masternodeListAtTip; - @synchronized (self.store.masternodeListQueriesNeedingQuorumsValidated) { - [self.store.masternodeListQueriesNeedingQuorumsValidated removeObject:blockHashDataAtTip]; - } - } - [self updateStoreWithProcessingResult:masternodeListAtTip result:mnListDiffResultAtTip completion:^(NSError *error) {}]; - [self.quorumRotationService updateAfterProcessingMasternodeListWithBlockHash:blockHashDataAtTip fromPeer:peer]; - } - } - [self.store saveQuorumSnapshot:result.snapshotAtHC completion:^(NSError * _Nonnull error) {}]; - [self.store saveQuorumSnapshot:result.snapshotAtH2C completion:^(NSError * _Nonnull error) {}]; - [self.store saveQuorumSnapshot:result.snapshotAtH3C completion:^(NSError * _Nonnull error) {}]; - [self.store saveQuorumSnapshot:result.snapshotAtH4C completion:^(NSError * _Nonnull error) {}]; - - for (DSMnDiffProcessingResult *diffResult in result.mnListDiffList) { - DSLog(@"[%@] •••• -> processed qrinfo +++ %u..%u %@ .. %@", self.chain.name, [self heightForBlockHash:diffResult.baseBlockHash], [self heightForBlockHash:diffResult.blockHash], uint256_hex(diffResult.baseBlockHash), uint256_hex(diffResult.blockHash)); - DSMasternodeList *diffMasternodeList = diffResult.masternodeList; - UInt256 diffBlockHash = diffMasternodeList.blockHash; - NSData *diffBlockHashData = uint256_data(diffBlockHash); - if (![self.quorumRotationService shouldProcessDiffResult:diffResult skipPresenceInRetrieval:YES]) { - [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; - } else if (![diffResult.neededMissingMasternodeLists count] || ![masternodeListQueriesNeedingQuorumsValidated containsObject:diffBlockHashData]) { - [self updateStoreWithProcessingResult:diffMasternodeList result:diffResult completion:^(NSError *error) {}]; - } - } - for (DSQuorumSnapshot *snapshot in result.snapshotList) { - [self.store saveQuorumSnapshot:snapshot completion:^(NSError * _Nonnull error) {}]; - } - - [self.store.activeQuorums unionOrderedSet:result.lastQuorumPerIndex]; -} - -- (void)processMissingMasternodeLists:(NSOrderedSet *)neededMissingMasternodeLists forMasternodeList:(DSMasternodeList *)masternodeList { - UInt256 masternodeListBlockHash = masternodeList.blockHash; - NSData *masternodeListBlockHashData = uint256_data(masternodeListBlockHash); - self.store.masternodeListAwaitingQuorumValidation = masternodeList; - NSMutableOrderedSet *neededMasternodeLists = [neededMissingMasternodeLists mutableCopy]; - [neededMasternodeLists addObject:masternodeListBlockHashData]; //also get the current one again - if (self.isSyncing) - [self getMasternodeListsForBlockHashes:neededMasternodeLists]; -} -- (void)updateStoreWithProcessingResult:(DSMasternodeList *)masternodeList result:(DSMnDiffProcessingResult *)result completion:(void (^)(NSError *error))completion { - if (uint256_eq(self.store.masternodeListAwaitingQuorumValidation.blockHash, masternodeList.blockHash)) { - self.store.masternodeListAwaitingQuorumValidation = nil; - } - [self.store.cachedCLSignatures addEntriesFromDictionary:result.clSignatures]; - [self.store saveMasternodeList:masternodeList - addedMasternodes:result.addedMasternodes - modifiedMasternodes:result.modifiedMasternodes - completion:^(NSError *error) { - completion(error); - if (!error || !([self.masternodeListDiffService retrievalQueueCount] + [self.quorumRotationService retrievalQueueCount])) { //if it is 0 then we most likely have wiped chain info - return; - } - [self wipeMasternodeInfo]; - if (self.isSyncing) { - dispatch_async(self.chain.networkingQueue, ^{ - [self getRecentMasternodeList]; - }); - } - }]; -} - - (void)peer:(DSPeer *)peer relayedMasternodeDiffMessage:(NSData *)message { DSLog(@"[%@: %@:%d] •••• -> received mnlistdiff: %@", self.chain.name, peer.host, peer.port, uint256_hex(message.SHA256)); @synchronized (self.masternodeListDiffService) { @@ -742,33 +391,53 @@ - (void)peer:(DSPeer *)peer relayedMasternodeDiffMessage:(NSData *)message { } dispatch_async(self.processingQueue, ^{ dispatch_group_enter(self.processingGroup); - DSMasternodeProcessorContext *ctx = [self createDiffMessageContext:self.chain.isTestnet isFromSnapshot:NO isDIP0024:NO peer:peer merkleRootLookup:^UInt256(UInt256 blockHash) { - DSBlock *lastBlock = [self lastBlockForBlockHash:blockHash fromPeer:peer]; - if (!lastBlock) { - [self.masternodeListDiffService issueWithMasternodeListFromPeer:peer]; - DSLog(@"[%@] Last Block missing", self.chain.name); - return UINT256_ZERO; + SLICE *message_slice = slice_ctor(message); + DMnDiffResult *result = dash_spv_masternode_processor_processing_processor_MasternodeProcessor_mn_list_diff_result_from_message(self.processor, message_slice, false, peer ? peer.version : self.chain.protocolVersion, false, ((__bridge void *)(peer))); + + if (result->error) { + uint8_t error_index = DProcessingErrorIndex(result->error); + + DSLog(@"[%@] Processing status (mndiff): %ul", self.chain.name, error_index); + switch (error_index) { + case dash_spv_masternode_processor_processing_processing_error_ProcessingError_MissingLists: + break; + case dash_spv_masternode_processor_processing_processing_error_ProcessingError_InvalidResult: + [self.masternodeListDiffService cleanRequestsInRetrieval]; + [self.masternodeListDiffService dequeueMasternodeListRequest]; + default: + [self.masternodeListDiffService issueWithMasternodeListFromPeer:peer]; + break; } - return lastBlock.merkleRoot; - }]; - [self processMasternodeDiffWith:message context:ctx completion:^(DSMnDiffProcessingResult * _Nonnull result) { - #if SAVE_MASTERNODE_DIFF_TO_FILE - UInt256 baseBlockHash = result.baseBlockHash; - UInt256 blockHash = result.blockHash; - DSLog(@"[%@] •••• -> processed mnlistdiff %u..%u %@ .. %@", self.chain.name, [self heightForBlockHash:baseBlockHash], [self heightForBlockHash:blockHash], uint256_hex(baseBlockHash), uint256_hex(blockHash)); - NSString *fileName = [NSString stringWithFormat:@"MNL_%@_%@__%d.dat", @([self heightForBlockHash:baseBlockHash]), @([self heightForBlockHash:blockHash]), peer.version]; - DSLog(@"[%@] •-• File %@ saved", self.chain.name, fileName); - [message saveToFile:fileName inDirectory:NSCachesDirectory]; - #endif - if (result.errorStatus) { - DSLog(@"[%@] Processing status: %ul", self.chain.name, result.errorStatus); - dispatch_group_leave(self.processingGroup); - return; + DMnDiffResultDtor(result); + dispatch_group_leave(self.processingGroup); + return; + } + u256 *block_hash = result->ok->o_1; + NSData *masternodeListBlockHashData = NSDataFromPtr(block_hash); + bool has_added_rotated_quorums = result->ok->o_2; + UInt256 masternodeListBlockHash = u256_cast(block_hash); + if (has_added_rotated_quorums && !self.chain.isRotatedQuorumsPresented) { + uint32_t masternodeListBlockHeight = [self heightForBlockHash:masternodeListBlockHash]; + self.chain.isRotatedQuorumsPresented = YES; + self.rotatedQuorumsActivationHeight = masternodeListBlockHeight; + if (self.isSyncing) { + dash_spv_masternode_processor_processing_processor_MasternodeProcessor_add_to_qr_info_retrieval_queue(self.chain.shareCore.processor->obj, block_hash); + [self.quorumRotationService dequeueMasternodeListRequest]; } - [self processMasternodeListDiffResult:result forPeer:peer skipPresenceInRetrieval:NO completion:^{ - dispatch_group_leave(self.processingGroup); - }]; - }]; + } + if (self.isSyncing) + [self.masternodeListDiffService updateAfterProcessingMasternodeListWithBlockHash:masternodeListBlockHashData fromPeer:peer]; + +#if SAVE_MASTERNODE_DIFF_TO_FILE + u256 *base_block_hash = result->ok->o_0; + uint32_t base_block_height = DHeightForBlockHash(self.chain.shareCore.processor->obj, base_block_hash); + uint32_t block_height = DHeightForBlockHash(self.chain.shareCore.processor->obj, block_hash); + NSString *fileName = [NSString stringWithFormat:@"MNL_%@_%@__%d.dat", @(base_block_height), @(block_height), peer.version]; + DSLog(@"[%@] •-• File %@ saved", self.chain.name, fileName); + [message saveToFile:fileName inDirectory:NSCachesDirectory]; +#endif + DMnDiffResultDtor(result); + dispatch_group_leave(self.processingGroup); }); } @@ -777,129 +446,104 @@ - (void)peer:(DSPeer *)peer relayedQuorumRotationInfoMessage:(NSData *)message { @synchronized (self.quorumRotationService) { self.quorumRotationService.timedOutAttempt = 0; } + uint32_t protocol_version = peer ? peer.version : self.chain.protocolVersion; dispatch_async(self.processingQueue, ^{ dispatch_group_enter(self.processingGroup); - MerkleRootFinder merkleRootLookup = ^UInt256(UInt256 blockHash) { - DSBlock *lastBlock = [self lastBlockForBlockHash:blockHash fromPeer:peer]; - if (!lastBlock) { + SLICE *slice_msg = slice_ctor(message); + DQRInfoResult *result = dash_spv_masternode_processor_processing_processor_MasternodeProcessor_qr_info_result_from_message(self.processor, slice_msg, false, protocol_version, self.chain.isRotatedQuorumsPresented, false, ((__bridge void *)(peer))); + if (result->error || !result->ok) { + uint8_t index = DProcessingErrorIndex(result->error); + DSLog(@"[%@] •••• Processing status (qrinfo): %u", self.chain.name, index); + DQRInfoResultDtor(result); + if (index == dash_spv_masternode_processor_processing_processing_error_ProcessingError_InvalidResult) { [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; - DSLog(@"[%@] Last Block missing", self.chain.name); - return UINT256_ZERO; - } - return lastBlock.merkleRoot; - }; - DSMasternodeProcessorContext *ctx = [self createDiffMessageContext:self.chain.isTestnet isFromSnapshot:NO isDIP0024:YES peer:peer merkleRootLookup:merkleRootLookup]; - [self processQRInfoWith:message context:ctx completion:^(DSQRInfoProcessingResult * _Nonnull result) { - if (result.errorStatus) { - DSLog(@"[%@] •••• Processing status: %u", self.chain.name, result.errorStatus); - dispatch_group_leave(self.processingGroup); - return; } - #if SAVE_MASTERNODE_DIFF_TO_FILE - UInt256 baseBlockHash = result.mnListDiffResultAtTip.baseBlockHash; - UInt256 blockHash = result.mnListDiffResultAtTip.blockHash; - DSLog(@"[%@] •••• -> processed qrinfo tip %u..%u %@ .. %@", self.chain.name, [self heightForBlockHash:baseBlockHash], [self heightForBlockHash:blockHash], uint256_hex(baseBlockHash), uint256_hex(blockHash)); - DSLog(@"[%@] •••• -> processed qrinfo h %u..%u %@ .. %@", self.chain.name, [self heightForBlockHash:result.mnListDiffResultAtH.baseBlockHash], [self heightForBlockHash:result.mnListDiffResultAtH.blockHash], uint256_hex(result.mnListDiffResultAtH.baseBlockHash), uint256_hex(result.mnListDiffResultAtH.blockHash)); - DSLog(@"[%@] •••• -> processed qrinfo h-c %u..%u %@ .. %@", self.chain.name, [self heightForBlockHash:result.mnListDiffResultAtHC.baseBlockHash], [self heightForBlockHash:result.mnListDiffResultAtHC.blockHash], uint256_hex(result.mnListDiffResultAtHC.baseBlockHash), uint256_hex(result.mnListDiffResultAtHC.blockHash)); - DSLog(@"[%@] •••• -> processed qrinfo h-2c %u..%u %@ .. %@", self.chain.name, [self heightForBlockHash:result.mnListDiffResultAtH2C.baseBlockHash], [self heightForBlockHash:result.mnListDiffResultAtH2C.blockHash], uint256_hex(result.mnListDiffResultAtH2C.baseBlockHash), uint256_hex(result.mnListDiffResultAtH2C.blockHash)); - DSLog(@"[%@] •••• -> processed qrinfo h-3c %u..%u %@ .. %@", self.chain.name, [self heightForBlockHash:result.mnListDiffResultAtH3C.baseBlockHash], [self heightForBlockHash:result.mnListDiffResultAtH3C.blockHash], uint256_hex(result.mnListDiffResultAtH3C.baseBlockHash), uint256_hex(result.mnListDiffResultAtH3C.blockHash)); - if (result.extraShare) { - DSLog(@"[%@] •••• -> processed qrinfo h-4c %u..%u %@ .. %@", self.chain.name, [self heightForBlockHash:result.mnListDiffResultAtH4C.baseBlockHash], [self heightForBlockHash:result.mnListDiffResultAtH4C.blockHash], uint256_hex(result.mnListDiffResultAtH4C.baseBlockHash), uint256_hex(result.mnListDiffResultAtH4C.blockHash)); - } - NSString *fileName = [NSString stringWithFormat:@"QRINFO_%@_%@__%d.dat", @([self heightForBlockHash:baseBlockHash]), @([self heightForBlockHash:blockHash]), peer.version]; - DSLog(@"[%@] •-• File %@ saved", self.chain.name, fileName); - [message saveToFile:fileName inDirectory:NSCachesDirectory]; - #endif - [self processQRInfoResult:result forPeer:peer completion:^{ - dispatch_group_leave(self.processingGroup); - }]; - }]; + dispatch_group_leave(self.processingGroup); + return; + } + u256 *block_hash = result->ok->o_1; + +#if SAVE_MASTERNODE_DIFF_TO_FILE + u256 *base_block_hash = result->ok->o_0; + uint32_t base_block_height = DHeightForBlockHash(self.chain.shareCore.processor->obj, base_block_hash); + uint32_t block_height = DHeightForBlockHash(self.chain.shareCore.processor->obj, block_hash); + NSString *fileName = [NSString stringWithFormat:@"QRINFO_%@_%@__%d.dat", @(base_block_height), @(block_height), peer.version]; + DSLog(@"[%@] •-• File %@ saved", self.chain.name, fileName); + [message saveToFile:fileName inDirectory:NSCachesDirectory]; +#endif + [self.quorumRotationService updateAfterProcessingMasternodeListWithBlockHash:NSDataFromPtr(block_hash) fromPeer:peer]; + DQRInfoResultDtor(result); + dispatch_group_leave(self.processingGroup); }); } -- (DSMasternodeProcessorContext *)createDiffMessageContext:(BOOL)useInsightAsBackup isFromSnapshot:(BOOL)isFromSnapshot isDIP0024:(BOOL)isDIP0024 peer:(DSPeer *_Nullable)peer merkleRootLookup:(MerkleRootFinder)merkleRootLookup { - DSMasternodeProcessorContext *mndiffContext = [[DSMasternodeProcessorContext alloc] init]; - [mndiffContext setUseInsightAsBackup:useInsightAsBackup]; - [mndiffContext setIsFromSnapshot:isFromSnapshot]; - [mndiffContext setIsDIP0024:isDIP0024]; - [mndiffContext setChain:self.chain]; - [mndiffContext setPeer:peer]; - [mndiffContext setMasternodeListLookup:^DSMasternodeList *(UInt256 blockHash) { - return [self masternodeListForBlockHash:blockHash withBlockHeightLookup:nil]; - }]; - [mndiffContext setBlockHeightLookup:^uint32_t(UInt256 blockHash) { - return [self heightForBlockHash:blockHash]; - }]; - [mndiffContext setMerkleRootLookup:merkleRootLookup]; - return mndiffContext; -} - -- (BOOL)hasMasternodeListCurrentlyBeingSaved { - return [self.store hasMasternodeListCurrentlyBeingSaved]; -} - -+ (void)saveMasternodeList:(DSMasternodeList *)masternodeList ++ (void)saveMasternodeList:(DArcMasternodeList *)masternodeList toChain:(DSChain *)chain - havingModifiedMasternodes:(NSDictionary *)modifiedMasternodes + havingModifiedMasternodes:(DMasternodeEntryMap *)modifiedMasternodes createUnknownBlocks:(BOOL)createUnknownBlocks inContext:(NSManagedObjectContext *)context completion:(void (^)(NSError *error))completion { - [DSMasternodeListStore saveMasternodeList:masternodeList + NSError *err = [DSMasternodeListStore saveMasternodeList:masternodeList toChain:chain havingModifiedMasternodes:modifiedMasternodes createUnknownBlocks:createUnknownBlocks - inContext:context - completion:completion]; + inContext:context]; + completion(err); } // MARK: - Quorums -- (DSQuorumEntry *)quorumEntryForChainLockRequestID:(UInt256)requestID withBlockHeightOffset:(uint32_t)blockHeightOffset { +- (DLLMQEntry *)quorumEntryForChainLockRequestID:(UInt256)requestID + withBlockHeightOffset:(uint32_t)blockHeightOffset { DSMerkleBlock *merkleBlock = [self.chain blockFromChainTip:blockHeightOffset]; return [self quorumEntryForChainLockRequestID:requestID forMerkleBlock:merkleBlock]; } -- (DSQuorumEntry *)quorumEntryForChainLockRequestID:(UInt256)requestID forBlockHeight:(uint32_t)blockHeight { +- (DLLMQEntry *)quorumEntryForChainLockRequestID:(UInt256)requestID + forBlockHeight:(uint32_t)blockHeight { DSMerkleBlock *merkleBlock = [self.chain blockAtHeight:blockHeight]; return [self quorumEntryForChainLockRequestID:requestID forMerkleBlock:merkleBlock]; } -- (DSQuorumEntry *)quorumEntryForChainLockRequestID:(UInt256)requestID forMerkleBlock:(DSMerkleBlock *)merkleBlock { - return [self.store quorumEntryForChainLockRequestID:requestID forMerkleBlock:merkleBlock]; +- (DLLMQEntry *)quorumEntryForChainLockRequestID:(UInt256)requestID + forMerkleBlock:(DSMerkleBlock *)merkleBlock { + u256 *request_id = u256_ctor_u(requestID); + u256 *block_hash = u256_ctor_u(merkleBlock.blockHash); + DLLMQEntry *entry = dash_spv_masternode_processor_processing_processor_MasternodeProcessor_quorum_entry_for_chain_lock_request_id(self.processor, request_id, block_hash, merkleBlock.height); + return entry; } -- (DSQuorumEntry *)quorumEntryForInstantSendRequestID:(UInt256)requestID withBlockHeightOffset:(uint32_t)blockHeightOffset { - return [self.store quorumEntryForInstantSendRequestID:requestID forMerkleBlock:[self.chain blockFromChainTip:blockHeightOffset]]; +- (DLLMQEntry *)quorumEntryForInstantSendRequestID:(UInt256)requestID + withBlockHeightOffset:(uint32_t)blockHeightOffset { + DSMerkleBlock *merkleBlock = [self.chain blockFromChainTip:blockHeightOffset]; + u256 *request_id = u256_ctor_u(requestID); + u256 *block_hash = u256_ctor_u(merkleBlock.blockHash); + DLLMQEntry *entry = dash_spv_masternode_processor_processing_processor_MasternodeProcessor_quorum_entry_for_instant_send_request_id(self.processor, request_id, block_hash, merkleBlock.height); + return entry; } -- (DSQuorumEntry *)quorumEntryForPlatformHavingQuorumHash:(UInt256)quorumHash forBlockHeight:(uint32_t)blockHeight { - return [self.store quorumEntryForPlatformHavingQuorumHash:quorumHash forBlockHeight:blockHeight]; +- (DLLMQEntry *)quorumEntryForPlatformHavingQuorumHash:(UInt256)quorumHash + forBlockHeight:(uint32_t)blockHeight { + u256 *quorum_hash = u256_ctor_u(quorumHash); + DLLMQEntry *entry = dash_spv_masternode_processor_processing_processor_MasternodeProcessor_quorum_entry_for_platform_having_quorum_hash(self.processor, quorum_hash, blockHeight); + return entry; } // MARK: - Meta information -- (void)checkPingTimesForCurrentMasternodeListInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(NSMutableDictionary *pingTimes, NSMutableDictionary *errors))completion { - __block NSArray *entries = self.currentMasternodeList.simplifiedMasternodeEntries; - [self.chain.chainManager.DAPIClient checkPingTimesForMasternodes:entries - completion:^(NSMutableDictionary *_Nonnull pingTimes, NSMutableDictionary *_Nonnull errors) { - [self.store savePlatformPingInfoForEntries:entries inContext:context]; - if (completion != nil) { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(pingTimes, errors); - }); - } - }]; - -} +- (void)checkPingTimesForCurrentMasternodeListInContext:(NSManagedObjectContext *)context + withCompletion:(void (^)(NSMutableDictionary *pingTimes, NSMutableDictionary *errors))completion { +// __block NSArray *entries = self.currentMasternodeList.simplifiedMasternodeEntries; +// [self.chain.chainManager.DAPIClient checkPingTimesForMasternodes:entries +// completion:^(NSMutableDictionary *_Nonnull pingTimes, NSMutableDictionary *_Nonnull errors) { +// [self.store savePlatformPingInfoForEntries:entries inContext:context]; +// if (completion != nil) { +// dispatch_async(dispatch_get_main_queue(), ^{ +// completion(pingTimes, errors); +// }); +// } +// }]; -- (UInt256)buildLLMQHashFor:(DSQuorumEntry *)quorum { - uint32_t workHeight = [self heightForBlockHash:quorum.quorumHash] - 8; - if (workHeight >= chain_core20_activation_height(self.chain.chainType)) { - NSData *bestCLSignature = [self CLSignatureForBlockHeight:workHeight]; - return [DSKeyManager NSDataFrom:quorum_build_llmq_hash_v20(quorum.llmqType, workHeight, bestCLSignature.bytes)].UInt256; - } else { - return [DSKeyManager NSDataFrom:quorum_build_llmq_hash(quorum.llmqType, quorum.quorumHash.u8)].UInt256; - } } @end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager+Protected.h b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager+Protected.h index 5852a45d1..ef420e298 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager+Protected.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager+Protected.h @@ -41,7 +41,7 @@ typedef NS_ENUM(NSUInteger, DSPeerManagerDesiredState) @property (nonatomic, readonly) NSUInteger connectFailures, misbehavingCount, maxConnectCount; @property (nonatomic, readonly) NSSet *connectedPeers; @property (nonatomic, readonly) DSPeerManagerDesiredState desiredState; -@property (nonatomic, readonly) DSMasternodeList *masternodeList; +@property (nonatomic, readonly) DArcMasternodeList *masternodeList; - (void)peerMisbehaving:(DSPeer *)peer errorMessage:(NSString *)errorMessage; - (void)chainSyncStopped; @@ -54,7 +54,8 @@ typedef NS_ENUM(NSUInteger, DSPeerManagerDesiredState) - (instancetype)initWithChain:(DSChain *)chain; -- (void)useMasternodeList:(DSMasternodeList *)masternodeList withConnectivityNonce:(uint64_t)connectivityNonce; +- (void)useMasternodeList:(DArcMasternodeList *)masternodeList + withConnectivityNonce:(uint64_t)connectivityNonce; - (void)clearRegisteredPeers; - (void)registerPeerAtLocation:(UInt128)IPAddress port:(uint32_t)port dapiPort:(uint32_t)dapiPort; diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m index cdfb882d0..46085c28d 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m @@ -26,10 +26,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +//#import "dash_shared_core.h" #import "DSAccount.h" #import "DSBackgroundManager.h" #import "DSBloomFilter.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" +#import "DSChain+Wallet.h" #import "DSChainEntity+CoreDataClass.h" #import "DSChainManager+Protected.h" #import "DSDerivationPath.h" @@ -37,7 +40,6 @@ #import "DSGovernanceObject.h" #import "DSGovernanceSyncManager.h" #import "DSGovernanceVote.h" -#import "DSMasternodeList.h" #import "DSMasternodeManager.h" #import "DSMerkleBlock.h" #import "DSMerkleBlockEntity+CoreDataClass.h" @@ -77,6 +79,11 @@ #define SYNC_COUNT_INFO @"SYNC_COUNT_INFO" +#define ERROR_NO_PEERS [NSError errorWithCode:1 localizedDescriptionKey:@"No peers found"] +#define ERROR_SYNC_TIMEOUT [NSError errorWithCode:500 descriptionKey:DSLocalizedString(@"Synchronization Timeout", @"An error message for notifying that chain sync has timed out")] +#define ERROR_NO_SERVICE(host) [NSError errorWithCode:500 descriptionKey:[NSString stringWithFormat:DSLocalizedString(@"Node at host %@ does not service network", nil), host]] +#define ERROR_NO_BLOOM(host) [NSError errorWithCode:500 descriptionKey:[NSString stringWithFormat:DSLocalizedString(@"Node at host %@ does not support bloom filtering", nil), host]] + @interface DSPeerManager () @property (nonatomic, strong) NSMutableOrderedSet *peers; @@ -89,7 +96,7 @@ @interface DSPeerManager () @property (nonatomic, strong) DSChain *chain; @property (nonatomic, assign) DSPeerManagerDesiredState desiredState; @property (nonatomic, assign) uint64_t masternodeListConnectivityNonce; -@property (nonatomic, strong) DSMasternodeList *masternodeList; +@property (nonatomic, assign) DArcMasternodeList *masternodeList; @property (nonatomic, readonly) dispatch_queue_t networkingQueue; @property (nonatomic, strong) NSManagedObjectContext *managedObjectContext; @@ -192,19 +199,21 @@ - (NSString *)downloadPeerName { return [self.downloadPeer.host stringByAppendingFormat:@":%d", self.downloadPeer.port]; } -- (NSArray *)dnsSeeds { - switch (self.chain.chainType.tag) { - case ChainType_MainNet: - return MAINNET_DNS_SEEDS; - case ChainType_TestNet: - return TESTNET_DNS_SEEDS; - case ChainType_DevNet: - return nil; //no dns seeds for devnets - default: - break; - } - return nil; -} +//- (NSArray *)dnsSeeds { +// struct Vec_String *vec = dash_spv_crypto_network_chain_type_ChainType_dns_seeds(self.chain.chainType); +// +// switch (self.chain.chainType.tag) { +// case ChainType_MainNet: +// return MAINNET_DNS_SEEDS; +// case ChainType_TestNet: +// return TESTNET_DNS_SEEDS; +// case ChainType_DevNet: +// return nil; //no dns seeds for devnets +// default: +// break; +// } +// return nil; +//} // MARK: - Peers + (DSPeer *)peerFromString:(NSString *)string forChain:(DSChain *)chain { @@ -243,6 +252,17 @@ - (void)clearPeers:(DSDisconnectReason)reason { _peers = nil; } } +- (NSArray *)peers:(uint32_t)peerCount withConnectivityNonce:(uint64_t)connectivityNonce { + Vec_dash_spv_masternode_processor_common_socket_address_SocketAddress *vec = + dash_spv_masternode_processor_models_masternode_list_MasternodeList_peer_addresses_with_connectivity_nonce(self.masternodeList->obj, connectivityNonce, peerCount); + NSMutableArray *mArray = [NSMutableArray array]; + for (int i = 0; i < vec->count; i++) { + dash_spv_masternode_processor_common_socket_address_SocketAddress *address = vec->values[i]; + [mArray addObject:[[DSPeer alloc] initWithAddress:u128_cast(address->ip_address) andPort:address->port onChain:self.chain]]; + } + Vec_dash_spv_masternode_processor_common_socket_address_SocketAddress_destroy(vec); + return mArray; +} - (NSMutableOrderedSet *)peers { if (_fixedPeer) return [NSMutableOrderedSet orderedSetWithObject:_fixedPeer]; @@ -269,7 +289,7 @@ - (NSMutableOrderedSet *)peers { [_peers addObjectsFromArray:[self registeredDevnetPeers]]; if (self.masternodeList) { - NSArray *masternodePeers = [self.masternodeList peers:8 withConnectivityNonce:self.masternodeListConnectivityNonce]; + NSArray *masternodePeers = [self peers:8 withConnectivityNonce:self.masternodeListConnectivityNonce]; [_peers addObjectsFromArray:masternodePeers]; } @@ -278,7 +298,7 @@ - (NSMutableOrderedSet *)peers { } if (self.masternodeList) { - NSArray *masternodePeers = [self.masternodeList peers:500 withConnectivityNonce:self.masternodeListConnectivityNonce]; + NSArray *masternodePeers = [self peers:500 withConnectivityNonce:self.masternodeListConnectivityNonce]; [_peers addObjectsFromArray:masternodePeers]; [self sortPeers]; return _peers; @@ -287,20 +307,22 @@ - (NSMutableOrderedSet *)peers { // DNS peer discovery NSTimeInterval now = [NSDate timeIntervalSince1970]; NSMutableArray *peers = [NSMutableArray arrayWithObject:[NSMutableArray array]]; - NSArray *dnsSeeds = [self dnsSeeds]; + Vec_String *dns_seeds = dash_spv_crypto_network_chain_type_ChainType_dns_seeds(self.chain.chainType); if (_peers.count < PEER_MAX_CONNECTIONS || ((DSPeer *)_peers[PEER_MAX_CONNECTIONS - 1]).timestamp + 3 * DAY_TIME_INTERVAL < now) { - while (peers.count < dnsSeeds.count) [peers addObject:[NSMutableArray array]]; + while (peers.count < dns_seeds->count) [peers addObject:[NSMutableArray array]]; } if (peers.count > 0) { - if ([dnsSeeds count]) { + if (dns_seeds->count) { dispatch_apply(peers.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t i) { NSString *servname = @(self.chain.standardPort).stringValue; struct addrinfo hints = {0, AF_UNSPEC, SOCK_STREAM, 0, 0, 0, NULL, NULL}, *servinfo, *p; UInt128 addr = {.u32 = {0, 0, CFSwapInt32HostToBig(0xffff), 0}}; + + char *dns_seed = dns_seeds->values[i]; - DSLog(@"[%@] [DSPeerManager] DNS lookup %@", self.chain.name, [dnsSeeds objectAtIndex:i]); - NSString *dnsSeed = [dnsSeeds objectAtIndex:i]; + DSLog(@"[%@] [DSPeerManager] DNS lookup %s", self.chain.name, dns_seed); + NSString *dnsSeed = [NSString stringWithUTF8String:dns_seed]; if (getaddrinfo([dnsSeed UTF8String], servname.UTF8String, &hints, &servinfo) == 0) { for (p = servinfo; p != NULL; p = p->ai_next) { if (p->ai_family == AF_INET) { @@ -325,7 +347,7 @@ - (NSMutableOrderedSet *)peers { freeaddrinfo(servinfo); } else { - DSLog(@"[%@] [DSPeerManager] failed getaddrinfo for %@", self.chain.name, dnsSeeds[i]); + DSLog(@"[%@] [DSPeerManager] failed getaddrinfo for %s", self.chain.name, dns_seed); } }); } @@ -334,6 +356,7 @@ - (NSMutableOrderedSet *)peers { if (![self.chain isMainnet] && ![self.chain isTestnet]) { [self sortPeers]; + Vec_String_destroy(dns_seeds); return _peers; } // if DNS peer discovery fails, fall back on a hard coded list of peers (list taken from satoshi client) @@ -375,7 +398,7 @@ - (NSMutableOrderedSet *)peers { [self sortPeers]; } - + Vec_String_destroy(dns_seeds); return _peers; } } @@ -409,7 +432,7 @@ - (void)peerMisbehaving:(DSPeer *)peer errorMessage:(NSString *)errorMessage { _peers = nil; } - [peer disconnectWithError:[NSError errorWithCode:500 localizedDescriptionKey:errorMessage]]; + [peer disconnectWithError:ERROR_500(errorMessage)]; DSLog(@"[%@] [DSPeerManager] peerMisbehaving -> peerManager::connect", self.chain.name); [self connect]; } @@ -640,14 +663,15 @@ - (NSArray *)registeredDevnetPeerServices { // MARK: - Using Masternode List for connectivitity -- (void)useMasternodeList:(DSMasternodeList *)masternodeList withConnectivityNonce:(uint64_t)connectivityNonce { +- (void)useMasternodeList:(DArcMasternodeList *)masternodeList + withConnectivityNonce:(uint64_t)connectivityNonce { self.masternodeList = masternodeList; self.masternodeListConnectivityNonce = connectivityNonce; BOOL connected = self.connected; - NSArray *peers = [masternodeList peers:500 withConnectivityNonce:connectivityNonce]; + NSArray *peers = [self peers:500 withConnectivityNonce:connectivityNonce]; @synchronized(self) { if (!_peers) { @@ -762,10 +786,9 @@ - (void)connect { [self chainSyncStopped]; DSLog(@"[%@] [DSPeerManager] No peers found -> SyncFailed", self.chain.name); dispatch_async(dispatch_get_main_queue(), ^{ - NSError *error = [NSError errorWithCode:1 localizedDescriptionKey:@"No peers found"]; [[NSNotificationCenter defaultCenter] postNotificationName:DSChainManagerSyncFailedNotification object:nil - userInfo:@{@"error": error, DSChainManagerNotificationChainKey: self.chain}]; + userInfo:@{@"error": ERROR_NO_PEERS, DSChainManagerNotificationChainKey: self.chain}]; }); } }); @@ -802,7 +825,7 @@ - (void)syncTimeout { } }); - [self disconnectDownloadPeerForError:[NSError errorWithCode:500 descriptionKey:DSLocalizedString(@"Synchronization Timeout", @"An error message for notifying that chain sync has timed out")] withCompletion:nil]; + [self disconnectDownloadPeerForError:ERROR_SYNC_TIMEOUT withCompletion:nil]; } - (void)restartSyncTimeout:(NSTimeInterval)afterDelay { [self cancelSyncTimeout]; @@ -832,13 +855,13 @@ - (void)peerConnected:(DSPeer *)peer { // drop peers that don't carry full blocks, or aren't synced yet // TODO: XXXX does this work with 0.11 pruned nodes? if (!(peer.services & SERVICES_NODE_NETWORK) || peer.lastBlockHeight + 10 < self.chain.lastSyncBlockHeight) { - [peer disconnectWithError:[NSError errorWithCode:500 descriptionKey:[NSString stringWithFormat:DSLocalizedString(@"Node at host %@ does not service network", nil), peer.host]]]; + [peer disconnectWithError:ERROR_NO_SERVICE(peer.host)]; return; } // drop peers that don't support SPV filtering if (peer.version >= 70206 && !(peer.services & SERVICES_NODE_BLOOM)) { - [peer disconnectWithError:[NSError errorWithCode:500 descriptionKey:[NSString stringWithFormat:DSLocalizedString(@"Node at host %@ does not support bloom filtering", nil), peer.host]]]; + [peer disconnectWithError:ERROR_NO_BLOOM(peer.host)]; return; } diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSSporkManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSSporkManager.m index bba224268..f92c01ffe 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSSporkManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSSporkManager.m @@ -24,6 +24,7 @@ // THE SOFTWARE. #import "DSSporkManager.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" #import "DSChainEntity+CoreDataProperties.h" #import "DSChainManager+Protected.h" diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.h index 886cda483..0565c5065 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.h @@ -77,7 +77,13 @@ typedef void (^DSTransactionRequestRelayCompletionBlock)(DSTransaction *tx, DSPa - (void)publishTransaction:(DSTransaction *)transaction completion:(void (^)(NSError * _Nullable error))completion; -- (void)confirmPaymentRequest:(DSPaymentRequest *)paymentRequest usingUserBlockchainIdentity:(DSBlockchainIdentity *_Nullable)blockchainIdentity fromAccount:(DSAccount *)account acceptInternalAddress:(BOOL)acceptInternalAddress acceptReusingAddress:(BOOL)acceptReusingAddress addressIsFromPasteboard:(BOOL)addressIsFromPasteboard requiresSpendingAuthenticationPrompt:(BOOL)requiresSpendingConfirmationPrompt +- (void)confirmPaymentRequest:(DSPaymentRequest *)paymentRequest + usingUserIdentity:(DSIdentity *_Nullable)identity + fromAccount:(DSAccount *)account + acceptInternalAddress:(BOOL)acceptInternalAddress + acceptReusingAddress:(BOOL)acceptReusingAddress + addressIsFromPasteboard:(BOOL)addressIsFromPasteboard +requiresSpendingAuthenticationPrompt:(BOOL)requiresSpendingConfirmationPrompt keepAuthenticatedIfErrorAfterAuthentication:(BOOL)keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:(DSTransactionCreationRequestingAdditionalInfoBlock)additionalInfoRequest presentChallenge:(DSTransactionChallengeBlock)challenge diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m index 9c2cd630a..536c3be34 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m @@ -27,19 +27,20 @@ #import "DSAccount.h" #import "DSAuthenticationManager.h" #import "DSBlock.h" -#import "DSBlockchainIdentity+Protected.h" -#import "DSBlockchainIdentityRegistrationTransition.h" +#import "DSIdentity+Protected.h" +#import "DSIdentityRegistrationTransition.h" #import "DSBloomFilter.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" +#import "DSChain+Transaction.h" +#import "DSChain+Wallet.h" #import "DSChainLock.h" #import "DSChainManager+Protected.h" -#import "DSCreditFundingTransaction.h" #import "DSDAPIPlatformNetworkService.h" #import "DSError.h" #import "DSEventManager.h" #import "DSIdentitiesManager.h" #import "DSInstantSendTransactionLock.h" -#import "DSMasternodeList.h" #import "DSMasternodeManager+Protected.h" #import "DSMerkleBlock.h" #import "DSOptionsManager.h" @@ -53,6 +54,7 @@ #import "DSTransactionHashEntity+CoreDataClass.h" #import "DSTransactionInput.h" #import "DSTransition.h" +#import "DSWallet+Identity.h" #import "DSWallet+Protected.h" #import "NSData+Dash.h" #import "NSDate+Utils.h" @@ -72,6 +74,12 @@ #define SAVE_MAX_TRANSACTIONS_INFO (DEBUG && 0) #define DEBUG_CHAIN_LOCKS_WAITING_FOR_QUORUMS (DEBUG && 0) +#define ERROR_NOT_SIGNED [NSError errorWithCode:401 localizedDescriptionKey:@"Dash transaction not signed"] +#define ERROR_SIGNING [NSError errorWithCode:401 localizedDescriptionKey:@"Error signing transaction"] +#define ERROR_NOT_CONNECTED [NSError errorWithCode:-1009 localizedDescriptionKey:@"Not connected to the Dash network"] +#define ERROR_TIMEOUT [NSError errorWithCode:DASH_PEER_TIMEOUT_CODE localizedDescriptionKey:@"Transaction canceled, network timeout"] +#define ERROR_DOUBLE_SPEND [NSError errorWithCode:401 localizedDescriptionKey:@"Double spend"] + @interface DSTransactionManager () @property (nonatomic, strong) NSMutableDictionary *txRelays, *txRequests; @@ -185,14 +193,14 @@ - (void)publishTransaction:(DSTransaction *)transaction completion:(void (^)(NSE if ([transaction transactionTypeRequiresInputs] && !transaction.isSigned) { if (completion) { [[DSEventManager sharedEventManager] saveEvent:@"transaction_manager:not_signed"]; - completion([NSError errorWithCode:401 localizedDescriptionKey:@"Dash transaction not signed"]); + completion(ERROR_NOT_SIGNED); } return; } else if (!self.peerManager.connected && self.peerManager.connectFailures >= MAX_CONNECT_FAILURES) { if (completion) { [[DSEventManager sharedEventManager] saveEvent:@"transaction_manager:not_connected"]; - completion([NSError errorWithCode:-1009 localizedDescriptionKey:@"Not connected to the Dash network"]); + completion(ERROR_NOT_CONNECTED); } return; @@ -418,7 +426,7 @@ - (void)txTimeout:(NSValue *)txHash { if (callback) { [[DSEventManager sharedEventManager] saveEvent:@"transaction_manager:tx_canceled_timeout"]; - callback([NSError errorWithCode:DASH_PEER_TIMEOUT_CODE localizedDescriptionKey:@"Transaction canceled, network timeout"]); + callback(ERROR_TIMEOUT); } }); } @@ -681,7 +689,7 @@ - (void)signAndPublishTransaction:(DSTransaction *)tx createdFromProtocolRequest if (!signedTransaction || !tx.isSigned) { if (!previouslyWasAuthenticated && !keepAuthenticatedIfErrorAfterAuthentication) [authenticationManager deauthenticate]; dispatch_async(dispatch_get_main_queue(), ^{ - signedCompletion(tx, [NSError errorWithCode:401 localizedDescriptionKey:@"Error signing transaction"], NO); + signedCompletion(tx, ERROR_SIGNING, NO); }); return; } @@ -864,15 +872,21 @@ - (void)insufficientFundsForTransactionCreatedFromProtocolRequest:(DSPaymentProt } } -- (void)confirmPaymentRequest:(DSPaymentRequest *)paymentRequest usingUserBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity fromAccount:(DSAccount *)account acceptInternalAddress:(BOOL)acceptInternalAddress acceptReusingAddress:(BOOL)acceptReusingAddress addressIsFromPasteboard:(BOOL)addressIsFromPasteboard requiresSpendingAuthenticationPrompt:(BOOL)requiresSpendingAuthenticationPrompt +- (void)confirmPaymentRequest:(DSPaymentRequest *)paymentRequest + usingUserIdentity:(DSIdentity *)identity + fromAccount:(DSAccount *)account + acceptInternalAddress:(BOOL)acceptInternalAddress + acceptReusingAddress:(BOOL)acceptReusingAddress + addressIsFromPasteboard:(BOOL)addressIsFromPasteboard +requiresSpendingAuthenticationPrompt:(BOOL)requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:(BOOL)keepAuthenticatedIfErrorAfterAuthentication - requestingAdditionalInfo:(DSTransactionCreationRequestingAdditionalInfoBlock)additionalInfoRequest - presentChallenge:(DSTransactionChallengeBlock)challenge - transactionCreationCompletion:(DSTransactionCreationCompletionBlock)transactionCreationCompletion - signedCompletion:(DSTransactionSigningCompletionBlock)signedCompletion - publishedCompletion:(DSTransactionPublishedCompletionBlock)publishedCompletion - errorNotificationBlock:(DSTransactionErrorNotificationBlock)errorNotificationBlock { - DSPaymentProtocolRequest *protocolRequest = [paymentRequest protocolRequestForBlockchainIdentity:blockchainIdentity onAccount:account inContext:[NSManagedObjectContext viewContext]]; + requestingAdditionalInfo:(DSTransactionCreationRequestingAdditionalInfoBlock)additionalInfoRequest + presentChallenge:(DSTransactionChallengeBlock)challenge +transactionCreationCompletion:(DSTransactionCreationCompletionBlock)transactionCreationCompletion + signedCompletion:(DSTransactionSigningCompletionBlock)signedCompletion + publishedCompletion:(DSTransactionPublishedCompletionBlock)publishedCompletion + errorNotificationBlock:(DSTransactionErrorNotificationBlock)errorNotificationBlock { + DSPaymentProtocolRequest *protocolRequest = [paymentRequest protocolRequestForIdentity:identity onAccount:account inContext:[NSManagedObjectContext viewContext]]; [self confirmProtocolRequest:protocolRequest forAmount:paymentRequest.amount fromAccount:account acceptInternalAddress:acceptInternalAddress acceptReusingAddress:acceptReusingAddress addressIsFromPasteboard:addressIsFromPasteboard acceptUncertifiedPayee:NO requiresSpendingAuthenticationPrompt:requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:additionalInfoRequest presentChallenge:challenge transactionCreationCompletion:transactionCreationCompletion signedCompletion:signedCompletion publishedCompletion:publishedCompletion requestRelayCompletion:nil errorNotificationBlock:errorNotificationBlock]; } @@ -943,18 +957,18 @@ - (void)fetchMempoolFromNetwork { } } -// MARK: - TransactionFetching - -- (void)fetchTransactionWithDAPIForTransactionHash:(UInt256)transactionHash success:(void (^)(DSTransaction *transaction))success - failure:(void (^)(NSError *error))failure { - DSDAPIPlatformNetworkService *networkService = self.chainManager.DAPIClient.DAPIPlatformNetworkService; - [networkService getTransactionById:uint256_hex(transactionHash) - success:^(NSDictionary *_Nonnull transactionDictionary) { - //[DSTransaction transactionWithMessage:<#(nonnull NSData *)#> - // onChain:<#(nonnull DSChain *)#>]} - } - failure:failure]; -} +//// MARK: - TransactionFetching +// +//- (void)fetchTransactionWithDAPIForTransactionHash:(UInt256)transactionHash success:(void (^)(DSTransaction *transaction))success +// failure:(void (^)(NSError *error))failure { +// DSDAPIPlatformNetworkService *networkService = self.chainManager.DAPIClient.DAPIPlatformNetworkService; +// [networkService getTransactionById:uint256_hex(transactionHash) +// success:^(NSDictionary *_Nonnull transactionDictionary) { +// //[DSTransaction transactionWithMessage:<#(nonnull NSData *)#> +// // onChain:<#(nonnull DSChain *)#>]} +// } +// failure:failure]; +//} - (void)fetchTransactionHavingHash:(UInt256)transactionHash { for (DSPeer *peer in self.peerManager.connectedPeers) { @@ -1099,22 +1113,17 @@ - (DSTransaction *)peer:(DSPeer *)peer requestedTransaction:(UInt256)txHash { if (callback && !isTransactionValid) { [self.publishedTx removeObjectForKey:hash]; - error = [NSError errorWithCode:401 localizedDescriptionKey:@"Double spend"]; + error = ERROR_DOUBLE_SPEND; } else if (transaction) { for (DSAccount *account in accounts) { - if (![account transactionForHash:txHash]) { - if ([account registerTransaction:transaction saveImmediately:YES]) { - [[NSManagedObjectContext chainContext] ds_saveInBlock]; - } - } + if (![account transactionForHash:txHash] && [account registerTransaction:transaction saveImmediately:YES]) + [[NSManagedObjectContext chainContext] ds_saveInBlock]; } } dispatch_async(self.chainManager.chain.networkingQueue, ^{ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(txTimeout:) object:hash]; - dispatch_async(dispatch_get_main_queue(), ^{ - if (callback) callback(error); - }); + dispatch_async(dispatch_get_main_queue(), ^{ if (callback) callback(error); }); }); // [peer sendPingMessageWithPongHandler:^(BOOL success) { // check if peer will relay the transaction back @@ -1276,34 +1285,31 @@ - (void)peer:(DSPeer *)peer relayedTransaction:(DSTransaction *)transaction inBl } } - BOOL isNewBlockchainIdentity = FALSE; - DSBlockchainIdentity *blockchainIdentity = nil; + BOOL isNewIdentity = FALSE; + DSIdentity *identity = nil; if (![transaction isMemberOfClass:[DSTransaction class]]) { //it's a special transaction BOOL registered = YES; //default to yes - if (![transaction isKindOfClass:[DSCreditFundingTransaction class]]) { - registered = [self.chain registerSpecialTransaction:transaction saveImmediately:block ? NO : YES]; + if (![transaction isKindOfClass:[DSAssetLockTransaction class]]) { + registered = [self.chain registerSpecialTransaction:transaction saveImmediately:!block]; } if (registered) { - if ([transaction isKindOfClass:[DSCreditFundingTransaction class]]) { - DSCreditFundingTransaction *creditFundingTransaction = (DSCreditFundingTransaction *)transaction; + if ([transaction isKindOfClass:[DSAssetLockTransaction class]]) { + DSAssetLockTransaction *assetLockTransaction = (DSAssetLockTransaction *)transaction; uint32_t index; - DSWallet *wallet = [self.chain walletHavingBlockchainIdentityCreditFundingRegistrationHash:creditFundingTransaction.creditBurnPublicKeyHash foundAtIndex:&index]; - - if (wallet) { - blockchainIdentity = [wallet blockchainIdentityForUniqueId:transaction.creditBurnIdentityIdentifier]; - } - - if (!blockchainIdentity) { + DSWallet *wallet = [self.chain walletHavingIdentityAssetLockRegistrationHash:assetLockTransaction.creditBurnPublicKeyHash foundAtIndex:&index]; + if (wallet) + identity = [wallet identityForUniqueId:transaction.creditBurnIdentityIdentifier]; + if (!identity) { [self.chain triggerUpdatesForLocalReferences:transaction]; if (wallet) { - blockchainIdentity = [wallet blockchainIdentityForUniqueId:transaction.creditBurnIdentityIdentifier]; - if (blockchainIdentity) isNewBlockchainIdentity = TRUE; + identity = [wallet identityForUniqueId:transaction.creditBurnIdentityIdentifier]; + if (identity) isNewIdentity = TRUE; } - } else if (blockchainIdentity && !blockchainIdentity.registrationCreditFundingTransaction) { - blockchainIdentity.registrationCreditFundingTransactionHash = creditFundingTransaction.txHash; + } else if (identity && !identity.registrationAssetLockTransaction) { + identity.registrationAssetLockTransactionHash = assetLockTransaction.txHash; } } else { [self.chain triggerUpdatesForLocalReferences:transaction]; @@ -1344,12 +1350,23 @@ - (void)peer:(DSPeer *)peer relayedTransaction:(DSTransaction *)transaction inBl addedNewAccount = TRUE; // New account was created dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSAccountNewAccountFromTransactionNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSChainManagerNotificationWalletKey: account.wallet, DSChainManagerNotificationAccountKey: account}]; + [[NSNotificationCenter defaultCenter] postNotificationName:DSAccountNewAccountFromTransactionNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSChainManagerNotificationWalletKey: account.wallet, + DSChainManagerNotificationAccountKey: account + }]; }); } else { // This means we were not authenticated, we should post a notification dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSAccountNewAccountShouldBeAddedFromTransactionNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSChainManagerNotificationWalletKey: account.wallet}]; + [[NSNotificationCenter defaultCenter] postNotificationName:DSAccountNewAccountShouldBeAddedFromTransactionNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSChainManagerNotificationWalletKey: account.wallet + }]; }); } break; @@ -1358,10 +1375,10 @@ - (void)peer:(DSPeer *)peer relayedTransaction:(DSTransaction *)transaction inBl // keep track of how many peers have or relay a tx, this indicates how likely the tx is to confirm if (callback || !peer || (!syncing && ![self.txRelays[hash] containsObject:peer])) { - if (!self.txRelays[hash]) self.txRelays[hash] = [NSMutableSet set]; - if (peer) { + if (!self.txRelays[hash]) + self.txRelays[hash] = [NSMutableSet set]; + if (peer) [self.txRelays[hash] addObject:peer]; - } if (callback) [self.publishedCallback removeObjectForKey:hash]; for (DSAccount *account in accountsAcceptingTransaction) { @@ -1369,8 +1386,8 @@ - (void)peer:(DSPeer *)peer relayedTransaction:(DSTransaction *)transaction inBl [account transactionForHash:transaction.txHash].blockHeight == TX_UNCONFIRMED && [account transactionForHash:transaction.txHash].timestamp == 0) { [account setBlockHeight:TX_UNCONFIRMED - andTimestamp:[NSDate timeIntervalSince1970] - forTransactionHashes:@[hash]]; // set timestamp when tx is verified + andTimestamp:[NSDate timeIntervalSince1970] + forTransactionHashes:@[hash]]; // set timestamp when tx is verified } } @@ -1383,11 +1400,21 @@ - (void)peer:(DSPeer *)peer relayedTransaction:(DSTransaction *)transaction inBl dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:DSTransactionManagerTransactionStatusDidChangeNotification object:nil - userInfo:@{DSChainManagerNotificationChainKey: self.chain, - DSTransactionManagerNotificationTransactionKey: transaction, - DSTransactionManagerNotificationTransactionChangesKey: @{DSTransactionManagerNotificationTransactionAcceptedStatusKey: @(YES)}}]; - [[NSNotificationCenter defaultCenter] postNotificationName:DSTransactionManagerTransactionReceivedNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain}]; - [[NSNotificationCenter defaultCenter] postNotificationName:DSWalletBalanceDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain}]; + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSTransactionManagerNotificationTransactionKey: transaction, + DSTransactionManagerNotificationTransactionChangesKey: @{DSTransactionManagerNotificationTransactionAcceptedStatusKey: @(YES)} + }]; + [[NSNotificationCenter defaultCenter] postNotificationName:DSTransactionManagerTransactionReceivedNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain + }]; + [[NSNotificationCenter defaultCenter] postNotificationName:DSWalletBalanceDidChangeNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain + }]; if (callback) callback(nil); }); @@ -1405,17 +1432,14 @@ - (void)peer:(DSPeer *)peer relayedTransaction:(DSTransaction *)transaction inBl [self.nonFalsePositiveTransactions addObject:hash]; - if (peer) { + if (peer) [self.txRequests[hash] removeObject:peer]; - } - - - if (block && [transaction isKindOfClass:[DSCreditFundingTransaction class]] && blockchainIdentity && isNewBlockchainIdentity) { - NSTimeInterval walletCreationTime = [blockchainIdentity.wallet walletCreationTime]; - if ((walletCreationTime == BIP39_WALLET_UNKNOWN_CREATION_TIME || walletCreationTime == BIP39_CREATION_TIME) && blockchainIdentity.wallet.defaultBlockchainIdentity == blockchainIdentity) { - [blockchainIdentity.wallet setGuessedWalletCreationTime:self.chain.lastSyncBlockTimestamp - HOUR_TIME_INTERVAL - (DAY_TIME_INTERVAL / arc4random() % DAY_TIME_INTERVAL)]; + if (block && [transaction isKindOfClass:[DSAssetLockTransaction class]] && identity && isNewIdentity) { + NSTimeInterval walletCreationTime = [identity.wallet walletCreationTime]; + if ((walletCreationTime == BIP39_WALLET_UNKNOWN_CREATION_TIME || walletCreationTime == BIP39_CREATION_TIME) && identity.wallet.defaultIdentity == identity) { + [identity.wallet setGuessedWalletCreationTime:self.chain.lastSyncBlockTimestamp - HOUR_TIME_INTERVAL - (DAY_TIME_INTERVAL / arc4random() % DAY_TIME_INTERVAL)]; } - [self.identitiesManager checkCreditFundingTransactionForPossibleNewIdentity:(DSCreditFundingTransaction *)transaction]; + [self.identitiesManager checkAssetLockTransactionForPossibleNewIdentity:(DSAssetLockTransaction *)transaction]; [self destroyTransactionsBloomFilter]; //We want to destroy it temporarily, while we wait for L2, no matter what the block should not be saved and needs to be refetched } else if (addedNewAccount) { [self destroyTransactionsBloomFilter]; @@ -1448,9 +1472,11 @@ - (void)peer:(DSPeer *)peer rejectedTransaction:(UInt256)txHash withCode:(uint8_ dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:DSTransactionManagerTransactionStatusDidChangeNotification object:nil - userInfo:@{DSChainManagerNotificationChainKey: self.chain, - DSTransactionManagerNotificationTransactionKey: transaction, - DSTransactionManagerNotificationTransactionChangesKey: @{DSTransactionManagerNotificationTransactionAcceptedStatusKey: @(NO)}}]; + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSTransactionManagerNotificationTransactionKey: transaction, + DSTransactionManagerNotificationTransactionChangesKey: @{DSTransactionManagerNotificationTransactionAcceptedStatusKey: @(NO)} + }]; [[NSNotificationCenter defaultCenter] postNotificationName:DSWalletBalanceDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain}]; #if TARGET_OS_IOS #if DEBUG @@ -1533,7 +1559,7 @@ - (void)peer:(DSPeer *)peer relayedInstantSendTransactionLock:(DSInstantSendTran [self.instantSendLocksWaitingForTransactions setObject:instantSendTransactionLock forKey:uint256_data(instantSendTransactionLock.transactionHash)]; } - if (!verified && !instantSendTransactionLock.intendedQuorum) { + if (!verified && !instantSendTransactionLock.intendedQuorumPublicKey) { //the quorum hasn't been retrieved yet [self.instantSendLocksWaitingForQuorums setObject:instantSendTransactionLock forKey:uint256_data(instantSendTransactionLock.transactionHash)]; } @@ -1575,17 +1601,17 @@ - (void)checkInstantSendLocksWaitingForQuorums { if (!account || !transaction || transaction.confirmed) { [self.instantSendLocksWaitingForQuorums removeObjectForKey:uint256_data(instantSendTransactionLock.transactionHash)]; } -#if DEBUG - DSMasternodeList *masternodeList = nil; - DSQuorumEntry *quorum = [instantSendTransactionLock findSigningQuorumReturnMasternodeList:&masternodeList]; - if (quorum && masternodeList) { - NSArray *quorumEntries = [masternodeList quorumEntriesRankedForInstantSendRequestID:[instantSendTransactionLock requestID]]; - NSUInteger index = [quorumEntries indexOfObject:quorum]; - DSLog(@"[%@] Quorum %@ found at index %lu for masternodeList at height %lu", self.chain.name, quorum, (unsigned long)index, (unsigned long)masternodeList.height); - DSLog(@"[%@] Quorum entries are %@", self.chain.name, quorumEntries); - } - DSLog(@"[%@] Could not verify %@", self.chain.name, instantSendTransactionLock); -#endif +//#if DEBUG +// DSMasternodeList *masternodeList = nil; +// DSQuorumEntry *quorum = [instantSendTransactionLock findSigningQuorumReturnMasternodeList:&masternodeList]; +// if (quorum && masternodeList) { +// NSArray *quorumEntries = [masternodeList quorumEntriesRankedForInstantSendRequestID:[instantSendTransactionLock requestID]]; +// NSUInteger index = [quorumEntries indexOfObject:quorum]; +// DSLog(@"[%@] Quorum %@ found at index %lu for masternodeList at height %lu", self.chain.name, quorum, (unsigned long)index, (unsigned long)masternodeList.height); +// DSLog(@"[%@] Quorum entries are %@", self.chain.name, quorumEntries); +// } +// DSLog(@"[%@] Could not verify %@", self.chain.name, instantSendTransactionLock); +//#endif } } } @@ -1718,7 +1744,7 @@ - (void)peer:(DSPeer *)peer relayedChainLock:(DSChainLock *)chainLock { [self.chainLocksWaitingForMerkleBlocks setObject:chainLock forKey:uint256_data(chainLock.blockHash)]; } - if (!verified && !chainLock.intendedQuorum) { + if (!verified && !chainLock.intendedQuorumPublicKey) { //the quorum hasn't been retrieved yet [self.chainLocksWaitingForQuorums setObject:chainLock forKey:uint256_data(chainLock.blockHash)]; } @@ -1736,20 +1762,25 @@ - (void)checkChainLocksWaitingForQuorums { DSMerkleBlock *block = [self.chain blockForBlockHash:chainLock.blockHash]; [self.chainLocksWaitingForQuorums removeObjectForKey:chainLockHashData]; dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSChainBlockWasLockedNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSChainNotificationBlockKey: block}]; + [[NSNotificationCenter defaultCenter] postNotificationName:DSChainBlockWasLockedNotification + object:nil + userInfo:@{ + DSChainManagerNotificationChainKey: self.chain, + DSChainNotificationBlockKey: block + }]; }); } else { -#if DEBUG_CHAIN_LOCKS_WAITING_FOR_QUORUMS - DSMasternodeList *masternodeList = nil; - DSQuorumEntry *quorum = [chainLock findSigningQuorumReturnMasternodeList:&masternodeList]; - if (quorum && masternodeList) { - NSArray *quorumEntries = [masternodeList quorumEntriesRankedForInstantSendRequestID:[chainLock requestID]]; - NSUInteger index = [quorumEntries indexOfObject:quorum]; - DSLog(@"[%@] Quorum %@ found at index %lu for masternodeList at height %lu", self.chain.name, quorum, (unsigned long)index, (unsigned long)masternodeList.height); - DSLog(@"[%@] Quorum entries are %@", self.chain.name, quorumEntries); - } - DSLog(@"[%@] Could not verify %@", self.chain.name, chainLock); -#endif +//#if DEBUG_CHAIN_LOCKS_WAITING_FOR_QUORUMS +// DSMasternodeList *masternodeList = nil; +// DSQuorumEntry *quorum = [chainLock findSigningQuorumReturnMasternodeList:&masternodeList]; +// if (quorum && masternodeList) { +// NSArray *quorumEntries = [masternodeList quorumEntriesRankedForInstantSendRequestID:[chainLock requestID]]; +// NSUInteger index = [quorumEntries indexOfObject:quorum]; +// DSLog(@"[%@] Quorum %@ found at index %lu for masternodeList at height %lu", self.chain.name, quorum, (unsigned long)index, (unsigned long)masternodeList.height); +// DSLog(@"[%@] Quorum entries are %@", self.chain.name, quorumEntries); +// } +// DSLog(@"[%@] Could not verify %@", self.chain.name, chainLock); +//#endif } } } diff --git a/DashSync/shared/Models/Managers/Service Managers/Auth/DSAuthenticationManager.m b/DashSync/shared/Models/Managers/Service Managers/Auth/DSAuthenticationManager.m index 4fd1e78a5..c3649d8dd 100644 --- a/DashSync/shared/Models/Managers/Service Managers/Auth/DSAuthenticationManager.m +++ b/DashSync/shared/Models/Managers/Service Managers/Auth/DSAuthenticationManager.m @@ -28,7 +28,9 @@ #import "DSAccount.h" #import "DSBIP39Mnemonic.h" #import "DSBiometricsAuthenticator.h" +#import "DSChain+Checkpoint.h" #import "DSChain+Protected.h" +#import "DSChain+Wallet.h" #import "DSChainsManager.h" #import "DSCheckpoint.h" #import "DSDerivationPath.h" diff --git a/DashSync/shared/Models/Managers/Service Managers/DSInsightManager.h b/DashSync/shared/Models/Managers/Service Managers/DSInsightManager.h index 73edc5492..699a9d506 100644 --- a/DashSync/shared/Models/Managers/Service Managers/DSInsightManager.h +++ b/DashSync/shared/Models/Managers/Service Managers/DSInsightManager.h @@ -28,6 +28,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)blockForBlockHash:(UInt256)blockHash onChain:(DSChain *)chain completion:(void (^)(DSBlock *_Nullable block, NSError *_Nullable error))completion; +- (void)blockForBlockHeight:(uint32_t)blockHeight onChain:(DSChain *)chain + completion:(void (^)(DSBlock *_Nullable block, NSError *_Nullable error))completion; - (void)queryInsightForTransactionWithHash:(UInt256)transactionHash onChain:(DSChain *)chain completion:(void (^)(DSTransaction *transaction, NSError *error))completion; diff --git a/DashSync/shared/Models/Managers/Service Managers/DSInsightManager.m b/DashSync/shared/Models/Managers/Service Managers/DSInsightManager.m index 77ec21af0..0b5c5b4f9 100644 --- a/DashSync/shared/Models/Managers/Service Managers/DSInsightManager.m +++ b/DashSync/shared/Models/Managers/Service Managers/DSInsightManager.m @@ -8,6 +8,7 @@ #import "DSInsightManager.h" #import "DSBlock.h" #import "DSChain.h" +#import "DSChain+Params.h" #import "DSMerkleBlock.h" #import "DSTransactionFactory.h" #import "NSData+Dash.h" @@ -106,6 +107,49 @@ - (void)blockForBlockHash:(UInt256)blockHash onChain:(DSChain *)chain completion NSString *insightURL = [chain isMainnet] ? INSIGHT_URL : TESTNET_INSIGHT_URL; [self queryInsight:insightURL forBlockWithHash:reversedBlockHash onChain:chain completion:completion]; } +- (void)blockForBlockHeight:(uint32_t)blockHeight onChain:(DSChain *)chain completion:(void (^)(DSBlock *block, NSError *error))completion { + NSAssert(blockHeight != UINT32_MAX, @"blockHeight must be set"); + NSParameterAssert(chain); + NSString *insightURL = [chain isMainnet] ? INSIGHT_URL : TESTNET_INSIGHT_URL; + NSParameterAssert(insightURL); + NSString *path = [[insightURL stringByAppendingPathComponent:BLOCK_PATH] stringByAppendingPathComponent:[@(blockHeight) stringValue]]; + NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:path] + cachePolicy:NSURLRequestReloadIgnoringCacheData + timeoutInterval:20.0]; + req.HTTPMethod = @"GET"; + DSLogPrivate(@"%@ GET: %@", req.URL.absoluteString, + [[NSString alloc] initWithData:req.HTTPBody + encoding:NSUTF8StringEncoding]); + + [[[NSURLSession sharedSession] dataTaskWithRequest:req + completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + if (error) { + completion(nil, error); + return; + } + + NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; + + if (error) { + DSLogPrivate(@"Error decoding response (%@) %@", req.URL.absoluteString, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); + completion(nil, [NSError errorWithCode:417 descriptionKey:[NSString stringWithFormat:DSLocalizedString(@"Unexpected response from %@", nil), + req.URL.host]]); + return; + } + NSNumber *version = json[@"version"]; + NSData *blockHash = [json[@"hash"] hexToData]; + NSData *previousBlockHash = [json[@"previousblockhash"] hexToData]; + NSData *merkleRoot = [json[@"merkleroot"] hexToData]; + NSNumber *timestamp = json[@"time"]; + NSString *targetString = json[@"bits"]; + NSData *chainWork = [json[@"chainwork"] hexToData]; + NSNumber *height = json[@"height"]; + DSBlock *block = [[DSBlock alloc] initWithVersion:[version unsignedIntValue] blockHash:blockHash.reverse.UInt256 prevBlock:previousBlockHash.reverse.UInt256 timestamp:timestamp.unsignedIntValue merkleRoot:merkleRoot.reverse.UInt256 target:[targetString.hexToData UInt32AtOffset:0] chainWork:chainWork.reverse.UInt256 height:height.unsignedIntValue onChain:chain]; + + completion(block, nil); + }] resume]; + +} - (void)queryInsight:(NSString *)insightURL forBlockWithHash:(UInt256)blockHash onChain:(DSChain *)chain completion:(void (^)(DSBlock *block, NSError *error))completion { NSParameterAssert(insightURL); @@ -148,6 +192,8 @@ - (void)queryInsight:(NSString *)insightURL forBlockWithHash:(UInt256)blockHash }] resume]; } + + - (void)queryInsightForTransactionWithHash:(UInt256)transactionHash onChain:(DSChain *)chain completion:(void (^)(DSTransaction *transaction, NSError *error))completion { NSParameterAssert(chain); diff --git a/DashSync/shared/Models/Managers/Service Managers/DSOptionsManager.h b/DashSync/shared/Models/Managers/Service Managers/DSOptionsManager.h index df52c504c..b8aa796c6 100644 --- a/DashSync/shared/Models/Managers/Service Managers/DSOptionsManager.h +++ b/DashSync/shared/Models/Managers/Service Managers/DSOptionsManager.h @@ -23,11 +23,11 @@ typedef NS_ENUM(NSUInteger, DSSyncType) DSSyncType_GovernanceVotes = 1 << 5, DSSyncType_GovernanceVoting = DSSyncType_Governance | DSSyncType_MasternodeList, DSSyncType_Sporks = 1 << 6, - DSSyncType_BlockchainIdentities = 1 << 7, + DSSyncType_Identities = 1 << 7, DSSyncType_DPNS = 1 << 8, DSSyncType_Dashpay = 1 << 9, DSSyncType_MultiAccountAutoDiscovery = 1 << 10, - DSSyncType_Default = DSSyncType_SPV | DSSyncType_Mempools | DSSyncType_VerifiedMasternodeList | DSSyncType_Sporks | DSSyncType_BlockchainIdentities | DSSyncType_DPNS | DSSyncType_Dashpay | DSSyncType_MultiAccountAutoDiscovery, + DSSyncType_Default = DSSyncType_SPV | DSSyncType_Mempools | DSSyncType_VerifiedMasternodeList | DSSyncType_Sporks | DSSyncType_Identities | DSSyncType_DPNS | DSSyncType_Dashpay | DSSyncType_MultiAccountAutoDiscovery, DSSyncType_NeedsWalletSyncType = DSSyncType_BaseSPV | DSSyncType_FullBlocks, DSSyncType_GetsNewBlocks = DSSyncType_SPV | DSSyncType_FullBlocks, }; diff --git a/DashSync/shared/Models/Managers/Service Managers/DSVersionManager.m b/DashSync/shared/Models/Managers/Service Managers/DSVersionManager.m index 9c0810762..3ac7eff8b 100644 --- a/DashSync/shared/Models/Managers/Service Managers/DSVersionManager.m +++ b/DashSync/shared/Models/Managers/Service Managers/DSVersionManager.m @@ -9,6 +9,7 @@ #import "DSAccount.h" #import "DSAuthenticationManager+UpdateSecureTime.h" #import "DSBIP39Mnemonic.h" +#import "DSChain+Wallet.h" #import "DSChainManager.h" #import "DSChainsManager.h" #import "DSDerivationPathFactory.h" diff --git a/DashSync/shared/Models/Masternode/DSLocalMasternode.h b/DashSync/shared/Models/Masternode/DSLocalMasternode.h index 447c5b9fa..a978f6cdc 100644 --- a/DashSync/shared/Models/Masternode/DSLocalMasternode.h +++ b/DashSync/shared/Models/Masternode/DSLocalMasternode.h @@ -6,7 +6,7 @@ // #import "BigIntTypes.h" -#import "dash_shared_core.h" +#import "DSKeyManager.h" #import NS_ASSUME_NONNULL_BEGIN @@ -88,23 +88,23 @@ typedef NS_ENUM(NSUInteger, DSLocalMasternodeStatus) - (void)saveInContext:(NSManagedObjectContext *)context; // BLS -- (OpaqueKey *_Nullable)operatorKeyFromSeed:(NSData *)seed; +- (DMaybeOpaqueKey *_Nullable)operatorKeyFromSeed:(NSData *)seed; // ECDSA -- (OpaqueKey *_Nullable)ownerKeyFromSeed:(NSData *)seed; -- (OpaqueKey *_Nullable)votingKeyFromSeed:(NSData *)seed; +- (DMaybeOpaqueKey *_Nullable)ownerKeyFromSeed:(NSData *)seed; +- (DMaybeOpaqueKey *_Nullable)votingKeyFromSeed:(NSData *)seed; // ED25519 -- (OpaqueKey *_Nullable)platformNodeKeyFromSeed:(NSData *)seed; +- (DMaybeOpaqueKey *_Nullable)platformNodeKeyFromSeed:(NSData *)seed; - (NSString *)operatorKeyStringFromSeed:(NSData *)seed; - (NSString *_Nullable)ownerKeyStringFromSeed:(NSData *)seed; - (NSString *_Nullable)votingKeyStringFromSeed:(NSData *)seed; - (NSString *_Nullable)platformNodeKeyStringFromSeed:(NSData *)seed; -- (BOOL)forceOperatorPublicKey:(OpaqueKey *)operatorPublicKey; -- (BOOL)forceOwnerPrivateKey:(OpaqueKey *)ownerPrivateKey; +- (BOOL)forceOperatorPublicKey:(dash_spv_crypto_keys_key_OpaqueKey *)operatorPublicKey; +- (BOOL)forceOwnerPrivateKey:(dash_spv_crypto_keys_key_OpaqueKey *)ownerPrivateKey; //the voting key can either be private or public key -- (BOOL)forceVotingKey:(OpaqueKey *)votingKey; -- (BOOL)forcePlatformNodeKey:(OpaqueKey *)platformNodeKey; +- (BOOL)forceVotingKey:(dash_spv_crypto_keys_key_OpaqueKey *)votingKey; +- (BOOL)forcePlatformNodeKey:(dash_spv_crypto_keys_key_OpaqueKey *)platformNodeKey; @end diff --git a/DashSync/shared/Models/Masternode/DSLocalMasternode.m b/DashSync/shared/Models/Masternode/DSLocalMasternode.m index 695931daa..e40902d99 100644 --- a/DashSync/shared/Models/Masternode/DSLocalMasternode.m +++ b/DashSync/shared/Models/Masternode/DSLocalMasternode.m @@ -9,7 +9,9 @@ #import "DSAccount.h" #import "DSAuthenticationKeysDerivationPath.h" #import "DSAuthenticationManager.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" +#import "DSChain+Wallet.h" #import "DSChainEntity+CoreDataProperties.h" #import "DSChainManager.h" #import "DSLocalMasternodeEntity+CoreDataClass.h" @@ -56,7 +58,7 @@ @interface DSLocalMasternode () @property (nonatomic, strong) NSMutableArray *providerUpdateServiceTransactions; @property (nonatomic, strong) NSMutableArray *providerUpdateRevocationTransactions; -@property (nonatomic, assign, readonly) OpaqueKey *ownerPrivateKey; +@property (nonatomic, assign, readonly) DOpaqueKey *ownerPrivateKey; @end @@ -179,27 +181,27 @@ - (void)registerInAssociatedWallets { [self.platformNodeKeysWallet registerPlatformNode:self]; } -- (BOOL)forceOperatorPublicKey:(OpaqueKey *)operatorPublicKey { +- (BOOL)forceOperatorPublicKey:(dash_spv_crypto_keys_key_OpaqueKey *)operatorPublicKey { if (self.operatorWalletIndex != UINT32_MAX) return NO; [self.ownerKeysWallet registerMasternodeOperator:self withOperatorPublicKey:operatorPublicKey]; return YES; } -- (BOOL)forceOwnerPrivateKey:(OpaqueKey *)ownerPrivateKey { +- (BOOL)forceOwnerPrivateKey:(dash_spv_crypto_keys_key_OpaqueKey *)ownerPrivateKey { if (self.ownerWalletIndex != UINT32_MAX) return NO; - if (!key_ecdsa_has_private_key(ownerPrivateKey->ecdsa)) return NO; + if (!dash_spv_crypto_keys_key_OpaqueKey_has_private_key(ownerPrivateKey)) return NO; [self.ownerKeysWallet registerMasternodeOwner:self withOwnerPrivateKey:ownerPrivateKey]; return YES; } //the voting key can either be private or public key -- (BOOL)forceVotingKey:(OpaqueKey *)votingKey { +- (BOOL)forceVotingKey:(dash_spv_crypto_keys_key_OpaqueKey *)votingKey { if (self.votingWalletIndex != UINT32_MAX) return NO; [self.ownerKeysWallet registerMasternodeVoter:self withVotingKey:votingKey]; return YES; } -- (BOOL)forcePlatformNodeKey:(OpaqueKey *)platformNodeKey { +- (BOOL)forcePlatformNodeKey:(dash_spv_crypto_keys_key_OpaqueKey *)platformNodeKey { if (self.platformNodeWalletIndex != UINT32_MAX) return NO; [self.platformNodeKeysWallet registerPlatformNode:self withKey:platformNodeKey]; return YES; @@ -293,12 +295,12 @@ - (NSString *)operatorPayoutAddress { // MARK: - Getting key from seed -- (OpaqueKey *)operatorKeyFromSeed:(NSData *)seed { +- (DMaybeOpaqueKey *)operatorKeyFromSeed:(NSData *)seed { DSAuthenticationKeysDerivationPath *providerOperatorKeysDerivationPath = [DSAuthenticationKeysDerivationPath providerOperatorKeysDerivationPathForWallet:self.operatorKeysWallet]; return [providerOperatorKeysDerivationPath privateKeyForHash160:[[NSData dataWithUInt384:self.providerRegistrationTransaction.operatorKey] hash160] fromSeed:seed]; } -- (OpaqueKey *)ownerKeyFromSeed:(NSData *)seed { +- (DMaybeOpaqueKey *)ownerKeyFromSeed:(NSData *)seed { if (!self.ownerKeysWallet || !seed) { return nil; } @@ -306,7 +308,7 @@ - (OpaqueKey *)ownerKeyFromSeed:(NSData *)seed { return [providerOwnerKeysDerivationPath privateKeyForHash160:self.providerRegistrationTransaction.ownerKeyHash fromSeed:seed]; } -- (OpaqueKey *)votingKeyFromSeed:(NSData *)seed { +- (DMaybeOpaqueKey *)votingKeyFromSeed:(NSData *)seed { if (!self.votingKeysWallet || !seed) { return nil; } @@ -314,7 +316,7 @@ - (OpaqueKey *)votingKeyFromSeed:(NSData *)seed { return [providerVotingKeysDerivationPath privateKeyForHash160:self.providerRegistrationTransaction.votingKeyHash fromSeed:seed]; } -- (OpaqueKey *)platformNodeKeyFromSeed:(NSData *)seed { +- (DMaybeOpaqueKey *)platformNodeKeyFromSeed:(NSData *)seed { if (!self.platformNodeKeysWallet || !seed) { return nil; } @@ -325,27 +327,31 @@ - (OpaqueKey *)platformNodeKeyFromSeed:(NSData *)seed { // MARK: - Getting key string from seed - (NSString *)operatorKeyStringFromSeed:(NSData *)seed { - OpaqueKey *key = [self operatorKeyFromSeed:seed]; - return [DSKeyManager secretKeyHexString:key]; + DMaybeOpaqueKey *key = [self operatorKeyFromSeed:seed]; + if (!key || !key->ok) return nil; + // TODO: free memory + return [DSKeyManager secretKeyHexString:key->ok]; } - (NSString *)ownerKeyStringFromSeed:(NSData *)seed { - OpaqueKey *key = [self ownerKeyFromSeed:seed]; - if (!key) return nil; - return [DSKeyManager secretKeyHexString:key]; + DMaybeOpaqueKey *key = [self ownerKeyFromSeed:seed]; + if (!key || !key->ok) return nil; + // TODO: free memory + return [DSKeyManager secretKeyHexString:key->ok]; } - (NSString *)votingKeyStringFromSeed:(NSData *)seed { - OpaqueKey *key = [self votingKeyFromSeed:seed]; - if (!key) return nil; - return [DSKeyManager secretKeyHexString:key]; + DMaybeOpaqueKey *key = [self votingKeyFromSeed:seed]; + if (!key || !key->ok) return nil; + // TODO: free memory + return [DSKeyManager secretKeyHexString:key->ok]; } - (NSString *)platformNodeKeyStringFromSeed:(NSData *)seed { - OpaqueKey *key = [self platformNodeKeyFromSeed:seed]; - if (!key) return nil; - // TODO: cleanup - return [DSKeyManager secretKeyHexString:key]; + DMaybeOpaqueKey *key = [self platformNodeKeyFromSeed:seed]; + if (!key || !key->ok) return nil; + // TODO: free memory + return [DSKeyManager secretKeyHexString:key->ok]; } // MARK: - Getting public key data @@ -447,7 +453,7 @@ - (void)registrationTransactionFundedByAccount:(DSAccount *)fundingAccount toAdd } // TODO: how do we know which version we should use: (self.version == 2 /*BLS Basic*/ && self.providerType == 1) // based on protocol_version? - OpaqueKey *platformNodeKey; + DMaybeOpaqueKey *platformNodeKey; if (self.platformNodeWalletIndex == UINT32_MAX) { self.platformNodeWalletIndex = (uint32_t)[platformNodeKeysDerivationPath firstUnusedIndex]; platformNodeKey = [platformNodeKeysDerivationPath firstUnusedPrivateKeyFromSeed:seed]; @@ -455,10 +461,10 @@ - (void)registrationTransactionFundedByAccount:(DSAccount *)fundingAccount toAdd platformNodeKey = [platformNodeKeysDerivationPath privateKeyAtIndex:self.platformNodeWalletIndex fromSeed:seed]; } - UInt256 platformNodeHash = [DSKeyManager publicKeyData:platformNodeKey].SHA256; + UInt256 platformNodeHash = [DSKeyManager publicKeyData:platformNodeKey->ok].SHA256; UInt160 platformNodeID = *(UInt160 *)&platformNodeHash; - OpaqueKey *ownerKey; + DMaybeOpaqueKey *ownerKey; if (self.ownerWalletIndex == UINT32_MAX) { self.ownerWalletIndex = (uint32_t)[providerOwnerKeysDerivationPath firstUnusedIndex]; ownerKey = [providerOwnerKeysDerivationPath firstUnusedPrivateKeyFromSeed:seed]; @@ -468,7 +474,7 @@ - (void)registrationTransactionFundedByAccount:(DSAccount *)fundingAccount toAdd UInt160 votingKeyHash; - UInt160 ownerKeyHash = [DSKeyManager publicKeyData:ownerKey].hash160; + UInt160 ownerKeyHash = [DSKeyManager publicKeyData:ownerKey->ok].hash160; if ([fundingAccount.wallet.chain.chainManager.sporkManager deterministicMasternodeListEnabled]) { DSAuthenticationKeysDerivationPath *providerVotingKeysDerivationPath = [DSAuthenticationKeysDerivationPath providerVotingKeysDerivationPathForWallet:self.votingKeysWallet]; @@ -493,10 +499,10 @@ - (void)registrationTransactionFundedByAccount:(DSAccount *)fundingAccount toAdd } else { operatorKey = [providerOperatorKeysDerivationPath publicKeyDataAtIndex:self.operatorWalletIndex].UInt384; } - uint16_t operatorKeyVersion = providerOperatorKeysDerivationPath.signingAlgorithm == KeyKind_BLS ? 1 : 2; + uint16_t operatorKeyVersion = dash_spv_crypto_keys_key_KeyKind_index(providerOperatorKeysDerivationPath.signingAlgorithm) == dash_spv_crypto_keys_key_KeyKind_BLS ? 1 : 2; DSProviderRegistrationTransaction *providerRegistrationTransaction = [[DSProviderRegistrationTransaction alloc] initWithProviderRegistrationTransactionVersion:2 type:0 mode:0 collateralOutpoint:collateral ipAddress:self.ipAddress port:self.port ownerKeyHash:ownerKeyHash operatorKey:operatorKey operatorKeyVersion:operatorKeyVersion votingKeyHash:votingKeyHash platformNodeID:platformNodeID operatorReward:0 scriptPayout:script onChain:fundingAccount.wallet.chain]; if (dsutxo_is_zero(collateral)) { - NSString *holdingAddress = [providerFundsDerivationPath receiveAddress]; + NSString *holdingAddress = [providerFundsDerivationPath registerAddressesWithGapLimit:1 error:nil].lastObject; NSData *scriptPayout = [DSKeyManager scriptPubKeyForAddress:holdingAddress forChain:self.holdingKeysWallet.chain]; [fundingAccount updateTransaction:providerRegistrationTransaction forAmounts:@[@(MASTERNODE_COST)] toOutputScripts:@[scriptPayout] withFee:YES]; @@ -536,11 +542,12 @@ - (void)updateTransactionFundedByAccount:(DSAccount *)fundingAccount toIPAddress DSAuthenticationKeysDerivationPath *providerOperatorKeysDerivationPath = [DSAuthenticationKeysDerivationPath providerOperatorKeysDerivationPathForWallet:self.operatorKeysWallet]; NSAssert(self.providerRegistrationTransaction, @"There must be a providerRegistrationTransaction linked here"); - OpaqueKey *operatorKey = [providerOperatorKeysDerivationPath privateKeyForHash160:[[NSData dataWithUInt384:self.providerRegistrationTransaction.operatorKey] hash160] fromSeed:seed]; + DMaybeOpaqueKey *operatorKey = [providerOperatorKeysDerivationPath privateKeyForHash160:[[NSData dataWithUInt384:self.providerRegistrationTransaction.operatorKey] hash160] fromSeed:seed]; DSProviderUpdateServiceTransaction *providerUpdateServiceTransaction = [[DSProviderUpdateServiceTransaction alloc] initWithProviderUpdateServiceTransactionVersion:1 providerTransactionHash:self.providerRegistrationTransaction.txHash ipAddress:ipAddress port:port scriptPayout:scriptPayout onChain:fundingAccount.wallet.chain]; [fundingAccount updateTransaction:providerUpdateServiceTransaction forAmounts:@[] toOutputScripts:@[] withFee:YES]; - [providerUpdateServiceTransaction signPayloadWithKey:operatorKey]; + [providerUpdateServiceTransaction signPayloadWithKey:operatorKey->ok]; + DMaybeOpaqueKeyDtor(operatorKey); // TODO: what about platformNodeKey? //there is no need to sign the payload here. completion(providerUpdateServiceTransaction); @@ -562,13 +569,14 @@ - (void)updateTransactionFundedByAccount:(DSAccount *)fundingAccount changeOpera NSData *scriptPayout = payoutAddress == nil ? [NSData data] : [DSKeyManager scriptPubKeyForAddress:payoutAddress forChain:fundingAccount.wallet.chain]; DSAuthenticationKeysDerivationPath *providerOwnerKeysDerivationPath = [DSAuthenticationKeysDerivationPath providerOwnerKeysDerivationPathForWallet:self.ownerKeysWallet]; NSAssert(self.providerRegistrationTransaction, @"There must be a providerRegistrationTransaction linked here"); - OpaqueKey *ownerKey = [providerOwnerKeysDerivationPath privateKeyForHash160:self.providerRegistrationTransaction.ownerKeyHash fromSeed:seed]; + DMaybeOpaqueKey *ownerKey = [providerOwnerKeysDerivationPath privateKeyForHash160:self.providerRegistrationTransaction.ownerKeyHash fromSeed:seed]; DSProviderUpdateRegistrarTransaction *providerUpdateRegistrarTransaction = [[DSProviderUpdateRegistrarTransaction alloc] initWithProviderUpdateRegistrarTransactionVersion:1 providerTransactionHash:self.providerRegistrationTransaction.txHash mode:0 operatorKey:operatorKey votingKeyHash:votingKeyHash scriptPayout:scriptPayout onChain:fundingAccount.wallet.chain]; [fundingAccount updateTransaction:providerUpdateRegistrarTransaction forAmounts:@[] toOutputScripts:@[] withFee:YES]; - [providerUpdateRegistrarTransaction signPayloadWithKey:ownerKey]; + [providerUpdateRegistrarTransaction signPayloadWithKey:ownerKey->ok]; + DMaybeOpaqueKeyDtor(ownerKey); //there is no need to sign the payload here. diff --git a/DashSync/shared/Models/Masternode/DSMasternodeList+Mndiff.h b/DashSync/shared/Models/Masternode/DSMasternodeList+Mndiff.h index dd17ff557..b5e2baca5 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeList+Mndiff.h +++ b/DashSync/shared/Models/Masternode/DSMasternodeList+Mndiff.h @@ -17,18 +17,17 @@ #import "DSChain.h" #import "DSMasternodeList.h" -#import "dash_shared_core.h" #import NS_ASSUME_NONNULL_BEGIN -@interface DSMasternodeList (Mndiff) - -+ (instancetype)masternodeListWith:(MasternodeList *)list onChain:(DSChain *)chain; - -- (MasternodeList *)ffi_malloc; -+ (void)ffi_free:(MasternodeList *)list; - -@end +//@interface DSMasternodeList (Mndiff) +// +//+ (instancetype)masternodeListWith:(MasternodeList *)list onChain:(DSChain *)chain; +// +//- (MasternodeList *)ffi_malloc; +//+ (void)ffi_free:(MasternodeList *)list; +// +//@end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Masternode/DSMasternodeList+Mndiff.m b/DashSync/shared/Models/Masternode/DSMasternodeList+Mndiff.m index 7eb40bdd4..b0e486bc7 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeList+Mndiff.m +++ b/DashSync/shared/Models/Masternode/DSMasternodeList+Mndiff.m @@ -15,97 +15,97 @@ // limitations under the License. // -#import "BigIntTypes.h" -#import "DSMasternodeList+Mndiff.h" -#import "DSQuorumEntry+Mndiff.h" -#import "DSSimplifiedMasternodeEntry+Mndiff.h" -#import "NSData+Dash.h" - -@implementation DSMasternodeList (Mndiff) - -+ (instancetype)masternodeListWith:(MasternodeList *)list onChain:(DSChain *)chain { - uintptr_t masternodes_count = list->masternodes_count; - NSDictionary *masternodes = [DSSimplifiedMasternodeEntry simplifiedEntriesWith:list->masternodes count:masternodes_count onChain:chain]; - NSDictionary *> *quorums = [DSQuorumEntry entriesWithMap:list->llmq_type_maps count:list->llmq_type_maps_count onChain:chain]; - UInt256 masternodeMerkleRoot = list->masternode_merkle_root ? *((UInt256 *)list->masternode_merkle_root) : UINT256_ZERO; - UInt256 quorumMerkleRoot = list->llmq_merkle_root ? *((UInt256 *)list->llmq_merkle_root) : UINT256_ZERO; - UInt256 blockHash = *((UInt256 *)list->block_hash); - return [self masternodeListWithSimplifiedMasternodeEntriesDictionary:masternodes - quorumEntriesDictionary:quorums - atBlockHash:blockHash - atBlockHeight:list->known_height - withMasternodeMerkleRootHash:masternodeMerkleRoot - withQuorumMerkleRootHash:quorumMerkleRoot - onChain:chain]; -} - -- (MasternodeList *)ffi_malloc { - NSDictionary *> *quorums = [self quorums]; - NSDictionary *masternodes = [self simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash]; - uintptr_t quorum_type_maps_count = quorums.count; - uintptr_t masternodes_count = masternodes.count; - MasternodeList *masternode_list = malloc(sizeof(MasternodeList)); - LLMQMap **quorum_type_maps = malloc(quorum_type_maps_count * sizeof(LLMQMap *)); - int i = 0; - int j = 0; - for (NSNumber *type in quorums) { - NSDictionary *quorumEntries = quorums[type]; - uintptr_t quorum_maps_count = quorumEntries.count; - LLMQMap *quorums_map = malloc(sizeof(LLMQMap)); - LLMQEntry **quorums_of_type = malloc(quorum_maps_count * sizeof(LLMQEntry *)); - j = 0; - for (NSData *hash in quorumEntries) { - quorums_of_type[j++] = [quorumEntries[hash] ffi_malloc]; - } - quorums_map->llmq_type = (uint8_t)[type unsignedIntegerValue]; - quorums_map->count = quorum_maps_count; - quorums_map->values = quorums_of_type; - quorum_type_maps[i++] = quorums_map; - } - masternode_list->llmq_type_maps = quorum_type_maps; - masternode_list->llmq_type_maps_count = quorum_type_maps_count; - MasternodeEntry **masternodes_values = malloc(masternodes_count * sizeof(MasternodeEntry *)); - i = 0; - for (NSData *hash in masternodes) { - masternodes_values[i++] = [masternodes[hash] ffi_malloc]; - } - masternode_list->masternodes = masternodes_values; - masternode_list->masternodes_count = masternodes_count; - masternode_list->block_hash = uint256_malloc([self blockHash]); - masternode_list->known_height = [self height]; - masternode_list->masternode_merkle_root = uint256_malloc([self masternodeMerkleRoot]); - masternode_list->llmq_merkle_root = uint256_malloc([self quorumMerkleRoot]); - return masternode_list; -} - -+ (void)ffi_free:(MasternodeList *)list { - if (!list) return; - free(list->block_hash); - if (list->masternodes_count > 0) { - for (int i = 0; i < list->masternodes_count; i++) { - [DSSimplifiedMasternodeEntry ffi_free:list->masternodes[i]]; - } - } - if (list->masternodes) - free(list->masternodes); - if (list->llmq_type_maps_count > 0) { - for (int i = 0; i < list->llmq_type_maps_count; i++) { - LLMQMap *map = list->llmq_type_maps[i]; - for (int j = 0; j < map->count; j++) { - [DSQuorumEntry ffi_free:map->values[j]]; - } - if (map->values) - free(map->values); - free(map); - } - } - if (list->llmq_type_maps) - free(list->llmq_type_maps); - if (list->masternode_merkle_root) - free(list->masternode_merkle_root); - if (list->llmq_merkle_root) - free(list->llmq_merkle_root); - free(list); -} - -@end +//#import "BigIntTypes.h" +//#import "DSMasternodeList+Mndiff.h" +//#import "DSQuorumEntry+Mndiff.h" +//#import "DSSimplifiedMasternodeEntry+Mndiff.h" +//#import "NSData+Dash.h" +// +//@implementation DSMasternodeList (Mndiff) +// +//+ (instancetype)masternodeListWith:(MasternodeList *)list onChain:(DSChain *)chain { +// uintptr_t masternodes_count = list->masternodes_count; +// NSDictionary *masternodes = [DSSimplifiedMasternodeEntry simplifiedEntriesWith:list->masternodes count:masternodes_count onChain:chain]; +// NSDictionary *> *quorums = [DSQuorumEntry entriesWithMap:list->llmq_type_maps count:list->llmq_type_maps_count onChain:chain]; +// UInt256 masternodeMerkleRoot = list->masternode_merkle_root ? *((UInt256 *)list->masternode_merkle_root) : UINT256_ZERO; +// UInt256 quorumMerkleRoot = list->llmq_merkle_root ? *((UInt256 *)list->llmq_merkle_root) : UINT256_ZERO; +// UInt256 blockHash = *((UInt256 *)list->block_hash); +// return [self masternodeListWithSimplifiedMasternodeEntriesDictionary:masternodes +// quorumEntriesDictionary:quorums +// atBlockHash:blockHash +// atBlockHeight:list->known_height +// withMasternodeMerkleRootHash:masternodeMerkleRoot +// withQuorumMerkleRootHash:quorumMerkleRoot +// onChain:chain]; +//} +// +//- (MasternodeList *)ffi_malloc { +// NSDictionary *> *quorums = [self quorums]; +// NSDictionary *masternodes = [self simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash]; +// uintptr_t quorum_type_maps_count = quorums.count; +// uintptr_t masternodes_count = masternodes.count; +// MasternodeList *masternode_list = malloc(sizeof(MasternodeList)); +// LLMQMap **quorum_type_maps = malloc(quorum_type_maps_count * sizeof(LLMQMap *)); +// int i = 0; +// int j = 0; +// for (NSNumber *type in quorums) { +// NSDictionary *quorumEntries = quorums[type]; +// uintptr_t quorum_maps_count = quorumEntries.count; +// LLMQMap *quorums_map = malloc(sizeof(LLMQMap)); +// LLMQEntry **quorums_of_type = malloc(quorum_maps_count * sizeof(LLMQEntry *)); +// j = 0; +// for (NSData *hash in quorumEntries) { +// quorums_of_type[j++] = [quorumEntries[hash] ffi_malloc]; +// } +// quorums_map->llmq_type = (uint8_t)[type unsignedIntegerValue]; +// quorums_map->count = quorum_maps_count; +// quorums_map->values = quorums_of_type; +// quorum_type_maps[i++] = quorums_map; +// } +// masternode_list->llmq_type_maps = quorum_type_maps; +// masternode_list->llmq_type_maps_count = quorum_type_maps_count; +// MasternodeEntry **masternodes_values = malloc(masternodes_count * sizeof(MasternodeEntry *)); +// i = 0; +// for (NSData *hash in masternodes) { +// masternodes_values[i++] = [masternodes[hash] ffi_malloc]; +// } +// masternode_list->masternodes = masternodes_values; +// masternode_list->masternodes_count = masternodes_count; +// masternode_list->block_hash = uint256_malloc([self blockHash]); +// masternode_list->known_height = [self height]; +// masternode_list->masternode_merkle_root = uint256_malloc([self masternodeMerkleRoot]); +// masternode_list->llmq_merkle_root = uint256_malloc([self quorumMerkleRoot]); +// return masternode_list; +//} +// +//+ (void)ffi_free:(MasternodeList *)list { +// if (!list) return; +// free(list->block_hash); +// if (list->masternodes_count > 0) { +// for (int i = 0; i < list->masternodes_count; i++) { +// [DSSimplifiedMasternodeEntry ffi_free:list->masternodes[i]]; +// } +// } +// if (list->masternodes) +// free(list->masternodes); +// if (list->llmq_type_maps_count > 0) { +// for (int i = 0; i < list->llmq_type_maps_count; i++) { +// LLMQMap *map = list->llmq_type_maps[i]; +// for (int j = 0; j < map->count; j++) { +// [DSQuorumEntry ffi_free:map->values[j]]; +// } +// if (map->values) +// free(map->values); +// free(map); +// } +// } +// if (list->llmq_type_maps) +// free(list->llmq_type_maps); +// if (list->masternode_merkle_root) +// free(list->masternode_merkle_root); +// if (list->llmq_merkle_root) +// free(list->llmq_merkle_root); +// free(list); +//} +// +//@end diff --git a/DashSync/shared/Models/Masternode/DSMasternodeList.h b/DashSync/shared/Models/Masternode/DSMasternodeList.h index b51a51b1f..b655a4290 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeList.h +++ b/DashSync/shared/Models/Masternode/DSMasternodeList.h @@ -6,7 +6,7 @@ // #import "BigIntTypes.h" -#import "DSQuorumEntry.h" +//#import "DSQuorumEntry.h" #import "dash_shared_core.h" #import @@ -17,42 +17,44 @@ NS_ASSUME_NONNULL_BEGIN -@class DSSimplifiedMasternodeEntry, DSChain, DSQuorumEntry, DSPeer; +@class DSChain, DSPeer; @interface DSMasternodeList : NSObject -@property (nonatomic, readonly) NSArray *simplifiedMasternodeEntries; -@property (nonatomic, readonly) NSArray *providerTxOrderedHashes; -@property (nonatomic, readonly) NSDictionary *> *quorums; -@property (nonatomic, readonly) UInt256 blockHash; -@property (nonatomic, readonly) uint32_t height; -@property (nonatomic, readonly) UInt256 masternodeMerkleRoot; -@property (nonatomic, readonly) UInt256 quorumMerkleRoot; -@property (nonatomic, readonly) NSUInteger quorumsCount; -@property (nonatomic, readonly) NSUInteger validQuorumsCount; -@property (nonatomic, readonly) uint64_t masternodeCount; -@property (nonatomic, readonly) uint64_t validMasternodeCount; +//@property (nonatomic, readonly) NSArray *simplifiedMasternodeEntries; +//@property (nonatomic, readonly) NSArray *providerTxOrderedHashes; +//@property (nonatomic, readonly) NSDictionary *> *quorums; +//@property (nonatomic, readonly) UInt256 blockHash; +//@property (nonatomic, readonly) uint32_t height; +//@property (nonatomic, readonly) UInt256 masternodeMerkleRoot; +//@property (nonatomic, readonly) UInt256 quorumMerkleRoot; +//@property (nonatomic, readonly) NSUInteger quorumsCount; +//@property (nonatomic, readonly) NSUInteger validQuorumsCount; +//@property (nonatomic, readonly) uint64_t masternodeCount; +//@property (nonatomic, readonly) uint64_t validMasternodeCount; @property (nonatomic, readonly) DSChain *chain; -@property (nonatomic, readonly) NSArray *reversedRegistrationTransactionHashes; -@property (nonatomic, readonly) NSDictionary *simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash; +//@property (nonatomic, readonly) NSArray *reversedRegistrationTransactionHashes; +//@property (nonatomic, readonly) NSDictionary *simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash; -+ (instancetype)masternodeListWithSimplifiedMasternodeEntries:(NSArray *)simplifiedMasternodeEntries quorumEntries:(NSArray *)quorumEntries atBlockHash:(UInt256)blockHash atBlockHeight:(uint32_t)blockHeight withMasternodeMerkleRootHash:(UInt256)masternodeMerkleRootHash withQuorumMerkleRootHash:(UInt256)quorumMerkleRootHash onChain:(DSChain *)chain; -+ (instancetype)masternodeListWithSimplifiedMasternodeEntriesDictionary:(NSDictionary *)simplifiedMasternodeEntries quorumEntriesDictionary:(NSDictionary *> *)quorumEntries atBlockHash:(UInt256)blockHash atBlockHeight:(uint32_t)blockHeight withMasternodeMerkleRootHash:(UInt256)masternodeMerkleRootHash withQuorumMerkleRootHash:(UInt256)quorumMerkleRootHash onChain:(DSChain *)chain; +//+ (instancetype)masternodeListWithStruct:(DMasternodeList *)masternodeList onChain:(DSChain *)chain; -- (NSDictionary *)scoreDictionaryForQuorumModifier:(UInt256)quorumModifier atBlockHeight:(uint32_t)blockHeight; -- (NSArray *)scoresForQuorumModifier:(UInt256)quorumModifier atBlockHeight:(uint32_t)blockHeight; +//+ (instancetype)masternodeListWithSimplifiedMasternodeEntries:(NSArray *)simplifiedMasternodeEntries quorumEntries:(NSArray *)quorumEntries atBlockHash:(UInt256)blockHash atBlockHeight:(uint32_t)blockHeight withMasternodeMerkleRootHash:(UInt256)masternodeMerkleRootHash withQuorumMerkleRootHash:(UInt256)quorumMerkleRootHash onChain:(DSChain *)chain; +//+ (instancetype)masternodeListWithSimplifiedMasternodeEntriesDictionary:(NSDictionary *)simplifiedMasternodeEntries quorumEntriesDictionary:(NSDictionary *> *)quorumEntries atBlockHash:(UInt256)blockHash atBlockHeight:(uint32_t)blockHeight withMasternodeMerkleRootHash:(UInt256)masternodeMerkleRootHash withQuorumMerkleRootHash:(UInt256)quorumMerkleRootHash onChain:(DSChain *)chain; -- (NSUInteger)validQuorumsCountOfType:(LLMQType)type; -- (NSDictionary *)quorumsOfType:(LLMQType)type; -- (NSUInteger)quorumsCountOfType:(LLMQType)type; +//- (NSDictionary *)scoreDictionaryForQuorumModifier:(UInt256)quorumModifier atBlockHeight:(uint32_t)blockHeight; +//- (NSArray *)scoresForQuorumModifier:(UInt256)quorumModifier atBlockHeight:(uint32_t)blockHeight; -- (NSArray *)validMasternodesForQuorumModifier:(UInt256)quorumModifier quorumCount:(NSUInteger)quorumCount; +//- (NSUInteger)validQuorumsCountOfType:(DLLMQType)type; +//- (NSDictionary *)quorumsOfType:(DLLMQType)type; +//- (NSUInteger)quorumsCountOfType:(DLLMQType)type; -- (NSArray *)allMasternodesForQuorumModifier:(UInt256)quorumModifier quorumCount:(NSUInteger)quorumCount blockHeightLookup:(BlockHeightFinder)blockHeightLookup; +//- (NSArray *)validMasternodesForQuorumModifier:(UInt256)quorumModifier quorumCount:(NSUInteger)quorumCount; -- (NSArray *)validMasternodesForQuorumModifier:(UInt256)quorumModifier quorumCount:(NSUInteger)quorumCount blockHeight:(uint32_t)blockHeight; +//- (NSArray *)allMasternodesForQuorumModifier:(UInt256)quorumModifier quorumCount:(NSUInteger)quorumCount blockHeightLookup:(BlockHeightFinder)blockHeightLookup; -- (UInt256)calculateMasternodeMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; +//- (NSArray *)validMasternodesForQuorumModifier:(UInt256)quorumModifier quorumCount:(NSUInteger)quorumCount blockHeight:(uint32_t)blockHeight; + +//- (UInt256)calculateMasternodeMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; - (NSDictionary *)compare:(DSMasternodeList *)other; - (NSDictionary *)compare:(DSMasternodeList *)other blockHeightLookup:(BlockHeightFinder)blockHeightLookup; @@ -61,28 +63,28 @@ NS_ASSUME_NONNULL_BEGIN - (NSDictionary *)listOfChangedNodesComparedTo:(DSMasternodeList *)previous; - (NSDictionary *)compare:(DSMasternodeList *)other usingOurString:(NSString *)ours usingTheirString:(NSString *)theirs blockHeightLookup:(BlockHeightFinder)blockHeightLookup; -- (DSQuorumEntry *_Nullable)quorumEntryForLockRequestID:(UInt256)requestID ofQuorumType:(LLMQType)quorumType; -- (DSQuorumEntry *_Nullable)quorumEntryForPlatformWithQuorumHash:(UInt256)quorumHash; +//- (DSQuorumEntry *_Nullable)quorumEntryForLockRequestID:(UInt256)requestID ofQuorumType:(DLLMQType)quorumType; +//- (DSQuorumEntry *_Nullable)quorumEntryForPlatformWithQuorumHash:(UInt256)quorumHash; -- (NSArray *)quorumEntriesRankedForInstantSendRequestID:(UInt256)requestID; +//- (NSArray *)quorumEntriesRankedForInstantSendRequestID:(UInt256)requestID; -- (NSArray *)peers:(uint32_t)peerCount withConnectivityNonce:(uint64_t)connectivityNonce; +//- (NSArray *)peers:(uint32_t)peerCount withConnectivityNonce:(uint64_t)connectivityNonce; -- (UInt256)masternodeMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; +//- (UInt256)masternodeMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; -- (NSArray *)hashesForMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; +//- (NSArray *)hashesForMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; -- (NSDictionary *)hashDictionaryForMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; +//- (NSDictionary *)hashDictionaryForMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; -- (NSDictionary *)toDictionaryUsingBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; +//- (NSDictionary *)toDictionaryUsingBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; -- (DSSimplifiedMasternodeEntry *)masternodeForRegistrationHash:(UInt256)registrationHash; +//- (DSSimplifiedMasternodeEntry *)masternodeForRegistrationHash:(UInt256)registrationHash; -- (DSMasternodeList *)mergedWithMasternodeList:(DSMasternodeList *)masternodeList; +//- (DSMasternodeList *)mergedWithMasternodeList:(DSMasternodeList *)masternodeList; - (BOOL)hasUnverifiedNonRotatedQuorums; - (BOOL)hasUnverifiedRotatedQuorums; -- (void)saveToJsonFile:(NSString *)fileName; -- (void)saveToJsonFileExtended:(NSString *)fileName; +//- (void)saveToJsonFile:(NSString *)fileName; +//- (void)saveToJsonFileExtended:(NSString *)fileName; @end diff --git a/DashSync/shared/Models/Masternode/DSMasternodeList.m b/DashSync/shared/Models/Masternode/DSMasternodeList.m index 1d388a407..6c2fae938 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeList.m +++ b/DashSync/shared/Models/Masternode/DSMasternodeList.m @@ -8,11 +8,12 @@ #import "DSMasternodeList.h" #import "BigIntTypes.h" #import "DSChain.h" +#import "DSKeyManager.h" #import "DSMerkleBlock.h" #import "DSMutableOrderedDataKeyDictionary.h" #import "DSPeer.h" -#import "DSQuorumEntry.h" -#import "DSSimplifiedMasternodeEntry.h" +//#import "DSQuorumEntry.h" +//#import "DSSimplifiedMasternodeEntry.h" #import "NSData+DSHash.h" #import "NSData+Dash.h" #import "NSManagedObject+Sugar.h" @@ -45,321 +46,356 @@ // flag bits (little endian): 00001011 [merkleRoot = 1, m1 = 1, tx1 = 0, tx2 = 1, m2 = 0, byte padding = 000] // hashes: [tx1, tx2, m2] -@interface DSMasternodeList () - -@property (nonatomic, strong) NSMutableDictionary *mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash; -@property (nonatomic, strong) DSChain *chain; -@property (nonatomic, assign) UInt256 blockHash; -@property (nonatomic, assign) UInt256 masternodeMerkleRoot; -@property (nonatomic, assign) UInt256 quorumMerkleRoot; -@property (nonatomic, assign) uint32_t knownHeight; -@property (nonatomic, strong) NSMutableDictionary *> *mQuorums; - -@end - -@implementation DSMasternodeList - -+ (instancetype)masternodeListWithSimplifiedMasternodeEntries:(NSArray *)simplifiedMasternodeEntries quorumEntries:(NSArray *)quorumEntries atBlockHash:(UInt256)blockHash atBlockHeight:(uint32_t)blockHeight withMasternodeMerkleRootHash:(UInt256)masternodeMerkleRootHash withQuorumMerkleRootHash:(UInt256)quorumMerkleRootHash onChain:(DSChain *)chain { - NSMutableDictionary *masternodeDictionary = [NSMutableDictionary dictionary]; - for (DSSimplifiedMasternodeEntry *entry in simplifiedMasternodeEntries) { - masternodeDictionary[uint256_data(entry.providerRegistrationTransactionHash).reverse] = entry; - } - NSMutableDictionary *quorumDictionary = [NSMutableDictionary dictionary]; - for (DSQuorumEntry *entry in quorumEntries) { - NSMutableDictionary *quorumDictionaryForType = [quorumDictionary objectForKey:@(entry.llmqType)]; - if (!quorumDictionaryForType) { - quorumDictionaryForType = [NSMutableDictionary dictionary]; - quorumDictionary[@(entry.llmqType)] = quorumDictionaryForType; - } - quorumDictionaryForType[uint256_data(entry.quorumHash)] = entry; - } - return [[self alloc] initWithSimplifiedMasternodeEntriesDictionary:masternodeDictionary quorumEntriesDictionary:quorumDictionary atBlockHash:blockHash atBlockHeight:blockHeight withMasternodeMerkleRootHash:masternodeMerkleRootHash withQuorumMerkleRootHash:quorumMerkleRootHash onChain:chain]; -} - -+ (instancetype)masternodeListWithSimplifiedMasternodeEntriesDictionary:(NSDictionary *)simplifiedMasternodeEntries quorumEntriesDictionary:(NSDictionary *> *)quorumEntries atBlockHash:(UInt256)blockHash atBlockHeight:(uint32_t)blockHeight withMasternodeMerkleRootHash:(UInt256)masternodeMerkleRootHash withQuorumMerkleRootHash:(UInt256)quorumMerkleRootHash onChain:(DSChain *)chain { - return [[self alloc] initWithSimplifiedMasternodeEntriesDictionary:simplifiedMasternodeEntries quorumEntriesDictionary:quorumEntries atBlockHash:blockHash atBlockHeight:blockHeight withMasternodeMerkleRootHash:masternodeMerkleRootHash withQuorumMerkleRootHash:quorumMerkleRootHash onChain:chain]; -} - -- (instancetype)initWithSimplifiedMasternodeEntriesDictionary:(NSDictionary *)simplifiedMasternodeEntries quorumEntriesDictionary:(NSDictionary *> *)quorumEntries atBlockHash:(UInt256)blockHash atBlockHeight:(uint32_t)blockHeight withMasternodeMerkleRootHash:(UInt256)masternodeMerkleRootHash withQuorumMerkleRootHash:(UInt256)quorumMerkleRootHash onChain:(DSChain *)chain { - NSParameterAssert(chain); - - if (!(self = [super init])) return nil; - self.masternodeMerkleRoot = masternodeMerkleRootHash; - self.quorumMerkleRoot = quorumMerkleRootHash; - self.knownHeight = blockHeight; - self.chain = chain; - self.blockHash = blockHash; - self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash = [simplifiedMasternodeEntries mutableCopy]; - self.mQuorums = [quorumEntries mutableCopy]; - return self; -} - -- (UInt256)masternodeMerkleRoot { - if (uint256_is_zero(_masternodeMerkleRoot)) { - return [self masternodeMerkleRootWithBlockHeightLookup:^uint32_t(UInt256 blockHash) { - return [self.chain heightForBlockHash:blockHash]; - }]; - } - return _masternodeMerkleRoot; -} - -- (UInt256)masternodeMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { - if (uint256_is_zero(_masternodeMerkleRoot)) { - self.masternodeMerkleRoot = [self calculateMasternodeMerkleRootWithBlockHeightLookup:blockHeightLookup]; - } - return _masternodeMerkleRoot; -} - -- (NSArray *)providerTxOrderedHashes { - NSArray *proTxHashes = [self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash allKeys]; - proTxHashes = [proTxHashes sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { - UInt256 hash1 = *(UInt256 *)((NSData *)obj1).bytes; - UInt256 hash2 = *(UInt256 *)((NSData *)obj2).bytes; - return uint256_sup(hash1, hash2) ? NSOrderedDescending : NSOrderedAscending; - }]; - return proTxHashes; -} - -- (NSArray *)hashesForMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { - NSArray *proTxHashes = [self providerTxOrderedHashes]; - NSMutableArray *simplifiedMasternodeListByRegistrationTransactionHashHashes = [NSMutableArray array]; - uint32_t height = blockHeightLookup(self.blockHash); - if (height == UINT32_MAX) { - DSLog(@"Block height lookup queried an unknown block %@", uint256_hex(self.blockHash)); - return nil; //this should never happen - } - for (NSData *proTxHash in proTxHashes) { - DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry = [self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash objectForKey:proTxHash]; - UInt256 simplifiedMasternodeEntryHash = [simplifiedMasternodeEntry simplifiedMasternodeEntryHashAtBlockHeight:height]; - [simplifiedMasternodeListByRegistrationTransactionHashHashes addObject:uint256_data(simplifiedMasternodeEntryHash)]; - } - return simplifiedMasternodeListByRegistrationTransactionHashHashes; -} - -- (NSDictionary *)hashDictionaryForMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { - NSArray *proTxHashes = [self providerTxOrderedHashes]; - - NSMutableDictionary *simplifiedMasternodeListByRegistrationTransactionHashHashes = [NSMutableDictionary dictionary]; - uint32_t height = blockHeightLookup(self.blockHash); - if (height == UINT32_MAX) { - DSLog(@"Block height lookup queried an unknown block %@", uint256_hex(self.blockHash)); - return nil; //this should never happen - } - for (NSData *proTxHash in proTxHashes) { - DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry = [self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash objectForKey:proTxHash]; - UInt256 simplifiedMasternodeEntryHash = [simplifiedMasternodeEntry simplifiedMasternodeEntryHashAtBlockHeight:height]; - simplifiedMasternodeListByRegistrationTransactionHashHashes[proTxHash] = uint256_data(simplifiedMasternodeEntryHash); - } - return simplifiedMasternodeListByRegistrationTransactionHashHashes; -} - -- (UInt256)calculateMasternodeMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { - NSArray *hashes = [self hashesForMerkleRootWithBlockHeightLookup:blockHeightLookup]; - if (hashes == nil || hashes.count == 0) { - return UINT256_ZERO; - } - NSData *data = [NSData merkleRootFromHashes:hashes]; - if (data == nil || data.length == 0) { - return UINT256_ZERO; - } - return [data UInt256]; -} - -- (UInt256)quorumMerkleRoot { - if (uint256_is_zero(_quorumMerkleRoot)) { - NSMutableArray *llmqCommitmentHashes = [NSMutableArray array]; - for (NSNumber *number in self.mQuorums) { - for (DSQuorumEntry *quorumEntry in [self.mQuorums[number] allValues]) { - [llmqCommitmentHashes addObject:uint256_data(quorumEntry.quorumEntryHash)]; - } - } - NSArray *sortedLlmqHashes = [llmqCommitmentHashes sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { - UInt256 hash1 = uint256_reverse([(NSData *)obj1 UInt256]); - UInt256 hash2 = uint256_reverse([(NSData *)obj2 UInt256]); - return uint256_sup(hash1, hash2) ? NSOrderedDescending : NSOrderedAscending; - }]; - self.quorumMerkleRoot = [[NSData merkleRootFromHashes:sortedLlmqHashes] UInt256]; - } - return _quorumMerkleRoot; -} - - -- (DSMutableOrderedDataKeyDictionary *)calculateScores:(UInt256)modifier { - NSMutableDictionary *scores = [NSMutableDictionary dictionary]; - for (NSData *registrationTransactionHash in self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash) { - DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry = self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[registrationTransactionHash]; - if (uint256_is_zero(simplifiedMasternodeEntry.confirmedHash)) { - continue; - } - NSMutableData *data = [NSMutableData data]; - [data appendData:[NSData dataWithUInt256:simplifiedMasternodeEntry.confirmedHashHashedWithProviderRegistrationTransactionHash].reverse]; - [data appendData:[NSData dataWithUInt256:modifier].reverse]; - UInt256 score = data.SHA256; - scores[[NSData dataWithUInt256:score]] = simplifiedMasternodeEntry; - } - DSMutableOrderedDataKeyDictionary *rankedScores = [[DSMutableOrderedDataKeyDictionary alloc] initWithMutableDictionary:scores keyAscending:YES]; - [rankedScores addIndex:@"providerRegistrationTransactionHash"]; - return rankedScores; -} - -- (UInt256)masternodeScore:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry modifier:(UInt256)modifier atBlockHeight:(uint32_t)blockHeight { - NSParameterAssert(simplifiedMasternodeEntry); - - if (uint256_is_zero([simplifiedMasternodeEntry confirmedHashAtBlockHeight:blockHeight])) { - return UINT256_ZERO; - } - NSMutableData *data = [NSMutableData data]; - [data appendData:[NSData dataWithUInt256:[simplifiedMasternodeEntry confirmedHashHashedWithProviderRegistrationTransactionHashAtBlockHeight:blockHeight]]]; - [data appendData:[NSData dataWithUInt256:modifier]]; - return data.SHA256; -} - -- (NSDictionary *)scoreDictionaryForQuorumModifier:(UInt256)quorumModifier atBlockHeight:(uint32_t)blockHeight { - NSMutableDictionary *scoreDictionary = [NSMutableDictionary dictionary]; - for (NSData *registrationTransactionHash in self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash) { - DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry = self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[registrationTransactionHash]; - UInt256 score = [self masternodeScore:simplifiedMasternodeEntry modifier:quorumModifier atBlockHeight:blockHeight]; - if (uint256_is_zero(score)) continue; - scoreDictionary[[NSData dataWithUInt256:score]] = simplifiedMasternodeEntry; - } - return scoreDictionary; -} - -- (NSArray *)scoresForQuorumModifier:(UInt256)quorumModifier atBlockHeight:(uint32_t)blockHeight { - NSDictionary *scoreDictionary = [self scoreDictionaryForQuorumModifier:quorumModifier atBlockHeight:blockHeight]; - NSArray *scores = [[scoreDictionary allKeys] sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { - UInt256 hash1 = *(UInt256 *)((NSData *)obj1).bytes; - UInt256 hash2 = *(UInt256 *)((NSData *)obj2).bytes; - return uint256_sup(hash1, hash2) ? NSOrderedAscending : NSOrderedDescending; - }]; - return scores; -} - -- (NSArray *)validMasternodesForQuorumModifier:(UInt256)quorumModifier quorumCount:(NSUInteger)quorumCount { - return [self validMasternodesForQuorumModifier:quorumModifier - quorumCount:quorumCount - blockHeight:^uint32_t(UInt256 blockHash) { - DSMerkleBlock *block = [self.chain blockForBlockHash:blockHash]; - if (!block) { - DSLog(@"Unknown block %@", uint256_reverse_hex(blockHash)); - NSAssert(block, @"block should be known"); - } - return block.height; - }(self.blockHash)]; -} - -- (NSArray *)allMasternodesForQuorumModifier:(UInt256)quorumModifier quorumCount:(NSUInteger)quorumCount blockHeightLookup:(BlockHeightFinder)blockHeightLookup { - uint32_t blockHeight = blockHeightLookup(self.blockHash); - NSDictionary *scoreDictionary = [self scoreDictionaryForQuorumModifier:quorumModifier atBlockHeight:blockHeight]; - NSArray *scores = [[scoreDictionary allKeys] sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { - UInt256 hash1 = *(UInt256 *)((NSData *)obj1).bytes; - UInt256 hash2 = *(UInt256 *)((NSData *)obj2).bytes; - return uint256_sup(hash1, hash2) ? NSOrderedAscending : NSOrderedDescending; - }]; - NSMutableArray *masternodes = [NSMutableArray array]; - NSUInteger masternodesInListCount = self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash.count; - for (int i = 0; i < masternodesInListCount && i < scores.count; i++) { - NSData *score = scores[i]; - DSSimplifiedMasternodeEntry *masternode = scoreDictionary[score]; - [masternodes addObject:masternode]; - } - return masternodes; -} - -- (NSArray *)validMasternodesForQuorumModifier:(UInt256)quorumModifier quorumCount:(NSUInteger)quorumCount blockHeight:(uint32_t)blockHeight { - NSDictionary *scoreDictionary = [self scoreDictionaryForQuorumModifier:quorumModifier atBlockHeight:blockHeight]; - NSArray *scores = [[scoreDictionary allKeys] sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { - UInt256 hash1 = *(UInt256 *)((NSData *)obj1).bytes; - UInt256 hash2 = *(UInt256 *)((NSData *)obj2).bytes; - return uint256_sup(hash1, hash2) ? NSOrderedAscending : NSOrderedDescending; - }]; - NSMutableArray *masternodes = [NSMutableArray array]; - NSUInteger masternodesInListCount = self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash.count; - for (int i = 0; i < masternodesInListCount && i < scores.count; i++) { - NSData *score = scores[i]; - DSSimplifiedMasternodeEntry *masternode = scoreDictionary[score]; - if ([masternode isValidAtBlockHeight:blockHeight]) { - [masternodes addObject:masternode]; - } - if (masternodes.count == quorumCount) break; - } - return masternodes; -} - -- (NSArray *)simplifiedMasternodeEntries { - return self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash.allValues; -} - -- (NSArray *)reversedRegistrationTransactionHashes { - return self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash.allKeys; -} - -- (uint64_t)masternodeCount { - return [self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash count]; -} - -- (uint64_t)validMasternodeCount { - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isValid == TRUE"]; - return [[self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash allValues] filteredArrayUsingPredicate:predicate].count; -} - -- (NSUInteger)quorumsCount { - NSUInteger count = 0; - for (NSNumber *type in self.mQuorums) { - count += self.mQuorums[type].count; - } - return count; -} - -- (NSUInteger)quorumsCountOfType:(LLMQType)type { - return self.mQuorums[@(type)].count; -} - -- (NSDictionary *)quorumsOfType:(LLMQType)type { - return self.mQuorums[@(type)]; -} - -- (NSUInteger)validQuorumsCount { - NSUInteger count = 0; - for (NSNumber *type in self.mQuorums) { - for (NSData *quorumHashData in self.mQuorums[type]) { - DSQuorumEntry *quorum = self.mQuorums[type][quorumHashData]; - if (quorum.verified) count++; - } - } - return count; -} - -- (NSUInteger)validQuorumsCountOfType:(LLMQType)type { - NSUInteger count = 0; - for (NSData *quorumHashData in self.mQuorums[@(type)]) { - DSQuorumEntry *quorum = self.mQuorums[@(type)][quorumHashData]; - if (quorum.verified) count++; - } - return count; -} - - -- (NSDictionary *)quorums { - NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; - NSMutableDictionary *> *q = [self.mQuorums copy]; - for (NSNumber *number in q) { - dictionary[number] = [q objectForKey:number]; - } - return [dictionary copy]; -} - -- (NSDictionary *)simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash { - return [self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash copy]; -} - -- (uint32_t)height { - if (!self.knownHeight || self.knownHeight == UINT32_MAX) { - self.knownHeight = [self.chain heightForBlockHash:self.blockHash]; - } - return self.knownHeight; -} - +//@interface DSMasternodeList () +// +//@property (nonatomic, assign, nullable) DMasternodeList *list; +// +////@property (nonatomic, strong) NSMutableDictionary *mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash; +//@property (nonatomic, strong) DSChain *chain; +////@property (nonatomic, assign) UInt256 blockHash; +////@property (nonatomic, assign) UInt256 masternodeMerkleRoot; +////@property (nonatomic, assign) UInt256 quorumMerkleRoot; +////@property (nonatomic, assign) uint32_t knownHeight; +////@property (nonatomic, strong) NSMutableDictionary *> *mQuorums; +// +//@end +// +//@implementation DSMasternodeList +//+ (instancetype)masternodeListWithStruct:(DMasternodeList *)masternodeList onChain:(DSChain *)chain { +// DSMasternodeList *list = [[super alloc] init]; +// list.list = masternodeList; +// list.chain = chain; +// return list; +//} +// +//+ (instancetype)masternodeListWithSimplifiedMasternodeEntries:(NSArray *)simplifiedMasternodeEntries +// quorumEntries:(NSArray *)quorumEntries +// atBlockHash:(UInt256)blockHash +// atBlockHeight:(uint32_t)blockHeight +// withMasternodeMerkleRootHash:(UInt256)masternodeMerkleRootHash +// withQuorumMerkleRootHash:(UInt256)quorumMerkleRootHash +// onChain:(DSChain *)chain { +// NSMutableDictionary *masternodeDictionary = [NSMutableDictionary dictionary]; +// for (DSSimplifiedMasternodeEntry *entry in simplifiedMasternodeEntries) { +// masternodeDictionary[uint256_data(entry.providerRegistrationTransactionHash).reverse] = entry; +// } +// NSMutableDictionary *quorumDictionary = [NSMutableDictionary dictionary]; +// for (DSQuorumEntry *entry in quorumEntries) { +// NSMutableDictionary *quorumDictionaryForType = [quorumDictionary objectForKey:@(entry.llmqType)]; +// if (!quorumDictionaryForType) { +// quorumDictionaryForType = [NSMutableDictionary dictionary]; +// quorumDictionary[@(entry.llmqType)] = quorumDictionaryForType; +// } +// quorumDictionaryForType[uint256_data(entry.quorumHash)] = entry; +// } +// return [[self alloc] initWithSimplifiedMasternodeEntriesDictionary:masternodeDictionary quorumEntriesDictionary:quorumDictionary atBlockHash:blockHash atBlockHeight:blockHeight withMasternodeMerkleRootHash:masternodeMerkleRootHash withQuorumMerkleRootHash:quorumMerkleRootHash onChain:chain]; +//} + +//+ (instancetype)masternodeListWithSimplifiedMasternodeEntriesDictionary:(NSDictionary *)simplifiedMasternodeEntries quorumEntriesDictionary:(NSDictionary *> *)quorumEntries atBlockHash:(UInt256)blockHash atBlockHeight:(uint32_t)blockHeight withMasternodeMerkleRootHash:(UInt256)masternodeMerkleRootHash withQuorumMerkleRootHash:(UInt256)quorumMerkleRootHash onChain:(DSChain *)chain { +// return [[self alloc] initWithSimplifiedMasternodeEntriesDictionary:simplifiedMasternodeEntries quorumEntriesDictionary:quorumEntries atBlockHash:blockHash atBlockHeight:blockHeight withMasternodeMerkleRootHash:masternodeMerkleRootHash withQuorumMerkleRootHash:quorumMerkleRootHash onChain:chain]; +//} + +//- (instancetype)initWithSimplifiedMasternodeEntriesDictionary:(NSDictionary *)simplifiedMasternodeEntries quorumEntriesDictionary:(NSDictionary *> *)quorumEntries atBlockHash:(UInt256)blockHash atBlockHeight:(uint32_t)blockHeight withMasternodeMerkleRootHash:(UInt256)masternodeMerkleRootHash withQuorumMerkleRootHash:(UInt256)quorumMerkleRootHash onChain:(DSChain *)chain { +// NSParameterAssert(chain); +// +// +// +// if (!(self = [super init])) return nil; +//// self.list = dash_spv_masternode_processor_models_masternode_list_MasternodeList_ctor(<#struct dash_spv_crypto_crypto_byte_util_UInt256 *block_hash#>, <#uint32_t known_height#>, <#struct dash_spv_crypto_crypto_byte_util_UInt256 *masternode_merkle_root#>, <#struct dash_spv_crypto_crypto_byte_util_UInt256 *llmq_merkle_root#>, <#struct std_collections_Map_keys_dash_spv_crypto_crypto_byte_util_UInt256_values_dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry *masternodes#>, <#struct std_collections_Map_keys_dash_spv_crypto_network_llmq_type_LLMQType_values_std_collections_Map_keys_dash_spv_crypto_crypto_byte_util_UInt256_values_dash_spv_crypto_llmq_entry_LLMQEntry *quorums#>) +//// self.masternodeMerkleRoot = masternodeMerkleRootHash; +//// self.quorumMerkleRoot = quorumMerkleRootHash; +//// self.knownHeight = blockHeight; +// self.chain = chain; +//// self.blockHash = blockHash; +//// self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash = [simplifiedMasternodeEntries mutableCopy]; +//// self.mQuorums = [quorumEntries mutableCopy]; +// return self; +//} +// +//- (UInt256)masternodeMerkleRoot { +// if (uint256_is_zero(_masternodeMerkleRoot)) { +// return [self masternodeMerkleRootWithBlockHeightLookup:^uint32_t(UInt256 blockHash) { +// return [self.chain heightForBlockHash:blockHash]; +// }]; +// } +// return _masternodeMerkleRoot; +//} +// +//- (UInt256)masternodeMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { +// if (uint256_is_zero(_masternodeMerkleRoot)) { +// self.masternodeMerkleRoot = [self calculateMasternodeMerkleRootWithBlockHeightLookup:blockHeightLookup]; +// } +// return _masternodeMerkleRoot; +//} +// +//- (NSArray *)providerTxOrderedHashes { +// struct Vec_dash_spv_crypto_crypto_byte_util_UInt256 *hashes = dash_spv_masternode_processor_models_masternode_list_MasternodeList_provider_tx_ordered_hashes(self.list); +// NSArray *proTxHashes = [self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash allKeys]; +// proTxHashes = [proTxHashes sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { +// UInt256 hash1 = *(UInt256 *)((NSData *)obj1).bytes; +// UInt256 hash2 = *(UInt256 *)((NSData *)obj2).bytes; +// return uint256_sup(hash1, hash2) ? NSOrderedDescending : NSOrderedAscending; +// }]; +// return proTxHashes; +//} +// +//- (NSArray *)hashesForMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { +// if (!self.list) return nil; +// uint32_t height = blockHeightLookup(self.blockHash); +// +// struct Vec_u8_32 *pro_tx_hashes = dash_spv_masternode_processor_models_masternode_list_MasternodeList_provider_tx_ordered_hashes((struct dash_spv_masternode_processor_models_masternode_list_MasternodeList *) self.list); +// if (!pro_tx_hashes) return nil; +// +//// NSArray *proTxHashes = [self providerTxOrderedHashes]; +// NSMutableArray *simplifiedMasternodeListByRegistrationTransactionHashHashes = [NSMutableArray array]; +// if (height == UINT32_MAX) { +// DSLog(@"Block height lookup queried an unknown block %@", uint256_hex(self.blockHash)); +// return nil; //this should never happen +// } +//// self.list->masternodes +// for (int i = 0; i < pro_tx_hashes->count; i++) { +// u256 *hash = pro_tx_hashes->values[i]; +// DMasternodeEntry *entry = masternode_list_map_by_key(self.list, hash); +// u256 *entry_hash = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_entry_hash_at(entry, height); +// +// } +// for (NSData *proTxHash in proTxHashes) { +// DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry = [self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash objectForKey:proTxHash]; +// UInt256 simplifiedMasternodeEntryHash = [simplifiedMasternodeEntry simplifiedMasternodeEntryHashAtBlockHeight:height]; +// [simplifiedMasternodeListByRegistrationTransactionHashHashes addObject:uint256_data(simplifiedMasternodeEntryHash)]; +// } +// return simplifiedMasternodeListByRegistrationTransactionHashHashes; +//} +// +//- (NSDictionary *)hashDictionaryForMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { +// NSArray *proTxHashes = [self providerTxOrderedHashes]; +// +// NSMutableDictionary *simplifiedMasternodeListByRegistrationTransactionHashHashes = [NSMutableDictionary dictionary]; +// uint32_t height = blockHeightLookup(self.blockHash); +// if (height == UINT32_MAX) { +// DSLog(@"Block height lookup queried an unknown block %@", uint256_hex(self.blockHash)); +// return nil; //this should never happen +// } +// for (NSData *proTxHash in proTxHashes) { +// DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry = [self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash objectForKey:proTxHash]; +// UInt256 simplifiedMasternodeEntryHash = [simplifiedMasternodeEntry simplifiedMasternodeEntryHashAtBlockHeight:height]; +// simplifiedMasternodeListByRegistrationTransactionHashHashes[proTxHash] = uint256_data(simplifiedMasternodeEntryHash); +// } +// return simplifiedMasternodeListByRegistrationTransactionHashHashes; +//} +// +//- (UInt256)calculateMasternodeMerkleRootWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { +// NSArray *hashes = [self hashesForMerkleRootWithBlockHeightLookup:blockHeightLookup]; +// if (hashes == nil || hashes.count == 0) { +// return UINT256_ZERO; +// } +// NSData *data = [NSData merkleRootFromHashes:hashes]; +// if (data == nil || data.length == 0) { +// return UINT256_ZERO; +// } +// return [data UInt256]; +//} +// +//- (UInt256)quorumMerkleRoot { +// if (uint256_is_zero(_quorumMerkleRoot)) { +// NSMutableArray *llmqCommitmentHashes = [NSMutableArray array]; +// for (NSNumber *number in self.mQuorums) { +// for (DSQuorumEntry *quorumEntry in [self.mQuorums[number] allValues]) { +// [llmqCommitmentHashes addObject:uint256_data(quorumEntry.quorumEntryHash)]; +// } +// } +// NSArray *sortedLlmqHashes = [llmqCommitmentHashes sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { +// UInt256 hash1 = uint256_reverse([(NSData *)obj1 UInt256]); +// UInt256 hash2 = uint256_reverse([(NSData *)obj2 UInt256]); +// return uint256_sup(hash1, hash2) ? NSOrderedDescending : NSOrderedAscending; +// }]; +// self.quorumMerkleRoot = [[NSData merkleRootFromHashes:sortedLlmqHashes] UInt256]; +// } +// return _quorumMerkleRoot; +//} +// +// +//- (DSMutableOrderedDataKeyDictionary *)calculateScores:(UInt256)modifier { +// NSMutableDictionary *scores = [NSMutableDictionary dictionary]; +// for (NSData *registrationTransactionHash in self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash) { +// DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry = self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[registrationTransactionHash]; +// if (uint256_is_zero(simplifiedMasternodeEntry.confirmedHash)) { +// continue; +// } +// NSMutableData *data = [NSMutableData data]; +// [data appendData:[NSData dataWithUInt256:simplifiedMasternodeEntry.confirmedHashHashedWithProviderRegistrationTransactionHash].reverse]; +// [data appendData:[NSData dataWithUInt256:modifier].reverse]; +// UInt256 score = data.SHA256; +// scores[[NSData dataWithUInt256:score]] = simplifiedMasternodeEntry; +// } +// DSMutableOrderedDataKeyDictionary *rankedScores = [[DSMutableOrderedDataKeyDictionary alloc] initWithMutableDictionary:scores keyAscending:YES]; +// [rankedScores addIndex:@"providerRegistrationTransactionHash"]; +// return rankedScores; +//} +// +//- (UInt256)masternodeScore:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry modifier:(UInt256)modifier atBlockHeight:(uint32_t)blockHeight { +// NSParameterAssert(simplifiedMasternodeEntry); +// +// if (uint256_is_zero([simplifiedMasternodeEntry confirmedHashAtBlockHeight:blockHeight])) { +// return UINT256_ZERO; +// } +// NSMutableData *data = [NSMutableData data]; +// [data appendData:[NSData dataWithUInt256:[simplifiedMasternodeEntry confirmedHashHashedWithProviderRegistrationTransactionHashAtBlockHeight:blockHeight]]]; +// [data appendData:[NSData dataWithUInt256:modifier]]; +// return data.SHA256; +//} +// +//- (NSDictionary *)scoreDictionaryForQuorumModifier:(UInt256)quorumModifier atBlockHeight:(uint32_t)blockHeight { +// NSMutableDictionary *scoreDictionary = [NSMutableDictionary dictionary]; +// for (NSData *registrationTransactionHash in self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash) { +// DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry = self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[registrationTransactionHash]; +// UInt256 score = [self masternodeScore:simplifiedMasternodeEntry modifier:quorumModifier atBlockHeight:blockHeight]; +// if (uint256_is_zero(score)) continue; +// scoreDictionary[[NSData dataWithUInt256:score]] = simplifiedMasternodeEntry; +// } +// return scoreDictionary; +//} +// +//- (NSArray *)scoresForQuorumModifier:(UInt256)quorumModifier atBlockHeight:(uint32_t)blockHeight { +// NSDictionary *scoreDictionary = [self scoreDictionaryForQuorumModifier:quorumModifier atBlockHeight:blockHeight]; +// NSArray *scores = [[scoreDictionary allKeys] sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { +// UInt256 hash1 = *(UInt256 *)((NSData *)obj1).bytes; +// UInt256 hash2 = *(UInt256 *)((NSData *)obj2).bytes; +// return uint256_sup(hash1, hash2) ? NSOrderedAscending : NSOrderedDescending; +// }]; +// return scores; +//} +// +//- (NSArray *)validMasternodesForQuorumModifier:(UInt256)quorumModifier quorumCount:(NSUInteger)quorumCount { +// return [self validMasternodesForQuorumModifier:quorumModifier +// quorumCount:quorumCount +// blockHeight:^uint32_t(UInt256 blockHash) { +// DSMerkleBlock *block = [self.chain blockForBlockHash:blockHash]; +// if (!block) { +// DSLog(@"Unknown block %@", uint256_reverse_hex(blockHash)); +// NSAssert(block, @"block should be known"); +// } +// return block.height; +// }(self.blockHash)]; +//} +// +//- (NSArray *)allMasternodesForQuorumModifier:(UInt256)quorumModifier quorumCount:(NSUInteger)quorumCount blockHeightLookup:(BlockHeightFinder)blockHeightLookup { +// uint32_t blockHeight = blockHeightLookup(self.blockHash); +// NSDictionary *scoreDictionary = [self scoreDictionaryForQuorumModifier:quorumModifier atBlockHeight:blockHeight]; +// NSArray *scores = [[scoreDictionary allKeys] sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { +// UInt256 hash1 = *(UInt256 *)((NSData *)obj1).bytes; +// UInt256 hash2 = *(UInt256 *)((NSData *)obj2).bytes; +// return uint256_sup(hash1, hash2) ? NSOrderedAscending : NSOrderedDescending; +// }]; +// NSMutableArray *masternodes = [NSMutableArray array]; +// NSUInteger masternodesInListCount = self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash.count; +// for (int i = 0; i < masternodesInListCount && i < scores.count; i++) { +// NSData *score = scores[i]; +// DSSimplifiedMasternodeEntry *masternode = scoreDictionary[score]; +// [masternodes addObject:masternode]; +// } +// return masternodes; +//} +// +//- (NSArray *)validMasternodesForQuorumModifier:(UInt256)quorumModifier quorumCount:(NSUInteger)quorumCount blockHeight:(uint32_t)blockHeight { +// NSDictionary *scoreDictionary = [self scoreDictionaryForQuorumModifier:quorumModifier atBlockHeight:blockHeight]; +// NSArray *scores = [[scoreDictionary allKeys] sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { +// UInt256 hash1 = *(UInt256 *)((NSData *)obj1).bytes; +// UInt256 hash2 = *(UInt256 *)((NSData *)obj2).bytes; +// return uint256_sup(hash1, hash2) ? NSOrderedAscending : NSOrderedDescending; +// }]; +// NSMutableArray *masternodes = [NSMutableArray array]; +// NSUInteger masternodesInListCount = self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash.count; +// for (int i = 0; i < masternodesInListCount && i < scores.count; i++) { +// NSData *score = scores[i]; +// DSSimplifiedMasternodeEntry *masternode = scoreDictionary[score]; +// if ([masternode isValidAtBlockHeight:blockHeight]) { +// [masternodes addObject:masternode]; +// } +// if (masternodes.count == quorumCount) break; +// } +// return masternodes; +//} +// +//- (NSArray *)simplifiedMasternodeEntries { +//// self.list-> +// return self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash.allValues; +//} +// +//- (NSArray *)reversedRegistrationTransactionHashes { +// return self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash.allKeys; +//} +// +//- (uint64_t)masternodeCount { +// return dash_spv_masternode_processor_models_masternode_list_MasternodeList_masternode_count(self.list); +//// return [self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash count]; +//} +// +//- (uint64_t)validMasternodeCount { +// +// NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isValid == TRUE"]; +// return [[self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash allValues] filteredArrayUsingPredicate:predicate].count; +//} +// +//- (NSUInteger)quorumsCount { +// return dash_spv_masternode_processor_models_masternode_list_MasternodeList_quorums_count(self.list); +// NSUInteger count = 0; +// for (NSNumber *type in self.mQuorums) { +// count += self.mQuorums[type].count; +// } +// return count; +//} +// +//- (NSUInteger)quorumsCountOfType:(DLLMQType)type { +// +// return self.mQuorums[@(type)].count; +//} +// +//- (NSDictionary *)quorumsOfType:(DLLMQType)type { +// return self.mQuorums[@(type)]; +//} +// +//- (NSUInteger)validQuorumsCount { +// NSUInteger count = 0; +// for (NSNumber *type in self.mQuorums) { +// for (NSData *quorumHashData in self.mQuorums[type]) { +// DSQuorumEntry *quorum = self.mQuorums[type][quorumHashData]; +// if (quorum.verified) count++; +// } +// } +// return count; +//} +// +//- (NSUInteger)validQuorumsCountOfType:(DLLMQType)type { +// NSUInteger count = 0; +// for (NSData *quorumHashData in self.mQuorums[@(type)]) { +// DSQuorumEntry *quorum = self.mQuorums[@(type)][quorumHashData]; +// if (quorum.verified) count++; +// } +// return count; +//} +// +// +//- (NSDictionary *)quorums { +// NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; +// NSMutableDictionary *> *q = [self.mQuorums copy]; +// for (NSNumber *number in q) { +// dictionary[number] = [q objectForKey:number]; +// } +// return [dictionary copy]; +//} +// +//- (NSDictionary *)simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash { +// return [self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash copy]; +//} +// +//- (uint32_t)height { +// if (!self.knownHeight || self.knownHeight == UINT32_MAX) { +// self.knownHeight = [self.chain heightForBlockHash:self.blockHash]; +// } +// return self.knownHeight; +//} +// /* - (BOOL)validateQuorumsWithMasternodeLists:(NSDictionary *)masternodeLists { for (DSQuorumEntry *quorum in self.quorums) { @@ -373,346 +409,352 @@ - (BOOL)validateQuorumsWithMasternodeLists:(NSDictionary *)masternodeLists { return TRUE; } */ - -- (void)saveToJsonFile:(NSString *)fileName { -// return; - NSMutableArray *json_nodes = [NSMutableArray arrayWithCapacity:self.masternodeCount]; - NSArray *proTxHashes = [self providerTxOrderedHashes]; - for (NSData *proTxHash in proTxHashes) { - DSSimplifiedMasternodeEntry *entry = self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[proTxHash]; - NSString *json_node = [NSString stringWithFormat:@"{\n\"proRegTxHash\": \"%@\", \n\"confirmedHash\": \"%@\", \n\"service\": \"%@\", \n\"pubKeyOperator\": \"%@\", \n\"votingAddress\": \"%@\", \n\"isValid\": %s, \n\"updateHeight\": %@, \n\"knownConfirmedAtHeight\": %@\n}", - uint256_hex(entry.providerRegistrationTransactionHash), - uint256_hex(entry.confirmedHash), - uint128_hex(entry.address), - uint384_hex(entry.operatorPublicKey), - uint160_data(entry.keyIDVoting).base58String, - entry.isValid ? "true" : "false", @(entry.updateHeight), @(entry.knownConfirmedAtHeight)]; - [json_nodes addObject:json_node]; - } - NSMutableArray *json_quorums = [NSMutableArray arrayWithCapacity:self.quorumsCount]; - NSArray *llmqTypes = [[self.mQuorums allKeys] sortedArrayUsingComparator:^NSComparisonResult(NSNumber* n1, NSNumber* n2) { - return [n1 compare:n2]; - }]; - for (NSNumber *llmqType in llmqTypes) { - NSMutableDictionary *quorumsForMasternodeType = self.mQuorums[llmqType]; - NSArray *llmqHashes = [quorumsForMasternodeType allKeys]; - llmqHashes = [llmqHashes sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { - UInt256 hash1 = *(UInt256 *)((NSData *)obj1).bytes; - UInt256 hash2 = *(UInt256 *)((NSData *)obj2).bytes; - return uint256_sup(hash1, hash2) ? NSOrderedDescending : NSOrderedAscending; - }]; - for (NSData *quorumHash in llmqHashes) { - DSQuorumEntry *entry = quorumsForMasternodeType[quorumHash]; - NSString *json_quorum = [NSString stringWithFormat:@"{\n\"version\": %@, \n\"llmqType\": %@, \n\"quorumHash\": \"%@\", \n\"quorumIndex\": %@, \n\"signersCount\": %@, \n\"signers\": \"%@\", \n\"validMembersCount\": %@, \n\"validMembers\": \"%@\", \n\"quorumPublicKey\": \"%@\", \n\"quorumVvecHash\": \"%@\", \n\"quorumSig\": \"%@\", \n\"membersSig\": \"%@\"\n}", - @(entry.version), - @(entry.llmqType), - uint256_hex(entry.quorumHash), - @(entry.quorumIndex), - @(entry.signersCount), - [entry signersBitset].hexString, - @(entry.validMembersCount), - [entry validMembersBitset].hexString, - uint384_hex(entry.quorumPublicKey), - uint256_hex(entry.quorumVerificationVectorHash), - uint768_hex(entry.quorumThresholdSignature), - uint768_hex(entry.allCommitmentAggregatedSignature)]; - - [json_quorums addObject:json_quorum]; - } - } - NSString *nodes = [NSString stringWithFormat:@"\n\"mnList\": [%@]", [json_nodes componentsJoinedByString:@","]]; - NSString *quorums = [NSString stringWithFormat:@"\n\"newQuorums\": [%@]", [json_quorums componentsJoinedByString:@","]]; - NSString *list = [NSString stringWithFormat:@"{\n\"blockHash\":\"%@\", \n\"knownHeight\":%@, \n\"masternodeMerkleRoot\":\"%@\", \n\"quorumMerkleRoot\":\"%@\", \n%@, \n%@\n}", uint256_hex(self.blockHash), @(self.knownHeight), uint256_hex(self.masternodeMerkleRoot), uint256_hex(self.quorumMerkleRoot), nodes, quorums]; - NSData* data = [list dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO]; - [data saveToFile:fileName inDirectory:NSCachesDirectory]; - DSLog(@"•-• File %@ saved", fileName); -} - -- (void)saveToJsonFileExtended:(NSString *)fileName { - NSMutableArray *json_nodes = [NSMutableArray arrayWithCapacity:self.masternodeCount]; - NSArray *proTxHashes = [self providerTxOrderedHashes]; - for (NSData *proTxHash in proTxHashes) { - DSSimplifiedMasternodeEntry *entry = self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[proTxHash]; - NSMutableArray *json_prev_public_keys = [NSMutableArray arrayWithCapacity:entry.previousOperatorPublicKeys.count]; - for (DSBlock *block in entry.previousOperatorPublicKeys) { - [json_prev_public_keys addObject:[NSString stringWithFormat:@"{\n\"block_height\":%u, \n\"block_hash\":\"%@\", \n\"public_key\":\"%@\"\n}", block.height, uint256_hex(block.blockHash), ((NSData *)entry.previousOperatorPublicKeys[block]).hexString]]; - } - NSMutableArray *json_prev_entry_hashes = [NSMutableArray arrayWithCapacity:entry.previousSimplifiedMasternodeEntryHashes.count]; - for (DSBlock *block in entry.previousSimplifiedMasternodeEntryHashes) { - [json_prev_entry_hashes addObject:[NSString stringWithFormat:@"{\n\"block_height\":%u, \n\"block_hash\":\"%@\", \n\"entry_hash\":\"%@\"\n}", block.height, uint256_hex(block.blockHash), ((NSData *)entry.previousSimplifiedMasternodeEntryHashes[block]).hexString]]; - } - NSMutableArray *json_prev_validities = [NSMutableArray arrayWithCapacity:entry.previousValidity.count]; - for (DSBlock *block in entry.previousValidity) { - [json_prev_validities addObject:[NSString stringWithFormat:@"{\n\"block_height\":%u, \n\"block_hash\":\"%@\", \n\"is_valid\":\%s\n}", block.height, uint256_hex(block.blockHash), ((NSNumber *) entry.previousValidity[block]).boolValue ? "true" : "false"]]; - } - - NSString *json_node = [NSString stringWithFormat:@"{\n\"provider_registration_transaction_hash\": \"%@\", \n\"confirmed_hash\": \"%@\", \n\"confirmed_hash_hashed_with_provider_registration_transaction_hash\": \"%@\", \n\"socket_address\": {\n\"ip_address\":\"%@\",\n\"port\":%@\n}, \n\"operator_public_key\": \"%@\", \n\"previous_operator_public_keys\": [\n%@\n], \n\"previous_entry_hashes\": [\n%@\n], \n\"previous_validity\": [\n%@\n], \n\"known_confirmed_at_height\": %@, \n\"update_height\": %@, \n\"key_id_voting\": \"%@\", \n\"isValid\": %s, \n\"entry_hash\": \"%@\"\n}", - uint256_hex(entry.providerRegistrationTransactionHash), - uint256_hex(entry.confirmedHash), - uint256_hex(entry.confirmedHashHashedWithProviderRegistrationTransactionHash), - uint128_hex(entry.address), - @(entry.port), - uint384_hex(entry.operatorPublicKey), - [NSString stringWithFormat:@"%@", [json_prev_public_keys componentsJoinedByString:@","]], - [NSString stringWithFormat:@"%@", [json_prev_entry_hashes componentsJoinedByString:@","]], - [NSString stringWithFormat:@"%@", [json_prev_validities componentsJoinedByString:@","]], - @(entry.knownConfirmedAtHeight), - @(entry.updateHeight), - uint160_data(entry.keyIDVoting).base58String, - entry.isValid ? "true" : "false", - uint256_hex(entry.simplifiedMasternodeEntryHash)]; - [json_nodes addObject:json_node]; - } - NSMutableArray *json_quorums = [NSMutableArray arrayWithCapacity:self.quorumsCount]; - NSArray *llmqTypes = [[self.mQuorums allKeys] sortedArrayUsingComparator:^NSComparisonResult(NSNumber* n1, NSNumber* n2) { - return [n1 compare:n2]; - }]; - for (NSNumber *llmqType in llmqTypes) { - NSMutableDictionary *quorumsForMasternodeType = self.mQuorums[llmqType]; - NSArray *llmqHashes = [quorumsForMasternodeType allKeys]; - llmqHashes = [llmqHashes sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { - UInt256 hash1 = *(UInt256 *)((NSData *)obj1).bytes; - UInt256 hash2 = *(UInt256 *)((NSData *)obj2).bytes; - return uint256_sup(hash1, hash2) ? NSOrderedDescending : NSOrderedAscending; - }]; - for (NSData *quorumHash in llmqHashes) { - DSQuorumEntry *entry = quorumsForMasternodeType[quorumHash]; - NSString *json_quorum = [NSString stringWithFormat:@"{\n\"version\": %@, \n\"llmqType\": %@, \n\"quorumHash\": \"%@\", \n\"quorumIndex\": %@, \n\"signersCount\": %@, \n\"signers\": \"%@\", \n\"validMembersCount\": %@, \n\"validMembers\": \"%@\", \n\"quorumPublicKey\": \"%@\", \n\"quorumVvecHash\": \"%@\", \n\"quorumSig\": \"%@\", \n\"membersSig\": \"%@\"\n}", - @(entry.version), - @(entry.llmqType), - uint256_hex(entry.quorumHash), - @(entry.quorumIndex), - @(entry.signersCount), - [entry signersBitset].hexString, - @(entry.validMembersCount), - [entry validMembersBitset].hexString, - uint384_hex(entry.quorumPublicKey), - uint256_hex(entry.quorumVerificationVectorHash), - uint768_hex(entry.quorumThresholdSignature), - uint768_hex(entry.allCommitmentAggregatedSignature)]; - - [json_quorums addObject:json_quorum]; - } - } - NSString *nodes = [NSString stringWithFormat:@"\n\"mnList\": [%@]", [json_nodes componentsJoinedByString:@","]]; - NSString *quorums = [NSString stringWithFormat:@"\n\"newQuorums\": [%@]", [json_quorums componentsJoinedByString:@","]]; - NSString *list = [NSString stringWithFormat:@"{\n\"blockHash\":\"%@\", \n\"knownHeight\":%@, \n\"masternodeMerkleRoot\":\"%@\", \n\"quorumMerkleRoot\":\"%@\", \n%@, \n%@\n}", uint256_hex(self.blockHash), @(self.knownHeight), uint256_hex(self.masternodeMerkleRoot), uint256_hex(self.quorumMerkleRoot), nodes, quorums]; - NSData* data = [list dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO]; - [data saveToFile:fileName inDirectory:NSCachesDirectory]; - DSLog(@"•-• File %@ saved", fileName); -} - -- (NSString *)description { - return [[super description] stringByAppendingString:[NSString stringWithFormat:@" {%u}", self.height]]; -} - -- (NSString *)debugDescription { -// [self saveToJsonFile]; - return [[super debugDescription] stringByAppendingString:[NSString stringWithFormat:@" {%u}", self.height]]; -} - -- (NSDictionary *)compareWithPrevious:(DSMasternodeList *)other { - return [self compareWithPrevious:other - blockHeightLookup:^uint32_t(UInt256 blockHash) { - return [self.chain heightForBlockHash:blockHash]; - }]; -} - -- (NSDictionary *)compareWithPrevious:(DSMasternodeList *)other blockHeightLookup:(BlockHeightFinder)blockHeightLookup { - return [self compare:other usingOurString:@"current" usingTheirString:@"previous" blockHeightLookup:blockHeightLookup]; -} - -- (NSDictionary *)compare:(DSMasternodeList *)other { - return [self compare:other - blockHeightLookup:^uint32_t(UInt256 blockHash) { - return [self.chain heightForBlockHash:blockHash]; - }]; -} - -- (NSDictionary *)compare:(DSMasternodeList *)other blockHeightLookup:(BlockHeightFinder)blockHeightLookup { - return [self compare:other usingOurString:@"ours" usingTheirString:@"theirs" blockHeightLookup:blockHeightLookup]; -} - -- (NSDictionary *)listOfChangedNodesComparedTo:(DSMasternodeList *)previous { - NSMutableArray *added = [NSMutableArray array]; - NSMutableArray *removed = [NSMutableArray array]; - NSMutableArray *addedValidity = [NSMutableArray array]; - NSMutableArray *removedValidity = [NSMutableArray array]; - for (NSData *data in self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash) { - DSSimplifiedMasternodeEntry *currentEntry = self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[data]; - DSSimplifiedMasternodeEntry *previousEntry = previous.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[data]; - if (currentEntry && !previousEntry) { - [added addObject:currentEntry]; - } else if ([currentEntry isValidAtBlockHeight:self.height] && ![previousEntry isValidAtBlockHeight:previous.height]) { - [addedValidity addObject:currentEntry]; - } else if (![currentEntry isValidAtBlockHeight:self.height] && [previousEntry isValidAtBlockHeight:previous.height]) { - [removedValidity addObject:currentEntry]; - } - } - - for (NSData *data in previous.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash) { - DSSimplifiedMasternodeEntry *currentEntry = self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[data]; - DSSimplifiedMasternodeEntry *previousEntry = previous.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[data]; - if (!currentEntry && previousEntry) { - [removed addObject:previousEntry]; - } - } - - return @{MASTERNODE_LIST_ADDED_NODES: added, MASTERNODE_LIST_REMOVED_NODES: removed, MASTERNODE_LIST_ADDED_VALIDITY: addedValidity, MASTERNODE_LIST_REMOVED_VALIDITY: removedValidity}; -} - -- (NSDictionary *)compare:(DSMasternodeList *)other usingOurString:(NSString *)ours usingTheirString:(NSString *)theirs blockHeightLookup:(BlockHeightFinder)blockHeightLookup { - NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; - for (NSData *data in self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash) { - DSSimplifiedMasternodeEntry *ourEntry = self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[data]; - DSSimplifiedMasternodeEntry *theirEntry = other.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[data]; - if (ourEntry && theirEntry) { - NSDictionary *entryComparison = [ourEntry compare:theirEntry ourBlockHash:self.blockHash theirBlockHash:other.blockHash usingOurString:ours usingTheirString:theirs blockHeightLookup:blockHeightLookup]; - if (entryComparison.count) { - dictionary[data] = entryComparison; - } - } else if (ourEntry) { - dictionary[data] = @{@"absent": uint256_hex(ourEntry.providerRegistrationTransactionHash)}; - } - } - return dictionary; -} - -- (NSDictionary *)toDictionaryUsingBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { - NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; - for (NSData *data in self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash) { - DSSimplifiedMasternodeEntry *ourEntry = self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[data]; - if (ourEntry) { - NSDictionary *entryDictionary = [ourEntry toDictionaryAtBlockHash:self.blockHash usingBlockHeightLookup:blockHeightLookup]; - dictionary[[data base64String]] = entryDictionary; - } - } - return dictionary; -} - -- (DSQuorumEntry *)quorumEntryForLockRequestID:(UInt256)requestID ofQuorumType:(LLMQType)quorumType { - NSArray *quorumsForLock = [self.quorums[@(quorumType)] allValues]; - UInt256 lowestValue = UINT256_MAX; - DSQuorumEntry *firstQuorum = nil; - for (DSQuorumEntry *quorumEntry in quorumsForLock) { - UInt256 orderingHash = uint256_reverse([quorumEntry orderingHashForRequestID:requestID forQuorumType:quorumType]); - if (uint256_sup(lowestValue, orderingHash)) { - lowestValue = orderingHash; - firstQuorum = quorumEntry; - } - } - return firstQuorum; -} - - -- (DSQuorumEntry *_Nullable)quorumEntryForPlatformWithQuorumHash:(UInt256)quorumHash ofQuorumType:(LLMQType)quorumType { - NSArray *quorumsForPlatform = [self.quorums[@(quorumType)] allValues]; - for (DSQuorumEntry *quorumEntry in quorumsForPlatform) { - if (uint256_eq(quorumEntry.quorumHash, quorumHash)) { - return quorumEntry; - } - NSAssert(!uint256_eq(quorumEntry.quorumHash, uint256_reverse(quorumHash)), @"these should not be inversed"); - } - return nil; -} - -- (DSQuorumEntry *)quorumEntryForPlatformWithQuorumHash:(UInt256)quorumHash { - return [self quorumEntryForPlatformWithQuorumHash:quorumHash ofQuorumType:quorum_type_for_platform(self.chain.chainType)]; -} - -- (NSArray *)quorumEntriesRankedForInstantSendRequestID:(UInt256)requestID { - LLMQType quorumType = quorum_type_for_chain_locks(self.chain.chainType); - NSArray *quorumsForIS = [self.quorums[@(quorumType)] allValues]; - NSMutableDictionary *orderedQuorumDictionary = [NSMutableDictionary dictionary]; - for (DSQuorumEntry *quorumEntry in quorumsForIS) { - UInt256 orderingHash = uint256_reverse([quorumEntry orderingHashForRequestID:requestID forQuorumType:quorumType]); - orderedQuorumDictionary[quorumEntry] = uint256_data(orderingHash); - } - NSArray *orderedQuorums = [orderedQuorumDictionary keysSortedByValueUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { - return uint256_sup([obj1 UInt256], [obj2 UInt256]) ? NSOrderedDescending : NSOrderedAscending; - }]; - return orderedQuorums; -} - -- (NSArray *)peers:(uint32_t)peerCount withConnectivityNonce:(uint64_t)connectivityNonce { - NSArray *registrationTransactionHashes = [self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash allKeys]; - NSArray *sortedHashes = [registrationTransactionHashes sortedArrayUsingComparator:^NSComparisonResult(NSData *_Nonnull obj1, NSData *_Nonnull obj2) { - UInt256 hash1 = [[[obj1 mutableCopy] appendUInt64:connectivityNonce] blake3]; - UInt256 hash2 = [[[obj2 mutableCopy] appendUInt64:connectivityNonce] blake3]; - return uint256_sup(hash1, hash2) ? NSOrderedDescending : NSOrderedAscending; - }]; - NSMutableArray *mArray = [NSMutableArray array]; - for (uint32_t i = 0; i < MIN(peerCount, self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash.count); i++) { - DSSimplifiedMasternodeEntry *masternodeEntry = self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[sortedHashes[i]]; - if (masternodeEntry.isValid) { - DSPeer *peer = [DSPeer peerWithSimplifiedMasternodeEntry:masternodeEntry]; - [mArray addObject:peer]; - } - } - return mArray; -} - -- (DSSimplifiedMasternodeEntry *)masternodeForRegistrationHash:(UInt256)registrationHash { - return self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[uint256_data(registrationHash)]; -} - -- (BOOL)hasUnverifiedNonRotatedQuorums { - for (NSNumber *quorumType in self.quorums) { - LLMQType llmqType = (LLMQType) quorumType.unsignedIntValue; - if (llmqType == quorum_type_for_isd_locks(self.chain.chainType) || !quorum_should_process_type_for_chain(llmqType, self.chain.chainType)) { - continue; - } - NSDictionary *quorumsOfType = self.quorums[quorumType]; - for (NSData *quorumHash in quorumsOfType) { - DSQuorumEntry *entry = quorumsOfType[quorumHash]; - if (!entry.verified) { - return YES; - } - } - } - return NO; -} - -- (BOOL)hasUnverifiedRotatedQuorums { - NSArray *quorumsForISDLock = [self.quorums[@(quorum_type_for_isd_locks(self.chain.chainType))] allValues]; - for (DSQuorumEntry *entry in quorumsForISDLock) { - if (!entry.verified) { - return YES; - } - } - return NO; -} - -- (DSQuorumEntry *_Nullable)quorumEntryOfType:(LLMQType)llmqType withQuorumHash:(UInt256)quorumHash { - NSDictionary *quorums = [self quorumsOfType:llmqType]; - for (NSData *hash in quorums) { - DSQuorumEntry *entry = quorums[hash]; - if (uint256_eq(entry.quorumHash, quorumHash)) { - return entry; - } - } - return NULL; -} - -- (DSMasternodeList *)mergedWithMasternodeList:(DSMasternodeList *)masternodeList { - for (NSData *proTxHash in self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash) { - DSSimplifiedMasternodeEntry *entry = self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[proTxHash]; - DSSimplifiedMasternodeEntry *newEntry = masternodeList.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[proTxHash]; - [entry mergedWithSimplifiedMasternodeEntry:newEntry atBlockHeight:masternodeList.height]; - } - for (NSNumber *quorumType in self.mQuorums) { - NSDictionary *quorumsOfType = self.quorums[quorumType]; - for (NSData *quorumHash in quorumsOfType) { - DSQuorumEntry *entry = quorumsOfType[quorumHash]; - if (!entry.verified) { - DSQuorumEntry *quorumEntry = [masternodeList quorumEntryOfType:(LLMQType)quorumType.unsignedIntegerValue withQuorumHash:entry.quorumHash]; - if (quorumEntry.verified) { - [entry mergedWithQuorumEntry:quorumEntry]; - } - } - } - } - return self; -} - -@end +// +//- (void)saveToJsonFile:(NSString *)fileName { +//// return; +// NSMutableArray *json_nodes = [NSMutableArray arrayWithCapacity:self.masternodeCount]; +// NSArray *proTxHashes = [self providerTxOrderedHashes]; +// for (NSData *proTxHash in proTxHashes) { +// DSSimplifiedMasternodeEntry *entry = self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[proTxHash]; +// NSString *json_node = [NSString stringWithFormat:@"{\n\"proRegTxHash\": \"%@\", \n\"confirmedHash\": \"%@\", \n\"service\": \"%@\", \n\"pubKeyOperator\": \"%@\", \n\"votingAddress\": \"%@\", \n\"isValid\": %s, \n\"updateHeight\": %@, \n\"knownConfirmedAtHeight\": %@\n}", +// uint256_hex(entry.providerRegistrationTransactionHash), +// uint256_hex(entry.confirmedHash), +// uint128_hex(entry.address), +// uint384_hex(entry.operatorPublicKey), +// uint160_data(entry.keyIDVoting).base58String, +// entry.isValid ? "true" : "false", @(entry.updateHeight), @(entry.knownConfirmedAtHeight)]; +// [json_nodes addObject:json_node]; +// } +// NSMutableArray *json_quorums = [NSMutableArray arrayWithCapacity:self.quorumsCount]; +// NSArray *llmqTypes = [[self.mQuorums allKeys] sortedArrayUsingComparator:^NSComparisonResult(NSNumber* n1, NSNumber* n2) { +// return [n1 compare:n2]; +// }]; +// for (NSNumber *llmqType in llmqTypes) { +// NSMutableDictionary *quorumsForMasternodeType = self.mQuorums[llmqType]; +// NSArray *llmqHashes = [quorumsForMasternodeType allKeys]; +// llmqHashes = [llmqHashes sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { +// UInt256 hash1 = *(UInt256 *)((NSData *)obj1).bytes; +// UInt256 hash2 = *(UInt256 *)((NSData *)obj2).bytes; +// return uint256_sup(hash1, hash2) ? NSOrderedDescending : NSOrderedAscending; +// }]; +// for (NSData *quorumHash in llmqHashes) { +// DSQuorumEntry *entry = quorumsForMasternodeType[quorumHash]; +// NSString *json_quorum = [NSString stringWithFormat:@"{\n\"version\": %@, \n\"llmqType\": %@, \n\"quorumHash\": \"%@\", \n\"quorumIndex\": %@, \n\"signersCount\": %@, \n\"signers\": \"%@\", \n\"validMembersCount\": %@, \n\"validMembers\": \"%@\", \n\"quorumPublicKey\": \"%@\", \n\"quorumVvecHash\": \"%@\", \n\"quorumSig\": \"%@\", \n\"membersSig\": \"%@\"\n}", +// @(entry.version), +// @(entry.llmqType), +// uint256_hex(entry.quorumHash), +// @(entry.quorumIndex), +// @(entry.signersCount), +// [entry signersBitset].hexString, +// @(entry.validMembersCount), +// [entry validMembersBitset].hexString, +// uint384_hex(entry.quorumPublicKey), +// uint256_hex(entry.quorumVerificationVectorHash), +// uint768_hex(entry.quorumThresholdSignature), +// uint768_hex(entry.allCommitmentAggregatedSignature)]; +// +// [json_quorums addObject:json_quorum]; +// } +// } +// NSString *nodes = [NSString stringWithFormat:@"\n\"mnList\": [%@]", [json_nodes componentsJoinedByString:@","]]; +// NSString *quorums = [NSString stringWithFormat:@"\n\"newQuorums\": [%@]", [json_quorums componentsJoinedByString:@","]]; +// NSString *list = [NSString stringWithFormat:@"{\n\"blockHash\":\"%@\", \n\"knownHeight\":%@, \n\"masternodeMerkleRoot\":\"%@\", \n\"quorumMerkleRoot\":\"%@\", \n%@, \n%@\n}", uint256_hex(self.blockHash), @(self.knownHeight), uint256_hex(self.masternodeMerkleRoot), uint256_hex(self.quorumMerkleRoot), nodes, quorums]; +// NSData* data = [list dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO]; +// [data saveToFile:fileName inDirectory:NSCachesDirectory]; +// DSLog(@"•-• File %@ saved", fileName); +//} +// +//- (void)saveToJsonFileExtended:(NSString *)fileName { +// NSMutableArray *json_nodes = [NSMutableArray arrayWithCapacity:self.masternodeCount]; +// NSArray *proTxHashes = [self providerTxOrderedHashes]; +// for (NSData *proTxHash in proTxHashes) { +// DSSimplifiedMasternodeEntry *entry = self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[proTxHash]; +// NSMutableArray *json_prev_public_keys = [NSMutableArray arrayWithCapacity:entry.previousOperatorPublicKeys.count]; +// for (DSBlock *block in entry.previousOperatorPublicKeys) { +// [json_prev_public_keys addObject:[NSString stringWithFormat:@"{\n\"block_height\":%u, \n\"block_hash\":\"%@\", \n\"public_key\":\"%@\"\n}", block.height, uint256_hex(block.blockHash), ((NSData *)entry.previousOperatorPublicKeys[block]).hexString]]; +// } +// NSMutableArray *json_prev_entry_hashes = [NSMutableArray arrayWithCapacity:entry.previousSimplifiedMasternodeEntryHashes.count]; +// for (DSBlock *block in entry.previousSimplifiedMasternodeEntryHashes) { +// [json_prev_entry_hashes addObject:[NSString stringWithFormat:@"{\n\"block_height\":%u, \n\"block_hash\":\"%@\", \n\"entry_hash\":\"%@\"\n}", block.height, uint256_hex(block.blockHash), ((NSData *)entry.previousSimplifiedMasternodeEntryHashes[block]).hexString]]; +// } +// NSMutableArray *json_prev_validities = [NSMutableArray arrayWithCapacity:entry.previousValidity.count]; +// for (DSBlock *block in entry.previousValidity) { +// [json_prev_validities addObject:[NSString stringWithFormat:@"{\n\"block_height\":%u, \n\"block_hash\":\"%@\", \n\"is_valid\":\%s\n}", block.height, uint256_hex(block.blockHash), ((NSNumber *) entry.previousValidity[block]).boolValue ? "true" : "false"]]; +// } +// +// NSString *json_node = [NSString stringWithFormat:@"{\n\"provider_registration_transaction_hash\": \"%@\", \n\"confirmed_hash\": \"%@\", \n\"confirmed_hash_hashed_with_provider_registration_transaction_hash\": \"%@\", \n\"socket_address\": {\n\"ip_address\":\"%@\",\n\"port\":%@\n}, \n\"operator_public_key\": \"%@\", \n\"previous_operator_public_keys\": [\n%@\n], \n\"previous_entry_hashes\": [\n%@\n], \n\"previous_validity\": [\n%@\n], \n\"known_confirmed_at_height\": %@, \n\"update_height\": %@, \n\"key_id_voting\": \"%@\", \n\"isValid\": %s, \n\"entry_hash\": \"%@\"\n}", +// uint256_hex(entry.providerRegistrationTransactionHash), +// uint256_hex(entry.confirmedHash), +// uint256_hex(entry.confirmedHashHashedWithProviderRegistrationTransactionHash), +// uint128_hex(entry.address), +// @(entry.port), +// uint384_hex(entry.operatorPublicKey), +// [NSString stringWithFormat:@"%@", [json_prev_public_keys componentsJoinedByString:@","]], +// [NSString stringWithFormat:@"%@", [json_prev_entry_hashes componentsJoinedByString:@","]], +// [NSString stringWithFormat:@"%@", [json_prev_validities componentsJoinedByString:@","]], +// @(entry.knownConfirmedAtHeight), +// @(entry.updateHeight), +// uint160_data(entry.keyIDVoting).base58String, +// entry.isValid ? "true" : "false", +// uint256_hex(entry.simplifiedMasternodeEntryHash)]; +// [json_nodes addObject:json_node]; +// } +// NSMutableArray *json_quorums = [NSMutableArray arrayWithCapacity:self.quorumsCount]; +// NSArray *llmqTypes = [[self.mQuorums allKeys] sortedArrayUsingComparator:^NSComparisonResult(NSNumber* n1, NSNumber* n2) { +// return [n1 compare:n2]; +// }]; +// for (NSNumber *llmqType in llmqTypes) { +// NSMutableDictionary *quorumsForMasternodeType = self.mQuorums[llmqType]; +// NSArray *llmqHashes = [quorumsForMasternodeType allKeys]; +// llmqHashes = [llmqHashes sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { +// UInt256 hash1 = *(UInt256 *)((NSData *)obj1).bytes; +// UInt256 hash2 = *(UInt256 *)((NSData *)obj2).bytes; +// return uint256_sup(hash1, hash2) ? NSOrderedDescending : NSOrderedAscending; +// }]; +// for (NSData *quorumHash in llmqHashes) { +// DSQuorumEntry *entry = quorumsForMasternodeType[quorumHash]; +// NSString *json_quorum = [NSString stringWithFormat:@"{\n\"version\": %@, \n\"llmqType\": %@, \n\"quorumHash\": \"%@\", \n\"quorumIndex\": %@, \n\"signersCount\": %@, \n\"signers\": \"%@\", \n\"validMembersCount\": %@, \n\"validMembers\": \"%@\", \n\"quorumPublicKey\": \"%@\", \n\"quorumVvecHash\": \"%@\", \n\"quorumSig\": \"%@\", \n\"membersSig\": \"%@\"\n}", +// @(entry.version), +// @(entry.llmqType), +// uint256_hex(entry.quorumHash), +// @(entry.quorumIndex), +// @(entry.signersCount), +// [entry signersBitset].hexString, +// @(entry.validMembersCount), +// [entry validMembersBitset].hexString, +// uint384_hex(entry.quorumPublicKey), +// uint256_hex(entry.quorumVerificationVectorHash), +// uint768_hex(entry.quorumThresholdSignature), +// uint768_hex(entry.allCommitmentAggregatedSignature)]; +// +// [json_quorums addObject:json_quorum]; +// } +// } +// NSString *nodes = [NSString stringWithFormat:@"\n\"mnList\": [%@]", [json_nodes componentsJoinedByString:@","]]; +// NSString *quorums = [NSString stringWithFormat:@"\n\"newQuorums\": [%@]", [json_quorums componentsJoinedByString:@","]]; +// NSString *list = [NSString stringWithFormat:@"{\n\"blockHash\":\"%@\", \n\"knownHeight\":%@, \n\"masternodeMerkleRoot\":\"%@\", \n\"quorumMerkleRoot\":\"%@\", \n%@, \n%@\n}", uint256_hex(self.blockHash), @(self.knownHeight), uint256_hex(self.masternodeMerkleRoot), uint256_hex(self.quorumMerkleRoot), nodes, quorums]; +// NSData* data = [list dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO]; +// [data saveToFile:fileName inDirectory:NSCachesDirectory]; +// DSLog(@"•-• File %@ saved", fileName); +//} +// +//- (NSString *)description { +// return [[super description] stringByAppendingString:[NSString stringWithFormat:@" {%u}", self.height]]; +//} +// +//- (NSString *)debugDescription { +//// [self saveToJsonFile]; +// return [[super debugDescription] stringByAppendingString:[NSString stringWithFormat:@" {%u}", self.height]]; +//} +// +//- (NSDictionary *)compareWithPrevious:(DSMasternodeList *)other { +// return [self compareWithPrevious:other +// blockHeightLookup:^uint32_t(UInt256 blockHash) { +// return [self.chain heightForBlockHash:blockHash]; +// }]; +//} +// +//- (NSDictionary *)compareWithPrevious:(DSMasternodeList *)other blockHeightLookup:(BlockHeightFinder)blockHeightLookup { +// return [self compare:other usingOurString:@"current" usingTheirString:@"previous" blockHeightLookup:blockHeightLookup]; +//} +// +//- (NSDictionary *)compare:(DSMasternodeList *)other { +// return [self compare:other +// blockHeightLookup:^uint32_t(UInt256 blockHash) { +// return [self.chain heightForBlockHash:blockHash]; +// }]; +//} +// +//- (NSDictionary *)compare:(DSMasternodeList *)other blockHeightLookup:(BlockHeightFinder)blockHeightLookup { +// return [self compare:other usingOurString:@"ours" usingTheirString:@"theirs" blockHeightLookup:blockHeightLookup]; +//} +// +//- (NSDictionary *)listOfChangedNodesComparedTo:(DSMasternodeList *)previous { +// NSMutableArray *added = [NSMutableArray array]; +// NSMutableArray *removed = [NSMutableArray array]; +// NSMutableArray *addedValidity = [NSMutableArray array]; +// NSMutableArray *removedValidity = [NSMutableArray array]; +// for (NSData *data in self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash) { +// DSSimplifiedMasternodeEntry *currentEntry = self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[data]; +// DSSimplifiedMasternodeEntry *previousEntry = previous.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[data]; +// if (currentEntry && !previousEntry) { +// [added addObject:currentEntry]; +// } else if ([currentEntry isValidAtBlockHeight:self.height] && ![previousEntry isValidAtBlockHeight:previous.height]) { +// [addedValidity addObject:currentEntry]; +// } else if (![currentEntry isValidAtBlockHeight:self.height] && [previousEntry isValidAtBlockHeight:previous.height]) { +// [removedValidity addObject:currentEntry]; +// } +// } +// +// for (NSData *data in previous.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash) { +// DSSimplifiedMasternodeEntry *currentEntry = self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[data]; +// DSSimplifiedMasternodeEntry *previousEntry = previous.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[data]; +// if (!currentEntry && previousEntry) { +// [removed addObject:previousEntry]; +// } +// } +// +// return @{MASTERNODE_LIST_ADDED_NODES: added, MASTERNODE_LIST_REMOVED_NODES: removed, MASTERNODE_LIST_ADDED_VALIDITY: addedValidity, MASTERNODE_LIST_REMOVED_VALIDITY: removedValidity}; +//} +// +//- (NSDictionary *)compare:(DSMasternodeList *)other usingOurString:(NSString *)ours usingTheirString:(NSString *)theirs blockHeightLookup:(BlockHeightFinder)blockHeightLookup { +// NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; +// for (NSData *data in self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash) { +// DSSimplifiedMasternodeEntry *ourEntry = self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[data]; +// DSSimplifiedMasternodeEntry *theirEntry = other.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[data]; +// if (ourEntry && theirEntry) { +// NSDictionary *entryComparison = [ourEntry compare:theirEntry ourBlockHash:self.blockHash theirBlockHash:other.blockHash usingOurString:ours usingTheirString:theirs blockHeightLookup:blockHeightLookup]; +// if (entryComparison.count) { +// dictionary[data] = entryComparison; +// } +// } else if (ourEntry) { +// dictionary[data] = @{@"absent": uint256_hex(ourEntry.providerRegistrationTransactionHash)}; +// } +// } +// return dictionary; +//} + +//- (NSDictionary *)toDictionaryUsingBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { +// NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; +// for (NSData *data in self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash) { +// DSSimplifiedMasternodeEntry *ourEntry = self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[data]; +// if (ourEntry) { +// NSDictionary *entryDictionary = [ourEntry toDictionaryAtBlockHash:self.blockHash usingBlockHeightLookup:blockHeightLookup]; +// dictionary[[data base64String]] = entryDictionary; +// } +// } +// return dictionary; +//} +// +//- (DSQuorumEntry *)quorumEntryForLockRequestID:(UInt256)requestID ofQuorumType:(DLLMQType)quorumType { +// NSArray *quorumsForLock = [self.quorums[@(quorumType)] allValues]; +// UInt256 lowestValue = UINT256_MAX; +// DSQuorumEntry *firstQuorum = nil; +// for (DSQuorumEntry *quorumEntry in quorumsForLock) { +// UInt256 orderingHash = uint256_reverse([quorumEntry orderingHashForRequestID:requestID forQuorumType:quorumType]); +// if (uint256_sup(lowestValue, orderingHash)) { +// lowestValue = orderingHash; +// firstQuorum = quorumEntry; +// } +// } +// return firstQuorum; +//} + + +//- (DLLMQEntry *_Nullable)quorumEntryForPlatformWithQuorumHash:(UInt256)quorumHash +// ofQuorumType:(DLLMQType)quorumType { +// u256 *quorum_hash = u256_ctor_u(quorumHash); +// return dash_spv_masternode_processor_models_masternode_list_MasternodeList_platform_llmq_with_quorum_hash(self.list, quorum_hash, &quorumType); +// +//// NSArray *quorumsForPlatform = [self.quorums[@(quorumType)] allValues]; +//// for (DSQuorumEntry *quorumEntry in quorumsForPlatform) { +//// if (uint256_eq(quorumEntry.quorumHash, quorumHash)) { +//// return quorumEntry; +//// } +//// NSAssert(!uint256_eq(quorumEntry.quorumHash, uint256_reverse(quorumHash)), @"these should not be inversed"); +//// } +//// return nil; +//} +// +//- (DLLMQEntry *)quorumEntryForPlatformWithQuorumHash:(UInt256)quorumHash { +// return [self quorumEntryForPlatformWithQuorumHash:quorumHash ofQuorumType:quorum_type_for_platform(self.chain.chainType)]; +//} + +//- (NSArray *)quorumEntriesRankedForInstantSendRequestID:(UInt256)requestID { +//// ordered_quorums_for_is_lock +// dash_spv_crypto_network_chain_type_ChainType_as_crate_fermented_types_dash_spv_crypto_network_chain_type_dash_spv_crypto_network_chain_type_IHaveChainSettings(self.chain.chainType); +// dash_spv_crypto_network_chain_type_ChainType_chain_locks_type(self.chain.chainType); +// +// DLLMQType quorumType = quorum_type_for_chain_locks(self.chain.chainType); +// NSArray *quorumsForIS = [self.quorums[@(quorumType)] allValues]; +// NSMutableDictionary *orderedQuorumDictionary = [NSMutableDictionary dictionary]; +// for (DSQuorumEntry *quorumEntry in quorumsForIS) { +// UInt256 orderingHash = uint256_reverse([quorumEntry orderingHashForRequestID:requestID forQuorumType:quorumType]); +// orderedQuorumDictionary[quorumEntry] = uint256_data(orderingHash); +// } +// NSArray *orderedQuorums = [orderedQuorumDictionary keysSortedByValueUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { +// return uint256_sup([obj1 UInt256], [obj2 UInt256]) ? NSOrderedDescending : NSOrderedAscending; +// }]; +// return orderedQuorums; +//} + +//- (NSArray *)peers:(uint32_t)peerCount withConnectivityNonce:(uint64_t)connectivityNonce { +// Vec_dash_spv_masternode_processor_common_socket_address_SocketAddress *vec = +// dash_spv_masternode_processor_models_masternode_list_MasternodeList_peer_addresses_with_connectivity_nonce(self.list, connectivityNonce, peerCount); +// NSMutableArray *mArray = [NSMutableArray array]; +// for (int i = 0; i < vec->count; i++) { +// dash_spv_masternode_processor_common_socket_address_SocketAddress *address = vec->values[i]; +// u128 *arr = address->ip_address; +// UInt128 addr = NSDataFromPtr(arr).UInt128; +// +// +//// UInt128 addr = [NSData dataWithBytes:(const void *)arr->values length:arr->count].UInt128; +// DSPeer *peer = [[DSPeer alloc] initWithAddress:addr andPort:address->port onChain:self.chain]; +// [mArray addObject:peer ]; +// } +// Vec_dash_spv_masternode_processor_common_socket_address_SocketAddress_destroy(vec); +// +// +//// NSArray *registrationTransactionHashes = [self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash allKeys]; +//// NSArray *sortedHashes = [registrationTransactionHashes sortedArrayUsingComparator:^NSComparisonResult(NSData *_Nonnull obj1, NSData *_Nonnull obj2) { +//// UInt256 hash1 = [[[obj1 mutableCopy] appendUInt64:connectivityNonce] blake3]; +//// UInt256 hash2 = [[[obj2 mutableCopy] appendUInt64:connectivityNonce] blake3]; +//// return uint256_sup(hash1, hash2) ? NSOrderedDescending : NSOrderedAscending; +//// }]; +//// NSMutableArray *mArray = [NSMutableArray array]; +//// for (uint32_t i = 0; i < MIN(peerCount, self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash.count); i++) { +//// DSSimplifiedMasternodeEntry *masternodeEntry = self.mSimplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[sortedHashes[i]]; +//// if (masternodeEntry.isValid) { +//// DSPeer *peer = [[DSPeer alloc] initWithAddress:masternodeEntry.address andPort:masternodeEntry.port onChain:masternodeEntry.chain]; +//// [mArray addObject:peer]; +//// } +//// } +// return mArray; +//} +// +//- (DSSimplifiedMasternodeEntry *)masternodeForRegistrationHash:(UInt256)registrationHash { +// return self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[uint256_data(registrationHash)]; +//} +// +//- (BOOL)hasUnverifiedNonRotatedQuorums { +// return dash_spv_masternode_processor_models_masternode_list_MasternodeList_has_unverified_regular_quorums(self.list, self.chain.chainType); +//} +// +//- (BOOL)hasUnverifiedRotatedQuorums { +// return dash_spv_masternode_processor_models_masternode_list_MasternodeList_has_unverified_rotated_quorums(self.list, self.chain.chainType); +//} +// +//- (DSQuorumEntry *_Nullable)quorumEntryOfType:(DLLMQType)llmqType withQuorumHash:(UInt256)quorumHash { +// NSDictionary *quorums = [self quorumsOfType:llmqType]; +// for (NSData *hash in quorums) { +// DSQuorumEntry *entry = quorums[hash]; +// if (uint256_eq(entry.quorumHash, quorumHash)) { +// return entry; +// } +// } +// return NULL; +//} +// +//- (DSMasternodeList *)mergedWithMasternodeList:(DSMasternodeList *)masternodeList { +// for (NSData *proTxHash in self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash) { +// DSSimplifiedMasternodeEntry *entry = self.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[proTxHash]; +// DSSimplifiedMasternodeEntry *newEntry = masternodeList.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash[proTxHash]; +// [entry mergedWithSimplifiedMasternodeEntry:newEntry atBlockHeight:masternodeList.height]; +// } +// for (NSNumber *quorumType in self.mQuorums) { +// NSDictionary *quorumsOfType = self.quorums[quorumType]; +// for (NSData *quorumHash in quorumsOfType) { +// DSQuorumEntry *entry = quorumsOfType[quorumHash]; +// if (!entry.verified) { +// +// DSQuorumEntry *quorumEntry = [masternodeList quorumEntryOfType:(DLLMQType)quorumType.unsignedIntegerValue withQuorumHash:entry.quorumHash]; +// if (quorumEntry.verified) { +// [entry mergedWithQuorumEntry:quorumEntry]; +// } +// } +// } +// } +// return self; +//} +// +//@end diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.h b/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.h index f21b2c9c7..c1600ffe2 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.h +++ b/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.h @@ -22,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN @interface DSMasternodeListDiffService : DSMasternodeListService -- (void)sendReversedHashes:(NSString *)baseBlockHash blockHash:(NSString *)blockHash; +//- (void)sendReversedHashes:(NSString *)baseBlockHash blockHash:(NSString *)blockHash; @end diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.m b/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.m index d4cc93789..4e2ecd982 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.m +++ b/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.m @@ -15,10 +15,11 @@ // limitations under the License. // +#import "DSChain+Params.h" #import "DSGetMNListDiffRequest.h" #import "DSMasternodeListDiffService.h" -#import "DSMasternodeListService+Protected.h" #import "DSMasternodeListStore+Protected.h" +#import "DSMasternodeManager.h" #import "NSString+Dash.h" @implementation DSMasternodeListDiffService @@ -26,25 +27,25 @@ @implementation DSMasternodeListDiffService - (void)composeMasternodeListRequest:(NSOrderedSet *)list { for (NSData *blockHashData in list) { // we should check the associated block still exists - if ([self.store hasBlockForBlockHash:blockHashData]) { + if ([self.chain.masternodeManager.store hasBlockForBlockHash:blockHashData]) { //there is the rare possibility we have the masternode list as a checkpoint, so lets first try that NSUInteger pos = [list indexOfObject:blockHashData]; UInt256 blockHash = blockHashData.UInt256; - DSMasternodeList *masternodeList = [self.delegate masternodeListServiceDidRequestFileFromBlockHash:self blockHash:blockHash]; - if (masternodeList) { + BOOL success = [self.chain.masternodeManager masternodeListServiceDidRequestFileFromBlockHash:self blockHash:blockHash]; + if (success) { [self removeFromRetrievalQueue:blockHashData]; [self checkWaitingForQuorums]; } else { // we need to go get it - UInt256 prevKnownBlockHash = [self.store closestKnownBlockHashForBlockHash:blockHash]; + UInt256 prevKnownBlockHash = [self.chain.masternodeManager.store closestKnownBlockHashForBlockHash:blockHash]; UInt256 prevInQueueBlockHash = (pos ? [list objectAtIndex:pos - 1].UInt256 : UINT256_ZERO); - UInt256 previousBlockHash = pos - ? ([self.store heightForBlockHash:prevKnownBlockHash] > [self.store heightForBlockHash:prevInQueueBlockHash] - ? prevKnownBlockHash - : prevInQueueBlockHash) - : prevKnownBlockHash; + u256 *prev_known_block_hash = u256_ctor_u(prevKnownBlockHash); + u256 *prev_in_queue_block_hash = u256_ctor_u(prevInQueueBlockHash); + uint32_t prevKnownHeight = DHeightForBlockHash(self.chain.shareCore.processor->obj, prev_known_block_hash); + uint32_t prevInQueueBlockHeight = DHeightForBlockHash(self.chain.shareCore.processor->obj, prev_in_queue_block_hash); + UInt256 previousBlockHash = pos ? (prevKnownHeight > prevInQueueBlockHeight ? prevKnownBlockHash : prevInQueueBlockHash) : prevKnownBlockHash; // request at: every new block - NSAssert(([self.store heightForBlockHash:previousBlockHash] != UINT32_MAX) || uint256_is_zero(previousBlockHash), @"This block height should be known"); +// NSAssert(([self.store heightForBlockHash:previousBlockHash] != UINT32_MAX) || uint256_is_zero(previousBlockHash), @"This block height should be known"); [self requestMasternodeListDiff:previousBlockHash forBlockHash:blockHash]; } } else { @@ -58,10 +59,12 @@ - (void)requestMasternodeListDiff:(UInt256)previousBlockHash forBlockHash:(UInt2 DSGetMNListDiffRequest *request = [DSGetMNListDiffRequest requestWithBaseBlockHash:previousBlockHash blockHash:blockHash]; DSMasternodeListRequest *matchedRequest = [self requestInRetrievalFor:previousBlockHash blockHash:blockHash]; if (matchedRequest) { - DSLog(@"[%@] •••• mnlistdiff request with such a range already in retrieval: %u..%u %@ .. %@", self.chain.name, [self.store heightForBlockHash:previousBlockHash], [self.store heightForBlockHash:blockHash], uint256_hex(previousBlockHash), uint256_hex(blockHash)); +// DSLog(@"[%@] •••• mnlistdiff request with such a range already in retrieval: %u..%u %@ .. %@", self.chain.name, [self.store heightForBlockHash:previousBlockHash], [self.store heightForBlockHash:blockHash], uint256_hex(previousBlockHash), uint256_hex(blockHash)); return; } - DSLog(@"[%@] •••• requestMasternodeListDiff: %u..%u %@ .. %@", self.chain.name, [self.store heightForBlockHash:previousBlockHash], [self.store heightForBlockHash:blockHash], uint256_hex(previousBlockHash), uint256_hex(blockHash)); + uint32_t prev_h = DHeightForBlockHash(self.chain.shareCore.processor->obj, u256_ctor_u(previousBlockHash)); + uint32_t h = DHeightForBlockHash(self.chain.shareCore.processor->obj, u256_ctor_u(blockHash)); + DSLog(@"[%@] •••• requestMasternodeListDiff: %u..%u %@ .. %@", self.chain.name, prev_h, h, uint256_hex(previousBlockHash), uint256_hex(blockHash)); [self sendMasternodeListRequest:request]; } @@ -71,10 +74,10 @@ - (void)requestMasternodeListDiff:(UInt256)previousBlockHash forBlockHash:(UInt2 // [service sendReversedHashes:@"00000bafbc94add76cb75e2ec92894837288a481e5c005f6563d91623bf8bc2c" blockHash:@"000000e6b51b9aba9754e6b4ef996ef1d142d6cfcc032c1fd7fc78ca6663ee0a"]; // [service sendReversedHashes:@"000000e6b51b9aba9754e6b4ef996ef1d142d6cfcc032c1fd7fc78ca6663ee0a" blockHash:@"00000009d7c0bcb59acf741f25239f45820eea178b74597d463ca80e104f753b"]; --(void)sendReversedHashes:(NSString *)baseBlockHash blockHash:(NSString *)blockHash { - DSGetMNListDiffRequest *request = [DSGetMNListDiffRequest requestWithBaseBlockHash:baseBlockHash.hexToData.reverse.UInt256 - blockHash:blockHash.hexToData.reverse.UInt256]; - [self sendMasternodeListRequest:request]; -} +//-(void)sendReversedHashes:(NSString *)baseBlockHash blockHash:(NSString *)blockHash { +// DSGetMNListDiffRequest *request = [DSGetMNListDiffRequest requestWithBaseBlockHash:baseBlockHash.hexToData.reverse.UInt256 +// blockHash:blockHash.hexToData.reverse.UInt256]; +// [self sendMasternodeListRequest:request]; +//} @end diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListService+Protected.h b/DashSync/shared/Models/Masternode/DSMasternodeListService+Protected.h index 6d7e838af..699f7d9a0 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListService+Protected.h +++ b/DashSync/shared/Models/Masternode/DSMasternodeListService+Protected.h @@ -15,20 +15,24 @@ // limitations under the License. // -#import "DSMasternodeListStore.h" -#import "DSMnDiffProcessingResult.h" +#import "DSKeyManager.h" +#import "DSMasternodeListService.h" +//#import "DSMasternodeListStore.h" +//#import "DSMnDiffProcessingResult.h" #import "DSPeer.h" NS_ASSUME_NONNULL_BEGIN - +@class DSMasternodeListStore; @interface DSMasternodeListService (Protected) @property (nonatomic, readonly) DSMasternodeListStore *store; -- (void)checkWaitingForQuorums; - (void)updateAfterProcessingMasternodeListWithBlockHash:(NSData *)blockHashData fromPeer:(DSPeer *)peer; -- (BOOL)shouldProcessDiffResult:(DSMnDiffProcessingResult *)diffResult skipPresenceInRetrieval:(BOOL)skipPresenceInRetrieval; -- (DSMasternodeListRequest*__nullable)requestInRetrievalFor:(UInt256)baseBlockHash blockHash:(UInt256)blockHash; +- (BOOL)shouldProcessDiffResult:(u256 *)block_hash + isValid:(BOOL)isValid + skipPresenceInRetrieval:(BOOL)skipPresenceInRetrieval; +//- (BOOL)shouldProcessDiffResult:(DSMnDiffProcessingResult *)diffResult skipPresenceInRetrieval:(BOOL)skipPresenceInRetrieval; +//- (DSMasternodeListRequest*__nullable)requestInRetrievalFor:(UInt256)baseBlockHash blockHash:(UInt256)blockHash; @end diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListService.h b/DashSync/shared/Models/Masternode/DSMasternodeListService.h index bb7c4de38..0987428c2 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListService.h +++ b/DashSync/shared/Models/Masternode/DSMasternodeListService.h @@ -18,8 +18,9 @@ #import "DSChain.h" #import "DSInsightManager.h" #import "DSMasternodeListRequest.h" -#import "DSMasternodeListStore.h" -#import "DSPeer.h" +//#import "DSMasternodeListStore.h" +//#import "DSMasternodeManager.h" +//#import "DSPeer.h" #import NS_ASSUME_NONNULL_BEGIN @@ -35,39 +36,41 @@ typedef NS_ENUM(NSUInteger, DSMasternodeListRequestMode) { DSMasternodeListRequestMode_QRINFO = 2, DSMasternodeListRequestMode_MIXED = DSMasternodeListRequestMode_MNLISTDIFF | DSMasternodeListRequestMode_QRINFO }; -@class DSMasternodeListService; +@class DSPeer, DSMasternodeListStore; -@protocol DSMasternodeListServiceDelegate - -- (DSMasternodeList *__nullable)masternodeListServiceDidRequestFileFromBlockHash:(DSMasternodeListService *)service blockHash:(UInt256)blockHash; -- (void)masternodeListServiceExceededMaxFailuresForMasternodeList:(DSMasternodeListService *)service blockHash:(UInt256)blockHash; -- (void)masternodeListServiceEmptiedRetrievalQueue:(DSMasternodeListService *)service; - -@end +//@protocol DSMasternodeListServiceDelegate +// +//- (BOOL)masternodeListServiceDidRequestFileFromBlockHash:(DSMasternodeListService *)service blockHash:(UInt256)blockHash; +////- (void)masternodeListServiceExceededMaxFailuresForMasternodeList:(DSMasternodeListService *)service blockHash:(UInt256)blockHash; +//- (void)masternodeListServiceEmptiedRetrievalQueue:(DSMasternodeListService *)service; +// +//@end @interface DSMasternodeListService : NSObject @property (nonatomic, readonly, nonnull) DSChain *chain; -@property (nonatomic, nullable) DSMasternodeList *currentMasternodeList; +//@property (nonatomic, assign, nullable) DMasternodeList *currentMasternodeList; @property (nonatomic, readonly) NSMutableSet *requestsInRetrieval; -@property (nonatomic, readonly) NSMutableOrderedSet *retrievalQueue; -@property (nonatomic, readonly) NSMutableOrderedSet *neededQueue; // TODO: Make storing hashes for tip list separately, to avoid +@property (nonatomic, readonly, assign) indexmap_IndexSet_u8_32 *retrievalQueue; +//@property (nonatomic, readonly) NSMutableOrderedSet *neededQueue; // TODO: Make storing hashes for tip list separately, to avoid @property (nonatomic, readonly) NSUInteger retrievalQueueCount; @property (nonatomic, readonly) NSUInteger retrievalQueueMaxAmount; -@property (nullable, nonatomic, weak) id delegate; +//@property (nullable, nonatomic, weak) id delegate; @property (nonatomic, assign) uint16_t timedOutAttempt; @property (nonatomic, assign) uint16_t timeOutObserverTry; -- (instancetype)initWithChain:(DSChain *)chain store:(DSMasternodeListStore *)store delegate:(id)delegate; +- (instancetype)initWithChain:(DSChain *)chain + store:(DSMasternodeListStore *)store; +// delegate:(id)delegate; -- (void)populateRetrievalQueueWithBlockHashes:(NSOrderedSet *)blockHashes; +//- (void)populateRetrievalQueueWithBlockHashes:(NSArray *)blockHashes processor:(MasternodeProcessor *)processor; - (void)getRecentMasternodeList; - (void)dequeueMasternodeListRequest; - (void)stop; -- (void)addToRetrievalQueue:(NSData *)masternodeBlockHashData; -- (void)addToRetrievalQueueArray:(NSArray *)masternodeBlockHashDataArray; +//- (void)addToRetrievalQueue:(NSData *)masternodeBlockHashData; +//- (void)addToRetrievalQueueArray:(NSArray *)masternodeBlockHashDataArray; - (void)cleanAllLists; - (void)cleanListsRetrievalQueue; - (void)cleanRequestsInRetrieval; @@ -84,6 +87,9 @@ typedef NS_ENUM(NSUInteger, DSMasternodeListRequestMode) { - (void)sendMasternodeListRequest:(DSMasternodeListRequest *)request; +- (void)checkWaitingForQuorums; +- (DSMasternodeListRequest*__nullable)requestInRetrievalFor:(UInt256)baseBlockHash blockHash:(UInt256)blockHash; + @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListService.m b/DashSync/shared/Models/Masternode/DSMasternodeListService.m index 5dcc72d68..0451bccaf 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListService.m +++ b/DashSync/shared/Models/Masternode/DSMasternodeListService.m @@ -15,28 +15,21 @@ // limitations under the License. // -#import "DSDAPIClient.h" -#import "DSMasternodeListService.h" #import "DSMasternodeListService+Protected.h" -#import "DSMasternodeListStore+Protected.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" -#import "DSChainManager.h" #import "DSChainManager+Protected.h" #import "DSGetMNListDiffRequest.h" #import "DSGetQRInfoRequest.h" #import "DSMasternodeManager+Protected.h" #import "DSMerkleBlock.h" #import "DSPeerManager+Protected.h" -#import "DSSimplifiedMasternodeEntry.h" #import "DSTransactionManager+Protected.h" #import "NSData+Dash.h" @interface DSMasternodeListService () // List Hashes of blocks for which masternode lists are need to be requested @property (nonatomic) DSMasternodeListStore *store; -@property (nonatomic, strong) NSMutableOrderedSet *retrievalQueue; -@property (nonatomic, strong) NSMutableOrderedSet *neededQueue; -@property (nonatomic, assign) NSUInteger retrievalQueueMaxAmount; // List: list of block ranges baseBlockHash + blockHash @property (nonatomic, strong) NSMutableSet *requestsInRetrieval; @property (nonatomic, strong) dispatch_source_t timeoutTimer; @@ -45,13 +38,12 @@ @interface DSMasternodeListService () @implementation DSMasternodeListService -- (instancetype)initWithChain:(DSChain *)chain store:(DSMasternodeListStore *)store delegate:(id)delegate { +- (instancetype)initWithChain:(DSChain *)chain store:(DSMasternodeListStore *)store { NSParameterAssert(chain); if (!(self = [super init])) return nil; _chain = chain; _store = store; - _delegate = delegate; - _retrievalQueue = [NSMutableOrderedSet orderedSet]; + // _retrievalQueue = [NSMutableOrderedSet orderedSet]; _requestsInRetrieval = [NSMutableSet set]; _timedOutAttempt = 0; _timeOutObserverTry = 0; @@ -62,7 +54,7 @@ - (void)startTimeOutObserver { [self cancelTimeOutObserver]; @synchronized (self) { NSSet *requestsInRetrieval = [self.requestsInRetrieval copy]; - NSUInteger masternodeListCount = [self.store knownMasternodeListsCount]; + uintptr_t masternodeListCount = DKnownMasternodeListsCount(self.chain.shareCore.cache->obj); self.timeOutObserverTry++; uint16_t timeOutObserverTry = self.timeOutObserverTry; dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(20 * (self.timedOutAttempt + 1) * NSEC_PER_SEC)); @@ -77,7 +69,9 @@ - (void)startTimeOutObserver { NSSet *requestsInRetrieval2 = [self.requestsInRetrieval copy]; NSMutableSet *leftToGet = [requestsInRetrieval mutableCopy]; [leftToGet intersectSet:requestsInRetrieval2]; - if ((masternodeListCount == [self.store knownMasternodeListsCount]) && [requestsInRetrieval isEqualToSet:leftToGet]) { + uintptr_t count = DKnownMasternodeListsCount(self.chain.shareCore.cache->obj); + + if ((masternodeListCount == count) && [requestsInRetrieval isEqualToSet:leftToGet]) { DSLog(@"[%@] %@ TimedOut", self.chain.name, self); self.timedOutAttempt++; [self disconnectFromDownloadPeer]; @@ -102,15 +96,6 @@ - (void)cancelTimeOutObserver { } } -- (NSString *)logListSet:(NSOrderedSet *)list { - NSString *str = @"\n"; - for (NSData *blockHashData in list) { - str = [str stringByAppendingString:[NSString stringWithFormat:@"•••• -> %d: %@,\n", - [self.store heightForBlockHash:blockHashData.UInt256], blockHashData.hexString]]; - } - return str; -} - - (void)checkWaitingForQuorums { if (![self retrievalQueueCount]) { [self.chain.chainManager.transactionManager checkWaitingForQuorums]; @@ -134,137 +119,75 @@ - (void)stop { } - (void)getRecentMasternodeList { - @synchronized(self.retrievalQueue) { - DSMerkleBlock *merkleBlock = [self.chain blockFromChainTip:0]; - if (!merkleBlock) { - // sometimes it happens while rescan - DSLog(@"[%@] getRecentMasternodeList: (no block exist) for tip", self.chain.name); - return; - } - UInt256 merkleBlockHash = merkleBlock.blockHash; - if ([self hasLatestBlockInRetrievalQueueWithHash:merkleBlockHash]) { - //we are asking for the same as the last one - return; - } - if ([self.store addBlockToValidationQueue:merkleBlock]) { - DSLog(@"[%@] MasternodeListService.Getting masternode list %u", self.chain.name, merkleBlock.height); - NSData *merkleBlockHashData = uint256_data(merkleBlockHash); - BOOL emptyRequestQueue = ![self retrievalQueueCount]; - [self addToRetrievalQueue:merkleBlockHashData]; - if (emptyRequestQueue) { - [self dequeueMasternodeListRequest]; - } - } - } -} - -- (void)setCurrentMasternodeList:(DSMasternodeList *_Nullable)currentMasternodeList { - if (self.chain.isEvolutionEnabled) { - if (!_currentMasternodeList) { - for (DSSimplifiedMasternodeEntry *masternodeEntry in currentMasternodeList.simplifiedMasternodeEntries) { - if (masternodeEntry.isValid) { - [self.chain.chainManager.DAPIClient addDAPINodeByAddress:masternodeEntry.ipAddressString]; - } - } - } else { - NSDictionary *updates = [currentMasternodeList listOfChangedNodesComparedTo:_currentMasternodeList]; - NSArray *added = updates[MASTERNODE_LIST_ADDED_NODES]; - NSArray *removed = updates[MASTERNODE_LIST_REMOVED_NODES]; - NSArray *addedValidity = updates[MASTERNODE_LIST_ADDED_VALIDITY]; - NSArray *removedValidity = updates[MASTERNODE_LIST_REMOVED_VALIDITY]; - for (DSSimplifiedMasternodeEntry *masternodeEntry in added) { - if (masternodeEntry.isValid) { - [self.chain.chainManager.DAPIClient addDAPINodeByAddress:masternodeEntry.ipAddressString]; - } - } - for (DSSimplifiedMasternodeEntry *masternodeEntry in addedValidity) { - [self.chain.chainManager.DAPIClient addDAPINodeByAddress:masternodeEntry.ipAddressString]; - } - for (DSSimplifiedMasternodeEntry *masternodeEntry in removed) { - [self.chain.chainManager.DAPIClient removeDAPINodeByAddress:masternodeEntry.ipAddressString]; - } - for (DSSimplifiedMasternodeEntry *masternodeEntry in removedValidity) { - [self.chain.chainManager.DAPIClient removeDAPINodeByAddress:masternodeEntry.ipAddressString]; - } - } - } - bool changed = _currentMasternodeList != currentMasternodeList; - _currentMasternodeList = currentMasternodeList; - if (changed) { - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DSCurrentMasternodeListDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSMasternodeManagerNotificationMasternodeListKey: self.currentMasternodeList ? self.currentMasternodeList : [NSNull null]}]; - }); - } -} - -- (void)populateRetrievalQueueWithBlockHashes:(NSOrderedSet *)blockHashes { - @synchronized(self.retrievalQueue) { - NSArray *orderedBlockHashes = [blockHashes sortedArrayUsingComparator:^NSComparisonResult(NSData *_Nonnull obj1, NSData *_Nonnull obj2) { - uint32_t height1 = [self.store heightForBlockHash:obj1.UInt256]; - uint32_t height2 = [self.store heightForBlockHash:obj2.UInt256]; - return (height1 > height2) ? NSOrderedDescending : NSOrderedAscending; - }]; - [self addToRetrievalQueueArray:orderedBlockHashes]; + DSMerkleBlock *merkleBlock = [self.chain blockFromChainTip:0]; + if (!merkleBlock) { + // sometimes it happens while rescan + DSLog(@"[%@] getRecentMasternodeList: (no block exist) for tip", self.chain.name); + return; } - [self dequeueMasternodeListRequest]; + u256 *block_hash = u256_ctor_u(merkleBlock.blockHash); + DBlock *block = DBlockCtor(merkleBlock.height, block_hash); + dash_spv_masternode_processor_processing_processor_MasternodeProcessor_get_recent_mn_list(self.chain.shareCore.processor->obj, block); } -- (BOOL)shouldProcessDiffResult:(DSMnDiffProcessingResult *)diffResult skipPresenceInRetrieval:(BOOL)skipPresenceInRetrieval { - DSMasternodeList *masternodeList = diffResult.masternodeList; - UInt256 masternodeListBlockHash = masternodeList.blockHash; - NSData *masternodeListBlockHashData = uint256_data(masternodeListBlockHash); - BOOL hasInRetrieval = [self.retrievalQueue containsObject:masternodeListBlockHashData]; -// uint32_t masternodeListBlockHeight = [self.store heightForBlockHash:masternodeListBlockHash]; - BOOL shouldNot = !hasInRetrieval && !skipPresenceInRetrieval; - //DSLog(@"•••• shouldProcessDiffResult: %d: %@ %d", masternodeListBlockHeight, uint256_reverse_hex(masternodeListBlockHash), !shouldNot); - if (shouldNot) { - //We most likely wiped data in the meantime - [self cleanRequestsInRetrieval]; - [self dequeueMasternodeListRequest]; - return NO; - } - BOOL isValid = [diffResult isTotallyValid]; - if (!isValid) { - DSLog(@"[%@] Invalid diff result: %@", self.chain.name, diffResult.debugDescription); - } - return isValid; - -} +//- (void)setCurrentMasternodeList:(Result_ok_dash_spv_masternode_processor_processing_mn_listdiff_result_MNListDiffResult_err_dash_spv_masternode_processor_processing_processing_error_ProcessingError *_Nullable)currentMasternodeList { +// if (self.chain.isEvolutionEnabled) { +// if (!_currentMasternodeList) { +// +// +// for (DSSimplifiedMasternodeEntry *masternodeEntry in currentMasternodeList.simplifiedMasternodeEntries) { +// if (masternodeEntry.isValid) { +// [self.chain.chainManager.DAPIClient addDAPINodeByAddress:masternodeEntry.ipAddressString]; +// } +// } +// } else { +// NSDictionary *updates = [currentMasternodeList listOfChangedNodesComparedTo:_currentMasternodeList]; +// NSArray *added = updates[MASTERNODE_LIST_ADDED_NODES]; +// NSArray *removed = updates[MASTERNODE_LIST_REMOVED_NODES]; +// NSArray *addedValidity = updates[MASTERNODE_LIST_ADDED_VALIDITY]; +// NSArray *removedValidity = updates[MASTERNODE_LIST_REMOVED_VALIDITY]; +// for (DSSimplifiedMasternodeEntry *masternodeEntry in added) { +// if (masternodeEntry.isValid) { +// [self.chain.chainManager.DAPIClient addDAPINodeByAddress:masternodeEntry.ipAddressString]; +// } +// } +// for (DSSimplifiedMasternodeEntry *masternodeEntry in addedValidity) { +// [self.chain.chainManager.DAPIClient addDAPINodeByAddress:masternodeEntry.ipAddressString]; +// } +// for (DSSimplifiedMasternodeEntry *masternodeEntry in removed) { +// [self.chain.chainManager.DAPIClient removeDAPINodeByAddress:masternodeEntry.ipAddressString]; +// } +// for (DSSimplifiedMasternodeEntry *masternodeEntry in removedValidity) { +// [self.chain.chainManager.DAPIClient removeDAPINodeByAddress:masternodeEntry.ipAddressString]; +// } +// } +// } +// bool changed = _currentMasternodeList != currentMasternodeList; +// _currentMasternodeList = currentMasternodeList; +// if (changed) { +// dispatch_async(dispatch_get_main_queue(), ^{ +// [[NSNotificationCenter defaultCenter] postNotificationName:DSCurrentMasternodeListDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSMasternodeManagerNotificationMasternodeListKey: self.currentMasternodeList ? [NSValue valueWithPointer:self.currentMasternodeList] : [NSNull null]}]; +// }); +// } +//} + +//- (void)populateRetrievalQueueWithBlockHashes:(NSArray *)blockHashes processor:(MasternodeProcessor *)processor { +// @synchronized(self.retrievalQueue) { +// [self addToRetrievalQueueArray:blockHashes]; +// } +// [self dequeueMasternodeListRequest]; +//} - (void)updateAfterProcessingMasternodeListWithBlockHash:(NSData *)blockHashData fromPeer:(DSPeer *)peer { + [self removeFromRetrievalQueue:blockHashData]; [self dequeueMasternodeListRequest]; [self checkWaitingForQuorums]; [[NSUserDefaults standardUserDefaults] removeObjectForKey:CHAIN_FAULTY_DML_MASTERNODE_PEERS]; } -- (void)addToRetrievalQueue:(NSData *)masternodeBlockHashData { - NSAssert(uint256_is_not_zero(masternodeBlockHashData.UInt256), @"the hash data must not be empty"); - [self.retrievalQueue addObject:masternodeBlockHashData]; - [self updateMasternodeRetrievalQueue]; -} - -- (void)addToRetrievalQueueArray:(NSArray *)masternodeBlockHashDataArray { - NSMutableArray *nonEmptyBlockHashes = [NSMutableArray array]; - for (NSData *blockHashData in masternodeBlockHashDataArray) { - NSAssert(uint256_is_not_zero(blockHashData.UInt256), @"We should not be adding an empty block hash"); - if (uint256_is_not_zero(blockHashData.UInt256)) { - [nonEmptyBlockHashes addObject:blockHashData]; - } - } - [self.retrievalQueue addObjectsFromArray:nonEmptyBlockHashes]; - [self updateMasternodeRetrievalQueue]; -} - - (void)removeFromRetrievalQueue:(NSData *)masternodeBlockHashData { - [self.retrievalQueue removeObject:masternodeBlockHashData]; - double count = self.retrievalQueue.count; - @synchronized (self.chain.chainManager.syncState) { - self.chain.chainManager.syncState.masternodeListSyncInfo.retrievalQueueCount = count; - self.chain.chainManager.syncState.masternodeListSyncInfo.retrievalQueueMaxAmount = (uint32_t) self.retrievalQueueMaxAmount; - DSLog(@"[%@] Masternode list queue updated: %f/%lu", self.chain.name, count, self.retrievalQueueMaxAmount); - [self.chain.chainManager notifySyncStateChanged]; - } + dash_spv_masternode_processor_processing_processor_MasternodeProcessor_remove_from_mn_list_retrieval_queue(self.chain.shareCore.processor->obj, u256_ctor(masternodeBlockHashData)); } - (void)cleanRequestsInRetrieval { @@ -272,47 +195,42 @@ - (void)cleanRequestsInRetrieval { } - (void)cleanListsRetrievalQueue { - [self.retrievalQueue removeAllObjects]; - @synchronized (self.chain.chainManager.syncState) { - self.chain.chainManager.syncState.masternodeListSyncInfo.retrievalQueueCount = 0; - self.chain.chainManager.syncState.masternodeListSyncInfo.retrievalQueueMaxAmount = (uint32_t) self.retrievalQueueMaxAmount; - DSLog(@"[%@] Masternode list queue cleaned up: 0/%lu", self.chain.name, self.retrievalQueueMaxAmount); - [self.chain.chainManager notifySyncStateChanged]; - } + dash_spv_masternode_processor_processing_processor_MasternodeProcessor_clean_mn_list_retrieval_queue(self.chain.shareCore.processor->obj); } - (void)cleanAllLists { - self.currentMasternodeList = nil; [self cleanListsRetrievalQueue]; [self cleanRequestsInRetrieval]; + // dispatch_async(dispatch_get_main_queue(), ^{ + // [[NSNotificationCenter defaultCenter] postNotificationName:DSCurrentMasternodeListDidChangeNotification + // object:nil + // userInfo:@{ + // DSChainManagerNotificationChainKey: self.chain, + // DSMasternodeManagerNotificationMasternodeListKey: [NSNull null] + // }]; + // }); + } - (DSPeerManager *)peerManager { return self.chain.chainManager.peerManager; } -- (NSUInteger)retrievalQueueCount { - return self.retrievalQueue.count; +- (indexmap_IndexSet_u8_32 *)retrievalQueue { + return dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_mn_list_retrieval_queue(self.chain.shareCore.cache->obj); } -- (void)updateMasternodeRetrievalQueue { - NSUInteger currentCount = self.retrievalQueue.count; - self.retrievalQueueMaxAmount = MAX(self.retrievalQueueMaxAmount, currentCount); - [self.retrievalQueue sortUsingComparator:^NSComparisonResult(NSData *_Nonnull obj1, NSData *_Nonnull obj2) { - return [self.store heightForBlockHash:obj1.UInt256] < [self.store heightForBlockHash:obj2.UInt256] ? NSOrderedAscending : NSOrderedDescending; - }]; - @synchronized (self.chain.chainManager.syncState) { - self.chain.chainManager.syncState.masternodeListSyncInfo.retrievalQueueCount = (uint32_t) currentCount; - self.chain.chainManager.syncState.masternodeListSyncInfo.retrievalQueueMaxAmount = (uint32_t) self.retrievalQueueMaxAmount; - DSLog(@"[%@] Masternode list queue updated: %lu/%lu", self.chain.name, currentCount, self.retrievalQueueMaxAmount); - [self.chain.chainManager notifySyncStateChanged]; - } +- (NSUInteger)retrievalQueueCount { + return dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_mn_list_retrieval_queue_count(self.chain.shareCore.cache->obj); +} +- (NSUInteger)retrievalQueueMaxAmount { + return dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_mn_list_retrieval_queue_get_max_amount(self.chain.shareCore.cache->obj); } - (void)fetchMasternodeListsToRetrieve:(void (^)(NSOrderedSet *listsToRetrieve))completion { if (![self retrievalQueueCount]) { DSLog(@"[%@] No masternode lists in retrieval: %@", self.chain.name, self); - [self.delegate masternodeListServiceEmptiedRetrievalQueue:self]; + [self.chain.masternodeManager masternodeListServiceEmptiedRetrievalQueue:self]; return; } if ([self.requestsInRetrieval count]) { @@ -331,7 +249,14 @@ - (void)fetchMasternodeListsToRetrieve:(void (^)(NSOrderedSet *listsTo } return; } - completion([self.retrievalQueue copy]); + indexmap_IndexSet_u8_32 *queue = [self retrievalQueue]; + + NSMutableOrderedSet *set = [NSMutableOrderedSet orderedSetWithCapacity:queue->count]; + for (int i = 0; i < queue->count; i++) { + [set addObject:NSDataFromPtr(queue->values[i])]; + } + indexmap_IndexSet_u8_32_destroy(queue); + completion(set); } - (DSMasternodeListRequest*__nullable)requestInRetrievalFor:(UInt256)baseBlockHash blockHash:(UInt256)blockHash { @@ -348,19 +273,6 @@ - (DSMasternodeListRequest*__nullable)requestInRetrievalFor:(UInt256)baseBlockHa - (BOOL)removeRequestInRetrievalForBaseBlockHash:(UInt256)baseBlockHash blockHash:(UInt256)blockHash { DSMasternodeListRequest *matchedRequest = [self requestInRetrievalFor:baseBlockHash blockHash:blockHash]; if (!matchedRequest) { - #if DEBUG - NSSet *requestsInRetrieval; - @synchronized (self.requestsInRetrieval) { - requestsInRetrieval = [self.requestsInRetrieval copy]; - } - NSMutableArray *requestsInRetrievalStrings = [NSMutableArray array]; - for (DSMasternodeListRequest *requestInRetrieval in requestsInRetrieval) { - [requestsInRetrievalStrings addObject:[requestInRetrieval logWithBlockHeightLookup:^uint32_t(UInt256 blockHash) { - return [self.store heightForBlockHash:blockHash]; - }]]; - } - DSLog(@"[%@] A masternode list (%@ .. %@) was received that is not set to be retrieved (%@)", self.chain.name, uint256_hex(baseBlockHash), uint256_hex(blockHash), [requestsInRetrievalStrings componentsJoinedByString:@", "]); - #endif /* DEBUG */ return NO; } @synchronized (self.requestsInRetrieval) { @@ -370,7 +282,7 @@ - (BOOL)removeRequestInRetrievalForBaseBlockHash:(UInt256)baseBlockHash blockHas } - (BOOL)hasLatestBlockInRetrievalQueueWithHash:(UInt256)blockHash { - return [self.retrievalQueue lastObject] && uint256_eq(blockHash, [self.retrievalQueue lastObject].UInt256); + return dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_has_latest_block_in_mn_list_retrieval_queue_with_hash(self.chain.shareCore.cache->obj, u256_ctor_u(blockHash)); } - (void)disconnectFromDownloadPeer { @@ -385,7 +297,7 @@ - (void)issueWithMasternodeListFromPeer:(DSPeer *)peer { //no need to remove local masternodes [self cleanListsRetrievalQueue]; [self.store deleteAllOnChain]; - [self.delegate masternodeListServiceExceededMaxFailuresForMasternodeList:self blockHash:self.currentMasternodeList.blockHash]; + [self.store removeOldMasternodeLists]; [[NSUserDefaults standardUserDefaults] removeObjectForKey:CHAIN_FAULTY_DML_MASTERNODE_PEERS]; [self getRecentMasternodeList]; } else { @@ -401,7 +313,7 @@ - (void)issueWithMasternodeListFromPeer:(DSPeer *)peer { } - (void)sendMasternodeListRequest:(DSMasternodeListRequest *)request { -// DSLog(@"•••• sendMasternodeListRequest: %@", [request toData].hexString); + // DSLog(@"•••• sendMasternodeListRequest: %@", [request toData].hexString); [self.peerManager sendRequest:request]; @synchronized (self.requestsInRetrieval) { [self.requestsInRetrieval addObject:request]; diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListStore+Protected.h b/DashSync/shared/Models/Masternode/DSMasternodeListStore+Protected.h index a9954344d..4849175e1 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListStore+Protected.h +++ b/DashSync/shared/Models/Masternode/DSMasternodeListStore+Protected.h @@ -16,27 +16,30 @@ // #import "BigIntTypes.h" -#import "DSMasternodeList.h" +//#import "DSMasternodeList.h" #import "DSMasternodeListStore.h" #import "DSMerkleBlock.h" -#import "DSQuorumEntry.h" +//#import "DSQuorumEntry.h" NS_ASSUME_NONNULL_BEGIN @interface DSMasternodeListStore (Protected) -@property (nonatomic, readwrite, nullable) DSMasternodeList *masternodeListAwaitingQuorumValidation; -@property (nonatomic, readonly) NSMutableSet *masternodeListQueriesNeedingQuorumsValidated; -@property (nonatomic, readwrite, assign) UInt256 lastQueriedBlockHash; //last by height, not by time queried +//@property (nonatomic, readwrite, nullable) DSMasternodeList *masternodeListAwaitingQuorumValidation; +//@property (nonatomic, readwrite, nullable) NSData *masternodeListBlockHashAwaitingQuorumValidation; +//@property (nonatomic, readonly) NSMutableSet *masternodeListQueriesNeedingQuorumsValidated; +//@property (nonatomic, readwrite, assign) UInt256 lastQueriedBlockHash; //last by height, not by time queried -- (void)savePlatformPingInfoForEntries:(NSArray *)entries - inContext:(NSManagedObjectContext *)context; +//- (void)savePlatformPingInfoForEntries:(NSArray *)entries +// inContext:(NSManagedObjectContext *)context; //- (void)checkPingTimesForMasternodesInContext:(NSManagedObjectContext *)context withCompletion:(void (^)(NSMutableDictionary *pingTimes, NSMutableDictionary *errors))completion; - (UInt256)closestKnownBlockHashForBlockHash:(UInt256)blockHash; -- (DSQuorumEntry *)quorumEntryForChainLockRequestID:(UInt256)requestID forMerkleBlock:(DSMerkleBlock *)merkleBlock; -- (DSQuorumEntry *)quorumEntryForInstantSendRequestID:(UInt256)requestID forMerkleBlock:(DSMerkleBlock *)merkleBlock; +//- (DLLMQEntry *)quorumEntryForChainLockRequestID:(UInt256)requestID +// forMerkleBlock:(DSMerkleBlock *)merkleBlock; +//- (DLLMQEntry *)quorumEntryForInstantSendRequestID:(UInt256)requestID +// forMerkleBlock:(DSMerkleBlock *)merkleBlock; -- (BOOL)addBlockToValidationQueue:(DSMerkleBlock *)merkleBlock; +//- (BOOL)addBlockToValidationQueue:(DSMerkleBlock *)merkleBlock; @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListStore.h b/DashSync/shared/Models/Masternode/DSMasternodeListStore.h index 89085e3aa..946e5b7f5 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListStore.h +++ b/DashSync/shared/Models/Masternode/DSMasternodeListStore.h @@ -16,10 +16,9 @@ // #import "BigIntTypes.h" -#import "DSChain.h" -#import "DSMasternodeList.h" -#import "DSQuorumSnapshot.h" +#import "DSKeyManager.h" #import +#import "NSManagedObject+Sugar.h" NS_ASSUME_NONNULL_BEGIN @@ -29,47 +28,34 @@ FOUNDATION_EXPORT NSString *const DSMasternodeManagerNotificationMasternodeListK FOUNDATION_EXPORT NSString *const DSQuorumListDidChangeNotification; #define CHAINLOCK_ACTIVATION_HEIGHT 1088640 - +@class DSChain; @interface DSMasternodeListStore : NSObject -@property (nonatomic, readonly) NSUInteger knownMasternodeListsCount; -@property (nonatomic, readonly) NSArray *recentMasternodeLists; @property (nonatomic, readonly) uint32_t earliestMasternodeListBlockHeight; @property (nonatomic, readonly) uint32_t lastMasternodeListBlockHeight; -@property (nonatomic, readonly) NSMutableDictionary *masternodeListsByBlockHash; -@property (nonatomic, readonly) NSMutableSet *masternodeListsBlockHashStubs; -@property (nonatomic, readonly) NSMutableOrderedSet *activeQuorums; - -@property (nonatomic, readonly) NSMutableDictionary *cachedQuorumSnapshots; -@property (nonatomic, readonly) NSMutableDictionary *cachedCLSignatures; +@property (nonatomic, readonly) dispatch_group_t savingGroup; - (instancetype)initWithChain:(DSChain *)chain; -- (void)setUp:(void (^)(DSMasternodeList *masternodeList))completion; +- (void)setUp; - (void)deleteAllOnChain; - (void)deleteEmptyMasternodeLists; - (BOOL)hasBlockForBlockHash:(NSData *)blockHashData; -- (BOOL)hasMasternodeListAt:(NSData *)blockHashData; -- (BOOL)hasMasternodeListCurrentlyBeingSaved; - (uint32_t)heightForBlockHash:(UInt256)blockhash; + - (void)loadLocalMasternodes; -- (DSMasternodeList *)loadMasternodeListAtBlockHash:(NSData *)blockHash withBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; -- (DSMasternodeList *_Nullable)loadMasternodeListsWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; -- (DSMasternodeList *_Nullable)masternodeListBeforeBlockHash:(UInt256)blockHash; -- (DSMasternodeList *_Nullable)masternodeListForBlockHash:(UInt256)blockHash withBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; -- (void)removeAllMasternodeLists; -- (void)removeOldMasternodeLists:(uint32_t)lastBlockHeight; +- (DArcMasternodeList *)loadMasternodeListAtBlockHash:(NSData *)blockHash + withBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; +- (DArcMasternodeList *)loadMasternodeListsWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; +- (void)removeOldMasternodeLists; - (void)removeOldSimplifiedMasternodeEntries; - -- (void)saveMasternodeList:(DSMasternodeList *)masternodeList - addedMasternodes:(NSDictionary *)addedMasternodes - modifiedMasternodes:(NSDictionary *)modifiedMasternodes - completion:(void (^)(NSError *error))completion; -- (void)saveQuorumSnapshot:(DSQuorumSnapshot *)quorumSnapshot - completion:(void (^)(NSError *error))completion; - -+ (void)saveMasternodeList:(DSMasternodeList *)masternodeList toChain:(DSChain *)chain havingModifiedMasternodes:(NSDictionary *)modifiedMasternodes createUnknownBlocks:(BOOL)createUnknownBlocks inContext:(NSManagedObjectContext *)context completion:(void (^)(NSError *error))completion; - -- (DSQuorumEntry *_Nullable)quorumEntryForPlatformHavingQuorumHash:(UInt256)quorumHash forBlockHeight:(uint32_t)blockHeight; +- (nullable NSError *)saveQuorumSnapshot:(DLLMQSnapshot *)quorumSnapshot + forBlockHash:(u256 *)block_hash; + ++ (nullable NSError *)saveMasternodeList:(DArcMasternodeList *)masternodeList + toChain:(DSChain *)chain + havingModifiedMasternodes:(DMasternodeEntryMap *)modifiedMasternodes + createUnknownBlocks:(BOOL)createUnknownBlocks + inContext:(NSManagedObjectContext *)context; @end diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListStore.m b/DashSync/shared/Models/Masternode/DSMasternodeListStore.m index 3211e1729..4bba3df78 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListStore.m +++ b/DashSync/shared/Models/Masternode/DSMasternodeListStore.m @@ -17,40 +17,23 @@ #import "DSMasternodeListStore.h" #import "DSAddressEntity+CoreDataClass.h" -#import "DSBlock.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" -#import "DSChainEntity+CoreDataProperties.h" -#import "DSChainManager.h" #import "DSChainManager+Protected.h" -#import "DSCheckpoint.h" -#import "DSDAPIClient.h" #import "DSLocalMasternodeEntity+CoreDataClass.h" #import "DSMasternodeListEntity+CoreDataClass.h" -#import "DSMerkleBlock.h" -#import "DSMerkleBlockEntity+CoreDataClass.h" -#import "DSMnDiffProcessingResult.h" -#import "DSOptionsManager.h" #import "DSQuorumEntryEntity+CoreDataClass.h" #import "DSQuorumSnapshotEntity+CoreDataClass.h" -#import "DSSimplifiedMasternodeEntry.h" #import "DSSimplifiedMasternodeEntryEntity+CoreDataClass.h" +#import "NSArray+Dash.h" #import "NSData+Dash.h" #import "NSError+Dash.h" -#import "NSManagedObject+Sugar.h" @interface DSMasternodeListStore () @property (nonatomic, strong) DSChain *chain; @property (nonatomic, strong) NSManagedObjectContext *managedObjectContext; -@property (nonatomic, strong) DSMasternodeList *masternodeListAwaitingQuorumValidation; -@property (nonatomic, strong) NSMutableDictionary *masternodeListsByBlockHash; -@property (nonatomic, strong) NSMutableSet *masternodeListsBlockHashStubs; -@property (nonatomic, strong) NSMutableSet *masternodeListQueriesNeedingQuorumsValidated; -@property (nonatomic, strong) NSMutableDictionary *cachedBlockHashHeights; @property (nonatomic, strong) dispatch_queue_t masternodeSavingQueue; -@property (nonatomic, assign) UInt256 lastQueriedBlockHash; //last by height, not by time queried -@property (atomic, assign) uint32_t masternodeListCurrentlyBeingSavedCount; -@property (nonatomic, strong) NSMutableOrderedSet *activeQuorums; @property (nonatomic, strong) dispatch_group_t savingGroup; @end @@ -60,98 +43,56 @@ - (instancetype)initWithChain:(DSChain *)chain { NSParameterAssert(chain); if (!(self = [super init])) return nil; _chain = chain; - _masternodeListsByBlockHash = [NSMutableDictionary dictionary]; - _masternodeListsBlockHashStubs = [NSMutableSet set]; - _masternodeListQueriesNeedingQuorumsValidated = [NSMutableSet set]; - _cachedBlockHashHeights = [NSMutableDictionary dictionary]; - _cachedQuorumSnapshots = [NSMutableDictionary dictionary]; - _masternodeListCurrentlyBeingSavedCount = 0; _masternodeSavingQueue = dispatch_queue_create([[NSString stringWithFormat:@"org.dashcore.dashsync.masternodesaving.%@", chain.uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL); _savingGroup = dispatch_group_create(); - self.lastQueriedBlockHash = UINT256_ZERO; self.managedObjectContext = chain.chainManagedObjectContext; return self; } -- (void)setUp:(void (^)(DSMasternodeList *masternodeList))completion { +- (void)setUp { [self deleteEmptyMasternodeLists]; //this is just for sanity purposes - [self loadMasternodeListsWithBlockHeightLookup:nil]; + DArcMasternodeList *list = [self loadMasternodeListsWithBlockHeightLookup:^uint32_t(UInt256 blockHash) { + return [self.chain heightForBlockHash:blockHash]; + }]; + if (list) { + dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_set_last_queried_mn_masternode_list(self.chain.shareCore.cache->obj, list); + } [self removeOldSimplifiedMasternodeEntries]; [self loadLocalMasternodes]; } -- (void)savePlatformPingInfoForEntries:(NSArray *)entries - inContext:(NSManagedObjectContext *)context { - [context performBlockAndWait:^{ - for (DSSimplifiedMasternodeEntry *entry in entries) { - [entry savePlatformPingInfoInContext:context]; - } - NSError *savingError = nil; - [context save:&savingError]; - }]; -} - -- (NSArray *)recentMasternodeLists { - return [[self.masternodeListsByBlockHash allValues] sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"height" ascending:YES]]]; -} - -- (NSUInteger)knownMasternodeListsCount { - @synchronized (self.masternodeListsByBlockHash) { - @synchronized (self.masternodeListsBlockHashStubs) { - NSMutableSet *masternodeListHashes = [NSMutableSet setWithArray:self.masternodeListsByBlockHash.allKeys]; - [masternodeListHashes addObjectsFromArray:[self.masternodeListsBlockHashStubs allObjects]]; - return [masternodeListHashes count]; - } - } -} +//- (void)savePlatformPingInfoForEntries:(NSArray *)entries +// inContext:(NSManagedObjectContext *)context { +// [context performBlockAndWait:^{ +// for (DSSimplifiedMasternodeEntry *entry in entries) { +// [entry savePlatformPingInfoInContext:context]; +// } +// NSError *savingError = nil; +// [context save:&savingError]; +// }]; +//} - (uint32_t)earliestMasternodeListBlockHeight { - uint32_t earliest = UINT32_MAX; - @synchronized (self.masternodeListsBlockHashStubs) { - for (NSData *blockHash in self.masternodeListsBlockHashStubs) { - earliest = MIN(earliest, [self heightForBlockHash:blockHash.UInt256]); - } - } - @synchronized (self.masternodeListsByBlockHash) { - for (NSData *blockHash in self.masternodeListsByBlockHash) { - earliest = MIN(earliest, [self heightForBlockHash:blockHash.UInt256]); - } - } - return earliest; + return dash_spv_masternode_processor_processing_processor_MasternodeProcessor_earliest_masternode_list_block_height(self.chain.shareCore.processor->obj); } - (uint32_t)lastMasternodeListBlockHeight { - uint32_t last = 0; - @synchronized (self.masternodeListsBlockHashStubs) { - for (NSData *blockHash in self.masternodeListsBlockHashStubs) { - last = MAX(last, [self heightForBlockHash:blockHash.UInt256]); - } - } - @synchronized (self.masternodeListsByBlockHash) { - for (NSData *blockHash in self.masternodeListsByBlockHash) { - last = MAX(last, [self heightForBlockHash:blockHash.UInt256]); - } - } - return last ? last : UINT32_MAX; + return DLastMasternodeListBlockHeight(self.chain.shareCore.processor->obj); } - (uint32_t)heightForBlockHash:(UInt256)blockhash { if (uint256_is_zero(blockhash)) return 0; - @synchronized (self.cachedBlockHashHeights) { - NSNumber *cachedHeightNumber = [self.cachedBlockHashHeights objectForKey:uint256_data(blockhash)]; - if (cachedHeightNumber) return [cachedHeightNumber intValue]; - uint32_t chainHeight = [self.chain heightForBlockHash:blockhash]; - if (chainHeight != UINT32_MAX) [self.cachedBlockHashHeights setObject:@(chainHeight) forKey:uint256_data(blockhash)]; - return chainHeight; - } + u256 *hash = u256_ctor_u(blockhash); + uint32_t cachedHeight = DHeightForBlockHash(self.chain.shareCore.processor->obj, hash); + return cachedHeight; } - (UInt256)closestKnownBlockHashForBlockHash:(UInt256)blockHash { - DSMasternodeList *masternodeList = [self masternodeListBeforeBlockHash:blockHash]; - if (masternodeList) - return masternodeList.blockHash; - else - return self.chain.genesisHash; + u256 *block_hash = u256_ctor_u(blockHash); + u256 *closest_block_hash = dash_spv_masternode_processor_processing_processor_MasternodeProcessor_closest_known_block_hash_for_block_hash(self.chain.shareCore.processor->obj, block_hash); + UInt256 known = u256_cast(closest_block_hash); + u256_dtor(closest_block_hash); + return known; } - (void)deleteAllOnChain { @@ -173,7 +114,7 @@ - (void)deleteEmptyMasternodeLists { NSArray *masternodeListEntities = [DSMasternodeListEntity fetchObjects:fetchRequest inContext:self.managedObjectContext]; for (DSMasternodeListEntity *entity in [masternodeListEntities copy]) { DSLog(@"[%@] DSMasternodeListStore.deleteEmptyMasternodeLists: %@", self.chain.name, entity); - [self.managedObjectContext deleteObject:entity]; + [self.managedObjectContext deleteObject:entity]; } [self.managedObjectContext ds_save]; }]; @@ -202,23 +143,6 @@ - (BOOL)hasBlockForBlockHash:(NSData *)blockHashData { return hasBlock; } - -- (BOOL)hasMasternodeListAt:(NSData *)blockHashData { - BOOL hasList; - @synchronized (self.masternodeListsByBlockHash) { - hasList = [self.masternodeListsByBlockHash objectForKey:blockHashData]; - } - BOOL hasStub; - @synchronized (self.masternodeListsBlockHashStubs) { - hasStub = [self.masternodeListsBlockHashStubs containsObject:blockHashData]; - } - return hasList || hasStub; -} - -- (BOOL)hasMasternodeListCurrentlyBeingSaved { - return !!self.masternodeListCurrentlyBeingSavedCount; -} - - (void)loadLocalMasternodes { NSFetchRequest *fetchRequest = [[DSLocalMasternodeEntity fetchRequest] copy]; [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"providerRegistrationTransaction.transactionHash.chain == %@", [self.chain chainEntityInContext:self.managedObjectContext]]]; @@ -228,177 +152,120 @@ - (void)loadLocalMasternodes { } } -- (DSMasternodeList *)loadMasternodeListAtBlockHash:(NSData *)blockHash withBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { - __block DSMasternodeList *masternodeList = nil; +- (DArcMasternodeList *)loadMasternodeListAtBlockHash:(NSData *)blockHash + withBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { + __block std_sync_Arc_dash_spv_masternode_processor_models_masternode_list_MasternodeList *masternode_list = nil; + DSLog(@"loadMasternodeListAtBlockHash: %@", blockHash.hexString); dispatch_group_enter(self.savingGroup); [self.managedObjectContext performBlockAndWait:^{ - DSMasternodeListEntity *masternodeListEntity = [DSMasternodeListEntity anyObjectInContext:self.managedObjectContext matching:@"block.chain == %@ && block.blockHash == %@", [self.chain chainEntityInContext:self.managedObjectContext], blockHash]; - NSMutableDictionary *simplifiedMasternodeEntryPool = [NSMutableDictionary dictionary]; - NSMutableDictionary *quorumEntryPool = [NSMutableDictionary dictionary]; - masternodeList = [masternodeListEntity masternodeListWithSimplifiedMasternodeEntryPool:[simplifiedMasternodeEntryPool copy] quorumEntryPool:quorumEntryPool withBlockHeightLookup:blockHeightLookup]; - if (masternodeList) { - DSLog(@"[%@] ••• addMasternodeList (loadMasternodeListAtBlockHash) -> %@: %@", self.chain.name, blockHash.hexString, masternodeList); - double count; - @synchronized (self.masternodeListsByBlockHash) { - [self.masternodeListsByBlockHash setObject:masternodeList forKey:blockHash]; - count = self.masternodeListsByBlockHash.count; - } - @synchronized (self.masternodeListsBlockHashStubs) { - [self.masternodeListsBlockHashStubs removeObject:blockHash]; - } - @synchronized (self.chain.chainManager.syncState) { - self.chain.chainManager.syncState.masternodeListSyncInfo.storedCount = count; - self.chain.chainManager.syncState.masternodeListSyncInfo.lastBlockHeight = self.lastMasternodeListBlockHeight; - [self.chain.chainManager notifySyncStateChanged]; - [self.chain.chainManager notifySyncStateChanged]; - } - DSLog(@"[%@] Loading Masternode List at height %u for blockHash %@ with %lu entries", self.chain.name, masternodeList.height, uint256_hex(masternodeList.blockHash), (unsigned long)masternodeList.simplifiedMasternodeEntries.count); - } + DSMasternodeListEntity *entity = [DSMasternodeListEntity anyObjectInContext:self.managedObjectContext matching:@"block.chain == %@ && block.blockHash == %@", [self.chain chainEntityInContext:self.managedObjectContext], blockHash]; + masternode_list = [entity masternodeListWithBlockHeightLookup:blockHeightLookup]; + DSLog(@"loadMasternodeListAtBlockHash loaded: %p", masternode_list); + if (masternode_list) + [self.chain.chainManager notifyMasternodeSyncStateChange:self.lastMasternodeListBlockHeight + storedCount:DMasternodeListLoaded(self.chain.shareCore.cache->obj, u256_ctor(blockHash), masternode_list)]; }]; dispatch_group_leave(self.savingGroup); - return masternodeList; + return masternode_list; } -- (DSMasternodeList *)loadMasternodeListsWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { - __block DSMasternodeList *currentList = nil; +- (DArcMasternodeList *)loadMasternodeListsWithBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { + __block DArcMasternodeList *currentList = nil; + DSLog(@"[%@] loadMasternodeListsWithBlockHeightLookup", self.chain.name); dispatch_group_enter(self.savingGroup); [self.managedObjectContext performBlockAndWait:^{ NSFetchRequest *fetchRequest = [[DSMasternodeListEntity fetchRequest] copy]; [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"block.chain == %@", [self.chain chainEntityInContext:self.managedObjectContext]]]; [fetchRequest setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"block.height" ascending:YES]]]; NSArray *masternodeListEntities = [DSMasternodeListEntity fetchObjects:fetchRequest inContext:self.managedObjectContext]; - NSMutableDictionary *simplifiedMasternodeEntryPool = [NSMutableDictionary dictionary]; - NSMutableDictionary *quorumEntryPool = [NSMutableDictionary dictionary]; + //DSLog(@"[%@] loadMasternodeListsWithBlockHeightLookup: stored count: %lu", self.chain.name, masternodeListEntities.count); + MasternodeProcessorCache *cache = self.chain.shareCore.cache->obj; + uint32_t neededMasternodeListHeight = self.chain.lastTerminalBlockHeight - 23; //2*8+7 + DSLog(@"[%@] loadMasternodeListsWithBlockHeightLookup: needed_height: %u", self.chain.name, neededMasternodeListHeight); for (uint32_t i = (uint32_t)masternodeListEntities.count - 1; i != UINT32_MAX; i--) { DSMasternodeListEntity *masternodeListEntity = [masternodeListEntities objectAtIndex:i]; - if ((i == masternodeListEntities.count - 1) || ((self.masternodeListsByBlockHash.count < 3) && (neededMasternodeListHeight >= masternodeListEntity.block.height))) { //either last one or there are less than 3 (we aim for 3) + + uintptr_t masternode_lists_count = DStoredMasternodeListsCount(cache); + DSLog(@"[%@] loadMasternodeListsWithBlockHeightLookup: cache count: %lu at %d", self.chain.name, masternode_lists_count, masternodeListEntity.block.height); + + if ((i == masternodeListEntities.count - 1) || ((masternode_lists_count < 3) && (neededMasternodeListHeight >= masternodeListEntity.block.height))) { //either last one or there are less than 3 (we aim for 3) //we only need a few in memory as new quorums will mostly be verified against recent masternode lists - DSMasternodeList *masternodeList = [masternodeListEntity masternodeListWithSimplifiedMasternodeEntryPool:[simplifiedMasternodeEntryPool copy] quorumEntryPool:quorumEntryPool withBlockHeightLookup:blockHeightLookup]; - [self.masternodeListsByBlockHash setObject:masternodeList forKey:uint256_data(masternodeList.blockHash)]; - double listCount = self.masternodeListsByBlockHash.count; - @synchronized (self.chain.chainManager.syncState) { - self.chain.chainManager.syncState.masternodeListSyncInfo.storedCount = listCount; - self.chain.chainManager.syncState.masternodeListSyncInfo.lastBlockHeight = self.lastMasternodeListBlockHeight; - [self.chain.chainManager notifySyncStateChanged]; - } + DArcMasternodeList *list = [masternodeListEntity masternodeListWithBlockHeightLookup:blockHeightLookup]; + DAddMasternodeList(cache, list->obj->block_hash, list); - [self.cachedBlockHashHeights setObject:@(masternodeListEntity.block.height) forKey:uint256_data(masternodeList.blockHash)]; - [simplifiedMasternodeEntryPool addEntriesFromDictionary:masternodeList.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash]; - [quorumEntryPool addEntriesFromDictionary:masternodeList.quorums]; - DSLog(@"[%@] Loading Masternode List at height %u for blockHash %@ with %lu entries", self.chain.name, masternodeList.height, uint256_hex(masternodeList.blockHash), (unsigned long)masternodeList.simplifiedMasternodeEntries.count); + [self.chain.chainManager notifyMasternodeSyncStateChange:self.lastMasternodeListBlockHeight + storedCount:DStoredMasternodeListsCount(cache)]; + DCacheBlockHeight(cache, list->obj->block_hash, masternodeListEntity.block.height); if (i == masternodeListEntities.count - 1) { - currentList = masternodeList; + if (currentList) + DArcMasternodeListDtor(currentList); + currentList = list; } neededMasternodeListHeight = masternodeListEntity.block.height - 8; } else { - //just keep a stub around - [self.cachedBlockHashHeights setObject:@(masternodeListEntity.block.height) forKey:masternodeListEntity.block.blockHash]; - [self.masternodeListsBlockHashStubs addObject:masternodeListEntity.block.blockHash]; + DSMerkleBlockEntity *block = masternodeListEntity.block; + uint32_t block_height = block.height; + u256 *block_hash = u256_ctor(block.blockHash); + DCacheBlockHeight(cache, block_hash, block_height); + DAddMasternodeListStub(cache, block_hash); } } }]; dispatch_group_leave(self.savingGroup); + DSLog(@"[%@] loadMasternodeListsWithBlockHeightLookup: loaded: %p", self.chain.name, currentList); + //dash_spv_masternode_processor_models_masternode_list_MasternodeList_print_description(currentList->obj); return currentList; } -- (DSMasternodeList *_Nullable)masternodeListBeforeBlockHash:(UInt256)blockHash { - uint32_t minDistance = UINT32_MAX; - uint32_t blockHeight = [self heightForBlockHash:blockHash]; - DSMasternodeList *closestMasternodeList = nil; - NSDictionary *lists; - @synchronized (self.masternodeListsByBlockHash) { - lists = [self.masternodeListsByBlockHash copy]; - } - - for (NSData *blockHashData in lists) { - uint32_t masternodeListBlockHeight = [self heightForBlockHash:blockHashData.UInt256]; - if (blockHeight <= masternodeListBlockHeight) continue; - uint32_t distance = blockHeight - masternodeListBlockHeight; - if (distance < minDistance) { - minDistance = distance; - closestMasternodeList = lists[blockHashData]; - } - } - if (self.chain.isMainnet && - closestMasternodeList.height < CHAINLOCK_ACTIVATION_HEIGHT && - blockHeight >= CHAINLOCK_ACTIVATION_HEIGHT) - return nil; //special mainnet case - return closestMasternodeList; -} - -- (DSMasternodeList *)masternodeListForBlockHash:(UInt256)blockHash withBlockHeightLookup:(BlockHeightFinder)blockHeightLookup { - NSData *blockHashData = uint256_data(blockHash); - DSMasternodeList *masternodeList = [self.masternodeListsByBlockHash objectForKey:blockHashData]; - if (!masternodeList && [self.masternodeListsBlockHashStubs containsObject:blockHashData]) { - masternodeList = [self loadMasternodeListAtBlockHash:blockHashData withBlockHeightLookup:blockHeightLookup]; - } - return masternodeList; -} - -- (void)removeAllMasternodeLists { - DSLog(@"[%@] ••• removeAllMasternodeLists -> ", self.chain.name); - @synchronized (self.masternodeListsByBlockHash) { - [self.masternodeListsByBlockHash removeAllObjects]; - } - @synchronized (self.masternodeListsBlockHashStubs) { - [self.masternodeListsBlockHashStubs removeAllObjects]; - } - self.masternodeListAwaitingQuorumValidation = nil; - @synchronized (self.chain.chainManager.syncState) { - self.chain.chainManager.syncState.masternodeListSyncInfo.lastBlockHeight = UINT32_MAX; - self.chain.chainManager.syncState.masternodeListSyncInfo.storedCount = 0; - DSLog(@"[%@] [DSMasternodeManager] All List Removed: %u/%u", self.chain.name, UINT32_MAX, 0); - [self.chain.chainManager notifySyncStateChanged]; - } -} - -- (void)removeOldMasternodeLists:(uint32_t)lastBlockHeight { - dispatch_group_enter(self.savingGroup); - [self.managedObjectContext performBlockAndWait:^{ - @autoreleasepool { - NSMutableArray *masternodeListBlockHashes = [[self.masternodeListsByBlockHash allKeys] mutableCopy]; - [masternodeListBlockHashes addObjectsFromArray:[self.masternodeListsBlockHashStubs allObjects]]; - NSArray *masternodeListEntities = [DSMasternodeListEntity objectsInContext:self.managedObjectContext matching:@"block.height < %@ && block.blockHash IN %@ && (block.usedByQuorums.@count == 0)", @(lastBlockHeight - 50), masternodeListBlockHashes]; - BOOL removedItems = !!masternodeListEntities.count; - for (DSMasternodeListEntity *masternodeListEntity in [masternodeListEntities copy]) { - DSLog(@"[%@] Removing masternodeList at height %u", self.chain.name, masternodeListEntity.block.height); - DSLog(@"[%@] quorums are %@", self.chain.name, masternodeListEntity.block.usedByQuorums); - //A quorum is on a block that can only have one masternode list. - //A block can have one quorum of each type. - //A quorum references the masternode list by it's block - //we need to check if this masternode list is being referenced by a quorum using the inverse of quorum.block.masternodeList - [self.managedObjectContext deleteObject:masternodeListEntity]; - @synchronized (self.masternodeListsByBlockHash) { - [self.masternodeListsByBlockHash removeObjectForKey:masternodeListEntity.block.blockHash]; - } - } - if (removedItems) { - - //Now we should delete old quorums - //To do this, first get the last 24 active masternode lists - //Then check for quorums not referenced by them, and delete those - NSArray *recentMasternodeLists = [DSMasternodeListEntity objectsSortedBy:@"block.height" ascending:NO offset:0 limit:10 inContext:self.managedObjectContext]; - uint32_t oldTime = lastBlockHeight - 24; - uint32_t oldestBlockHeight = recentMasternodeLists.count ? MIN([recentMasternodeLists lastObject].block.height, oldTime) : oldTime; - NSArray *oldQuorums = [DSQuorumEntryEntity objectsInContext:self.managedObjectContext matching:@"chain == %@ && SUBQUERY(referencedByMasternodeLists, $masternodeList, $masternodeList.block.height > %@).@count == 0", [self.chain chainEntityInContext:self.managedObjectContext], @(oldestBlockHeight)]; - for (DSQuorumEntryEntity *unusedQuorumEntryEntity in [oldQuorums copy]) { - [self.managedObjectContext deleteObject:unusedQuorumEntryEntity]; +- (void)removeOldMasternodeLists { + uint32_t heightToDelete = dash_spv_masternode_processor_processing_processor_MasternodeProcessor_calculate_outdated_height(self.chain.shareCore.processor->obj); + if (heightToDelete > 0 && heightToDelete != UINT32_MAX) { + uint32_t h = heightToDelete - 50; + DRemoveMasternodeListsBefore(self.chain.shareCore.cache->obj, h); + dispatch_group_enter(self.savingGroup); + [self.managedObjectContext performBlockAndWait:^{ + DCache *cache = self.chain.shareCore.cache->obj; + std_collections_HashSet_u8_32 *set = dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_known_masternode_lists_block_hashes(cache); + NSArray *masternodeListBlockHashes = [NSArray ffi_from_hash_set:set]; + [NSArray ffi_destroy_hash_set:set]; + @autoreleasepool { + // NSMutableArray *masternodeListBlockHashes = [[self.masternodeListsByBlockHash allKeys] mutableCopy]; + // [masternodeListBlockHashes addObjectsFromArray:[self.masternodeListsBlockHashStubs allObjects]]; + NSArray *masternodeListEntities = [DSMasternodeListEntity objectsInContext:self.managedObjectContext matching:@"block.height < %@ && block.blockHash IN %@ && (block.usedByQuorums.@count == 0)", @(h), masternodeListBlockHashes]; + BOOL removedItems = !!masternodeListEntities.count; + for (DSMasternodeListEntity *masternodeListEntity in [masternodeListEntities copy]) { + DSLog(@"[%@] Removing masternodeList at height %u", self.chain.name, masternodeListEntity.block.height); + DSLog(@"[%@] quorums are %@", self.chain.name, masternodeListEntity.block.usedByQuorums); + //A quorum is on a block that can only have one masternode list. + //A block can have one quorum of each type. + //A quorum references the masternode list by it's block + //we need to check if this masternode list is being referenced by a quorum using the inverse of quorum.block.masternodeList + [self.managedObjectContext deleteObject:masternodeListEntity]; + NSData *blockHash = masternodeListEntity.block.blockHash; + u256 *block_hash = Arr_u8_32_ctor(32, (uint8_t *) blockHash.bytes); + DRemoveMasternodeList(cache, block_hash); } - [self.managedObjectContext ds_save]; - - @synchronized (self.chain.chainManager.syncState) { - self.chain.chainManager.syncState.masternodeListSyncInfo.storedCount = self.masternodeListsByBlockHash.count; - self.chain.chainManager.syncState.masternodeListSyncInfo.lastBlockHeight = self.lastMasternodeListBlockHeight; - [self.chain.chainManager notifySyncStateChanged]; + if (removedItems) { + + //Now we should delete old quorums + //To do this, first get the last 24 active masternode lists + //Then check for quorums not referenced by them, and delete those + NSArray *recentMasternodeLists = [DSMasternodeListEntity objectsSortedBy:@"block.height" ascending:NO offset:0 limit:10 inContext:self.managedObjectContext]; + uint32_t oldTime = heightToDelete - 24; + uint32_t oldestBlockHeight = recentMasternodeLists.count ? MIN([recentMasternodeLists lastObject].block.height, oldTime) : oldTime; + NSArray *oldQuorums = [DSQuorumEntryEntity objectsInContext:self.managedObjectContext matching:@"chain == %@ && SUBQUERY(referencedByMasternodeLists, $masternodeList, $masternodeList.block.height > %@).@count == 0", [self.chain chainEntityInContext:self.managedObjectContext], @(oldestBlockHeight)]; + for (DSQuorumEntryEntity *unusedQuorumEntryEntity in [oldQuorums copy]) { + [self.managedObjectContext deleteObject:unusedQuorumEntryEntity]; + } + [self.managedObjectContext ds_save]; + [self.chain.chainManager notifyMasternodeSyncStateChange:self.lastMasternodeListBlockHeight + storedCount:DStoredMasternodeListsCount(cache)]; } } - } - }]; - dispatch_group_leave(self.savingGroup); -} + }]; + dispatch_group_leave(self.savingGroup); + } -- (void)removeOldQuorumSnapshots { - // TODO: implement mechanics of deletion outdated quorum snapshots from rust cache } - (void)removeOldSimplifiedMasternodeEntries { @@ -413,13 +280,11 @@ - (void)removeOldSimplifiedMasternodeEntries { [self.managedObjectContext deleteObject:simplifiedMasternodeEntryEntity]; deletedSomething = TRUE; deletionCount++; - if ((deletionCount % 3000) == 0) { + if ((deletionCount % 3000) == 0) [self.managedObjectContext ds_save]; - } } - if (deletedSomething) { + if (deletedSomething) [self.managedObjectContext ds_save]; - } }]; dispatch_group_leave(self.savingGroup); } @@ -431,74 +296,23 @@ - (void)notifyMasternodeListUpdate { }); } -- (void)saveMasternodeList:(DSMasternodeList *)masternodeList addedMasternodes:(NSDictionary *)addedMasternodes modifiedMasternodes:(NSDictionary *)modifiedMasternodes completion:(void (^)(NSError *error))completion { - UInt256 blockHash = masternodeList.blockHash; - NSData *blockHashData = uint256_data(blockHash); - if ([self hasMasternodeListAt:blockHashData]) { - // in rare race conditions this might already exist - // but also as we can get it from different sources - // with different quorums verification status - DSMasternodeList *storedList = [self.masternodeListsByBlockHash objectForKey:blockHashData]; - if (storedList) { - masternodeList = [storedList mergedWithMasternodeList:masternodeList]; - } else { - completion(NULL); - return; - } - } - NSArray *updatedSimplifiedMasternodeEntries = [addedMasternodes.allValues arrayByAddingObjectsFromArray:modifiedMasternodes.allValues]; - [self.chain updateAddressUsageOfSimplifiedMasternodeEntries:updatedSimplifiedMasternodeEntries]; - double count; - @synchronized (self.masternodeListsByBlockHash) { - [self.masternodeListsByBlockHash setObject:masternodeList forKey:blockHashData]; - count = self.masternodeListsByBlockHash.count; - } - @synchronized (self.chain.chainManager.syncState) { - self.chain.chainManager.syncState.masternodeListSyncInfo.storedCount = count; - self.chain.chainManager.syncState.masternodeListSyncInfo.lastBlockHeight = self.lastMasternodeListBlockHeight; - [self.chain.chainManager notifySyncStateChanged]; - } - [self notifyMasternodeListUpdate]; - dispatch_group_enter(self.savingGroup); - //We will want to create unknown blocks if they came from insight - BOOL createUnknownBlocks = masternodeList.chain.allowInsightBlocksForVerification; - self.masternodeListCurrentlyBeingSavedCount++; - //This will create a queue for masternodes to be saved without blocking the networking queue - [DSMasternodeListStore saveMasternodeList:masternodeList - toChain:self.chain - havingModifiedMasternodes:modifiedMasternodes - createUnknownBlocks:createUnknownBlocks - inContext:self.managedObjectContext - completion:^(NSError *error) { - self.masternodeListCurrentlyBeingSavedCount--; - dispatch_group_leave(self.savingGroup); - if (error) { - DSLog(@"[%@] Finished saving MNL at height %u with error: %@", self.chain.name, [self heightForBlockHash:masternodeList.blockHash], error.description); - } - completion(error); - }]; -} - -- (void)saveQuorumSnapshot:(DSQuorumSnapshot *)quorumSnapshot - completion:(void (^)(NSError *error))completion { +- (nullable NSError *)saveQuorumSnapshot:(DLLMQSnapshot *)quorumSnapshot + forBlockHash:(u256 *)block_hash { if (!quorumSnapshot) { - return; + return NULL; } - UInt256 blockHash = quorumSnapshot.blockHash; - NSData *blockHashData = uint256_data(blockHash); - uint32_t blockHeight = [self heightForBlockHash:blockHash]; - if ([self.cachedQuorumSnapshots objectForKey:blockHashData]) { - return; - } - DSLog(@"[%@] Queued saving Quorum Snapshot for: %u: %@", self.chain.name, blockHeight, uint256_hex(blockHash)); - [self.cachedQuorumSnapshots setObject:quorumSnapshot forKey:blockHashData]; + uint32_t blockHeight = DHeightForBlockHash(self.chain.shareCore.processor->obj, block_hash); + NSData *blockHashData = NSDataFromPtr(block_hash); + UInt256 blockHash = blockHashData.UInt256; dispatch_group_enter(self.savingGroup); NSManagedObjectContext *context = self.managedObjectContext; + __block NSError *result = nil; + [context performBlockAndWait:^{ @autoreleasepool { BOOL createUnknownBlocks = self.chain.allowInsightBlocksForVerification; DSChainEntity *chainEntity = [self.chain chainEntityInContext:context]; - DSMerkleBlockEntity *merkleBlockEntity = [DSMerkleBlockEntity merkleBlockEntityForBlockHash:blockHash inContext:context]; + DSMerkleBlockEntity *merkleBlockEntity = [DSMerkleBlockEntity merkleBlockEntityForBlockHash:blockHashData inContext:context]; if (!merkleBlockEntity) { merkleBlockEntity = [DSMerkleBlockEntity merkleBlockEntityForBlockHashFromCheckpoint:blockHash chain:self.chain inContext:context]; } @@ -506,7 +320,7 @@ - (void)saveQuorumSnapshot:(DSQuorumSnapshot *)quorumSnapshot NSError *error = nil; if (!merkleBlockEntity) { if (createUnknownBlocks) { - merkleBlockEntity = [DSMerkleBlockEntity createMerkleBlockEntityForBlockHash:blockHash blockHeight:blockHeight chainEntity:chainEntity inContext:context]; + merkleBlockEntity = [DSMerkleBlockEntity createMerkleBlockEntityForBlockHash:blockHashData blockHeight:blockHeight chainEntity:chainEntity inContext:context]; } else { DSLog(@"[%@] Merkle block should exist for block hash %@", self.chain.name, blockHashData.hexString); error = [NSError errorWithCode:600 localizedDescriptionKey:@"Merkle block should exist"]; @@ -525,29 +339,33 @@ - (void)saveQuorumSnapshot:(DSQuorumSnapshot *)quorumSnapshot DSLog(@"[%@] Finished saving Quorum Snapshot at height %u: %@", self.chain.name, blockHeight, uint256_hex(blockHash)); } error = [context ds_save]; - if (completion) { - completion(error); - } + result = error; +// if (completion) { +// completion(error); +// } } }]; dispatch_group_leave(self.savingGroup); + return result; } -+ (void)saveMasternodeList:(DSMasternodeList *)masternodeList - toChain:(DSChain *)chain - havingModifiedMasternodes:(NSDictionary *)modifiedMasternodes - createUnknownBlocks:(BOOL)createUnknownBlocks - inContext:(NSManagedObjectContext *)context - completion:(void (^)(NSError *error))completion { - DSLog(@"[%@] Queued saving MNL at height %u: %@", chain.name, masternodeList.height, uint256_hex(masternodeList.blockHash)); ++ (nullable NSError *)saveMasternodeList:(DArcMasternodeList *)masternodeList + toChain:(DSChain *)chain + havingModifiedMasternodes:(DMasternodeEntryMap *)modifiedMasternodes + createUnknownBlocks:(BOOL)createUnknownBlocks + inContext:(NSManagedObjectContext *)context { + + DSLog(@"[%@] Queued saving MNL at height %u (%@)", chain.name, masternodeList->obj->known_height, uint256_hex(u256_cast(masternodeList->obj->block_hash))); + __block NSError *result = nil; + [context performBlockAndWait:^{ //masternodes @autoreleasepool { DSChainEntity *chainEntity = [chain chainEntityInContext:context]; - UInt256 mnlBlockHash = masternodeList.blockHash; - uint32_t mnlHeight = masternodeList.height; + UInt256 mnlBlockHash = u256_cast(masternodeList->obj->block_hash); + uint32_t mnlHeight = masternodeList->obj->known_height; NSData *mnlBlockHashData = uint256_data(mnlBlockHash); - DSMerkleBlockEntity *merkleBlockEntity = [DSMerkleBlockEntity merkleBlockEntityForBlockHash:mnlBlockHash inContext:context]; + DSMerkleBlockEntity *merkleBlockEntity = [DSMerkleBlockEntity merkleBlockEntityForBlockHash:mnlBlockHashData inContext:context]; if (!merkleBlockEntity) { merkleBlockEntity = [DSMerkleBlockEntity merkleBlockEntityForBlockHashFromCheckpoint:mnlBlockHash chain:chain inContext:context]; } @@ -556,7 +374,7 @@ + (void)saveMasternodeList:(DSMasternodeList *)masternodeList BOOL shouldMerge = false; if (!merkleBlockEntity) { if (createUnknownBlocks) { - merkleBlockEntity = [DSMerkleBlockEntity createMerkleBlockEntityForBlockHash:mnlBlockHash blockHeight:mnlHeight chainEntity:chainEntity inContext:context]; + merkleBlockEntity = [DSMerkleBlockEntity createMerkleBlockEntityForBlockHash:mnlBlockHashData blockHeight:mnlHeight chainEntity:chainEntity inContext:context]; } else { DSLog(@"[%@] Merkle block should exist for block hash %@", chain.name, mnlBlockHashData); error = [NSError errorWithCode:600 localizedDescriptionKey:@"Merkle block should exist"]; @@ -571,52 +389,81 @@ + (void)saveMasternodeList:(DSMasternodeList *)masternodeList DSMasternodeListEntity *masternodeListEntity = merkleBlockEntity.masternodeList; NSArray *knownSimplifiedMasternodeEntryEntities = [DSSimplifiedMasternodeEntryEntity objectsInContext:context matching:@"chain == %@", chainEntity]; + DSLog(@"[%@] MNL knownSimplifiedMasternodeEntryEntities (should_merge): %lu", chain.name, knownSimplifiedMasternodeEntryEntities.count); NSMutableDictionary *indexedKnownSimplifiedMasternodeEntryEntities = [NSMutableDictionary dictionary]; for (DSSimplifiedMasternodeEntryEntity *simplifiedMasternodeEntryEntity in knownSimplifiedMasternodeEntryEntities) { - NSData *proRegTxHash = simplifiedMasternodeEntryEntity.providerRegistrationTransactionHash; - [indexedKnownSimplifiedMasternodeEntryEntities setObject:simplifiedMasternodeEntryEntity forKey:proRegTxHash]; + [indexedKnownSimplifiedMasternodeEntryEntities setObject:simplifiedMasternodeEntryEntity forKey:simplifiedMasternodeEntryEntity.providerRegistrationTransactionHash]; } NSDictionary *indexedMasternodes = [indexedKnownSimplifiedMasternodeEntryEntities copy]; + NSMutableSet *votingAddressStrings = [NSMutableSet set]; NSMutableSet *operatorAddressStrings = [NSMutableSet set]; + NSMutableSet *platformNodeAddressStrings = [NSMutableSet set]; NSMutableSet *providerRegistrationTransactionHashes = [NSMutableSet set]; - NSArray *masternodes = masternodeList.simplifiedMasternodeEntries; - // TODO: check do we have to do the same for platform node addresses - for (DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry in masternodes) { - [votingAddressStrings addObject:simplifiedMasternodeEntry.votingAddress]; - [operatorAddressStrings addObject:simplifiedMasternodeEntry.operatorAddress]; - [providerRegistrationTransactionHashes addObject:uint256_data(simplifiedMasternodeEntry.providerRegistrationTransactionHash)]; + + for (int i = 0; i < masternodeList->obj->masternodes->count; i++) { + DMasternodeEntry *entry = masternodeList->obj->masternodes->values[i]; + NSString *votingAddress = [DSKeyManager NSStringFrom:DMasternodeEntryVotingAddress(entry, chain.chainType)]; + NSString *operatorAddress = [DSKeyManager NSStringFrom:DMasternodeEntryOperatorPublicKeyAddress(entry, chain.chainType)]; + NSString *platformNodeAddress = [DSKeyManager NSStringFrom:DMasternodeEntryEvoNodeAddress(entry, chain.chainType)]; + NSData *proRegTxHash = NSDataFromPtr(entry->provider_registration_transaction_hash); + [votingAddressStrings addObject:votingAddress]; + [operatorAddressStrings addObject:operatorAddress]; + [platformNodeAddressStrings addObject:platformNodeAddress]; + [providerRegistrationTransactionHashes addObject:proRegTxHash]; } //this is the initial list sync so lets speed things up a little bit with some optimizations NSDictionary *votingAddresses = [DSAddressEntity findAddressesAndIndexIn:votingAddressStrings onChain:(DSChain *)chain inContext:context]; - NSDictionary *operatorAddresses = [DSAddressEntity findAddressesAndIndexIn:votingAddressStrings onChain:(DSChain *)chain inContext:context]; + NSDictionary *operatorAddresses = [DSAddressEntity findAddressesAndIndexIn:operatorAddressStrings onChain:(DSChain *)chain inContext:context]; + NSDictionary *platformNodeAddresses = [DSAddressEntity findAddressesAndIndexIn:platformNodeAddressStrings onChain:(DSChain *)chain inContext:context]; NSDictionary *localMasternodes = [DSLocalMasternodeEntity findLocalMasternodesAndIndexForProviderRegistrationHashes:providerRegistrationTransactionHashes inContext:context]; - NSAssert(masternodes, @"A masternode must have entries to be saved"); - for (DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry in masternodes) { - NSData *proRegTxHash = uint256_data(simplifiedMasternodeEntry.providerRegistrationTransactionHash); + NSAssert(masternodeList->obj->masternodes->count > 0, @"A masternode must have entries to be saved"); + + for (int i = 0; i < masternodeList->obj->masternodes->count; i++) { + DMasternodeEntry *entry = masternodeList->obj->masternodes->values[i]; + NSData *proRegTxHash = [NSData dataWithBytes:(const void *)entry->provider_registration_transaction_hash->values length:32]; DSSimplifiedMasternodeEntryEntity *simplifiedMasternodeEntryEntity = [indexedMasternodes objectForKey:proRegTxHash]; if (!simplifiedMasternodeEntryEntity) { simplifiedMasternodeEntryEntity = [DSSimplifiedMasternodeEntryEntity managedObjectInBlockedContext:context]; - [simplifiedMasternodeEntryEntity setAttributesFromSimplifiedMasternodeEntry:simplifiedMasternodeEntry atBlockHeight:mnlHeight knownOperatorAddresses:operatorAddresses knownVotingAddresses:votingAddresses localMasternodes:localMasternodes onChainEntity:chainEntity]; - } else if (simplifiedMasternodeEntry.updateHeight >= mnlHeight) { + [simplifiedMasternodeEntryEntity setAttributesFromSimplifiedMasternodeEntry:entry + atBlockHeight:mnlHeight + knownOperatorAddresses:operatorAddresses + knownVotingAddresses:votingAddresses + platformNodeAddresses:platformNodeAddresses + localMasternodes:localMasternodes + onChain:chain + onChainEntity:chainEntity]; + } else if (entry->update_height >= mnlHeight) { // it was updated in this masternode list - [simplifiedMasternodeEntryEntity updateAttributesFromSimplifiedMasternodeEntry:simplifiedMasternodeEntry atBlockHeight:mnlHeight knownOperatorAddresses:operatorAddresses knownVotingAddresses:votingAddresses localMasternodes:localMasternodes]; + [simplifiedMasternodeEntryEntity updateAttributesFromSimplifiedMasternodeEntry:entry + atBlockHeight:mnlHeight + knownOperatorAddresses:operatorAddresses + knownVotingAddresses:votingAddresses + platformNodeAddresses:platformNodeAddresses + localMasternodes:localMasternodes + onChain:chain]; } [masternodeListEntity addMasternodesObject:simplifiedMasternodeEntryEntity]; } - for (NSData *simplifiedMasternodeEntryHash in modifiedMasternodes) { - DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry = modifiedMasternodes[simplifiedMasternodeEntryHash]; - NSData *proRegTxHash = uint256_data(simplifiedMasternodeEntry.providerRegistrationTransactionHash); + for (int i = 0; i < modifiedMasternodes->count; i++) { + DMasternodeEntry *modified = modifiedMasternodes->values[i]; + NSData *proRegTxHash = [NSData dataWithBytes:(const void *)modified->provider_registration_transaction_hash->values length:32]; DSSimplifiedMasternodeEntryEntity *simplifiedMasternodeEntryEntity = [indexedMasternodes objectForKey:proRegTxHash]; NSAssert(simplifiedMasternodeEntryEntity, @"this masternode must be present (%@)", proRegTxHash.hexString); - [simplifiedMasternodeEntryEntity updateAttributesFromSimplifiedMasternodeEntry:simplifiedMasternodeEntry atBlockHeight:mnlHeight knownOperatorAddresses:operatorAddresses knownVotingAddresses:votingAddresses localMasternodes:localMasternodes]; + [simplifiedMasternodeEntryEntity updateAttributesFromSimplifiedMasternodeEntry:modified + atBlockHeight:mnlHeight + knownOperatorAddresses:operatorAddresses + knownVotingAddresses:votingAddresses + platformNodeAddresses:platformNodeAddresses + localMasternodes:localMasternodes + onChain:chain]; } - NSDictionary *> *quorums = masternodeList.quorums; - for (NSNumber *llmqType in quorums) { - NSDictionary *quorumsForMasternodeType = quorums[llmqType]; - for (NSData *quorumHash in quorumsForMasternodeType) { - DSQuorumEntry *potentialQuorumEntry = quorumsForMasternodeType[quorumHash]; - DSQuorumEntryEntity *entity = [DSQuorumEntryEntity quorumEntryEntityFromPotentialQuorumEntryForMerging:potentialQuorumEntry inContext:context]; + for (int i = 0; i < masternodeList->obj->quorums->count; i++) { + std_collections_Map_keys_u8_arr_32_values_dash_spv_crypto_llmq_entry_LLMQEntry *quorums_of_type = masternodeList->obj->quorums->values[i]; + for (int j = 0; j < quorums_of_type->count; j++) { +// u256 *llmq_hash = quorums_of_type->keys[j]; + DLLMQEntry *potential_entry = quorums_of_type->values[j]; + DSQuorumEntryEntity *entity = [DSQuorumEntryEntity quorumEntryEntityFromPotentialQuorumEntryForMerging:potential_entry inContext:context onChain:chain]; if (entity) { [masternodeListEntity addQuorumsObject:entity]; } @@ -624,168 +471,151 @@ + (void)saveMasternodeList:(DSMasternodeList *)masternodeList } chainEntity.baseBlockHash = mnlBlockHashData; DSLog(@"[%@] Finished merging MNL at height %u: %@", chain.name, mnlHeight, mnlBlockHashData.hexString); +// DSLog(@"[%@] MasternodeListEntity: %@", chain.name, masternodeListEntity.debugDescription); } else if (!error) { DSMasternodeListEntity *masternodeListEntity = [DSMasternodeListEntity managedObjectInBlockedContext:context]; masternodeListEntity.block = merkleBlockEntity; - masternodeListEntity.masternodeListMerkleRoot = uint256_data(masternodeList.masternodeMerkleRoot); - masternodeListEntity.quorumListMerkleRoot = uint256_data(masternodeList.quorumMerkleRoot); + UInt256 blockHash = u256_cast(masternodeList->obj->block_hash); + u256 *masternode_merkle_root = masternodeList->obj->masternode_merkle_root; + u256 *llmq_merkle_root = masternodeList->obj->llmq_merkle_root; + NSData *mnMerkleRoot, *llmqMerkleRoot; + if (masternode_merkle_root) { + UInt256 root = u256_cast(masternode_merkle_root); + if (uint256_is_zero(root)) { + mnMerkleRoot = NSDataFromPtr(dash_spv_masternode_processor_models_masternode_list_MasternodeList_calculate_masternodes_merkle_root(masternodeList->obj, [chain heightForBlockHash:blockHash])); + } else { + mnMerkleRoot = NSDataFromPtr(masternode_merkle_root); + } + + } else { + mnMerkleRoot = NSDataFromPtr(dash_spv_masternode_processor_models_masternode_list_MasternodeList_calculate_masternodes_merkle_root(masternodeList->obj, [chain heightForBlockHash:blockHash])); + } + if (llmq_merkle_root) { + UInt256 root = u256_cast(llmq_merkle_root); + if (uint256_is_zero(root)) { + llmqMerkleRoot = NSDataFromPtr(dash_spv_masternode_processor_models_masternode_list_MasternodeList_calculate_llmq_merkle_root(masternodeList->obj)); + if (!llmqMerkleRoot) llmqMerkleRoot = uint256_data(UINT256_ZERO); + } else { + llmqMerkleRoot = NSDataFromPtr(llmq_merkle_root); + } + + } else { + llmqMerkleRoot = NSDataFromPtr(dash_spv_masternode_processor_models_masternode_list_MasternodeList_calculate_llmq_merkle_root(masternodeList->obj)); + if (!llmqMerkleRoot) llmqMerkleRoot = uint256_data(UINT256_ZERO); + + } + + masternodeListEntity.masternodeListMerkleRoot = mnMerkleRoot; + masternodeListEntity.quorumListMerkleRoot = llmqMerkleRoot; NSArray *knownSimplifiedMasternodeEntryEntities = [DSSimplifiedMasternodeEntryEntity objectsInContext:context matching:@"chain == %@", chainEntity]; + DSLog(@"[%@] MNL knownSimplifiedMasternodeEntryEntities: [%lu] %@ --- %@", chain.name, knownSimplifiedMasternodeEntryEntities.count, mnMerkleRoot.hexString, llmqMerkleRoot.hexString); NSMutableDictionary *indexedKnownSimplifiedMasternodeEntryEntities = [NSMutableDictionary dictionary]; for (DSSimplifiedMasternodeEntryEntity *simplifiedMasternodeEntryEntity in knownSimplifiedMasternodeEntryEntities) { NSData *proRegTxHash = simplifiedMasternodeEntryEntity.providerRegistrationTransactionHash; +// DSLog(@"knownSimplifiedMasternodeEntry: indexed: %@", proRegTxHash.hexString); [indexedKnownSimplifiedMasternodeEntryEntities setObject:simplifiedMasternodeEntryEntity forKey:proRegTxHash]; } NSDictionary *indexedMasternodes = [indexedKnownSimplifiedMasternodeEntryEntities copy]; + NSMutableSet *votingAddressStrings = [NSMutableSet set]; NSMutableSet *operatorAddressStrings = [NSMutableSet set]; + NSMutableSet *platformNodeAddressStrings = [NSMutableSet set]; NSMutableSet *providerRegistrationTransactionHashes = [NSMutableSet set]; - NSArray *masternodes = masternodeList.simplifiedMasternodeEntries; - for (DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry in masternodes) { - [votingAddressStrings addObject:simplifiedMasternodeEntry.votingAddress]; - [operatorAddressStrings addObject:simplifiedMasternodeEntry.operatorAddress]; - [providerRegistrationTransactionHashes addObject:uint256_data(simplifiedMasternodeEntry.providerRegistrationTransactionHash)]; + + for (int i = 0; i < masternodeList->obj->masternodes->count; i++) { + DMasternodeEntry *entry = masternodeList->obj->masternodes->values[i]; + NSString *votingAddress = [DSKeyManager NSStringFrom:DMasternodeEntryVotingAddress(entry, chain.chainType)]; + NSString *operatorAddress = [DSKeyManager NSStringFrom:DMasternodeEntryOperatorPublicKeyAddress(entry, chain.chainType)]; + NSString *platformNodeAddress = [DSKeyManager NSStringFrom:DMasternodeEntryEvoNodeAddress(entry, chain.chainType)]; + NSData *proRegTxHash = NSDataFromPtr(entry->provider_registration_transaction_hash); + [votingAddressStrings addObject:votingAddress]; + [operatorAddressStrings addObject:operatorAddress]; + [platformNodeAddressStrings addObject:platformNodeAddress]; + [providerRegistrationTransactionHashes addObject:proRegTxHash]; } + //this is the initial list sync so lets speed things up a little bit with some optimizations NSDictionary *votingAddresses = [DSAddressEntity findAddressesAndIndexIn:votingAddressStrings onChain:(DSChain *)chain inContext:context]; - NSDictionary *operatorAddresses = [DSAddressEntity findAddressesAndIndexIn:votingAddressStrings onChain:(DSChain *)chain inContext:context]; + NSDictionary *operatorAddresses = [DSAddressEntity findAddressesAndIndexIn:operatorAddressStrings onChain:(DSChain *)chain inContext:context]; NSDictionary *localMasternodes = [DSLocalMasternodeEntity findLocalMasternodesAndIndexForProviderRegistrationHashes:providerRegistrationTransactionHashes inContext:context]; - NSAssert(masternodes, @"A masternode must have entries to be saved"); - for (DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry in masternodes) { - NSData *proRegTxHash = uint256_data(simplifiedMasternodeEntry.providerRegistrationTransactionHash); + NSDictionary *platformNodeAddresses = [DSAddressEntity findAddressesAndIndexIn:platformNodeAddressStrings onChain:(DSChain *)chain inContext:context]; + NSAssert(masternodeList->obj->masternodes, @"A masternode must have entries to be saved"); + for (int i = 0; i < masternodeList->obj->masternodes->count; i++) { + DMasternodeEntry *entry = masternodeList->obj->masternodes->values[i]; + NSData *proRegTxHash = NSDataFromPtr(entry->provider_registration_transaction_hash); + DSSimplifiedMasternodeEntryEntity *simplifiedMasternodeEntryEntity = [indexedMasternodes objectForKey:proRegTxHash]; +// DSLog(@"knownSimplifiedMasternodeEntry: MAYBE Indexed?: %@ = %@", proRegTxHash.hexString, simplifiedMasternodeEntryEntity.providerRegistrationTransactionHash.hexString); if (!simplifiedMasternodeEntryEntity) { simplifiedMasternodeEntryEntity = [DSSimplifiedMasternodeEntryEntity managedObjectInBlockedContext:context]; - [simplifiedMasternodeEntryEntity setAttributesFromSimplifiedMasternodeEntry:simplifiedMasternodeEntry atBlockHeight:mnlHeight knownOperatorAddresses:operatorAddresses knownVotingAddresses:votingAddresses localMasternodes:localMasternodes onChainEntity:chainEntity]; - } else if (simplifiedMasternodeEntry.updateHeight >= mnlHeight) { + [simplifiedMasternodeEntryEntity setAttributesFromSimplifiedMasternodeEntry:entry + atBlockHeight:mnlHeight + knownOperatorAddresses:operatorAddresses + knownVotingAddresses:votingAddresses + platformNodeAddresses:platformNodeAddresses + localMasternodes:localMasternodes + onChain:chain + onChainEntity:chainEntity]; + } else if (entry->update_height >= mnlHeight) { // it was updated in this masternode list - [simplifiedMasternodeEntryEntity updateAttributesFromSimplifiedMasternodeEntry:simplifiedMasternodeEntry atBlockHeight:mnlHeight knownOperatorAddresses:operatorAddresses knownVotingAddresses:votingAddresses localMasternodes:localMasternodes]; + [simplifiedMasternodeEntryEntity updateAttributesFromSimplifiedMasternodeEntry:entry + atBlockHeight:mnlHeight + knownOperatorAddresses:operatorAddresses + knownVotingAddresses:votingAddresses + platformNodeAddresses:platformNodeAddresses + localMasternodes:localMasternodes + onChain:chain]; } [masternodeListEntity addMasternodesObject:simplifiedMasternodeEntryEntity]; } - for (NSData *simplifiedMasternodeEntryHash in modifiedMasternodes) { - DSSimplifiedMasternodeEntry *simplifiedMasternodeEntry = modifiedMasternodes[simplifiedMasternodeEntryHash]; - NSData *proRegTxHash = uint256_data(simplifiedMasternodeEntry.providerRegistrationTransactionHash); + + for (int i = 0; i < modifiedMasternodes->count; i++) { + DMasternodeEntry *modified = modifiedMasternodes->values[i]; + NSData *proRegTxHash = NSDataFromPtr(modified->provider_registration_transaction_hash); DSSimplifiedMasternodeEntryEntity *simplifiedMasternodeEntryEntity = [indexedMasternodes objectForKey:proRegTxHash]; NSAssert(simplifiedMasternodeEntryEntity, @"this masternode must be present (%@)", proRegTxHash.hexString); - [simplifiedMasternodeEntryEntity updateAttributesFromSimplifiedMasternodeEntry:simplifiedMasternodeEntry atBlockHeight:mnlHeight knownOperatorAddresses:operatorAddresses knownVotingAddresses:votingAddresses localMasternodes:localMasternodes]; + [simplifiedMasternodeEntryEntity updateAttributesFromSimplifiedMasternodeEntry:modified + atBlockHeight:mnlHeight + knownOperatorAddresses:operatorAddresses + knownVotingAddresses:votingAddresses + platformNodeAddresses:platformNodeAddresses + localMasternodes:localMasternodes + onChain:chain]; } - NSDictionary *> *quorums = masternodeList.quorums; - for (NSNumber *llmqType in quorums) { - NSDictionary *quorumsForMasternodeType = quorums[llmqType]; - for (NSData *quorumHash in quorumsForMasternodeType) { - DSQuorumEntry *potentialQuorumEntry = quorumsForMasternodeType[quorumHash]; - DSQuorumEntryEntity *entity = [DSQuorumEntryEntity quorumEntryEntityFromPotentialQuorumEntry:potentialQuorumEntry inContext:context]; + for (int i = 0; i < masternodeList->obj->quorums->count; i++) { + std_collections_Map_keys_u8_arr_32_values_dash_spv_crypto_llmq_entry_LLMQEntry *quorums_of_type = masternodeList->obj->quorums->values[i]; + for (int j = 0; j < quorums_of_type->count; j++) { +// u256 *llmq_hash = quorums_of_type->keys[j]; + DLLMQEntry *potential_entry = quorums_of_type->values[j]; + DSQuorumEntryEntity *entity = [DSQuorumEntryEntity quorumEntryEntityFromPotentialQuorumEntryForMerging:potential_entry inContext:context onChain:chain]; if (entity) { [masternodeListEntity addQuorumsObject:entity]; } + } } chainEntity.baseBlockHash = mnlBlockHashData; DSLog(@"[%@] Finished saving MNL at height %u", chain.name, mnlHeight); - } else { +// DSLog(@"[%@] MasternodeListEntity: %@", chain.name, masternodeListEntity.debugDescription); + } else { + DSLog(@"[%@] Finished deleting MNL at height %u", chain.name, mnlHeight); chainEntity.baseBlockHash = uint256_data(chain.genesisHash); [DSLocalMasternodeEntity deleteAllOnChainEntity:chainEntity]; [DSSimplifiedMasternodeEntryEntity deleteAllOnChainEntity:chainEntity]; [DSQuorumEntryEntity deleteAllOnChainEntity:chainEntity]; } + DSLog(@"[%@] MNL at height %u (%@) --> SAVE", chain.name, masternodeList->obj->known_height, uint256_hex(mnlBlockHash)); [context ds_save]; - if (completion) { - completion(error); - } - + DSLog(@"[%@] MNL at height %u (%@) <-- SAVE", chain.name, masternodeList->obj->known_height, uint256_hex(mnlBlockHash)); + result = error; +// if (completion) { +// completion(error); +// } +// } }]; -} - -- (DSQuorumEntry *_Nullable)quorumEntryForPlatformHavingQuorumHash:(UInt256)quorumHash forBlockHeight:(uint32_t)blockHeight { - DSBlock *block = [self.chain blockAtHeightOrLastTerminal:blockHeight]; - return block ? [self quorumEntryForPlatformHavingQuorumHash:quorumHash forBlock:block] : nil; -} - -- (DSQuorumEntry *_Nullable)activeQuorumForTypeQuorumHash:(UInt256)quorumHash ofQuorumType:(LLMQType)quorumType { - for (DSQuorumEntry *quorumEntry in self.activeQuorums) { - if (uint256_eq(quorumEntry.quorumHash, quorumHash) && quorumEntry.llmqType == quorumType) { - return quorumEntry; - } - } - return nil; -} - - -- (DSQuorumEntry *)quorumEntryForPlatformHavingQuorumHash:(UInt256)quorumHash forBlock:(DSBlock *)block { - DSMasternodeList *masternodeList = [self masternodeListForBlockHash:block.blockHash withBlockHeightLookup:nil]; - if (!masternodeList) { - masternodeList = [self masternodeListBeforeBlockHash:block.blockHash]; - } - if (!masternodeList) { - DSLog(@"[%@] No masternode list found yet", self.chain.name); - return nil; - } - if (block.height - masternodeList.height > 32) { - DSLog(@"[%@] Masternode list is too old", self.chain.name); - return nil; - } - DSQuorumEntry *quorumEntry = [masternodeList quorumEntryForPlatformWithQuorumHash:quorumHash]; - if (quorumEntry == nil) { - quorumEntry = [self activeQuorumForTypeQuorumHash:quorumHash ofQuorumType:quorum_type_for_platform(self.chain.chainType)]; - } - if (quorumEntry == nil) { - quorumEntry = [self quorumEntryForPlatformHavingQuorumHash:quorumHash forBlockHeight:block.height - 1]; - } - return quorumEntry; -} - -- (DSQuorumEntry *)quorumEntryForLockRequestID:(UInt256)requestID - ofQuorumType:(LLMQType)quorumType - forMerkleBlock:(DSMerkleBlock *)merkleBlock - withExpirationOffset:(uint32_t)offset { - UInt256 blockHash = merkleBlock.blockHash; - DSQuorumEntry *activeQuorum = [self activeQuorumForTypeQuorumHash:blockHash ofQuorumType:quorumType]; - if (activeQuorum) { - return activeQuorum; - } - DSMasternodeList *masternodeList = [self masternodeListBeforeBlockHash:blockHash]; - if (!masternodeList) { - DSLog(@"[%@] No masternode list found yet", self.chain.name); - return nil; - } - if (merkleBlock.height - masternodeList.height > offset) { - DSLog(@"[%@] Masternode list for is too old (age: %d masternodeList height %d merkle block height %d)", self.chain.name, - merkleBlock.height - masternodeList.height, masternodeList.height, merkleBlock.height); - return nil; - } - - return [masternodeList quorumEntryForLockRequestID:requestID ofQuorumType:quorumType]; -} - -- (DSQuorumEntry *)quorumEntryForChainLockRequestID:(UInt256)requestID forMerkleBlock:(DSMerkleBlock *)merkleBlock { - return [self quorumEntryForLockRequestID:requestID - ofQuorumType:quorum_type_for_chain_locks(self.chain.chainType) - forMerkleBlock:merkleBlock - withExpirationOffset:24]; -} - -- (DSQuorumEntry *)quorumEntryForInstantSendRequestID:(UInt256)requestID forMerkleBlock:(DSMerkleBlock *)merkleBlock { - return [self quorumEntryForLockRequestID:requestID - ofQuorumType:quorum_type_for_is_locks(self.chain.chainType) - forMerkleBlock:merkleBlock - withExpirationOffset:32]; -} - -- (BOOL)addBlockToValidationQueue:(DSMerkleBlock *)merkleBlock { - UInt256 merkleBlockHash = merkleBlock.blockHash; - //DSLog(@"addBlockToValidationQueue: %u:%@", merkleBlock.height, uint256_hex(merkleBlockHash)); - NSData *merkleBlockHashData = uint256_data(merkleBlockHash); - if ([self hasMasternodeListAt:merkleBlockHashData]) { - DSLog(@"[%@] Already have that masternode list (or in stub) %u", self.chain.name, merkleBlock.height); - return NO; - } - self.lastQueriedBlockHash = merkleBlockHash; - @synchronized (self.masternodeListQueriesNeedingQuorumsValidated) { - [self.masternodeListQueriesNeedingQuorumsValidated addObject:merkleBlockHashData]; - } - return YES; + return result; } @end diff --git a/DashSync/shared/Models/Masternode/DSMasternodeProcessorContext.h b/DashSync/shared/Models/Masternode/DSMasternodeProcessorContext.h index 22d5c5743..c4d0af794 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeProcessorContext.h +++ b/DashSync/shared/Models/Masternode/DSMasternodeProcessorContext.h @@ -15,47 +15,47 @@ // limitations under the License. // -#import "BigIntTypes.h" -#import "DSChain.h" -#import "DSMasternodeList.h" -#import "DSQuorumSnapshot.h" -#import "DSPeer.h" -#import +//#import "BigIntTypes.h" +//#import "DSChain.h" +//#import "DSMasternodeList.h" +//#import "DSQuorumSnapshot.h" +//#import "DSPeer.h" +//#import NS_ASSUME_NONNULL_BEGIN -typedef DSMasternodeList *_Nullable(^_Nullable MasternodeListFinder)(UInt256 blockHash); -typedef UInt256(^_Nullable MerkleRootFinder)(UInt256 blockHash); -typedef DSMerkleBlock *_Nullable(^_Nullable MerkleBlockFinder)(UInt256 blockHash); +//typedef DSMasternodeList *_Nullable(^_Nullable MasternodeListFinder)(UInt256 blockHash); +//typedef UInt256(^_Nullable MerkleRootFinder)(UInt256 blockHash); +//typedef DSMerkleBlock *_Nullable(^_Nullable MerkleBlockFinder)(UInt256 blockHash); -@interface DSMasternodeProcessorContext : NSObject - -@property (nonatomic) DSChain *chain; -@property (nonatomic, nullable) DSPeer *peer; -@property (nonatomic) BOOL useInsightAsBackup; -@property (nonatomic) BOOL isFromSnapshot; -@property (nonatomic) BOOL isDIP0024; -@property (nonatomic, copy) MasternodeListFinder masternodeListLookup; -@property (nonatomic, copy) BlockHeightFinder blockHeightLookup; -@property (nonatomic, copy) MerkleRootFinder merkleRootLookup; - - -- (uint32_t)blockHeightForBlockHash:(UInt256)blockHash; -- (UInt256)merkleRootForBlockHash:(UInt256)blockHash; -- (DSBlock *_Nullable)blockForBlockHeight:(uint32_t)blockHeight; -- (NSData *_Nullable)CLSignatureForBlockHash:(UInt256)blockHash; -- (DSQuorumSnapshot *_Nullable)quorumSnapshotForBlockHash:(UInt256)blockHash; -- (DSMasternodeList *_Nullable)masternodeListForBlockHash:(UInt256)blockHash; - -- (BOOL)saveCLSignature:(UInt256)blockHash signature:(UInt768)signature; -- (BOOL)saveQuorumSnapshot:(DSQuorumSnapshot *)snapshot; -- (BOOL)saveMasternodeList:(DSMasternodeList *)masternodeList forBlockHash:(UInt256)blockHash; - - - -- (void)blockUntilGetInsightForBlockHash:(UInt256)blockHash; -- (ProcessingError)shouldProcessDiffWithRange:(UInt256)baseBlockHash blockHash:(UInt256)blockHash; - -@end +//@interface DSMasternodeProcessorContext : NSObject +// +//@property (nonatomic) DSChain *chain; +//@property (nonatomic, nullable) DSPeer *peer; +//@property (nonatomic) BOOL useInsightAsBackup; +//@property (nonatomic) BOOL isFromSnapshot; +//@property (nonatomic) BOOL isDIP0024; +//@property (nonatomic, copy) MasternodeListFinder masternodeListLookup; +//@property (nonatomic, copy) BlockHeightFinder blockHeightLookup; +//@property (nonatomic, copy) MerkleRootFinder merkleRootLookup; +// +// +//- (uint32_t)blockHeightForBlockHash:(UInt256)blockHash; +//- (UInt256)merkleRootForBlockHash:(UInt256)blockHash; +//- (DSBlock *_Nullable)blockForBlockHeight:(uint32_t)blockHeight; +//- (NSData *_Nullable)CLSignatureForBlockHash:(UInt256)blockHash; +//- (DSQuorumSnapshot *_Nullable)quorumSnapshotForBlockHash:(UInt256)blockHash; +//- (DSMasternodeList *_Nullable)masternodeListForBlockHash:(UInt256)blockHash; +// +//- (BOOL)saveCLSignature:(UInt256)blockHash signature:(UInt768)signature; +//- (BOOL)saveQuorumSnapshot:(DSQuorumSnapshot *)snapshot; +//- (BOOL)saveMasternodeList:(DSMasternodeList *)masternodeList forBlockHash:(UInt256)blockHash; +// +// +// +//- (void)blockUntilGetInsightForBlockHash:(UInt256)blockHash; +//- (ProcessingError)shouldProcessDiffWithRange:(UInt256)baseBlockHash blockHash:(UInt256)blockHash; +// +//@end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Masternode/DSMasternodeProcessorContext.m b/DashSync/shared/Models/Masternode/DSMasternodeProcessorContext.m index 1e53060b1..4529911a2 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeProcessorContext.m +++ b/DashSync/shared/Models/Masternode/DSMasternodeProcessorContext.m @@ -14,93 +14,93 @@ // See the License for the specific language governing permissions and // limitations under the License. // - -#import "DSMasternodeProcessorContext.h" -#import "NSData+Dash.h" -#import "DSChain+Protected.h" -#import "DSChainManager.h" -#import "DSMasternodeManager.h" - -@implementation DSMasternodeProcessorContext - -- (uint32_t)blockHeightForBlockHash:(UInt256)blockHash { - return self.blockHeightLookup(blockHash); -} - -- (DSMerkleBlock *_Nullable)blockForBlockHeight:(uint32_t)blockHeight { - return [self.chain blockAtHeight:blockHeight]; - -} -- (UInt256)merkleRootForBlockHash:(UInt256)blockHash { - return self.merkleRootLookup(blockHash); -} - -- (NSData *_Nullable)CLSignatureForBlockHash:(UInt256)blockHash { - return [self.chain.chainManager.masternodeManager CLSignatureForBlockHash:blockHash]; -} - -- (DSQuorumSnapshot *_Nullable)quorumSnapshotForBlockHash:(UInt256)blockHash { - return [self.chain.chainManager.masternodeManager quorumSnapshotForBlockHash:blockHash]; -} - -- (DSMasternodeList *_Nullable)masternodeListForBlockHash:(UInt256)blockHash { - return self.masternodeListLookup(blockHash); -} - -- (BOOL)saveCLSignature:(UInt256)blockHash signature:(UInt768)signature { - return [self.chain.chainManager.masternodeManager saveCLSignature:uint256_data(blockHash) signatureData:uint768_data(signature)]; -} - -- (BOOL)saveQuorumSnapshot:(DSQuorumSnapshot *)snapshot { - return [self.chain.chainManager.masternodeManager saveQuorumSnapshot:snapshot]; -} - -- (BOOL)saveMasternodeList:(DSMasternodeList *)masternodeList forBlockHash:(UInt256)blockHash { - return [self.chain.chainManager.masternodeManager saveMasternodeList:masternodeList forBlockHash:blockHash]; -} - -- (void)blockUntilGetInsightForBlockHash:(UInt256)blockHash { - [self.chain blockUntilGetInsightForBlockHash:blockHash]; -} - -- (NSString *)description { - return [[super description] stringByAppendingString:[NSString stringWithFormat:@" {%@}: [%@: %@ (%u)] genesis: %@ protocol: %u, insight: %i, from_snapshot: %i, dip-24: %i}", self.chain.name, self.peer.location, self.peer.useragent, self.peer.version, uint256_hex(self.chain.genesisHash), self.chain.protocolVersion, self.useInsightAsBackup, self.isFromSnapshot, self.isDIP0024]]; -} - -- (ProcessingError)shouldProcessDiffWithRange:(UInt256)baseBlockHash blockHash:(UInt256)blockHash { - uint32_t baseBlockHeight = [self blockHeightForBlockHash:baseBlockHash]; - uint32_t blockHeight = [self blockHeightForBlockHash:blockHash]; - if (blockHeight == UINT32_MAX) { - DSLog(@"•••• shouldProcessDiffWithRange: unknown blockHash: %u..%u %@ .. %@", baseBlockHeight, blockHeight, uint256_reverse_hex(baseBlockHash), uint256_reverse_hex(blockHash)); - return ProcessingError_UnknownBlockHash; - } - DSChain *chain = self.chain; - DSMasternodeManager *manager = chain.chainManager.masternodeManager; - DSMasternodeListService *service = self.isDIP0024 ? manager.quorumRotationService : manager.masternodeListDiffService; - BOOL hasRemovedFromRetrieval = [service removeRequestInRetrievalForBaseBlockHash:baseBlockHash blockHash:blockHash]; - if (!hasRemovedFromRetrieval) { - DSLog(@"•••• shouldProcessDiffWithRange: persist in retrieval: %u..%u %@ .. %@", baseBlockHeight, blockHeight, uint256_reverse_hex(baseBlockHash), uint256_reverse_hex(blockHash)); - return ProcessingError_PersistInRetrieval; - } - NSData *blockHashData = uint256_data(blockHash); - DSMasternodeList *list = self.masternodeListLookup(blockHash); - BOOL needToVerifyRotatedQuorums = self.isDIP0024 && (!manager.quorumRotationService.masternodeListAtH || [manager.quorumRotationService.masternodeListAtH hasUnverifiedRotatedQuorums]); - BOOL needToVerifyNonRotatedQuorums = !self.isDIP0024 && [list hasUnverifiedNonRotatedQuorums]; - BOOL noNeedToVerifyQuorums = !(needToVerifyRotatedQuorums || needToVerifyNonRotatedQuorums); - BOOL hasLocallyStored = [manager.store hasMasternodeListAt:blockHashData]; - if (hasLocallyStored && noNeedToVerifyQuorums) { - DSLog(@"•••• shouldProcessDiffWithRange: already persist: %u: %@ needToVerifyRotatedQuorums: %d needToVerifyNonRotatedQuorums: %d", blockHeight, uint256_reverse_hex(blockHash), needToVerifyRotatedQuorums, needToVerifyNonRotatedQuorums); - [service removeFromRetrievalQueue:blockHashData]; - return ProcessingError_LocallyStored; - } - DSMasternodeList *baseMasternodeList = self.masternodeListLookup(baseBlockHash); - if (!baseMasternodeList && !uint256_eq(chain.genesisHash, baseBlockHash) && uint256_is_not_zero(baseBlockHash)) { - // this could have been deleted in the meantime, if so rerequest - [service issueWithMasternodeListFromPeer:self.peer]; - DSLog(@"•••• No base masternode list at: %d: %@", baseBlockHeight, uint256_reverse_hex(baseBlockHash)); - return ProcessingError_HasNoBaseBlockHash; - } - return ProcessingError_None; -} - -@end +// +//#import "DSMasternodeProcessorContext.h" +//#import "NSData+Dash.h" +//#import "DSChain+Protected.h" +//#import "DSChainManager.h" +//#import "DSMasternodeManager.h" +// +//@implementation DSMasternodeProcessorContext +// +//- (uint32_t)blockHeightForBlockHash:(UInt256)blockHash { +// return self.blockHeightLookup(blockHash); +//} +// +//- (DSMerkleBlock *_Nullable)blockForBlockHeight:(uint32_t)blockHeight { +// return [self.chain blockAtHeight:blockHeight]; +// +//} +//- (UInt256)merkleRootForBlockHash:(UInt256)blockHash { +// return self.merkleRootLookup(blockHash); +//} +// +//- (NSData *_Nullable)CLSignatureForBlockHash:(UInt256)blockHash { +// return [self.chain.chainManager.masternodeManager CLSignatureForBlockHash:blockHash]; +//} +// +//- (DSQuorumSnapshot *_Nullable)quorumSnapshotForBlockHash:(UInt256)blockHash { +// return [self.chain.chainManager.masternodeManager quorumSnapshotForBlockHash:blockHash]; +//} +// +//- (DSMasternodeList *_Nullable)masternodeListForBlockHash:(UInt256)blockHash { +// return self.masternodeListLookup(blockHash); +//} +// +//- (BOOL)saveCLSignature:(UInt256)blockHash signature:(UInt768)signature { +// return [self.chain.chainManager.masternodeManager saveCLSignature:uint256_data(blockHash) signatureData:uint768_data(signature)]; +//} +// +//- (BOOL)saveQuorumSnapshot:(DSQuorumSnapshot *)snapshot { +// return [self.chain.chainManager.masternodeManager saveQuorumSnapshot:snapshot]; +//} +// +//- (BOOL)saveMasternodeList:(DSMasternodeList *)masternodeList forBlockHash:(UInt256)blockHash { +// return [self.chain.chainManager.masternodeManager saveMasternodeList:masternodeList forBlockHash:blockHash]; +//} +// +//- (void)blockUntilGetInsightForBlockHash:(UInt256)blockHash { +// [self.chain blockUntilGetInsightForBlockHash:blockHash]; +//} +// +//- (NSString *)description { +// return [[super description] stringByAppendingString:[NSString stringWithFormat:@" {%@}: [%@: %@ (%u)] genesis: %@ protocol: %u, insight: %i, from_snapshot: %i, dip-24: %i}", self.chain.name, self.peer.location, self.peer.useragent, self.peer.version, uint256_hex(self.chain.genesisHash), self.chain.protocolVersion, self.useInsightAsBackup, self.isFromSnapshot, self.isDIP0024]]; +//} +// +//- (ProcessingError)shouldProcessDiffWithRange:(UInt256)baseBlockHash blockHash:(UInt256)blockHash { +// uint32_t baseBlockHeight = [self blockHeightForBlockHash:baseBlockHash]; +// uint32_t blockHeight = [self blockHeightForBlockHash:blockHash]; +// if (blockHeight == UINT32_MAX) { +// DSLog(@"•••• shouldProcessDiffWithRange: unknown blockHash: %u..%u %@ .. %@", baseBlockHeight, blockHeight, uint256_reverse_hex(baseBlockHash), uint256_reverse_hex(blockHash)); +// return ProcessingError_UnknownBlockHash; +// } +// DSChain *chain = self.chain; +// DSMasternodeManager *manager = chain.chainManager.masternodeManager; +// DSMasternodeListService *service = self.isDIP0024 ? manager.quorumRotationService : manager.masternodeListDiffService; +// BOOL hasRemovedFromRetrieval = [service removeRequestInRetrievalForBaseBlockHash:baseBlockHash blockHash:blockHash]; +// if (!hasRemovedFromRetrieval) { +// DSLog(@"•••• shouldProcessDiffWithRange: persist in retrieval: %u..%u %@ .. %@", baseBlockHeight, blockHeight, uint256_reverse_hex(baseBlockHash), uint256_reverse_hex(blockHash)); +// return ProcessingError_PersistInRetrieval; +// } +// NSData *blockHashData = uint256_data(blockHash); +// DSMasternodeList *list = self.masternodeListLookup(blockHash); +// BOOL needToVerifyRotatedQuorums = self.isDIP0024 && (!manager.quorumRotationService.masternodeListAtH || [manager.quorumRotationService.masternodeListAtH hasUnverifiedRotatedQuorums]); +// BOOL needToVerifyNonRotatedQuorums = !self.isDIP0024 && [list hasUnverifiedNonRotatedQuorums]; +// BOOL noNeedToVerifyQuorums = !(needToVerifyRotatedQuorums || needToVerifyNonRotatedQuorums); +// BOOL hasLocallyStored = [manager.store hasMasternodeListAt:blockHashData]; +// if (hasLocallyStored && noNeedToVerifyQuorums) { +// DSLog(@"•••• shouldProcessDiffWithRange: already persist: %u: %@ needToVerifyRotatedQuorums: %d needToVerifyNonRotatedQuorums: %d", blockHeight, uint256_reverse_hex(blockHash), needToVerifyRotatedQuorums, needToVerifyNonRotatedQuorums); +// [service removeFromRetrievalQueue:blockHashData]; +// return ProcessingError_LocallyStored; +// } +// DSMasternodeList *baseMasternodeList = self.masternodeListLookup(baseBlockHash); +// if (!baseMasternodeList && !uint256_eq(chain.genesisHash, baseBlockHash) && uint256_is_not_zero(baseBlockHash)) { +// // this could have been deleted in the meantime, if so rerequest +// [service issueWithMasternodeListFromPeer:self.peer]; +// DSLog(@"•••• No base masternode list at: %d: %@", baseBlockHeight, uint256_reverse_hex(baseBlockHash)); +// return ProcessingError_HasNoBaseBlockHash; +// } +// return ProcessingError_None; +//} +// +//@end diff --git a/DashSync/shared/Models/Masternode/DSMnDiffProcessingResult.h b/DashSync/shared/Models/Masternode/DSMnDiffProcessingResult.h index 43b097dea..3fb589a7a 100644 --- a/DashSync/shared/Models/Masternode/DSMnDiffProcessingResult.h +++ b/DashSync/shared/Models/Masternode/DSMnDiffProcessingResult.h @@ -15,36 +15,36 @@ // limitations under the License. // -#import "DSMasternodeList.h" -#import "dash_shared_core.h" +//#import "DSMasternodeList.h" +//#import "dash_shared_core.h" #import NS_ASSUME_NONNULL_BEGIN -@interface DSMnDiffProcessingResult : NSObject - -@property (nonatomic) uint8_t errorStatus; -@property (nonatomic) UInt256 baseBlockHash; -@property (nonatomic) UInt256 blockHash; -@property (nonatomic) BOOL foundCoinbase; -@property (nonatomic) BOOL validCoinbase; -@property (nonatomic) BOOL rootMNListValid; -@property (nonatomic) BOOL rootQuorumListValid; -@property (nonatomic) BOOL validQuorums; -@property (nonatomic) DSMasternodeList *masternodeList; -@property (nonatomic) NSDictionary *addedMasternodes; -@property (nonatomic) NSDictionary *modifiedMasternodes; -@property (nonatomic) NSArray *addedQuorums; -@property (nonatomic) NSOrderedSet *neededMissingMasternodeLists; -/// -@property (nonatomic) NSDictionary *clSignatures; - -+ (instancetype)processingResultWith:(MNListDiffResult *)result onChain:(DSChain *)chain; - -- (BOOL)isValid; -- (BOOL)isTotallyValid; -- (BOOL)hasRotatedQuorumsForChain:(DSChain *)chain; - -@end +//@interface DSMnDiffProcessingResult : NSObject +// +//@property (nonatomic) uint8_t errorStatus; +//@property (nonatomic) UInt256 baseBlockHash; +//@property (nonatomic) UInt256 blockHash; +//@property (nonatomic) BOOL foundCoinbase; +//@property (nonatomic) BOOL validCoinbase; +//@property (nonatomic) BOOL rootMNListValid; +//@property (nonatomic) BOOL rootQuorumListValid; +//@property (nonatomic) BOOL validQuorums; +//@property (nonatomic) DSMasternodeList *masternodeList; +//@property (nonatomic) NSDictionary *addedMasternodes; +//@property (nonatomic) NSDictionary *modifiedMasternodes; +//@property (nonatomic) NSArray *addedQuorums; +//@property (nonatomic) NSOrderedSet *neededMissingMasternodeLists; +///// +//@property (nonatomic) NSDictionary *clSignatures; +// +////+ (instancetype)processingResultWith:(struct dash_spv_masternode_processor_processing_mn_listdiff_result_MNListDiffResult *)result onChain:(DSChain *)chain; +// +//- (BOOL)isValid; +//- (BOOL)isTotallyValid; +////- (BOOL)hasRotatedQuorumsForChain:(DSChain *)chain; +// +//@end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Masternode/DSMnDiffProcessingResult.m b/DashSync/shared/Models/Masternode/DSMnDiffProcessingResult.m index 60b2d443e..07cf2afb9 100644 --- a/DashSync/shared/Models/Masternode/DSMnDiffProcessingResult.m +++ b/DashSync/shared/Models/Masternode/DSMnDiffProcessingResult.m @@ -15,84 +15,84 @@ // limitations under the License. // -#import "NSData+Dash.h" -#import "DSMnDiffProcessingResult.h" -#import "DSMasternodeList+Mndiff.h" -#import "DSQuorumEntry+Mndiff.h" -#import "DSSimplifiedMasternodeEntry+Mndiff.h" +//#import "NSData+Dash.h" +//#import "DSMnDiffProcessingResult.h" +//#import "DSMasternodeList+Mndiff.h" +//#import "DSQuorumEntry+Mndiff.h" +//#import "DSSimplifiedMasternodeEntry+Mndiff.h" -@implementation DSMnDiffProcessingResult +//@implementation DSMnDiffProcessingResult -+ (instancetype)processingResultWith:(MNListDiffResult *)result onChain:(DSChain *)chain { - DSMnDiffProcessingResult *processingResult = [[DSMnDiffProcessingResult alloc] init]; - uint8_t errorStatus = result->error_status; - processingResult.errorStatus = errorStatus; - if (errorStatus > 0) { - return processingResult; - } - if (result->masternode_list == NULL) { - DSLog(@"[%@] DSQRInfoProcessingResult.error.unknown", chain.name); - processingResult.errorStatus = ProcessingError_ParseError; - return processingResult; - } - - [processingResult setErrorStatus:errorStatus]; - [processingResult setBaseBlockHash:*(UInt256 *)result->base_block_hash]; - [processingResult setBlockHash:*(UInt256 *)result->block_hash]; - [processingResult setFoundCoinbase:result->has_found_coinbase]; - [processingResult setValidCoinbase:result->has_valid_coinbase]; - [processingResult setRootMNListValid:result->has_valid_mn_list_root]; - [processingResult setRootQuorumListValid:result->has_valid_llmq_list_root]; - [processingResult setValidQuorums:result->has_valid_quorums]; - MasternodeList *result_masternode_list = result->masternode_list; - [processingResult setMasternodeList:[DSMasternodeList masternodeListWith:result_masternode_list onChain:chain]]; - NSDictionary *addedMasternodes = [DSSimplifiedMasternodeEntry simplifiedEntriesWith:result->added_masternodes count:result->added_masternodes_count onChain:chain]; - [processingResult setAddedMasternodes:addedMasternodes]; - NSDictionary *modifiedMasternodes = [DSSimplifiedMasternodeEntry simplifiedEntriesWith:result->modified_masternodes count:result->modified_masternodes_count onChain:chain]; - [processingResult setModifiedMasternodes:modifiedMasternodes]; - NSArray *addedQuorums = [DSQuorumEntry entriesWith:result->added_quorums count:result->added_quorums_count onChain:chain]; - [processingResult setAddedQuorums:addedQuorums]; - uint8_t(**needed_masternode_lists)[32] = result->needed_masternode_lists; - uintptr_t needed_masternode_lists_count = result->needed_masternode_lists_count; - NSMutableOrderedSet *neededMissingMasternodeLists = [NSMutableOrderedSet orderedSetWithCapacity:needed_masternode_lists_count]; - for (NSUInteger i = 0; i < needed_masternode_lists_count; i++) { - NSData *hash = [NSData dataWithBytes:needed_masternode_lists[i] length:32]; - [neededMissingMasternodeLists addObject:hash]; - } - [processingResult setNeededMissingMasternodeLists:[neededMissingMasternodeLists copy]]; - uint8_t (**quorums_cl_signatures_hashes)[32] = result->quorums_cl_signatures_hashes; - uint8_t (**quorums_cl_signatures)[96] = result->quorums_cl_signatures; - uintptr_t quorums_cl_sigs_count = result->quorums_cl_sigs_count; - NSMutableDictionary *clSignatures = [NSMutableDictionary dictionaryWithCapacity:quorums_cl_sigs_count]; - for (NSUInteger i = 0; i < quorums_cl_sigs_count; i++) { - [clSignatures setObject:uint768_data(*(UInt768 *)quorums_cl_signatures[i]) - forKey:uint256_data(*(UInt256 *)quorums_cl_signatures_hashes[i])]; - } - [processingResult setClSignatures:clSignatures]; - return processingResult; -} - -- (BOOL)isValid { -// return self.foundCoinbase && self.validQuorums && self.rootMNListValid && self.rootQuorumListValid; - return self.foundCoinbase && self.rootQuorumListValid; -} -- (BOOL)isTotallyValid { - return [self isValid] && self.validCoinbase; -} - -- (BOOL)hasRotatedQuorumsForChain:(DSChain*)chain { - return [[self.addedQuorums indexesOfObjectsPassingTest:^BOOL(DSQuorumEntry * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { - // TODO: make it more reliable as quorum type values may change - return obj.llmqType == quorum_type_for_isd_locks(chain.chainType) && (*stop = TRUE); - }] count] > 0; -} - -- (NSString *)debugDescription { - return [NSString stringWithFormat:@"•-• %@: {\nvalidity: %d%d%d%d%d, \nmasternodeList: %@, \nmasternodes: [added:%lu, modified:%lu], \nquorums: [added: %lu],\nneeded: [%@]}", - [super debugDescription], self.foundCoinbase, self.validCoinbase, self.rootMNListValid, self.rootQuorumListValid, self.validQuorums, - self.masternodeList, self.addedMasternodes.count, self.modifiedMasternodes.count, self.addedQuorums.count, self.neededMissingMasternodeLists - - ]; -} +//+ (instancetype)processingResultWith:(struct dash_spv_masternode_processor_processing_mn_listdiff_result_MNListDiffResult *)result onChain:(DSChain *)chain { +// DSMnDiffProcessingResult *processingResult = [[DSMnDiffProcessingResult alloc] init]; +// uint8_t errorStatus = result->error_status; +// processingResult.errorStatus = errorStatus; +// if (errorStatus > 0) { +// return processingResult; +// } +// if (result->masternode_list == NULL) { +// DSLog(@"[%@] DSQRInfoProcessingResult.error.unknown", chain.name); +// processingResult.errorStatus = ProcessingError_ParseError; +// return processingResult; +// } +// +// [processingResult setErrorStatus:errorStatus]; +// [processingResult setBaseBlockHash:*(UInt256 *)result->base_block_hash]; +// [processingResult setBlockHash:*(UInt256 *)result->block_hash]; +// [processingResult setFoundCoinbase:result->has_found_coinbase]; +// [processingResult setValidCoinbase:result->has_valid_coinbase]; +// [processingResult setRootMNListValid:result->has_valid_mn_list_root]; +// [processingResult setRootQuorumListValid:result->has_valid_llmq_list_root]; +// [processingResult setValidQuorums:result->has_valid_quorums]; +// MasternodeList *result_masternode_list = result->masternode_list; +// [processingResult setMasternodeList:[DSMasternodeList masternodeListWith:result_masternode_list onChain:chain]]; +// NSDictionary *addedMasternodes = [DSSimplifiedMasternodeEntry simplifiedEntriesWith:result->added_masternodes count:result->added_masternodes_count onChain:chain]; +// [processingResult setAddedMasternodes:addedMasternodes]; +// NSDictionary *modifiedMasternodes = [DSSimplifiedMasternodeEntry simplifiedEntriesWith:result->modified_masternodes count:result->modified_masternodes_count onChain:chain]; +// [processingResult setModifiedMasternodes:modifiedMasternodes]; +// NSArray *addedQuorums = [DSQuorumEntry entriesWith:result->added_quorums count:result->added_quorums_count onChain:chain]; +// [processingResult setAddedQuorums:addedQuorums]; +// uint8_t(**needed_masternode_lists)[32] = result->needed_masternode_lists; +// uintptr_t needed_masternode_lists_count = result->needed_masternode_lists_count; +// NSMutableOrderedSet *neededMissingMasternodeLists = [NSMutableOrderedSet orderedSetWithCapacity:needed_masternode_lists_count]; +// for (NSUInteger i = 0; i < needed_masternode_lists_count; i++) { +// NSData *hash = [NSData dataWithBytes:needed_masternode_lists[i] length:32]; +// [neededMissingMasternodeLists addObject:hash]; +// } +// [processingResult setNeededMissingMasternodeLists:[neededMissingMasternodeLists copy]]; +// uint8_t (**quorums_cl_signatures_hashes)[32] = result->quorums_cl_signatures_hashes; +// uint8_t (**quorums_cl_signatures)[96] = result->quorums_cl_signatures; +// uintptr_t quorums_cl_sigs_count = result->quorums_cl_sigs_count; +// NSMutableDictionary *clSignatures = [NSMutableDictionary dictionaryWithCapacity:quorums_cl_sigs_count]; +// for (NSUInteger i = 0; i < quorums_cl_sigs_count; i++) { +// [clSignatures setObject:uint768_data(*(UInt768 *)quorums_cl_signatures[i]) +// forKey:uint256_data(*(UInt256 *)quorums_cl_signatures_hashes[i])]; +// } +// [processingResult setClSignatures:clSignatures]; +// return processingResult; +//} -@end +//- (BOOL)isValid { +//// return self.foundCoinbase && self.validQuorums && self.rootMNListValid && self.rootQuorumListValid; +// return self.foundCoinbase && self.rootQuorumListValid; +//} +//- (BOOL)isTotallyValid { +// return [self isValid] && self.validCoinbase; +//} +// +////- (BOOL)hasRotatedQuorumsForChain:(DSChain*)chain { +//// return [[self.addedQuorums indexesOfObjectsPassingTest:^BOOL(DSQuorumEntry * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { +//// // TODO: make it more reliable as quorum type values may change +//// return obj.llmqType == quorum_type_for_isd_locks(chain.chainType) && (*stop = TRUE); +//// }] count] > 0; +////} +// +//- (NSString *)debugDescription { +// return [NSString stringWithFormat:@"•-• %@: {\nvalidity: %d%d%d%d%d, \nmasternodeList: %@, \nmasternodes: [added:%lu, modified:%lu], \nquorums: [added: %lu],\nneeded: [%@]}", +// [super debugDescription], self.foundCoinbase, self.validCoinbase, self.rootMNListValid, self.rootQuorumListValid, self.validQuorums, +// self.masternodeList, self.addedMasternodes.count, self.modifiedMasternodes.count, self.addedQuorums.count, self.neededMissingMasternodeLists +// +// ]; +//} +// +//@end diff --git a/DashSync/shared/Models/Masternode/DSQRInfoProcessingResult.h b/DashSync/shared/Models/Masternode/DSQRInfoProcessingResult.h index 8d67dfe0e..d3f9764bd 100644 --- a/DashSync/shared/Models/Masternode/DSQRInfoProcessingResult.h +++ b/DashSync/shared/Models/Masternode/DSQRInfoProcessingResult.h @@ -15,37 +15,37 @@ // limitations under the License. // -#import "DSChain.h" -#import "DSMnDiffProcessingResult.h" -#import "DSQuorumSnapshot+Mndiff.h" -#import "dash_shared_core.h" -#import +//#import "DSChain.h" +//#import "DSMnDiffProcessingResult.h" +//#import "DSQuorumSnapshot+Mndiff.h" +//#import "dash_shared_core.h" +//#import NS_ASSUME_NONNULL_BEGIN -@interface DSQRInfoProcessingResult : NSObject - -@property (nonatomic) uint8_t errorStatus; -@property (nonatomic) DSQuorumSnapshot *snapshotAtHC; -@property (nonatomic) DSQuorumSnapshot *snapshotAtH2C; -@property (nonatomic) DSQuorumSnapshot *snapshotAtH3C; -@property (nonatomic) DSQuorumSnapshot *_Nullable snapshotAtH4C; - -@property (nonatomic) DSMnDiffProcessingResult *mnListDiffResultAtTip; -@property (nonatomic) DSMnDiffProcessingResult *mnListDiffResultAtH; -@property (nonatomic) DSMnDiffProcessingResult *mnListDiffResultAtHC; -@property (nonatomic) DSMnDiffProcessingResult *mnListDiffResultAtH2C; -@property (nonatomic) DSMnDiffProcessingResult *mnListDiffResultAtH3C; -@property (nonatomic) DSMnDiffProcessingResult *_Nullable mnListDiffResultAtH4C; - -@property (nonatomic) BOOL extraShare; - -@property (nonatomic) NSOrderedSet *lastQuorumPerIndex; -@property (nonatomic) NSOrderedSet *snapshotList; -@property (nonatomic) NSOrderedSet *mnListDiffList; - -+ (instancetype)processingResultWith:(QRInfoResult *)result onChain:(DSChain *)chain; - -@end - +//@interface DSQRInfoProcessingResult : NSObject +// +//@property (nonatomic) uint8_t errorStatus; +//@property (nonatomic) DSQuorumSnapshot *snapshotAtHC; +//@property (nonatomic) DSQuorumSnapshot *snapshotAtH2C; +//@property (nonatomic) DSQuorumSnapshot *snapshotAtH3C; +//@property (nonatomic) DSQuorumSnapshot *_Nullable snapshotAtH4C; +// +//@property (nonatomic) DSMnDiffProcessingResult *mnListDiffResultAtTip; +//@property (nonatomic) DSMnDiffProcessingResult *mnListDiffResultAtH; +//@property (nonatomic) DSMnDiffProcessingResult *mnListDiffResultAtHC; +//@property (nonatomic) DSMnDiffProcessingResult *mnListDiffResultAtH2C; +//@property (nonatomic) DSMnDiffProcessingResult *mnListDiffResultAtH3C; +//@property (nonatomic) DSMnDiffProcessingResult *_Nullable mnListDiffResultAtH4C; +// +//@property (nonatomic) BOOL extraShare; +// +////@property (nonatomic) NSOrderedSet *lastQuorumPerIndex; +//@property (nonatomic) NSOrderedSet *snapshotList; +//@property (nonatomic) NSOrderedSet *mnListDiffList; +// +////+ (instancetype)processingResultWith:(QRInfoResult *)result onChain:(DSChain *)chain; +// +//@end +// NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Masternode/DSQRInfoProcessingResult.m b/DashSync/shared/Models/Masternode/DSQRInfoProcessingResult.m index b96d5d120..46ebd930a 100644 --- a/DashSync/shared/Models/Masternode/DSQRInfoProcessingResult.m +++ b/DashSync/shared/Models/Masternode/DSQRInfoProcessingResult.m @@ -15,81 +15,81 @@ // limitations under the License. // -#import "DSQRInfoProcessingResult.h" - -@implementation DSQRInfoProcessingResult - -+ (instancetype)processingResultWith:(QRInfoResult *)result onChain:(DSChain *)chain { - DSQRInfoProcessingResult *processingResult = [[DSQRInfoProcessingResult alloc] init]; - uint8_t errorStatus = result->error_status; - processingResult.errorStatus = errorStatus; - if (errorStatus > 0) { - DSLog(@"[%@] DSQRInfoProcessingResult.error %ul", chain.name, errorStatus); - return processingResult; - } - if (result->result_at_tip == NULL) { - DSLog(@"[%@] DSQRInfoProcessingResult.error.unknown", chain.name); - processingResult.errorStatus = ProcessingError_ParseError; - return processingResult; - } - MNListDiffResult *diffResultAtHC = result->result_at_h_c; - MNListDiffResult *diffResultAtH2C = result->result_at_h_2c; - MNListDiffResult *diffResultAtH3C = result->result_at_h_3c; - MNListDiffResult *diffResultAtH4C = result->result_at_h_4c; - processingResult.snapshotAtHC = [DSQuorumSnapshot quorumSnapshotWith:result->snapshot_at_h_c forBlockHash:*((UInt256 *)diffResultAtHC->block_hash)]; - processingResult.snapshotAtH2C = [DSQuorumSnapshot quorumSnapshotWith:result->snapshot_at_h_2c forBlockHash:*((UInt256 *)diffResultAtH2C->block_hash)]; - processingResult.snapshotAtH3C = [DSQuorumSnapshot quorumSnapshotWith:result->snapshot_at_h_3c forBlockHash:*((UInt256 *)diffResultAtH3C->block_hash)]; - BOOL extraShare = result->extra_share; - processingResult.extraShare = extraShare; - - processingResult.mnListDiffResultAtTip = [DSMnDiffProcessingResult processingResultWith:result->result_at_tip onChain:chain]; - processingResult.mnListDiffResultAtH = [DSMnDiffProcessingResult processingResultWith:result->result_at_h onChain:chain]; - processingResult.mnListDiffResultAtHC = [DSMnDiffProcessingResult processingResultWith:diffResultAtHC onChain:chain]; - processingResult.mnListDiffResultAtH2C = [DSMnDiffProcessingResult processingResultWith:diffResultAtH2C onChain:chain]; - processingResult.mnListDiffResultAtH3C = [DSMnDiffProcessingResult processingResultWith:diffResultAtH3C onChain:chain]; - if (extraShare) { - processingResult.snapshotAtH4C = [DSQuorumSnapshot quorumSnapshotWith:result->snapshot_at_h_4c forBlockHash:*((UInt256 *)diffResultAtH4C->block_hash)]; - processingResult.mnListDiffResultAtH4C = [DSMnDiffProcessingResult processingResultWith:diffResultAtH4C onChain:chain]; - } - NSMutableOrderedSet *lastQuorumPerIndex = [NSMutableOrderedSet orderedSet]; - for (NSUInteger i = 0; i < result->last_quorum_per_index_count; i++) { - DSQuorumEntry *entry = [[DSQuorumEntry alloc] initWithEntry:result->last_quorum_per_index[i] onChain:chain]; - [lastQuorumPerIndex addObject:entry]; - } - processingResult.lastQuorumPerIndex = [lastQuorumPerIndex copy]; - NSAssert(result->quorum_snapshot_list_count == result->mn_list_diff_list_count, @"Num of snapshots & diffs should be equal"); - NSMutableOrderedSet *snapshotList = [NSMutableOrderedSet orderedSet]; - NSMutableOrderedSet *mnListDiffList = [NSMutableOrderedSet orderedSet]; - for (NSUInteger i = 0; i < result->quorum_snapshot_list_count; i++) { - MNListDiffResult *diff = result->mn_list_diff_list[i] ; - DSQuorumSnapshot *snapshot = [DSQuorumSnapshot quorumSnapshotWith:result->quorum_snapshot_list[i] forBlockHash:*((UInt256 *)diff->block_hash)]; - DSMnDiffProcessingResult *mnListDiff = [DSMnDiffProcessingResult processingResultWith:diff onChain:chain]; - [snapshotList addObject:snapshot]; - [mnListDiffList addObject:mnListDiff]; - } - processingResult.snapshotList = snapshotList; - processingResult.mnListDiffList = mnListDiffList; - return processingResult; -} - - -- (NSString *)debugDescription { - return [NSString stringWithFormat:@"%@: {\n diffs: [\ntip: %@,\nh: %@,\nh-c: %@,\nh-2c: %@,\nh-3c: %@,\nh-4c: %@\n],\n snapshots: [\nh-c: %@,\nh-2c: %@,\nh-3c: %@,\nh-4c: %@], \n lastQuorums: %@, \n diffs: %@, \n, snapshots: %@ \n]}", - [super debugDescription], - self.mnListDiffResultAtTip, - self.mnListDiffResultAtH, - self.mnListDiffResultAtHC, - self.mnListDiffResultAtH2C, - self.mnListDiffResultAtH3C, - self.mnListDiffResultAtH4C, - self.snapshotAtHC, - self.snapshotAtH2C, - self.snapshotAtH3C, - self.snapshotAtH4C, - self.lastQuorumPerIndex, - self.snapshotList, - self.mnListDiffList - ]; -} - -@end +//#import "DSQRInfoProcessingResult.h" +// +//@implementation DSQRInfoProcessingResult +// +//+ (instancetype)processingResultWith:(QRInfoResult *)result onChain:(DSChain *)chain { +// DSQRInfoProcessingResult *processingResult = [[DSQRInfoProcessingResult alloc] init]; +// uint8_t errorStatus = result->error_status; +// processingResult.errorStatus = errorStatus; +// if (errorStatus > 0) { +// DSLog(@"[%@] DSQRInfoProcessingResult.error %ul", chain.name, errorStatus); +// return processingResult; +// } +// if (result->result_at_tip == NULL) { +// DSLog(@"[%@] DSQRInfoProcessingResult.error.unknown", chain.name); +// processingResult.errorStatus = ProcessingError_ParseError; +// return processingResult; +// } +// MNListDiffResult *diffResultAtHC = result->result_at_h_c; +// MNListDiffResult *diffResultAtH2C = result->result_at_h_2c; +// MNListDiffResult *diffResultAtH3C = result->result_at_h_3c; +// MNListDiffResult *diffResultAtH4C = result->result_at_h_4c; +// processingResult.snapshotAtHC = [DSQuorumSnapshot quorumSnapshotWith:result->snapshot_at_h_c forBlockHash:*((UInt256 *)diffResultAtHC->block_hash)]; +// processingResult.snapshotAtH2C = [DSQuorumSnapshot quorumSnapshotWith:result->snapshot_at_h_2c forBlockHash:*((UInt256 *)diffResultAtH2C->block_hash)]; +// processingResult.snapshotAtH3C = [DSQuorumSnapshot quorumSnapshotWith:result->snapshot_at_h_3c forBlockHash:*((UInt256 *)diffResultAtH3C->block_hash)]; +// BOOL extraShare = result->extra_share; +// processingResult.extraShare = extraShare; +// +// processingResult.mnListDiffResultAtTip = [DSMnDiffProcessingResult processingResultWith:result->result_at_tip onChain:chain]; +// processingResult.mnListDiffResultAtH = [DSMnDiffProcessingResult processingResultWith:result->result_at_h onChain:chain]; +// processingResult.mnListDiffResultAtHC = [DSMnDiffProcessingResult processingResultWith:diffResultAtHC onChain:chain]; +// processingResult.mnListDiffResultAtH2C = [DSMnDiffProcessingResult processingResultWith:diffResultAtH2C onChain:chain]; +// processingResult.mnListDiffResultAtH3C = [DSMnDiffProcessingResult processingResultWith:diffResultAtH3C onChain:chain]; +// if (extraShare) { +// processingResult.snapshotAtH4C = [DSQuorumSnapshot quorumSnapshotWith:result->snapshot_at_h_4c forBlockHash:*((UInt256 *)diffResultAtH4C->block_hash)]; +// processingResult.mnListDiffResultAtH4C = [DSMnDiffProcessingResult processingResultWith:diffResultAtH4C onChain:chain]; +// } +// NSMutableOrderedSet *lastQuorumPerIndex = [NSMutableOrderedSet orderedSet]; +// for (NSUInteger i = 0; i < result->last_quorum_per_index_count; i++) { +// DSQuorumEntry *entry = [[DSQuorumEntry alloc] initWithEntry:result->last_quorum_per_index[i] onChain:chain]; +// [lastQuorumPerIndex addObject:entry]; +// } +// processingResult.lastQuorumPerIndex = [lastQuorumPerIndex copy]; +// NSAssert(result->quorum_snapshot_list_count == result->mn_list_diff_list_count, @"Num of snapshots & diffs should be equal"); +// NSMutableOrderedSet *snapshotList = [NSMutableOrderedSet orderedSet]; +// NSMutableOrderedSet *mnListDiffList = [NSMutableOrderedSet orderedSet]; +// for (NSUInteger i = 0; i < result->quorum_snapshot_list_count; i++) { +// MNListDiffResult *diff = result->mn_list_diff_list[i] ; +// DSQuorumSnapshot *snapshot = [DSQuorumSnapshot quorumSnapshotWith:result->quorum_snapshot_list[i] forBlockHash:*((UInt256 *)diff->block_hash)]; +// DSMnDiffProcessingResult *mnListDiff = [DSMnDiffProcessingResult processingResultWith:diff onChain:chain]; +// [snapshotList addObject:snapshot]; +// [mnListDiffList addObject:mnListDiff]; +// } +// processingResult.snapshotList = snapshotList; +// processingResult.mnListDiffList = mnListDiffList; +// return processingResult; +//} +// +// +//- (NSString *)debugDescription { +// return [NSString stringWithFormat:@"%@: {\n diffs: [\ntip: %@,\nh: %@,\nh-c: %@,\nh-2c: %@,\nh-3c: %@,\nh-4c: %@\n],\n snapshots: [\nh-c: %@,\nh-2c: %@,\nh-3c: %@,\nh-4c: %@], \n diffs: %@, \n, snapshots: %@ \n]}", +// [super debugDescription], +// self.mnListDiffResultAtTip, +// self.mnListDiffResultAtH, +// self.mnListDiffResultAtHC, +// self.mnListDiffResultAtH2C, +// self.mnListDiffResultAtH3C, +// self.mnListDiffResultAtH4C, +// self.snapshotAtHC, +// self.snapshotAtH2C, +// self.snapshotAtH3C, +// self.snapshotAtH4C, +//// self.lastQuorumPerIndex, +// self.snapshotList, +// self.mnListDiffList +// ]; +//} +// +//@end diff --git a/DashSync/shared/Models/Masternode/DSQuorumEntry+Mndiff.h b/DashSync/shared/Models/Masternode/DSQuorumEntry+Mndiff.h index 5427ea640..fba4909f6 100644 --- a/DashSync/shared/Models/Masternode/DSQuorumEntry+Mndiff.h +++ b/DashSync/shared/Models/Masternode/DSQuorumEntry+Mndiff.h @@ -15,22 +15,22 @@ // limitations under the License. // -#import "DSChain.h" -#import "DSQuorumEntry.h" -#import "dash_shared_core.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface DSQuorumEntry (Mndiff) - -+ (NSDictionary *> *)entriesWithMap:(LLMQMap *_Nullable *_Nonnull)entries count:(uintptr_t)count onChain:(DSChain *)chain; -+ (NSArray *)entriesWith:(LLMQEntry *_Nullable *_Nonnull)entries count:(uintptr_t)count onChain:(DSChain *)chain; - -- (LLMQEntry *)ffi_malloc; -+ (void)ffi_free:(LLMQEntry *)entry; - -@end - - -NS_ASSUME_NONNULL_END +//#import "DSChain.h" +//#import "DSQuorumEntry.h" +//#import "dash_shared_core.h" +//#import +// +//NS_ASSUME_NONNULL_BEGIN +// +//@interface DSQuorumEntry (Mndiff) +// +////+ (NSDictionary *> *)entriesWithMap:(LLMQMap *_Nullable *_Nonnull)entries count:(uintptr_t)count onChain:(DSChain *)chain; +////+ (NSArray *)entriesWith:(LLMQEntry *_Nullable *_Nonnull)entries count:(uintptr_t)count onChain:(DSChain *)chain; +//// +////- (LLMQEntry *)ffi_malloc; +////+ (void)ffi_free:(LLMQEntry *)entry; +// +//@end +// +// +//NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Masternode/DSQuorumEntry+Mndiff.m b/DashSync/shared/Models/Masternode/DSQuorumEntry+Mndiff.m index da662b673..1b96001be 100644 --- a/DashSync/shared/Models/Masternode/DSQuorumEntry+Mndiff.m +++ b/DashSync/shared/Models/Masternode/DSQuorumEntry+Mndiff.m @@ -14,73 +14,73 @@ // See the License for the specific language governing permissions and // limitations under the License. // - -#import "DSQuorumEntry+Mndiff.h" -#import "NSData+Dash.h" - -@implementation DSQuorumEntry (Mndiff) - -+ (NSDictionary *> *)entriesWithMap:(LLMQMap *_Nullable *_Nonnull)entries count:(uintptr_t)count onChain:(DSChain *)chain { - NSMutableDictionary *> *quorums = [NSMutableDictionary dictionaryWithCapacity:count]; - for (NSUInteger i = 0; i < count; i++) { - LLMQMap *llmq_map = entries[i]; - LLMQType llmqType = (LLMQType)llmq_map->llmq_type; - NSMutableDictionary *quorumsOfType = [[NSMutableDictionary alloc] initWithCapacity:llmq_map->count]; - for (NSUInteger j = 0; j < llmq_map->count; j++) { - LLMQEntry *quorum_entry = llmq_map->values[j]; - NSData *hash = [NSData dataWithBytes:quorum_entry->llmq_hash length:32]; - DSQuorumEntry *entry = [[DSQuorumEntry alloc] initWithEntry:quorum_entry onChain:chain]; - [quorumsOfType setObject:entry forKey:hash]; - } - [quorums setObject:quorumsOfType forKey:@(llmqType)]; - } - return quorums; -} - -+ (NSArray *)entriesWith:(LLMQEntry *_Nullable *_Nonnull)entries count:(uintptr_t)count onChain:(DSChain *)chain { - NSMutableArray *result = [NSMutableArray arrayWithCapacity:count]; - for (NSUInteger i = 0; i < count; i++) { - [result addObject:[[DSQuorumEntry alloc] initWithEntry:entries[i] onChain:chain]]; - } - return result; -} - -- (LLMQEntry *)ffi_malloc { - LLMQEntry *quorum_entry = malloc(sizeof(LLMQEntry)); - quorum_entry->all_commitment_aggregated_signature = uint768_malloc([self allCommitmentAggregatedSignature]); - quorum_entry->commitment_hash = uint256_malloc([self commitmentHash]); - quorum_entry->llmq_type = (int8_t) [self llmqType]; - quorum_entry->entry_hash = uint256_malloc([self quorumEntryHash]); - quorum_entry->llmq_hash = uint256_malloc([self quorumHash]); - quorum_entry->public_key = uint384_malloc([self quorumPublicKey]); - quorum_entry->threshold_signature = uint768_malloc([self quorumThresholdSignature]); - quorum_entry->verification_vector_hash = uint256_malloc([self quorumVerificationVectorHash]); - quorum_entry->saved = [self saved]; - NSData *signersBitset = [self signersBitset]; - quorum_entry->signers_bitset = data_malloc(signersBitset); - quorum_entry->signers_bitset_length = signersBitset.length; - quorum_entry->signers_count = [self signersCount]; - NSData *validMembersBitset = [self validMembersBitset]; - quorum_entry->valid_members_bitset = data_malloc(validMembersBitset); - quorum_entry->valid_members_bitset_length = validMembersBitset.length; - quorum_entry->valid_members_count = [self validMembersCount]; - quorum_entry->verified = [self verified]; - quorum_entry->version = [self version]; - return quorum_entry; -} - -+ (void)ffi_free:(LLMQEntry *)entry { - free(entry->all_commitment_aggregated_signature); - if (entry->commitment_hash) - free(entry->commitment_hash); - free(entry->entry_hash); - free(entry->llmq_hash); - free(entry->public_key); - free(entry->threshold_signature); - free(entry->verification_vector_hash); - free(entry->signers_bitset); - free(entry->valid_members_bitset); - free(entry); -} - -@end +// +//#import "DSQuorumEntry+Mndiff.h" +//#import "NSData+Dash.h" +// +//@implementation DSQuorumEntry (Mndiff) +// +////+ (NSDictionary *> *)entriesWithMap:(LLMQMap *_Nullable *_Nonnull)entries count:(uintptr_t)count onChain:(DSChain *)chain { +//// NSMutableDictionary *> *quorums = [NSMutableDictionary dictionaryWithCapacity:count]; +//// for (NSUInteger i = 0; i < count; i++) { +//// LLMQMap *llmq_map = entries[i]; +//// DLLMQType llmqType = (LLMQType)llmq_map->llmq_type; +//// NSMutableDictionary *quorumsOfType = [[NSMutableDictionary alloc] initWithCapacity:llmq_map->count]; +//// for (NSUInteger j = 0; j < llmq_map->count; j++) { +//// LLMQEntry *quorum_entry = llmq_map->values[j]; +//// NSData *hash = [NSData dataWithBytes:quorum_entry->llmq_hash length:32]; +//// DSQuorumEntry *entry = [[DSQuorumEntry alloc] initWithEntry:quorum_entry onChain:chain]; +//// [quorumsOfType setObject:entry forKey:hash]; +//// } +//// [quorums setObject:quorumsOfType forKey:@(llmqType)]; +//// } +//// return quorums; +////} +//// +////+ (NSArray *)entriesWith:(LLMQEntry *_Nullable *_Nonnull)entries count:(uintptr_t)count onChain:(DSChain *)chain { +//// NSMutableArray *result = [NSMutableArray arrayWithCapacity:count]; +//// for (NSUInteger i = 0; i < count; i++) { +//// [result addObject:[[DSQuorumEntry alloc] initWithEntry:entries[i] onChain:chain]]; +//// } +//// return result; +////} +//// +////- (LLMQEntry *)ffi_malloc { +//// LLMQEntry *quorum_entry = malloc(sizeof(LLMQEntry)); +//// quorum_entry->all_commitment_aggregated_signature = uint768_malloc([self allCommitmentAggregatedSignature]); +//// quorum_entry->commitment_hash = uint256_malloc([self commitmentHash]); +//// quorum_entry->llmq_type = (int8_t) [self llmqType]; +//// quorum_entry->entry_hash = uint256_malloc([self quorumEntryHash]); +//// quorum_entry->llmq_hash = uint256_malloc([self quorumHash]); +//// quorum_entry->public_key = uint384_malloc([self quorumPublicKey]); +//// quorum_entry->threshold_signature = uint768_malloc([self quorumThresholdSignature]); +//// quorum_entry->verification_vector_hash = uint256_malloc([self quorumVerificationVectorHash]); +//// quorum_entry->saved = [self saved]; +//// NSData *signersBitset = [self signersBitset]; +//// quorum_entry->signers_bitset = data_malloc(signersBitset); +//// quorum_entry->signers_bitset_length = signersBitset.length; +//// quorum_entry->signers_count = [self signersCount]; +//// NSData *validMembersBitset = [self validMembersBitset]; +//// quorum_entry->valid_members_bitset = data_malloc(validMembersBitset); +//// quorum_entry->valid_members_bitset_length = validMembersBitset.length; +//// quorum_entry->valid_members_count = [self validMembersCount]; +//// quorum_entry->verified = [self verified]; +//// quorum_entry->version = [self version]; +//// return quorum_entry; +////} +//// +////+ (void)ffi_free:(LLMQEntry *)entry { +//// free(entry->all_commitment_aggregated_signature); +//// if (entry->commitment_hash) +//// free(entry->commitment_hash); +//// free(entry->entry_hash); +//// free(entry->llmq_hash); +//// free(entry->public_key); +//// free(entry->threshold_signature); +//// free(entry->verification_vector_hash); +//// free(entry->signers_bitset); +//// free(entry->valid_members_bitset); +//// free(entry); +////} +// +//@end diff --git a/DashSync/shared/Models/Masternode/DSQuorumEntry.h b/DashSync/shared/Models/Masternode/DSQuorumEntry.h index c211c2633..c434665df 100644 --- a/DashSync/shared/Models/Masternode/DSQuorumEntry.h +++ b/DashSync/shared/Models/Masternode/DSQuorumEntry.h @@ -10,7 +10,7 @@ #import "dash_shared_core.h" #import -@class DSChain, DSMasternodeList, DSQuorumEntryEntity; +@class DSChain, DSQuorumEntryEntity; NS_ASSUME_NONNULL_BEGIN @@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) UInt256 quorumVerificationVectorHash; @property (nonatomic, readonly) UInt768 allCommitmentAggregatedSignature; @property (nonatomic, readonly) int32_t signersCount; -@property (nonatomic, readonly) LLMQType llmqType; +@property (nonatomic, readonly) DLLMQType llmqType; @property (nonatomic, readonly) int32_t validMembersCount; @property (nonatomic, readonly) NSData *signersBitset; @property (nonatomic, readonly) NSData *validMembersBitset; @@ -36,9 +36,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) BOOL verified; @property (nonatomic, assign) BOOL saved; -- (instancetype)initWithVersion:(uint16_t)version type:(LLMQType)type quorumHash:(UInt256)quorumHash quorumIndex:(uint32_t)quorumIndex quorumPublicKey:(UInt384)quorumPublicKey quorumEntryHash:(UInt256)commitmentHash verified:(BOOL)verified onChain:(DSChain *)chain; +- (instancetype)initWithVersion:(uint16_t)version type:(DLLMQType)type quorumHash:(UInt256)quorumHash quorumIndex:(uint32_t)quorumIndex quorumPublicKey:(UInt384)quorumPublicKey quorumEntryHash:(UInt256)commitmentHash verified:(BOOL)verified onChain:(DSChain *)chain; - (instancetype)initWithVersion:(uint16_t)version - type:(LLMQType)type + type:(DLLMQType)type quorumHash:(UInt256)quorumHash quorumIndex:(uint32_t)quorumIndex signersCount:(int32_t)signersCount @@ -52,18 +52,18 @@ allCommitmentAggregatedSignature:(UInt768)allCommitmentAggregatedSignature quorumEntryHash:(UInt256)quorumEntryHash onChain:(DSChain *)chain; -- (instancetype)initWithEntry:(LLMQEntry *)entry onChain:(DSChain *)chain; +//- (instancetype)initWithEntry:(DLLMQType *)entry onChain:(DSChain *)chain; -- (BOOL)validateWithMasternodeList:(DSMasternodeList *)masternodeList; -- (BOOL)validateWithMasternodeList:(DSMasternodeList *)masternodeList blockHeightLookup:(BlockHeightFinder)blockHeightLookup; +//- (BOOL)validateWithMasternodeList:(DSMasternodeList *)masternodeList; +//- (BOOL)validateWithMasternodeList:(DSMasternodeList *)masternodeList blockHeightLookup:(BlockHeightFinder)blockHeightLookup; - (DSQuorumEntryEntity *)matchingQuorumEntryEntityInContext:(NSManagedObjectContext *)context; -- (UInt256)orderingHashForRequestID:(UInt256)requestID forQuorumType:(LLMQType)quorumType; +- (UInt256)orderingHashForRequestID:(UInt256)requestID forQuorumType:(DLLMQType)quorumType; -+ (uint32_t)quorumSizeForType:(LLMQType)type; +//+ (uint32_t)quorumSizeForType:(DLLMQType)type; -- (void)mergedWithQuorumEntry:(DSQuorumEntry *)quorumEntry; +//- (void)mergedWithQuorumEntry:(DSQuorumEntry *)quorumEntry; - (BOOL)useLegacyBLSScheme; diff --git a/DashSync/shared/Models/Masternode/DSQuorumEntry.m b/DashSync/shared/Models/Masternode/DSQuorumEntry.m index 903df11e7..b80d7c486 100644 --- a/DashSync/shared/Models/Masternode/DSQuorumEntry.m +++ b/DashSync/shared/Models/Masternode/DSQuorumEntry.m @@ -1,277 +1,280 @@ +//// +//// DSQuorumEntry.m +//// DashSync +//// +//// Created by Sam Westrich on 4/25/19. +//// // -// DSQuorumEntry.m -// DashSync +//#import "dash_shared_core.h" +//#import "DSQuorumEntry.h" +//#import "DSBlock.h" +//#import "DSChainManager.h" +//#import "DSKeyManager.h" +////#import "DSMasternodeList.h" +////#import "DSMasternodeList+Mndiff.h" +//#import "DSMasternodeManager.h" +//#import "DSMerkleBlock.h" +//#import "DSQuorumEntry+Mndiff.h" +//#import "DSQuorumEntryEntity+CoreDataClass.h" +////#import "DSSimplifiedMasternodeEntry.h" +//#import "NSData+Dash.h" +//#import "NSManagedObject+Sugar.h" +//#import "NSMutableData+Dash.h" // -// Created by Sam Westrich on 4/25/19. +//@interface DSQuorumEntry () // - -#import "DSQuorumEntry.h" -#import "DSBlock.h" -#import "DSChainManager.h" -#import "DSMasternodeList.h" -#import "DSMasternodeList+Mndiff.h" -#import "DSMasternodeManager.h" -#import "DSMerkleBlock.h" -#import "DSQuorumEntry+Mndiff.h" -#import "DSQuorumEntryEntity+CoreDataClass.h" -#import "DSSimplifiedMasternodeEntry.h" -#import "NSData+Dash.h" -#import "NSManagedObject+Sugar.h" -#import "NSMutableData+Dash.h" - -@interface DSQuorumEntry () - -@property (nonatomic, assign) uint16_t version; -@property (nonatomic, assign) uint32_t quorumIndex; -@property (nonatomic, assign) UInt256 quorumHash; -@property (nonatomic, assign) UInt384 quorumPublicKey; -@property (nonatomic, assign) UInt768 quorumThresholdSignature; -@property (nonatomic, assign) UInt256 quorumVerificationVectorHash; -@property (nonatomic, assign) UInt768 allCommitmentAggregatedSignature; -@property (nonatomic, assign) int32_t signersCount; -@property (nonatomic, assign) LLMQType llmqType; -@property (nonatomic, assign) int32_t validMembersCount; -@property (nonatomic, strong) NSData *signersBitset; -@property (nonatomic, strong) NSData *validMembersBitset; -@property (nonatomic, assign) UInt256 quorumEntryHash; -@property (nonatomic, assign) UInt256 commitmentHash; -@property (nonatomic, assign) uint32_t length; -@property (nonatomic, strong) DSChain *chain; -@property (nonatomic, assign) BOOL verified; - -@end - -@implementation DSQuorumEntry - -- (id)copyWithZone:(NSZone *)zone { - DSQuorumEntry *copy = [[[self class] alloc] init]; - if (!copy) return nil; - // Copy NSObject subclasses - [copy setSignersBitset:self.signersBitset]; - [copy setValidMembersBitset:self.validMembersBitset]; - - // Set primitives - [copy setVersion:self.version]; - [copy setQuorumHash:self.quorumHash]; - [copy setQuorumPublicKey:self.quorumPublicKey]; - [copy setQuorumThresholdSignature:self.quorumThresholdSignature]; - [copy setQuorumVerificationVectorHash:self.quorumVerificationVectorHash]; - [copy setAllCommitmentAggregatedSignature:self.allCommitmentAggregatedSignature]; - [copy setSignersCount:self.signersCount]; - [copy setLlmqType:self.llmqType]; - [copy setValidMembersCount:self.validMembersCount]; - [copy setQuorumEntryHash:self.quorumEntryHash]; - [copy setCommitmentHash:self.commitmentHash]; -// [copy setLength:self.length]; - [copy setQuorumIndex:self.quorumIndex]; - [copy setChain:self.chain]; - - return copy; -} - -- (instancetype)initWithVersion:(uint16_t)version - type:(LLMQType)type - quorumHash:(UInt256)quorumHash - quorumIndex:(uint32_t)quorumIndex - signersCount:(int32_t)signersCount - signersBitset:(NSData *)signersBitset - validMembersCount:(int32_t)validMembersCount - validMembersBitset:(NSData *)validMembersBitset - quorumPublicKey:(UInt384)quorumPublicKey - quorumVerificationVectorHash:(UInt256)quorumVerificationVectorHash - quorumThresholdSignature:(UInt768)quorumThresholdSignature -allCommitmentAggregatedSignature:(UInt768)allCommitmentAggregatedSignature - quorumEntryHash:(UInt256)quorumEntryHash - onChain:(DSChain *)chain { - if (!(self = [super init])) return nil; - - self.llmqType = type; - self.version = version; - self.quorumHash = quorumHash; - self.quorumIndex = quorumIndex; - self.signersCount = signersCount; - self.signersBitset = signersBitset; - self.validMembersCount = validMembersCount; - self.validMembersBitset = validMembersBitset; - self.quorumPublicKey = quorumPublicKey; - self.quorumVerificationVectorHash = quorumVerificationVectorHash; - self.quorumVerificationVectorHash = quorumVerificationVectorHash; - self.quorumThresholdSignature = quorumThresholdSignature; - self.allCommitmentAggregatedSignature = allCommitmentAggregatedSignature; - self.quorumEntryHash = quorumEntryHash; - self.chain = chain; - - return self; -} - -- (instancetype)initWithVersion:(uint16_t)version type:(LLMQType)type quorumHash:(UInt256)quorumHash quorumIndex:(uint32_t)quorumIndex quorumPublicKey:(UInt384)quorumPublicKey quorumEntryHash:(UInt256)quorumEntryHash verified:(BOOL)verified onChain:(DSChain *)chain { - if (!(self = [super init])) return nil; - - self.llmqType = type; - self.version = version; - self.quorumHash = quorumHash; - self.quorumPublicKey = quorumPublicKey; - self.quorumEntryHash = quorumEntryHash; - self.quorumIndex = quorumIndex; - self.verified = verified; - self.chain = chain; - self.saved = TRUE; - - return self; -} - -- (instancetype)initWithEntry:(LLMQEntry *)entry onChain:(DSChain *)chain { - if (!(self = [super init])) return nil; - self.allCommitmentAggregatedSignature = *((UInt768 *)entry->all_commitment_aggregated_signature); - if (entry->commitment_hash) { - self.commitmentHash = *((UInt256 *)entry->commitment_hash); - } - self.llmqType = entry->llmq_type; - self.quorumEntryHash = *((UInt256 *)entry->entry_hash); - self.quorumHash = *((UInt256 *)entry->llmq_hash); - self.quorumPublicKey = *((UInt384 *)entry->public_key); - self.quorumThresholdSignature = *((UInt768 *)entry->threshold_signature); - self.quorumVerificationVectorHash = *((UInt256 *)entry->verification_vector_hash); - self.quorumIndex = entry->index; - self.saved = entry->saved; - self.signersBitset = [NSData dataWithBytes:entry->signers_bitset length:entry->signers_bitset_length]; - self.signersCount = (uint32_t)entry->signers_count; - self.validMembersBitset = [NSData dataWithBytes:entry->valid_members_bitset length:entry->valid_members_bitset_length]; - self.validMembersCount = (uint32_t)entry->valid_members_count; - self.verified = entry->verified; - self.version = entry->version; - self.chain = chain; - return self; -} - -- (NSData *)toData { - NSMutableData *data = [NSMutableData data]; - [data appendUInt16:self.version]; - [data appendUInt8:self.llmqType]; - [data appendUInt256:self.quorumHash]; - if (self.version == LLMQVersion_Indexed || self.version == LLMQVersion_BLSBasicIndexed) - [data appendUInt32:self.quorumIndex]; - [data appendVarInt:self.signersCount]; - [data appendData:self.signersBitset]; - [data appendVarInt:self.validMembersCount]; - [data appendData:self.validMembersBitset]; - [data appendUInt384:self.quorumPublicKey]; - [data appendUInt256:self.quorumVerificationVectorHash]; - [data appendUInt768:self.quorumThresholdSignature]; - [data appendUInt768:self.allCommitmentAggregatedSignature]; - return data; -} - -- (UInt256)commitmentHash { - if (uint256_is_zero(_commitmentHash)) { - NSData *data = [self commitmentData]; - _commitmentHash = [data SHA256_2]; - } - return _commitmentHash; -} - -- (NSData *)commitmentData { - NSMutableData *data = [NSMutableData data]; - [data appendVarInt:self.llmqType]; - [data appendUInt256:self.quorumHash]; - if (self.version == LLMQVersion_Indexed || self.version == LLMQVersion_BLSBasicIndexed) - [data appendUInt32:self.quorumIndex]; - [data appendVarInt:self.validMembersCount]; - [data appendData:self.validMembersBitset]; - [data appendUInt384:self.quorumPublicKey]; - [data appendUInt256:self.quorumVerificationVectorHash]; - return data; -} - -- (uint32_t)quorumThreshold { - return quorum_threshold_for_type(self.llmqType); -} - -- (BOOL)validateWithMasternodeList:(DSMasternodeList *)masternodeList { - return [self validateWithMasternodeList:masternodeList - blockHeightLookup:^uint32_t(UInt256 blockHash) { - DSMerkleBlock *block = [self.chain blockForBlockHash:blockHash]; - if (!block) { - DSLog(@"[%@] Unknown block %@", self.chain.name, uint256_reverse_hex(blockHash)); - NSAssert(block, @"block should be known"); - } - return block.height; - }]; -} - -- (BOOL)validateWithMasternodeList:(DSMasternodeList *)masternodeList blockHeightLookup:(BlockHeightFinder)blockHeightLookup { - if (!masternodeList) { - DSLog(@"[%@] Trying to validate a quorum without a masternode list", self.chain.name); - return NO; - } - MasternodeList *list = [masternodeList ffi_malloc]; - LLMQEntry *quorum = [self ffi_malloc]; - BOOL is_valid = validate_masternode_list(list, quorum, blockHeightLookup(masternodeList.blockHash), self.chain.chainType, NULL); - [DSMasternodeList ffi_free:list]; - [DSQuorumEntry ffi_free:quorum]; - self.verified = is_valid; - return is_valid; -} - -- (DSQuorumEntryEntity *)matchingQuorumEntryEntityInContext:(NSManagedObjectContext *)context { - return [DSQuorumEntryEntity anyObjectInContext:context matching:@"quorumPublicKeyData == %@", uint384_data(self.quorumPublicKey)]; -} - -- (UInt256)orderingHashForRequestID:(UInt256)requestID forQuorumType:(LLMQType)quorumType { - NSMutableData *data = [NSMutableData data]; - [data appendVarInt:quorumType]; - [data appendUInt256:self.quorumHash]; - [data appendUInt256:requestID]; - return [data SHA256_2]; -} - -+ (uint32_t)quorumSizeForType:(LLMQType)type { - return quorum_size_for_type(type); -} - - -- (NSString *)description { - uint32_t height = [self.chain heightForBlockHash:self.quorumHash]; - return [[super description] stringByAppendingString:[NSString stringWithFormat:@" - %u", height]]; -} - -- (NSString *)debugDescription { - uint32_t height = [self.chain heightForBlockHash:self.quorumHash]; - return [[super debugDescription] stringByAppendingString:[NSString stringWithFormat:@" - %u -%u", height, self.version]]; -} - -- (BOOL)isEqual:(id)object { - if (self == object) return YES; - if (![object isKindOfClass:[DSQuorumEntry class]]) return NO; - return uint256_eq(self.quorumEntryHash, ((DSQuorumEntry *)object).quorumEntryHash); -} - -- (NSUInteger)hash { - return [uint256_data(self.quorumEntryHash) hash]; -} - -- (void)mergedWithQuorumEntry:(DSQuorumEntry *)quorumEntry { - self.allCommitmentAggregatedSignature = quorumEntry.allCommitmentAggregatedSignature; - self.commitmentHash = quorumEntry.commitmentHash; - self.llmqType = quorumEntry.llmqType; - self.quorumEntryHash = quorumEntry.quorumEntryHash; - self.quorumHash = quorumEntry.quorumHash; - self.quorumPublicKey = quorumEntry.quorumPublicKey; - self.quorumThresholdSignature = quorumEntry.quorumThresholdSignature; - self.quorumVerificationVectorHash = quorumEntry.quorumVerificationVectorHash; - self.quorumIndex = quorumEntry.quorumIndex; - self.saved = quorumEntry.saved; - self.signersBitset = quorumEntry.signersBitset; - self.signersCount = quorumEntry.signersCount; - self.validMembersBitset = quorumEntry.validMembersBitset; - self.validMembersCount = quorumEntry.validMembersCount; - self.verified = quorumEntry.verified; - self.version = quorumEntry.version; - self.chain = quorumEntry.chain; -} - -- (BOOL)useLegacyBLSScheme { - return self.version <= 2; -} - -@end +//@property (nonatomic, assign) uint16_t version; +//@property (nonatomic, assign) uint32_t quorumIndex; +//@property (nonatomic, assign) UInt256 quorumHash; +//@property (nonatomic, assign) UInt384 quorumPublicKey; +//@property (nonatomic, assign) UInt768 quorumThresholdSignature; +//@property (nonatomic, assign) UInt256 quorumVerificationVectorHash; +//@property (nonatomic, assign) UInt768 allCommitmentAggregatedSignature; +//@property (nonatomic, assign) int32_t signersCount; +//@property (nonatomic, assign) LLMQTyp llmqType; +//@property (nonatomic, assign) int32_t validMembersCount; +//@property (nonatomic, strong) NSData *signersBitset; +//@property (nonatomic, strong) NSData *validMembersBitset; +//@property (nonatomic, assign) UInt256 quorumEntryHash; +//@property (nonatomic, assign) UInt256 commitmentHash; +//@property (nonatomic, assign) uint32_t length; +//@property (nonatomic, strong) DSChain *chain; +//@property (nonatomic, assign) BOOL verified; +// +//@end +// +//@implementation DSQuorumEntry +// +//- (id)copyWithZone:(NSZone *)zone { +// DSQuorumEntry *copy = [[[self class] alloc] init]; +// if (!copy) return nil; +// // Copy NSObject subclasses +// [copy setSignersBitset:self.signersBitset]; +// [copy setValidMembersBitset:self.validMembersBitset]; +// +// // Set primitives +// [copy setVersion:self.version]; +// [copy setQuorumHash:self.quorumHash]; +// [copy setQuorumPublicKey:self.quorumPublicKey]; +// [copy setQuorumThresholdSignature:self.quorumThresholdSignature]; +// [copy setQuorumVerificationVectorHash:self.quorumVerificationVectorHash]; +// [copy setAllCommitmentAggregatedSignature:self.allCommitmentAggregatedSignature]; +// [copy setSignersCount:self.signersCount]; +// [copy setLlmqType:self.llmqType]; +// [copy setValidMembersCount:self.validMembersCount]; +// [copy setQuorumEntryHash:self.quorumEntryHash]; +// [copy setCommitmentHash:self.commitmentHash]; +//// [copy setLength:self.length]; +// [copy setQuorumIndex:self.quorumIndex]; +// [copy setChain:self.chain]; +// +// return copy; +//} +// +//- (instancetype)initWithVersion:(uint16_t)version +// type:(DLLMQType)type +// quorumHash:(UInt256)quorumHash +// quorumIndex:(uint32_t)quorumIndex +// signersCount:(int32_t)signersCount +// signersBitset:(NSData *)signersBitset +// validMembersCount:(int32_t)validMembersCount +// validMembersBitset:(NSData *)validMembersBitset +// quorumPublicKey:(UInt384)quorumPublicKey +// quorumVerificationVectorHash:(UInt256)quorumVerificationVectorHash +// quorumThresholdSignature:(UInt768)quorumThresholdSignature +//allCommitmentAggregatedSignature:(UInt768)allCommitmentAggregatedSignature +// quorumEntryHash:(UInt256)quorumEntryHash +// onChain:(DSChain *)chain { +// if (!(self = [super init])) return nil; +// +// self.llmqType = type; +// self.version = version; +// self.quorumHash = quorumHash; +// self.quorumIndex = quorumIndex; +// self.signersCount = signersCount; +// self.signersBitset = signersBitset; +// self.validMembersCount = validMembersCount; +// self.validMembersBitset = validMembersBitset; +// self.quorumPublicKey = quorumPublicKey; +// self.quorumVerificationVectorHash = quorumVerificationVectorHash; +// self.quorumVerificationVectorHash = quorumVerificationVectorHash; +// self.quorumThresholdSignature = quorumThresholdSignature; +// self.allCommitmentAggregatedSignature = allCommitmentAggregatedSignature; +// self.quorumEntryHash = quorumEntryHash; +// self.chain = chain; +// +// return self; +//} +// +//- (instancetype)initWithVersion:(uint16_t)version type:(DLLMQType)type quorumHash:(UInt256)quorumHash quorumIndex:(uint32_t)quorumIndex quorumPublicKey:(UInt384)quorumPublicKey quorumEntryHash:(UInt256)quorumEntryHash verified:(BOOL)verified onChain:(DSChain *)chain { +// if (!(self = [super init])) return nil; +// +// self.llmqType = type; +// self.version = version; +// self.quorumHash = quorumHash; +// self.quorumPublicKey = quorumPublicKey; +// self.quorumEntryHash = quorumEntryHash; +// self.quorumIndex = quorumIndex; +// self.verified = verified; +// self.chain = chain; +// self.saved = TRUE; +// +// return self; +//} +// +////- (instancetype)initWithEntry:(DLLMQEntry *)entry onChain:(DSChain *)chain { +//// if (!(self = [super init])) return nil; +//// self.allCommitmentAggregatedSignature = *((UInt768 *)entry->all_commitment_aggregated_signature); +//// if (entry->commitment_hash) { +//// self.commitmentHash = *((UInt256 *)entry->commitment_hash); +//// } +//// self.llmqType = entry->llmq_type; +//// self.quorumEntryHash = *((UInt256 *)entry->entry_hash); +//// self.quorumHash = *((UInt256 *)entry->llmq_hash); +//// self.quorumPublicKey = *((UInt384 *)entry->public_key); +//// self.quorumThresholdSignature = *((UInt768 *)entry->threshold_signature); +//// self.quorumVerificationVectorHash = *((UInt256 *)entry->verification_vector_hash); +//// self.quorumIndex = entry->index; +//// self.saved = entry->saved; +//// self.signersBitset = [NSData dataWithBytes:entry->signers_bitset length:entry->signers_bitset_length]; +//// self.signersCount = (uint32_t)entry->signers_count; +//// self.validMembersBitset = [NSData dataWithBytes:entry->valid_members_bitset length:entry->valid_members_bitset_length]; +//// self.validMembersCount = (uint32_t)entry->valid_members_count; +//// self.verified = entry->verified; +//// self.version = entry->version; +//// self.chain = chain; +//// return self; +////} +// +////- (NSData *)toData { +//// NSMutableData *data = [NSMutableData data]; +//// [data appendUInt16:self.version]; +//// [data appendUInt8:self.llmqType]; +//// [data appendUInt256:self.quorumHash]; +//// if (self.version == LLMQVersion_Indexed || self.version == LLMQVersion_BLSBasicIndexed) +//// [data appendUInt32:self.quorumIndex]; +//// [data appendVarInt:self.signersCount]; +//// [data appendData:self.signersBitset]; +//// [data appendVarInt:self.validMembersCount]; +//// [data appendData:self.validMembersBitset]; +//// [data appendUInt384:self.quorumPublicKey]; +//// [data appendUInt256:self.quorumVerificationVectorHash]; +//// [data appendUInt768:self.quorumThresholdSignature]; +//// [data appendUInt768:self.allCommitmentAggregatedSignature]; +//// return data; +////} +//// +////- (UInt256)commitmentHash { +//// if (uint256_is_zero(_commitmentHash)) { +//// NSData *data = [self commitmentData]; +//// _commitmentHash = [data SHA256_2]; +//// } +//// return _commitmentHash; +////} +//// +////- (NSData *)commitmentData { +//// NSMutableData *data = [NSMutableData data]; +//// [data appendVarInt:self.llmqType]; +//// [data appendUInt256:self.quorumHash]; +//// if (self.version == LLMQVersion_Indexed || self.version == LLMQVersion_BLSBasicIndexed) +//// [data appendUInt32:self.quorumIndex]; +//// [data appendVarInt:self.validMembersCount]; +//// [data appendData:self.validMembersBitset]; +//// [data appendUInt384:self.quorumPublicKey]; +//// [data appendUInt256:self.quorumVerificationVectorHash]; +//// return data; +////} +//// +////- (uint32_t)quorumThreshold { +//// return quorum_threshold_for_type(self.llmqType); +////} +// +////- (BOOL)validateWithMasternodeList:(DSMasternodeList *)masternodeList { +//// return [self validateWithMasternodeList:masternodeList +//// blockHeightLookup:^uint32_t(UInt256 blockHash) { +//// DSMerkleBlock *block = [self.chain blockForBlockHash:blockHash]; +//// if (!block) { +//// DSLog(@"[%@] Unknown block %@", self.chain.name, uint256_reverse_hex(blockHash)); +//// NSAssert(block, @"block should be known"); +//// } +//// return block.height; +//// }]; +////} +//// +////- (BOOL)validateWithMasternodeList:(DSMasternodeList *)masternodeList blockHeightLookup:(BlockHeightFinder)blockHeightLookup { +//// if (!masternodeList) { +//// DSLog(@"[%@] Trying to validate a quorum without a masternode list", self.chain.name); +//// return NO; +//// } +//// MasternodeList_ +//// MasternodeList *list = [masternodeList ffi_malloc]; +//// LLMQEntry *quorum = [self ffi_malloc]; +//// BOOL is_valid = validate_masternode_list(list, quorum, blockHeightLookup(masternodeList.blockHash), self.chain.chainType, NULL); +//// [DSMasternodeList ffi_free:list]; +//// [DSQuorumEntry ffi_free:quorum]; +//// self.verified = is_valid; +//// return is_valid; +////} +// +//- (DSQuorumEntryEntity *)matchingQuorumEntryEntityInContext:(NSManagedObjectContext *)context { +// return [DSQuorumEntryEntity anyObjectInContext:context matching:@"quorumPublicKeyData == %@", uint384_data(self.quorumPublicKey)]; +//} +// +//- (UInt256)orderingHashForRequestID:(UInt256)requestID forQuorumType:(DLLMQType)quorumType { +// NSMutableData *data = [NSMutableData data]; +// [data appendVarInt:quorumType]; +// [data appendUInt256:self.quorumHash]; +// [data appendUInt256:requestID]; +// return [data SHA256_2]; +//} +// +////+ (uint32_t)quorumSizeForType:(DLLMQType)type { +//// return quorum_size_for_type(type); +////} +// +// +//- (NSString *)description { +// uint32_t height = [self.chain heightForBlockHash:self.quorumHash]; +// return [[super description] stringByAppendingString:[NSString stringWithFormat:@" - %u", height]]; +//} +// +//- (NSString *)debugDescription { +// uint32_t height = [self.chain heightForBlockHash:self.quorumHash]; +// return [[super debugDescription] stringByAppendingString:[NSString stringWithFormat:@" - %u -%u", height, self.version]]; +//} +// +//- (BOOL)isEqual:(id)object { +// if (self == object) return YES; +// if (![object isKindOfClass:[DSQuorumEntry class]]) return NO; +// return uint256_eq(self.quorumEntryHash, ((DSQuorumEntry *)object).quorumEntryHash); +//} +// +//- (NSUInteger)hash { +// return [uint256_data(self.quorumEntryHash) hash]; +//} +// +//- (void)mergedWithQuorumEntry:(DSQuorumEntry *)quorumEntry { +// self.allCommitmentAggregatedSignature = quorumEntry.allCommitmentAggregatedSignature; +// self.commitmentHash = quorumEntry.commitmentHash; +// self.llmqType = quorumEntry.llmqType; +// self.quorumEntryHash = quorumEntry.quorumEntryHash; +// self.quorumHash = quorumEntry.quorumHash; +// self.quorumPublicKey = quorumEntry.quorumPublicKey; +// self.quorumThresholdSignature = quorumEntry.quorumThresholdSignature; +// self.quorumVerificationVectorHash = quorumEntry.quorumVerificationVectorHash; +// self.quorumIndex = quorumEntry.quorumIndex; +// self.saved = quorumEntry.saved; +// self.signersBitset = quorumEntry.signersBitset; +// self.signersCount = quorumEntry.signersCount; +// self.validMembersBitset = quorumEntry.validMembersBitset; +// self.validMembersCount = quorumEntry.validMembersCount; +// self.verified = quorumEntry.verified; +// self.version = quorumEntry.version; +// self.chain = quorumEntry.chain; +//} +// +//- (BOOL)useLegacyBLSScheme { +// return self.version <= 2; +//} +// +//@end diff --git a/DashSync/shared/Models/Masternode/DSQuorumRotationService.h b/DashSync/shared/Models/Masternode/DSQuorumRotationService.h index 4139b8b6f..9bb0f7d50 100644 --- a/DashSync/shared/Models/Masternode/DSQuorumRotationService.h +++ b/DashSync/shared/Models/Masternode/DSQuorumRotationService.h @@ -22,12 +22,13 @@ NS_ASSUME_NONNULL_BEGIN @interface DSQuorumRotationService : DSMasternodeListService -@property (nonatomic) DSMasternodeList *masternodeListAtTip; -@property (nonatomic) DSMasternodeList *masternodeListAtH; -@property (nonatomic) DSMasternodeList *masternodeListAtHC; -@property (nonatomic) DSMasternodeList *masternodeListAtH2C; -@property (nonatomic) DSMasternodeList *masternodeListAtH3C; -@property (nonatomic) DSMasternodeList *masternodeListAtH4C; +//@property (nonatomic, assign, nullable) struct dash_spv_masternode_processor_processing_qr_info_result_QRInfoResult *last_result; +//@property (nonatomic, assign, nullable) DMasternodeList *masternodeListAtTip; +//@property (nonatomic, assign, nullable) DMasternodeList *masternodeListAtH; +//@property (nonatomic, assign, nullable) DMasternodeList *masternodeListAtHC; +//@property (nonatomic, assign, nullable) DMasternodeList *masternodeListAtH2C; +//@property (nonatomic, assign, nullable) DMasternodeList *masternodeListAtH3C; +//@property (nonatomic, assign, nullable) DMasternodeList *masternodeListAtH4C; @end diff --git a/DashSync/shared/Models/Masternode/DSQuorumRotationService.m b/DashSync/shared/Models/Masternode/DSQuorumRotationService.m index dcfb6a254..b1ed09080 100644 --- a/DashSync/shared/Models/Masternode/DSQuorumRotationService.m +++ b/DashSync/shared/Models/Masternode/DSQuorumRotationService.m @@ -15,6 +15,7 @@ // limitations under the License. // +#import "DSChain+Params.h" #import "DSGetQRInfoRequest.h" #import "DSQuorumRotationService.h" #import "DSMasternodeListService+Protected.h" @@ -22,10 +23,6 @@ @implementation DSQuorumRotationService -- (DSMasternodeList *)currentMasternodeList { - return self.masternodeListAtTip; -} - - (void)composeMasternodeListRequest:(NSOrderedSet *)list { NSData *blockHashData = [list lastObject]; if (!blockHashData) { @@ -34,68 +31,42 @@ - (void)composeMasternodeListRequest:(NSOrderedSet *)list { if ([self.store hasBlockForBlockHash:blockHashData]) { UInt256 blockHash = blockHashData.UInt256; UInt256 previousBlockHash = [self.store closestKnownBlockHashForBlockHash:blockHash]; - NSAssert(([self.store heightForBlockHash:previousBlockHash] != UINT32_MAX) || uint256_is_zero(previousBlockHash), @"This block height should be known"); +// NSAssert(([self.store heightForBlockHash:previousBlockHash] != UINT32_MAX) || uint256_is_zero(previousBlockHash), @"This block height should be known"); [self requestQuorumRotationInfo:previousBlockHash forBlockHash:blockHash]; } else { DSLog(@"[%@] Missing block (%@)", self.chain.name, blockHashData.hexString); - [self removeFromRetrievalQueue:blockHashData]; + dash_spv_masternode_processor_processing_processor_MasternodeProcessor_remove_from_qr_info_retrieval_queue(self.chain.shareCore.processor->obj, u256_ctor(blockHashData)); } - /* - NSMutableDictionary *hashes = [NSMutableDictionary dictionary]; - for (NSData *blockHashData in list) { - // we should check the associated block still exists - if ([self.store hasBlockForBlockHash:blockHashData]) { - //there is the rare possibility we have the masternode list as a checkpoint, so lets first try that - NSUInteger pos = [list indexOfObject:blockHashData]; - UInt256 blockHash = blockHashData.UInt256; - // No checkpoints for qrinfo at this moment - UInt256 prevKnownBlockHash = [self.store closestKnownBlockHashForBlockHash:blockHash]; - UInt256 prevInQueueBlockHash = (pos ? [list objectAtIndex:pos - 1].UInt256 : UINT256_ZERO); - UInt256 previousBlockHash = pos - ? ([self.store heightForBlockHash:prevKnownBlockHash] > [self.store heightForBlockHash:prevInQueueBlockHash] - ? prevKnownBlockHash - : prevInQueueBlockHash) - : prevKnownBlockHash; - [hashes setObject:uint256_data(blockHash) forKey:uint256_data(previousBlockHash)]; - NSAssert(([self.store heightForBlockHash:previousBlockHash] != UINT32_MAX) || uint256_is_zero(previousBlockHash), @"This block height should be known"); - [self requestQuorumRotationInfo:previousBlockHash forBlockHash:blockHash]; - } else { - DSLog(@"Missing block (%@)", blockHashData.hexString); - [self removeFromRetrievalQueue:blockHashData]; - } - }*/ +} +- (indexmap_IndexSet_u8_32 *)retrievalQueue { + return dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_qr_info_retrieval_queue(self.chain.shareCore.cache->obj); } - +- (NSUInteger)retrievalQueueCount { + return dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_qr_info_retrieval_queue_count(self.chain.shareCore.cache->obj); +} +- (NSUInteger)retrievalQueueMaxAmount { + return dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_qr_info_retrieval_queue_get_max_amount(self.chain.shareCore.cache->obj); +} +- (BOOL)hasLatestBlockInRetrievalQueueWithHash:(UInt256)blockHash { + return dash_spv_masternode_processor_processing_processor_cache_MasternodeProcessorCache_has_latest_block_in_qr_info_retrieval_queue_with_hash(self.chain.shareCore.cache->obj, u256_ctor_u(blockHash)); +} +- (void)removeFromRetrievalQueue:(NSData *)masternodeBlockHashData { + dash_spv_masternode_processor_processing_processor_MasternodeProcessor_remove_from_qr_info_retrieval_queue(self.chain.shareCore.processor->obj, u256_ctor(masternodeBlockHashData)); +} +- (void)cleanListsRetrievalQueue { + dash_spv_masternode_processor_processing_processor_MasternodeProcessor_clean_qr_info_retrieval_queue(self.chain.shareCore.processor->obj); +} - (void)getRecentMasternodeList { - @synchronized(self.retrievalQueue) { - DSMerkleBlock *merkleBlock = [self.chain blockFromChainTip:0]; - if (!merkleBlock) { - // sometimes it happens while rescan - DSLog(@"[%@] getRecentMasternodeList: (no block exist) for tip", self.chain.name); - return; - } - UInt256 merkleBlockHash = merkleBlock.blockHash; - if ([self hasLatestBlockInRetrievalQueueWithHash:merkleBlockHash]) { - //we are asking for the same as the last one - return; - } - uint32_t lastHeight = merkleBlock.height; - DKGParams dkgParams = self.chain.isDevnetAny ? DKG_DEVNET_DIP_0024 : DKG_60_75; - uint32_t rotationOffset = dkgParams.mining_window_end; - uint32_t updateInterval = dkgParams.interval; - BOOL needUpdate = !self.masternodeListAtH || [self.masternodeListAtH hasUnverifiedRotatedQuorums] || - (lastHeight % updateInterval == rotationOffset && lastHeight >= [self.store heightForBlockHash:self.masternodeListAtH.blockHash] + rotationOffset); - if (needUpdate && [self.store addBlockToValidationQueue:merkleBlock]) { - DSLog(@"[%@] QuorumRotationService.Getting masternode list %u", self.chain.name, merkleBlock.height); - NSData *merkleBlockHashData = uint256_data(merkleBlockHash); - BOOL emptyRequestQueue = ![self retrievalQueueCount]; - [self addToRetrievalQueue:merkleBlockHashData]; - if (emptyRequestQueue) { - [self dequeueMasternodeListRequest]; - } - } + DSMerkleBlock *merkleBlock = [self.chain blockFromChainTip:0]; + if (!merkleBlock) { + // sometimes it happens while rescan + DSLog(@"[%@] getRecentMasternodeList: (no block exist) for tip", self.chain.name); + return; } + u256 *block_hash = u256_ctor_u(merkleBlock.blockHash); + DBlock *block = DBlockCtor(merkleBlock.height, block_hash); + dash_spv_masternode_processor_processing_processor_MasternodeProcessor_get_recent_qr_info(self.chain.shareCore.processor->obj, block); } - (void)requestQuorumRotationInfo:(UInt256)previousBlockHash forBlockHash:(UInt256)blockHash { diff --git a/DashSync/shared/Models/Masternode/DSQuorumSnapshot+Mndiff.h b/DashSync/shared/Models/Masternode/DSQuorumSnapshot+Mndiff.h index 262d3409c..7718bd240 100644 --- a/DashSync/shared/Models/Masternode/DSQuorumSnapshot+Mndiff.h +++ b/DashSync/shared/Models/Masternode/DSQuorumSnapshot+Mndiff.h @@ -15,21 +15,21 @@ // limitations under the License. // -#import -#import "DSChain.h" -#import "DSQuorumSnapshot.h" -#import "dash_shared_core.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface DSQuorumSnapshot (Mndiff) - -+ (instancetype)quorumSnapshotWith:(LLMQSnapshot *)quorumSnapshot forBlockHash:(UInt256)blockHash; - -- (LLMQSnapshot *)ffi_malloc; -+ (void)ffi_free:(LLMQSnapshot *)entry; - -@end - -NS_ASSUME_NONNULL_END - +//#import +//#import "DSChain.h" +//#import "DSQuorumSnapshot.h" +//#import "dash_shared_core.h" +// +//NS_ASSUME_NONNULL_BEGIN +// +////@interface DSQuorumSnapshot (Mndiff) +//// +////+ (instancetype)quorumSnapshotWith:(LLMQSnapshot *)quorumSnapshot forBlockHash:(UInt256)blockHash; +//// +////- (LLMQSnapshot *)ffi_malloc; +////+ (void)ffi_free:(LLMQSnapshot *)entry; +//// +////@end +// +//NS_ASSUME_NONNULL_END +// diff --git a/DashSync/shared/Models/Masternode/DSQuorumSnapshot+Mndiff.m b/DashSync/shared/Models/Masternode/DSQuorumSnapshot+Mndiff.m index 7ca914c69..6deaec3e3 100644 --- a/DashSync/shared/Models/Masternode/DSQuorumSnapshot+Mndiff.m +++ b/DashSync/shared/Models/Masternode/DSQuorumSnapshot+Mndiff.m @@ -15,48 +15,48 @@ // limitations under the License. // -#import "DSQuorumSnapshot+Mndiff.h" - -@implementation DSQuorumSnapshot (Mndiff) - -+ (instancetype)quorumSnapshotWith:(LLMQSnapshot *)quorumSnapshot forBlockHash:(UInt256)blockHash { - DSQuorumSnapshot *snapshot = [[DSQuorumSnapshot alloc] init]; - NSUInteger memberListLength = quorumSnapshot->member_list_length; - NSData *memberList = [NSData dataWithBytes:quorumSnapshot->member_list length:memberListLength]; - NSUInteger skipListLength = quorumSnapshot->skip_list_length; - NSMutableArray *skipList = [NSMutableArray arrayWithCapacity:skipListLength]; - const int32_t *skip_list_bytes = quorumSnapshot->skip_list; - for (NSUInteger i = 0; i < skipListLength; i++) { - [skipList addObject:@(skip_list_bytes[i])]; - } - [snapshot setMemberList:[memberList copy]]; - [snapshot setSkipList:[skipList copy]]; - [snapshot setSkipListMode:quorumSnapshot->skip_list_mode]; - [snapshot setBlockHash:blockHash]; - return snapshot; -} - -- (LLMQSnapshot *)ffi_malloc { - LLMQSnapshot *entry = malloc(sizeof(LLMQSnapshot)); - NSUInteger skipListCount = [self.skipList count]; - int32_t *skipList = malloc(skipListCount * sizeof(int32_t)); - NSUInteger i = 0; - for (NSNumber *skipMember in self.skipList) { - skipList[i] = skipMember.intValue; - i++; - } - entry->member_list = data_malloc(self.memberList); - entry->member_list_length = self.memberList.length; - entry->skip_list = skipList; - entry->skip_list_length = skipListCount; - entry->skip_list_mode = self.skipListMode; - return entry; -} - -+ (void)ffi_free:(LLMQSnapshot *)entry { - free(entry->member_list); - free(entry->skip_list); - free(entry); -} - -@end +//#import "DSQuorumSnapshot+Mndiff.h" +// +//@implementation DSQuorumSnapshot (Mndiff) +// +//+ (instancetype)quorumSnapshotWith:(LLMQSnapshot *)quorumSnapshot forBlockHash:(UInt256)blockHash { +// DSQuorumSnapshot *snapshot = [[DSQuorumSnapshot alloc] init]; +// NSUInteger memberListLength = quorumSnapshot->member_list_length; +// NSData *memberList = [NSData dataWithBytes:quorumSnapshot->member_list length:memberListLength]; +// NSUInteger skipListLength = quorumSnapshot->skip_list_length; +// NSMutableArray *skipList = [NSMutableArray arrayWithCapacity:skipListLength]; +// const int32_t *skip_list_bytes = quorumSnapshot->skip_list; +// for (NSUInteger i = 0; i < skipListLength; i++) { +// [skipList addObject:@(skip_list_bytes[i])]; +// } +// [snapshot setMemberList:[memberList copy]]; +// [snapshot setSkipList:[skipList copy]]; +// [snapshot setSkipListMode:quorumSnapshot->skip_list_mode]; +// [snapshot setBlockHash:blockHash]; +// return snapshot; +//} +// +//- (LLMQSnapshot *)ffi_malloc { +// LLMQSnapshot *entry = malloc(sizeof(LLMQSnapshot)); +// NSUInteger skipListCount = [self.skipList count]; +// int32_t *skipList = malloc(skipListCount * sizeof(int32_t)); +// NSUInteger i = 0; +// for (NSNumber *skipMember in self.skipList) { +// skipList[i] = skipMember.intValue; +// i++; +// } +// entry->member_list = data_malloc(self.memberList); +// entry->member_list_length = self.memberList.length; +// entry->skip_list = skipList; +// entry->skip_list_length = skipListCount; +// entry->skip_list_mode = self.skipListMode; +// return entry; +//} +// +//+ (void)ffi_free:(LLMQSnapshot *)entry { +// free(entry->member_list); +// free(entry->skip_list); +// free(entry); +//} +// +//@end diff --git a/DashSync/shared/Models/Masternode/DSQuorumSnapshot.h b/DashSync/shared/Models/Masternode/DSQuorumSnapshot.h index 7faab6aec..7a276a5ba 100644 --- a/DashSync/shared/Models/Masternode/DSQuorumSnapshot.h +++ b/DashSync/shared/Models/Masternode/DSQuorumSnapshot.h @@ -14,21 +14,21 @@ // See the License for the specific language governing permissions and // limitations under the License. // - -#import "BigIntTypes.h" -#import "dash_shared_core.h" -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface DSQuorumSnapshot : NSObject - -@property (nonatomic) NSData *memberList; -@property (nonatomic) NSArray *skipList; -@property (nonatomic) LLMQSnapshotSkipMode skipListMode; - -@property (nonatomic) UInt256 blockHash; - -@end - -NS_ASSUME_NONNULL_END +// +//#import "BigIntTypes.h" +//#import "dash_shared_core.h" +//#import +// +//NS_ASSUME_NONNULL_BEGIN +// +//@interface DSQuorumSnapshot : NSObject +// +//@property (nonatomic) NSData *memberList; +//@property (nonatomic) NSArray *skipList; +//@property (nonatomic) dash_spv_masternode_processor_common_llmq_snapshot_skip_mode_LLMQSnapshotSkipMode skipListMode; +// +//@property (nonatomic) UInt256 blockHash; +// +//@end +// +//NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Masternode/DSQuorumSnapshot.m b/DashSync/shared/Models/Masternode/DSQuorumSnapshot.m index b83da7190..a89fe019a 100644 --- a/DashSync/shared/Models/Masternode/DSQuorumSnapshot.m +++ b/DashSync/shared/Models/Masternode/DSQuorumSnapshot.m @@ -15,14 +15,14 @@ // limitations under the License. // -#import "DSQuorumSnapshot.h" - -@implementation DSQuorumSnapshot - -- (NSString *)debugDescription { - return [NSString stringWithFormat:@"%@: mode: %d, members: %@, skipped: %@", - [super debugDescription], self.skipListMode, self.memberList, self.skipList - ]; -} - -@end +//#import "DSQuorumSnapshot.h" +// +//@implementation DSQuorumSnapshot +// +//- (NSString *)debugDescription { +// return [NSString stringWithFormat:@"%@: mode: %d, members: %@, skipped: %@", +// [super debugDescription], self.skipListMode, self.memberList, self.skipList +// ]; +//} +// +//@end diff --git a/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry+Mndiff.h b/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry+Mndiff.h index 31cc5be97..58d85e95c 100644 --- a/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry+Mndiff.h +++ b/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry+Mndiff.h @@ -17,18 +17,17 @@ #import "DSChain.h" #import "DSSimplifiedMasternodeEntry.h" -#import "dash_shared_core.h" #import NS_ASSUME_NONNULL_BEGIN @interface DSSimplifiedMasternodeEntry (Mndiff) -+ (instancetype)simplifiedEntryWith:(MasternodeEntry *)entry onChain:(DSChain *)chain; -+ (NSDictionary *)simplifiedEntriesWith:(MasternodeEntry *_Nullable *_Nonnull)entries count:(uintptr_t)count onChain:(DSChain *)chain; - -- (MasternodeEntry *)ffi_malloc; -+ (void)ffi_free:(MasternodeEntry *)entry; +//+ (instancetype)simplifiedEntryWith:(MasternodeEntry *)entry onChain:(DSChain *)chain; +//+ (NSDictionary *)simplifiedEntriesWith:(MasternodeEntry *_Nullable *_Nonnull)entries count:(uintptr_t)count onChain:(DSChain *)chain; +// +//- (MasternodeEntry *)ffi_malloc; +//+ (void)ffi_free:(MasternodeEntry *)entry; @end diff --git a/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry+Mndiff.m b/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry+Mndiff.m index 4c09a71a3..f7dd323f4 100644 --- a/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry+Mndiff.m +++ b/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry+Mndiff.m @@ -21,184 +21,184 @@ @implementation DSSimplifiedMasternodeEntry (Mndiff) -+ (instancetype)simplifiedEntryWith:(MasternodeEntry *)entry onChain:(DSChain *)chain { - UInt256 confirmedHash = *((UInt256 *)entry->confirmed_hash); - // TODO: Refactor to avoid unnecessary SHAing - /*uint8_t (*confirmed_hash_hashed_with_provider_registration_transaction_hash)[32] = entry->confirmed_hash_hashed_with_provider_registration_transaction_hash; - NSData *confirmedHashHashedWithProviderRegistrationTransactionHashData = confirmed_hash_hashed_with_provider_registration_transaction_hash - ? [NSData dataWithBytes:confirmed_hash_hashed_with_provider_registration_transaction_hash length:32] - : nil; - UInt256 confirmedHashHashedWithProviderRegistrationTransactionHash = [confirmedHashHashedWithProviderRegistrationTransactionHashData UInt256];*/ - BOOL isValid = entry->is_valid; - UInt160 keyIDVoting = *((UInt160 *)entry->key_id_voting); - uint32_t knownConfirmedAtHeight = entry->known_confirmed_at_height; - UInt256 simplifiedMasternodeEntryHash = *((UInt256 *)entry->entry_hash); - OperatorPublicKey *operator_public_key = entry->operator_public_key; - UInt384 operatorPublicKey = *((UInt384 *)operator_public_key->data); - uint16_t operatorPublicKeyVersion = operator_public_key->version; - uintptr_t previous_operator_public_keys_count = entry->previous_operator_public_keys_count; - BlockOperatorPublicKey *previous_operator_public_keys = entry->previous_operator_public_keys; - NSMutableDictionary *operatorPublicKeys = [NSMutableDictionary dictionaryWithCapacity:previous_operator_public_keys_count]; - for (NSUInteger i = 0; i < previous_operator_public_keys_count; i++) { - BlockOperatorPublicKey prev_operator_public_key = previous_operator_public_keys[i]; - UInt256 blockHash = *((UInt256 *)prev_operator_public_key.block_hash); - uint32_t blockHeight = prev_operator_public_key.block_height; - NSData *block = [NSData dataWithBlockHash:blockHash height:blockHeight]; - NSMutableData *data = [NSMutableData dataWithUInt384:*((UInt384 *)prev_operator_public_key.key)]; - [data appendData:[NSData dataWithUInt16:prev_operator_public_key.version]]; - [operatorPublicKeys setObject:data forKey:block]; - } - uintptr_t previous_entry_hashes_count = entry->previous_entry_hashes_count; - MasternodeEntryHash *previous_entry_hashes = entry->previous_entry_hashes; - NSMutableDictionary *masternodeEntryHashes = [NSMutableDictionary dictionaryWithCapacity:previous_entry_hashes_count]; - for (NSUInteger i = 0; i < previous_entry_hashes_count; i++) { - MasternodeEntryHash entry_hash = previous_entry_hashes[i]; - UInt256 blockHash = *((UInt256 *)entry_hash.block_hash); - uint32_t blockHeight = entry_hash.block_height; - NSData *block = [NSData dataWithBlockHash:blockHash height:blockHeight]; - NSData *hash = [NSData dataWithBytes:entry_hash.hash length:32]; - [masternodeEntryHashes setObject:hash forKey:block]; - } - uintptr_t previous_validity_count = entry->previous_validity_count; - Validity *previous_validity = entry->previous_validity; - NSMutableDictionary *validities = [NSMutableDictionary dictionaryWithCapacity:previous_validity_count]; - for (NSUInteger i = 0; i < previous_validity_count; i++) { - Validity validity = previous_validity[i]; - UInt256 blockHash = *((UInt256 *)validity.block_hash); - uint32_t blockHeight = validity.block_height; - NSData *block = [NSData dataWithBlockHash:blockHash height:blockHeight]; - NSNumber *isValid = [NSNumber numberWithBool:validity.is_valid]; - [validities setObject:isValid forKey:block]; - } - UInt256 providerRegistrationTransactionHash = *((UInt256 *)entry->provider_registration_transaction_hash); - UInt128 address = *((UInt128 *)entry->ip_address); - uint16_t port = entry->port; - uint32_t updateHeight = entry->update_height; - uint16_t type = entry->mn_type; - uint16_t platformHTTPPort = entry->platform_http_port; - UInt160 platformNodeID = *((UInt160 *)entry->platform_node_id); - return [self simplifiedMasternodeEntryWithProviderRegistrationTransactionHash:providerRegistrationTransactionHash - confirmedHash:confirmedHash - address:address - port:port - operatorBLSPublicKey:operatorPublicKey - operatorPublicKeyVersion:operatorPublicKeyVersion - previousOperatorBLSPublicKeys:operatorPublicKeys - keyIDVoting:keyIDVoting - isValid:isValid - type:type - platformHTTPPort:platformHTTPPort - platformNodeID:platformNodeID - previousValidity:validities - knownConfirmedAtHeight:knownConfirmedAtHeight - updateHeight:updateHeight - simplifiedMasternodeEntryHash:simplifiedMasternodeEntryHash - previousSimplifiedMasternodeEntryHashes:masternodeEntryHashes - onChain:chain]; -} -+ (NSDictionary *)simplifiedEntriesWith:(MasternodeEntry *_Nullable *_Nonnull)entries count:(uintptr_t)count onChain:(DSChain *)chain { - NSMutableDictionary *masternodes = [NSMutableDictionary dictionaryWithCapacity:count]; - for (NSUInteger i = 0; i < count; i++) { - MasternodeEntry *c_entry = entries[i]; - DSSimplifiedMasternodeEntry *entry = [DSSimplifiedMasternodeEntry simplifiedEntryWith:c_entry onChain:chain]; - UInt256 hash = uint256_reverse(entry.providerRegistrationTransactionHash); - [masternodes setObject:entry forKey:uint256_data(hash)]; - } - return masternodes; -} - -- (MasternodeEntry *)ffi_malloc { - uint32_t known_confirmed_at_height = [self knownConfirmedAtHeight]; - NSDictionary *previousOperatorPublicKeys = [self previousOperatorPublicKeys]; - NSDictionary *previousSimplifiedMasternodeEntryHashes = [self previousSimplifiedMasternodeEntryHashes]; - NSDictionary *previousValidity = [self previousValidity]; - MasternodeEntry *masternode_entry = malloc(sizeof(MasternodeEntry)); - masternode_entry->confirmed_hash = uint256_malloc([self confirmedHash]); - masternode_entry->confirmed_hash_hashed_with_provider_registration_transaction_hash = uint256_malloc([self confirmedHashHashedWithProviderRegistrationTransactionHash]); - masternode_entry->is_valid = [self isValid]; - masternode_entry->key_id_voting = uint160_malloc([self keyIDVoting]); - masternode_entry->known_confirmed_at_height = known_confirmed_at_height; - masternode_entry->entry_hash = uint256_malloc([self simplifiedMasternodeEntryHash]); - OperatorPublicKey *operator_public_key = malloc(sizeof(OperatorPublicKey)); - memcpy(operator_public_key->data, [self operatorPublicKey].u8, sizeof(UInt384)); - operator_public_key->version = self.operatorPublicKeyVersion; - masternode_entry->operator_public_key = operator_public_key; - NSUInteger previousOperatorPublicKeysCount = [previousOperatorPublicKeys count]; - BlockOperatorPublicKey *previous_operator_public_keys = malloc(previousOperatorPublicKeysCount * sizeof(BlockOperatorPublicKey)); - NSUInteger i = 0; - for (NSData *block in previousOperatorPublicKeys) { - NSData *keyVersionData = previousOperatorPublicKeys[block]; - UInt256 blockHash = *(UInt256 *)(block.bytes); - uint32_t blockHeight = *(uint32_t *)(block.bytes + sizeof(UInt256)); - BlockOperatorPublicKey obj = {.block_height = blockHeight}; - memcpy(obj.block_hash, blockHash.u8, sizeof(UInt256)); - if (keyVersionData.length == 48) { - obj.version = 0; - memcpy(obj.key, keyVersionData.bytes, sizeof(UInt384)); - } else { - UInt384 keyData = [keyVersionData UInt384AtOffset:0]; - obj.version = [keyVersionData UInt16AtOffset:48]; - memcpy(obj.key, keyData.u8, sizeof(UInt384)); - } - previous_operator_public_keys[i++] = obj; - } - masternode_entry->previous_operator_public_keys = previous_operator_public_keys; - masternode_entry->previous_operator_public_keys_count = previousOperatorPublicKeysCount; - NSUInteger previousSimplifiedMasternodeEntryHashesCount = [previousSimplifiedMasternodeEntryHashes count]; - MasternodeEntryHash *previous_masternode_entry_hashes = malloc(previousSimplifiedMasternodeEntryHashesCount * sizeof(MasternodeEntryHash)); - i = 0; - for (NSData *block in previousSimplifiedMasternodeEntryHashes) { - NSData *hashData = previousSimplifiedMasternodeEntryHashes[block]; - UInt256 blockHash = *(UInt256 *)(block.bytes); - uint32_t blockHeight = *(uint32_t *)(block.bytes + sizeof(UInt256)); - MasternodeEntryHash obj = {.block_height = blockHeight}; - memcpy(obj.hash, hashData.bytes, sizeof(UInt256)); - memcpy(obj.block_hash, blockHash.u8, sizeof(UInt256)); - previous_masternode_entry_hashes[i++] = obj; - } - masternode_entry->previous_entry_hashes = previous_masternode_entry_hashes; - masternode_entry->previous_entry_hashes_count = previousSimplifiedMasternodeEntryHashesCount; - NSUInteger previousValidityCount = [previousValidity count]; - Validity *previous_validity = malloc(previousValidityCount * sizeof(Validity)); - i = 0; - for (NSData *block in previousValidity) { - NSNumber *flag = previousValidity[block]; - UInt256 blockHash = *(UInt256 *)(block.bytes); - uint32_t blockHeight = *(uint32_t *)(block.bytes + sizeof(UInt256)); - Validity obj = {.block_height = blockHeight, .is_valid = [flag boolValue]}; - memcpy(obj.block_hash, blockHash.u8, sizeof(UInt256)); - previous_validity[i++] = obj; - } - masternode_entry->previous_validity = previous_validity; - masternode_entry->previous_validity_count = previousValidityCount; - masternode_entry->provider_registration_transaction_hash = uint256_malloc([self providerRegistrationTransactionHash]); - masternode_entry->ip_address = uint128_malloc([self address]); - masternode_entry->port = [self port]; - masternode_entry->update_height = [self updateHeight]; - masternode_entry->mn_type = [self type]; - masternode_entry->platform_http_port = [self platformHTTPPort]; - masternode_entry->platform_node_id = uint160_malloc([self platformNodeID]); - return masternode_entry; - -} - -+ (void)ffi_free:(MasternodeEntry *)entry { - free(entry->confirmed_hash); - if (entry->confirmed_hash_hashed_with_provider_registration_transaction_hash) - free(entry->confirmed_hash_hashed_with_provider_registration_transaction_hash); - free(entry->operator_public_key); - free(entry->entry_hash); - free(entry->ip_address); - free(entry->key_id_voting); - free(entry->platform_node_id); - free(entry->provider_registration_transaction_hash); - if (entry->previous_entry_hashes) - free(entry->previous_entry_hashes); - if (entry->previous_operator_public_keys) - free(entry->previous_operator_public_keys); - if (entry->previous_validity) - free(entry->previous_validity); - free(entry); -} +//+ (instancetype)simplifiedEntryWith:(MasternodeEntry *)entry onChain:(DSChain *)chain { +// UInt256 confirmedHash = *((UInt256 *)entry->confirmed_hash); +// // TODO: Refactor to avoid unnecessary SHAing +// /*uint8_t (*confirmed_hash_hashed_with_provider_registration_transaction_hash)[32] = entry->confirmed_hash_hashed_with_provider_registration_transaction_hash; +// NSData *confirmedHashHashedWithProviderRegistrationTransactionHashData = confirmed_hash_hashed_with_provider_registration_transaction_hash +// ? [NSData dataWithBytes:confirmed_hash_hashed_with_provider_registration_transaction_hash length:32] +// : nil; +// UInt256 confirmedHashHashedWithProviderRegistrationTransactionHash = [confirmedHashHashedWithProviderRegistrationTransactionHashData UInt256];*/ +// BOOL isValid = entry->is_valid; +// UInt160 keyIDVoting = *((UInt160 *)entry->key_id_voting); +// uint32_t knownConfirmedAtHeight = entry->known_confirmed_at_height; +// UInt256 simplifiedMasternodeEntryHash = *((UInt256 *)entry->entry_hash); +// OperatorPublicKey *operator_public_key = entry->operator_public_key; +// UInt384 operatorPublicKey = *((UInt384 *)operator_public_key->data); +// uint16_t operatorPublicKeyVersion = operator_public_key->version; +// uintptr_t previous_operator_public_keys_count = entry->previous_operator_public_keys_count; +// BlockOperatorPublicKey *previous_operator_public_keys = entry->previous_operator_public_keys; +// NSMutableDictionary *operatorPublicKeys = [NSMutableDictionary dictionaryWithCapacity:previous_operator_public_keys_count]; +// for (NSUInteger i = 0; i < previous_operator_public_keys_count; i++) { +// BlockOperatorPublicKey prev_operator_public_key = previous_operator_public_keys[i]; +// UInt256 blockHash = *((UInt256 *)prev_operator_public_key.block_hash); +// uint32_t blockHeight = prev_operator_public_key.block_height; +// NSData *block = [NSData dataWithBlockHash:blockHash height:blockHeight]; +// NSMutableData *data = [NSMutableData dataWithUInt384:*((UInt384 *)prev_operator_public_key.key)]; +// [data appendData:[NSData dataWithUInt16:prev_operator_public_key.version]]; +// [operatorPublicKeys setObject:data forKey:block]; +// } +// uintptr_t previous_entry_hashes_count = entry->previous_entry_hashes_count; +// MasternodeEntryHash *previous_entry_hashes = entry->previous_entry_hashes; +// NSMutableDictionary *masternodeEntryHashes = [NSMutableDictionary dictionaryWithCapacity:previous_entry_hashes_count]; +// for (NSUInteger i = 0; i < previous_entry_hashes_count; i++) { +// MasternodeEntryHash entry_hash = previous_entry_hashes[i]; +// UInt256 blockHash = *((UInt256 *)entry_hash.block_hash); +// uint32_t blockHeight = entry_hash.block_height; +// NSData *block = [NSData dataWithBlockHash:blockHash height:blockHeight]; +// NSData *hash = [NSData dataWithBytes:entry_hash.hash length:32]; +// [masternodeEntryHashes setObject:hash forKey:block]; +// } +// uintptr_t previous_validity_count = entry->previous_validity_count; +// Validity *previous_validity = entry->previous_validity; +// NSMutableDictionary *validities = [NSMutableDictionary dictionaryWithCapacity:previous_validity_count]; +// for (NSUInteger i = 0; i < previous_validity_count; i++) { +// Validity validity = previous_validity[i]; +// UInt256 blockHash = *((UInt256 *)validity.block_hash); +// uint32_t blockHeight = validity.block_height; +// NSData *block = [NSData dataWithBlockHash:blockHash height:blockHeight]; +// NSNumber *isValid = [NSNumber numberWithBool:validity.is_valid]; +// [validities setObject:isValid forKey:block]; +// } +// UInt256 providerRegistrationTransactionHash = *((UInt256 *)entry->provider_registration_transaction_hash); +// UInt128 address = *((UInt128 *)entry->ip_address); +// uint16_t port = entry->port; +// uint32_t updateHeight = entry->update_height; +// uint16_t type = entry->mn_type; +// uint16_t platformHTTPPort = entry->platform_http_port; +// UInt160 platformNodeID = *((UInt160 *)entry->platform_node_id); +// return [self simplifiedMasternodeEntryWithProviderRegistrationTransactionHash:providerRegistrationTransactionHash +// confirmedHash:confirmedHash +// address:address +// port:port +// operatorBLSPublicKey:operatorPublicKey +// operatorPublicKeyVersion:operatorPublicKeyVersion +// previousOperatorBLSPublicKeys:operatorPublicKeys +// keyIDVoting:keyIDVoting +// isValid:isValid +// type:type +// platformHTTPPort:platformHTTPPort +// platformNodeID:platformNodeID +// previousValidity:validities +// knownConfirmedAtHeight:knownConfirmedAtHeight +// updateHeight:updateHeight +// simplifiedMasternodeEntryHash:simplifiedMasternodeEntryHash +// previousSimplifiedMasternodeEntryHashes:masternodeEntryHashes +// onChain:chain]; +//} +//+ (NSDictionary *)simplifiedEntriesWith:(MasternodeEntry *_Nullable *_Nonnull)entries count:(uintptr_t)count onChain:(DSChain *)chain { +// NSMutableDictionary *masternodes = [NSMutableDictionary dictionaryWithCapacity:count]; +// for (NSUInteger i = 0; i < count; i++) { +// MasternodeEntry *c_entry = entries[i]; +// DSSimplifiedMasternodeEntry *entry = [DSSimplifiedMasternodeEntry simplifiedEntryWith:c_entry onChain:chain]; +// UInt256 hash = uint256_reverse(entry.providerRegistrationTransactionHash); +// [masternodes setObject:entry forKey:uint256_data(hash)]; +// } +// return masternodes; +//} +// +//- (MasternodeEntry *)ffi_malloc { +// uint32_t known_confirmed_at_height = [self knownConfirmedAtHeight]; +// NSDictionary *previousOperatorPublicKeys = [self previousOperatorPublicKeys]; +// NSDictionary *previousSimplifiedMasternodeEntryHashes = [self previousSimplifiedMasternodeEntryHashes]; +// NSDictionary *previousValidity = [self previousValidity]; +// MasternodeEntry *masternode_entry = malloc(sizeof(MasternodeEntry)); +// masternode_entry->confirmed_hash = uint256_malloc([self confirmedHash]); +// masternode_entry->confirmed_hash_hashed_with_provider_registration_transaction_hash = uint256_malloc([self confirmedHashHashedWithProviderRegistrationTransactionHash]); +// masternode_entry->is_valid = [self isValid]; +// masternode_entry->key_id_voting = uint160_malloc([self keyIDVoting]); +// masternode_entry->known_confirmed_at_height = known_confirmed_at_height; +// masternode_entry->entry_hash = uint256_malloc([self simplifiedMasternodeEntryHash]); +// OperatorPublicKey *operator_public_key = malloc(sizeof(OperatorPublicKey)); +// memcpy(operator_public_key->data, [self operatorPublicKey].u8, sizeof(UInt384)); +// operator_public_key->version = self.operatorPublicKeyVersion; +// masternode_entry->operator_public_key = operator_public_key; +// NSUInteger previousOperatorPublicKeysCount = [previousOperatorPublicKeys count]; +// BlockOperatorPublicKey *previous_operator_public_keys = malloc(previousOperatorPublicKeysCount * sizeof(BlockOperatorPublicKey)); +// NSUInteger i = 0; +// for (NSData *block in previousOperatorPublicKeys) { +// NSData *keyVersionData = previousOperatorPublicKeys[block]; +// UInt256 blockHash = *(UInt256 *)(block.bytes); +// uint32_t blockHeight = *(uint32_t *)(block.bytes + sizeof(UInt256)); +// BlockOperatorPublicKey obj = {.block_height = blockHeight}; +// memcpy(obj.block_hash, blockHash.u8, sizeof(UInt256)); +// if (keyVersionData.length == 48) { +// obj.version = 0; +// memcpy(obj.key, keyVersionData.bytes, sizeof(UInt384)); +// } else { +// UInt384 keyData = [keyVersionData UInt384AtOffset:0]; +// obj.version = [keyVersionData UInt16AtOffset:48]; +// memcpy(obj.key, keyData.u8, sizeof(UInt384)); +// } +// previous_operator_public_keys[i++] = obj; +// } +// masternode_entry->previous_operator_public_keys = previous_operator_public_keys; +// masternode_entry->previous_operator_public_keys_count = previousOperatorPublicKeysCount; +// NSUInteger previousSimplifiedMasternodeEntryHashesCount = [previousSimplifiedMasternodeEntryHashes count]; +// MasternodeEntryHash *previous_masternode_entry_hashes = malloc(previousSimplifiedMasternodeEntryHashesCount * sizeof(MasternodeEntryHash)); +// i = 0; +// for (NSData *block in previousSimplifiedMasternodeEntryHashes) { +// NSData *hashData = previousSimplifiedMasternodeEntryHashes[block]; +// UInt256 blockHash = *(UInt256 *)(block.bytes); +// uint32_t blockHeight = *(uint32_t *)(block.bytes + sizeof(UInt256)); +// MasternodeEntryHash obj = {.block_height = blockHeight}; +// memcpy(obj.hash, hashData.bytes, sizeof(UInt256)); +// memcpy(obj.block_hash, blockHash.u8, sizeof(UInt256)); +// previous_masternode_entry_hashes[i++] = obj; +// } +// masternode_entry->previous_entry_hashes = previous_masternode_entry_hashes; +// masternode_entry->previous_entry_hashes_count = previousSimplifiedMasternodeEntryHashesCount; +// NSUInteger previousValidityCount = [previousValidity count]; +// Validity *previous_validity = malloc(previousValidityCount * sizeof(Validity)); +// i = 0; +// for (NSData *block in previousValidity) { +// NSNumber *flag = previousValidity[block]; +// UInt256 blockHash = *(UInt256 *)(block.bytes); +// uint32_t blockHeight = *(uint32_t *)(block.bytes + sizeof(UInt256)); +// Validity obj = {.block_height = blockHeight, .is_valid = [flag boolValue]}; +// memcpy(obj.block_hash, blockHash.u8, sizeof(UInt256)); +// previous_validity[i++] = obj; +// } +// masternode_entry->previous_validity = previous_validity; +// masternode_entry->previous_validity_count = previousValidityCount; +// masternode_entry->provider_registration_transaction_hash = uint256_malloc([self providerRegistrationTransactionHash]); +// masternode_entry->ip_address = uint128_malloc([self address]); +// masternode_entry->port = [self port]; +// masternode_entry->update_height = [self updateHeight]; +// masternode_entry->mn_type = [self type]; +// masternode_entry->platform_http_port = [self platformHTTPPort]; +// masternode_entry->platform_node_id = uint160_malloc([self platformNodeID]); +// return masternode_entry; +// +//} +// +//+ (void)ffi_free:(MasternodeEntry *)entry { +// free(entry->confirmed_hash); +// if (entry->confirmed_hash_hashed_with_provider_registration_transaction_hash) +// free(entry->confirmed_hash_hashed_with_provider_registration_transaction_hash); +// free(entry->operator_public_key); +// free(entry->entry_hash); +// free(entry->ip_address); +// free(entry->key_id_voting); +// free(entry->platform_node_id); +// free(entry->provider_registration_transaction_hash); +// if (entry->previous_entry_hashes) +// free(entry->previous_entry_hashes); +// if (entry->previous_operator_public_keys) +// free(entry->previous_operator_public_keys); +// if (entry->previous_validity) +// free(entry->previous_validity); +// free(entry); +//} @end diff --git a/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry.h b/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry.h index 21df4bb6c..33dcf9e99 100644 --- a/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry.h +++ b/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry.h @@ -70,6 +70,6 @@ - (NSDictionary *)toDictionaryAtBlockHash:(UInt256)blockHash usingBlockHeightLookup:(BlockHeightFinder)blockHeightLookup; - (void)setPlatformPing:(uint64_t)platformPing at:(NSDate *)time; - (void)savePlatformPingInfoInContext:(NSManagedObjectContext *)context; -- (void)mergedWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)masternodeEntry atBlockHeight:(uint32_t)blockHeight; +//- (void)mergedWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)masternodeEntry atBlockHeight:(uint32_t)blockHeight; @end diff --git a/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry.m b/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry.m index 201bc2bde..99e9cbfa7 100644 --- a/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry.m +++ b/DashSync/shared/Models/Masternode/DSSimplifiedMasternodeEntry.m @@ -7,6 +7,7 @@ #import "DSSimplifiedMasternodeEntry.h" #import "DSBlock.h" +#import "DSChain+Params.h" #import "DSKeyManager.h" #import "DSMerkleBlock.h" #import "DSMutableOrderedDataKeyDictionary.h" @@ -296,9 +297,12 @@ - (void)setProviderRegistrationTransactionHash:(UInt256)providerRegistrationTran } + (UInt256)hashConfirmedHash:(UInt256)confirmedHash withProviderRegistrationTransactionHash:(UInt256)providerRegistrationTransactionHash { - ByteArray byte_array = masternode_hash_confirmed_hash(confirmedHash.u8, providerRegistrationTransactionHash.u8); - NSData *data = [DSKeyManager NSDataFrom:byte_array]; - return data.UInt256; + u256 *confirmed_hash = u256_ctor_u(confirmedHash); + u256 *provider_reg_tx_hash = u256_ctor_u(providerRegistrationTransactionHash); + u256 *result = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_hash_confirmed_hash(confirmed_hash, provider_reg_tx_hash); + UInt256 hash = u256_cast(result); + u256_dtor(result); + return hash; } - (void)updateConfirmedHashHashedWithProviderRegistrationTransactionHash { @@ -479,36 +483,48 @@ - (void)savePlatformPingInfoInContext:(NSManagedObjectContext *)context { masternodeEntity.platformPingDate = self.platformPingDate; } -- (void)mergedWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)masternodeEntry atBlockHeight:(uint32_t)blockHeight { - if (self.updateHeight < blockHeight) { - self.updateHeight = blockHeight; - if (!uint128_eq(self.address, masternodeEntry.address)) { - self.address = masternodeEntry.address; - } - if (!uint256_eq(self.confirmedHash, masternodeEntry.confirmedHash)) { - self.confirmedHash = masternodeEntry.confirmedHash; - self.knownConfirmedAtHeight = masternodeEntry.knownConfirmedAtHeight; - } - if (self.port != masternodeEntry.port) { - self.port = masternodeEntry.port; - } - if (!uint160_eq(self.keyIDVoting, masternodeEntry.keyIDVoting)) { - self.keyIDVoting = masternodeEntry.keyIDVoting; - } - if (!uint384_eq(self.operatorPublicKey, masternodeEntry.operatorPublicKey)) { - self.operatorPublicKey = masternodeEntry.operatorPublicKey; - self.operatorPublicKeyVersion = masternodeEntry.operatorPublicKeyVersion; - } - if (self.isValid != masternodeEntry.isValid) { - self.isValid = masternodeEntry.isValid; - } - self.simplifiedMasternodeEntryHash = masternodeEntry.simplifiedMasternodeEntryHash; - [self mergePreviousFieldsUsingSimplifiedMasternodeEntrysPreviousFields:masternodeEntry atBlockHeight:blockHeight]; - } - else if (blockHeight < self.updateHeight) { - [self mergePreviousFieldsUsingSimplifiedMasternodeEntrysPreviousFields:masternodeEntry atBlockHeight:blockHeight]; - } -} +//- (void)mergedWithSimplifiedMasternodeEntry:(DMasternodeEntry *)masternodeEntry atBlockHeight:(uint32_t)blockHeight { +// if (self.updateHeight < blockHeight) { +// self.updateHeight = blockHeight; +// u128 *addr = u128_ctor_u(self.address); +// BOOL addr_are_equal = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_address_is_equal_to(masternodeEntry, addr); +// u128_dtor(addr); +// if (!addr_are_equal) { +// self.address = *((UInt128 *)masternodeEntry->socket_address->ip_address); +// } +// u256 *confirmed_hash = u256_ctor_u(self.confirmedHash); +// BOOL confirmed_hashes_are_equal = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_confirmed_hash_is_equal_to(masternodeEntry, confirmed_hash); +// u256_dtor(confirmed_hash); +// if (!confirmed_hashes_are_equal) { +// self.confirmedHash = *((UInt256 *)masternodeEntry->confirmed_hash->values); +// self.knownConfirmedAtHeight = *(masternodeEntry->known_confirmed_at_height); +// } +// if (self.port != masternodeEntry->socket_address->port) { +// self.port = masternodeEntry->socket_address->port; +// } +// u160 *key_id = u160_ctor_u(self.keyIDVoting); +// BOOL key_ids_are_equal = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_key_id_is_equal_to(masternodeEntry, key_id); +// u160_dtor(key_id); +// if (!key_ids_are_equal) { +// self.keyIDVoting = *((UInt160 *)masternodeEntry->key_id_voting->values); +// } +// u384 *pub_key = u384_ctor_u(self.operatorPublicKey); +// BOOL pubkeys_are_equal = dash_spv_masternode_processor_models_masternode_entry_MasternodeEntry_operator_pub_key_is_equal_to(masternodeEntry, pub_key); +// u384_dtor(pub_key); +// if (!pubkeys_are_equal) { +// self.operatorPublicKey = *((UInt384 *)masternodeEntry->operator_public_key->data->values); +// self.operatorPublicKeyVersion = masternodeEntry->operator_public_key->version; +// } +// if (self.isValid != masternodeEntry->is_valid) { +// self.isValid = masternodeEntry->is_valid; +// } +// self.simplifiedMasternodeEntryHash = *((UInt256 *)masternodeEntry->entry_hash->values); +// [self mergePreviousFieldsUsingSimplifiedMasternodeEntrysPreviousFields:masternodeEntry atBlockHeight:blockHeight]; +// } +// else if (blockHeight < self.updateHeight) { +// [self mergePreviousFieldsUsingSimplifiedMasternodeEntrysPreviousFields:masternodeEntry atBlockHeight:blockHeight]; +// } +//} - (NSDictionary *)blockHashDictionaryFromBlockDictionary:(NSDictionary *)blockHashDictionary { NSMutableDictionary *rDictionary = [NSMutableDictionary dictionary]; @@ -523,26 +539,78 @@ - (void)mergedWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)maste return rDictionary; } -- (void)mergePreviousFieldsUsingSimplifiedMasternodeEntrysPreviousFields:(DSSimplifiedMasternodeEntry *)entry atBlockHeight:(uint32_t)blockHeight { - //SimplifiedMasternodeEntryHashes - NSDictionary *oldPreviousSimplifiedMasternodeEntryHashesDictionary = entry.previousSimplifiedMasternodeEntryHashes; - if (oldPreviousSimplifiedMasternodeEntryHashesDictionary && oldPreviousSimplifiedMasternodeEntryHashesDictionary.count) { - self.previousSimplifiedMasternodeEntryHashes = [NSDictionary mergeDictionary:self.previousSimplifiedMasternodeEntryHashes withDictionary:oldPreviousSimplifiedMasternodeEntryHashesDictionary]; +- (void)mergePreviousFieldsUsingSimplifiedMasternodeEntrysPreviousFields:(DMasternodeEntry *)entry + atBlockHeight:(uint32_t)blockHeight { + std_collections_Map_keys_dash_spv_masternode_processor_common_block_Block_values_u8_arr_32 *prev_entry_hashes = entry->previous_entry_hashes; + NSMutableDictionary *prevEntryHashes = [NSMutableDictionary dictionaryWithCapacity:prev_entry_hashes->count]; + for (int i = 0; i < prev_entry_hashes->count; i++) { + DBlock *key = prev_entry_hashes->keys[i]; + NSMutableData *d = [NSMutableData dataWithBytes:key->hash->values length:32]; + [d appendUInt32:key->height]; + u256 *value = prev_entry_hashes->values[i]; + [prevEntryHashes setObject:NSDataFromPtr(value) forKey:d]; } - - //OperatorBLSPublicKeys - NSDictionary *oldPreviousOperatorBLSPublicKeysDictionary = entry.previousOperatorPublicKeys; - if (oldPreviousOperatorBLSPublicKeysDictionary && oldPreviousOperatorBLSPublicKeysDictionary.count) { - self.previousOperatorPublicKeys = [NSDictionary mergeDictionary:self.previousOperatorPublicKeys withDictionary:oldPreviousOperatorBLSPublicKeysDictionary]; + if (!self.previousSimplifiedMasternodeEntryHashes || [self.previousSimplifiedMasternodeEntryHashes count] == 0) { + self.previousSimplifiedMasternodeEntryHashes = prevEntryHashes; + } else { + NSMutableDictionary *mergedDictionary = [self.previousSimplifiedMasternodeEntryHashes mutableCopy]; + [mergedDictionary addEntriesFromDictionary:prevEntryHashes]; + self.previousSimplifiedMasternodeEntryHashes = mergedDictionary; } - - //MasternodeValidity - NSDictionary *oldPreviousValidityDictionary = entry.previousValidity; - if (oldPreviousValidityDictionary && oldPreviousValidityDictionary.count) { - self.previousValidity = [NSDictionary mergeDictionary:self.previousValidity withDictionary:oldPreviousValidityDictionary]; + std_collections_Map_keys_dash_spv_masternode_processor_common_block_Block_values_dash_spv_crypto_keys_operator_public_key_OperatorPublicKey *prev_operator_keys = entry->previous_operator_public_keys; + NSMutableDictionary *prevOperatorKeys = [NSMutableDictionary dictionaryWithCapacity:prev_operator_keys->count]; + // TODO: key version lost here + for (int i = 0; i < prev_operator_keys->count; i++) { + DBlock *key = prev_operator_keys->keys[i]; + NSMutableData *d = [NSMutableData dataWithBytes:key->hash->values length:32]; + [d appendUInt32:key->height]; + dash_spv_crypto_keys_operator_public_key_OperatorPublicKey *value = prev_operator_keys->values[i]; + [prevOperatorKeys setObject:NSDataFromPtr(value->data) forKey:d]; + } + if (!self.previousOperatorPublicKeys || [self.previousOperatorPublicKeys count] == 0) { + self.previousOperatorPublicKeys = prevOperatorKeys; + } else { + NSMutableDictionary *mergedDictionary = [self.previousOperatorPublicKeys mutableCopy]; + [mergedDictionary addEntriesFromDictionary:prevOperatorKeys]; + self.previousOperatorPublicKeys = mergedDictionary; + } + std_collections_Map_keys_dash_spv_masternode_processor_common_block_Block_values_bool *prev_validity = entry->previous_validity; + NSMutableDictionary *prevValidity = [NSMutableDictionary dictionaryWithCapacity:prev_validity->count]; + for (int i = 0; i < prev_validity->count; i++) { + DBlock *key = prev_validity->keys[i]; + NSMutableData *d = [NSMutableData dataWithBytes:key->hash->values length:32]; + [d appendUInt32:key->height]; + bool value = prev_validity->values[i]; + [prevValidity setObject:@(value) forKey:d]; + } + if (!self.previousValidity || [self.previousValidity count] == 0) { + self.previousValidity = prevValidity; + } else { + NSMutableDictionary *mergedDictionary = [self.previousValidity mutableCopy]; + [mergedDictionary addEntriesFromDictionary:prevValidity]; + self.previousValidity = mergedDictionary; } - if (uint256_is_not_zero(self.confirmedHash) && uint256_is_not_zero(entry.confirmedHash) && (self.knownConfirmedAtHeight > blockHeight)) { + + +// NSDictionary *oldPreviousSimplifiedMasternodeEntryHashesDictionary = entry.previousSimplifiedMasternodeEntryHashes; +// if (oldPreviousSimplifiedMasternodeEntryHashesDictionary && oldPreviousSimplifiedMasternodeEntryHashesDictionary.count) { +// self.previousSimplifiedMasternodeEntryHashes = [NSDictionary mergeDictionary:self.previousSimplifiedMasternodeEntryHashes withDictionary:oldPreviousSimplifiedMasternodeEntryHashesDictionary]; +// } +// +// //OperatorBLSPublicKeys +// NSDictionary *oldPreviousOperatorBLSPublicKeysDictionary = entry.previousOperatorPublicKeys; +// if (oldPreviousOperatorBLSPublicKeysDictionary && oldPreviousOperatorBLSPublicKeysDictionary.count) { +// self.previousOperatorPublicKeys = [NSDictionary mergeDictionary:self.previousOperatorPublicKeys withDictionary:oldPreviousOperatorBLSPublicKeysDictionary]; +// } +// +// //MasternodeValidity +// NSDictionary *oldPreviousValidityDictionary = entry.previousValidity; +// if (oldPreviousValidityDictionary && oldPreviousValidityDictionary.count) { +// self.previousValidity = [NSDictionary mergeDictionary:self.previousValidity withDictionary:oldPreviousValidityDictionary]; +// } + + if (uint256_is_not_zero(self.confirmedHash) && !u_is_zero(entry->confirmed_hash) && (self.knownConfirmedAtHeight > blockHeight)) { //we now know it was confirmed earlier so update to earlier self.knownConfirmedAtHeight = blockHeight; } diff --git a/DashSync/shared/Models/Network/DSPeer.h b/DashSync/shared/Models/Network/DSPeer.h index db8421e13..3b8881f0f 100644 --- a/DashSync/shared/Models/Network/DSPeer.h +++ b/DashSync/shared/Models/Network/DSPeer.h @@ -28,8 +28,6 @@ #import "BigIntTypes.h" #import "DSChain.h" -//#import "DSGovernanceHashesRequest.h" -//#import "DSGovernanceSyncRequest.h" #import "DSMessageRequest.h" #import @@ -179,7 +177,7 @@ typedef NS_ENUM(uint32_t, DSSyncCountInfo); typedef void (^MempoolCompletionBlock)(BOOL success, BOOL needed, BOOL interruptedByDisconnect); -@class DSPeer, DSTransaction, DSMerkleBlock, DSBlock, DSChain, DSSpork, DSGovernanceObject, DSGovernanceVote, DSTransactionLockVote, DSInstantSendTransactionLock, DSChainLock; +@class DSPeer, DSTransaction, DSMerkleBlock, DSBlock, DSChain, DSSpork, DSGovernanceObject, DSGovernanceVote, DSTransactionLockVote, DSInstantSendTransactionLock, DSChainLock, DSGovernanceSyncRequest, DSGovernanceHashesRequest; @protocol DSPeerDelegate @required @@ -248,7 +246,7 @@ typedef void (^MempoolCompletionBlock)(BOOL success, BOOL needed, BOOL interrupt @end -typedef NS_ENUM(NSUInteger, DSPeerStatus) +typedef NS_ENUM(NSInteger, DSPeerStatus) { DSPeerStatus_Unknown = -1, DSPeerStatus_Disconnected = 0, @@ -257,14 +255,13 @@ typedef NS_ENUM(NSUInteger, DSPeerStatus) DSPeerStatus_Banned }; -typedef NS_ENUM(NSUInteger, DSPeerType) +typedef NS_ENUM(NSInteger, DSPeerType) { DSPeerType_Unknown = -1, DSPeerType_FullNode = 0, DSPeerType_MasterNode }; -@class DSGovernanceSyncRequest, DSGovernanceHashesRequest; @interface DSPeer : NSObject @@ -308,11 +305,11 @@ typedef NS_ENUM(NSUInteger, DSPeerType) @property (nonatomic, readonly) DSChain *chain; + (instancetype)peerWithAddress:(UInt128)address andPort:(uint16_t)port onChain:(DSChain *)chain; -+ (instancetype)peerWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry; +//+ (instancetype)peerWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry; + (instancetype)peerWithHost:(NSString *)host onChain:(DSChain *)chain; - (instancetype)initWithAddress:(UInt128)address andPort:(uint16_t)port onChain:(DSChain *)chain; -- (instancetype)initWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry; +//- (instancetype)initWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry; - (instancetype)initWithAddress:(UInt128)address port:(uint16_t)port onChain:(DSChain *)chain timestamp:(NSTimeInterval)timestamp services:(uint64_t)services; - (instancetype)initWithHost:(NSString *)host onChain:(DSChain *)chain; diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index 5a3cadb03..42bca9fa3 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -28,8 +28,9 @@ #import "DSPeer.h" #import "DSAddrRequest.h" -#import "DSBlockchainIdentityRegistrationTransition.h" +#import "DSIdentityRegistrationTransition.h" #import "DSBloomFilter.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" #import "DSChainEntity+CoreDataClass.h" #import "DSChainLock.h" @@ -48,7 +49,6 @@ #import "DSInstantSendTransactionLock.h" #import "DSInvRequest.h" #import "DSKeyManager.h" -#import "DSMasternodeManager.h" #import "DSMerkleBlock.h" #import "DSNotFoundRequest.h" #import "DSOptionsManager.h" @@ -56,7 +56,6 @@ #import "DSPeerManager.h" #import "DSPingRequest.h" #import "DSReachabilityManager.h" -#import "DSSimplifiedMasternodeEntry.h" #import "DSSpork.h" #import "DSSporkManager.h" #import "DSTransaction.h" @@ -143,9 +142,9 @@ + (instancetype)peerWithHost:(NSString *)host onChain:(DSChain *)chain { return [[self alloc] initWithHost:host onChain:chain]; } -+ (instancetype)peerWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry { - return [[self alloc] initWithSimplifiedMasternodeEntry:simplifiedMasternodeEntry]; -} +//+ (instancetype)peerWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry { +// return [[self alloc] initWithSimplifiedMasternodeEntry:simplifiedMasternodeEntry]; +//} - (instancetype)initWithAddress:(UInt128)address andPort:(uint16_t)port onChain:(DSChain *)chain { if (!(self = [super init])) return nil; @@ -157,9 +156,9 @@ - (instancetype)initWithAddress:(UInt128)address andPort:(uint16_t)port onChain: return self; } -- (instancetype)initWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry { - return [self initWithAddress:simplifiedMasternodeEntry.address andPort:simplifiedMasternodeEntry.port onChain:simplifiedMasternodeEntry.chain]; -} +//- (instancetype)initWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry { +// return [self initWithAddress:simplifiedMasternodeEntry.address andPort:simplifiedMasternodeEntry.port onChain:simplifiedMasternodeEntry.chain]; +//} - (instancetype)initWithHost:(NSString *)host onChain:(DSChain *)chain { if (!chain) return nil; diff --git a/DashSync/shared/Models/Payment/DSPaymentProtocol.m b/DashSync/shared/Models/Payment/DSPaymentProtocol.m index b0aa68bf7..3bc4608a3 100644 --- a/DashSync/shared/Models/Payment/DSPaymentProtocol.m +++ b/DashSync/shared/Models/Payment/DSPaymentProtocol.m @@ -27,6 +27,7 @@ #import "DSPaymentProtocol.h" #import "DSChain.h" +#import "DSChain+Params.h" #import "DSTransaction.h" #import "NSData+Dash.h" #import "NSDate+Utils.h" diff --git a/DashSync/shared/Models/Payment/DSPaymentRequest.h b/DashSync/shared/Models/Payment/DSPaymentRequest.h index 64d5efbc2..1f2d59fb0 100644 --- a/DashSync/shared/Models/Payment/DSPaymentRequest.h +++ b/DashSync/shared/Models/Payment/DSPaymentRequest.h @@ -30,7 +30,7 @@ NS_ASSUME_NONNULL_BEGIN -@class DSPaymentProtocolRequest, DSPaymentProtocolPayment, DSPaymentProtocolACK, DSChain, DSBlockchainIdentity, DSAccount; +@class DSPaymentProtocolRequest, DSPaymentProtocolPayment, DSPaymentProtocolACK, DSChain, DSIdentity, DSAccount; // BIP21 bitcoin payment request URI https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki @interface DSPaymentRequest : NSObject @@ -61,22 +61,36 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithData:(NSData *)data onChain:(DSChain *)chain; - (instancetype)initWithURL:(NSURL *)url onChain:(DSChain *)chain; -- (DSPaymentProtocolRequest *)protocolRequestForBlockchainIdentity:(DSBlockchainIdentity *_Nullable)blockchainIdentity onAccount:(DSAccount *)account inContext:(NSManagedObjectContext *)context; +- (DSPaymentProtocolRequest *)protocolRequestForIdentity:(DSIdentity *_Nullable)identity + onAccount:(DSAccount *)account + inContext:(NSManagedObjectContext *)context; -- (BOOL)isValidAsDashpayPaymentRequestForBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity onAccount:(DSAccount *)account inContext:(NSManagedObjectContext *)context; +- (BOOL)isValidAsDashpayPaymentRequestForIdentity:(DSIdentity *)identity + onAccount:(DSAccount *)account + inContext:(NSManagedObjectContext *)context; -- (NSString *)paymentAddressForBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity onAccount:(DSAccount *)account fallbackToPaymentAddressIfIssue:(BOOL)fallbackToPaymentAddressIfIssue inContext:(NSManagedObjectContext *)context; +- (NSString *)paymentAddressForIdentity:(DSIdentity *)identity + onAccount:(DSAccount *)account + fallbackToPaymentAddressIfIssue:(BOOL)fallbackToPaymentAddressIfIssue + inContext:(NSManagedObjectContext *)context; - (void)fetchBIP70WithTimeout:(NSTimeInterval)timeout completion:(void (^)(DSPaymentProtocolRequest *req, NSError *error))completion; // fetches a BIP70 request over HTTP and calls completion block // https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki -+ (void)fetch:(NSString *)url scheme:(NSString *)scheme callbackScheme:(NSString *)callbackScheme onChain:(DSChain *)chain timeout:(NSTimeInterval)timeout - completion:(void (^)(DSPaymentProtocolRequest *req, NSError *error))completion; ++ (void)fetch:(NSString *)url + scheme:(NSString *)scheme +callbackScheme:(NSString *)callbackScheme + onChain:(DSChain *)chain + timeout:(NSTimeInterval)timeout + completion:(void (^)(DSPaymentProtocolRequest *req, NSError *error))completion; // posts a BIP70 payment object to the specified URL -+ (void)postPayment:(DSPaymentProtocolPayment *)payment scheme:(NSString *)scheme to:(NSString *)paymentURL onChain:(DSChain *)chain ++ (void)postPayment:(DSPaymentProtocolPayment *)payment + scheme:(NSString *)scheme + to:(NSString *)paymentURL + onChain:(DSChain *)chain timeout:(NSTimeInterval)timeout completion:(void (^)(DSPaymentProtocolACK *ack, NSError *error))completion; diff --git a/DashSync/shared/Models/Payment/DSPaymentRequest.m b/DashSync/shared/Models/Payment/DSPaymentRequest.m index 54d62d751..22967e26f 100644 --- a/DashSync/shared/Models/Payment/DSPaymentRequest.m +++ b/DashSync/shared/Models/Payment/DSPaymentRequest.m @@ -28,10 +28,11 @@ #import "DSPaymentRequest.h" #import "DSAccount.h" -#import "DSBlockchainIdentity.h" +#import "DSIdentity.h" #import "DSBlockchainIdentityEntity+CoreDataClass.h" #import "DSBlockchainIdentityUsernameEntity+CoreDataClass.h" #import "DSChain.h" +#import "DSChain+Params.h" #import "DSCurrencyPriceObject.h" #import "DSDashpayUserEntity+CoreDataClass.h" #import "DSFriendRequestEntity+CoreDataClass.h" @@ -97,9 +98,11 @@ - (void)setString:(NSString *)string { NSURL *url = [NSURL URLWithString:s]; if (!url || !url.scheme) { - if ([DSKeyManager isValidDashAddress:s forChain:self.chain] || - [s isValidDashPrivateKeyOnChain:self.chain] || - [DSKeyManager isValidDashBIP38Key:s]) { + + if (dash_spv_crypto_bip_bip38_is_valid_payment_request_address((char *)[s UTF8String], self.chain.chainType)) { +// if ([DSKeyManager isValidDashAddress:s forChain:self.chain] || +// [s isValidDashPrivateKeyOnChain:self.chain] || +// [DSKeyManager isValidDashBIP38Key:s]) { url = [NSURL URLWithString:[NSString stringWithFormat:@"dash://%@", s]]; self.scheme = @"dash"; } @@ -257,11 +260,13 @@ - (BOOL)isValidAsNonDashpayPaymentRequest { } } -- (BOOL)isValidAsDashpayPaymentRequestForBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity onAccount:(DSAccount *)account inContext:(NSManagedObjectContext *)context { +- (BOOL)isValidAsDashpayPaymentRequestForIdentity:(DSIdentity *)identity + onAccount:(DSAccount *)account + inContext:(NSManagedObjectContext *)context { if ([self.scheme isEqualToString:@"dash"]) { __block DSIncomingFundsDerivationPath *friendshipDerivationPath = nil; [context performBlockAndWait:^{ - DSDashpayUserEntity *dashpayUserEntity = [blockchainIdentity matchingDashpayUserInContext:context]; + DSDashpayUserEntity *dashpayUserEntity = [identity matchingDashpayUserInContext:context]; for (DSFriendRequestEntity *friendRequest in dashpayUserEntity.incomingRequests) { if ([[friendRequest.sourceContact.associatedBlockchainIdentity.dashpayUsername stringValue] isEqualToString:self.dashpayUsername]) { @@ -289,8 +294,11 @@ - (BOOL)isValidAsDashpayPaymentRequestForBlockchainIdentity:(DSBlockchainIdentit } } -- (NSString *)paymentAddressForBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity onAccount:(DSAccount *)account fallbackToPaymentAddressIfIssue:(BOOL)fallbackToPaymentAddressIfIssue inContext:(NSManagedObjectContext *)context { - if (!blockchainIdentity || !self.dashpayUsername) { +- (NSString *)paymentAddressForIdentity:(DSIdentity *)identity + onAccount:(DSAccount *)account + fallbackToPaymentAddressIfIssue:(BOOL)fallbackToPaymentAddressIfIssue + inContext:(NSManagedObjectContext *)context { + if (!identity || !self.dashpayUsername) { if (fallbackToPaymentAddressIfIssue) { return [self paymentAddress]; } else { @@ -299,7 +307,7 @@ - (NSString *)paymentAddressForBlockchainIdentity:(DSBlockchainIdentity *)blockc } __block DSIncomingFundsDerivationPath *friendshipDerivationPath = nil; [context performBlockAndWait:^{ - DSDashpayUserEntity *dashpayUserEntity = [blockchainIdentity matchingDashpayUserInContext:context]; + DSDashpayUserEntity *dashpayUserEntity = [identity matchingDashpayUserInContext:context]; for (DSFriendRequestEntity *friendRequest in dashpayUserEntity.incomingRequests) { if ([[friendRequest.sourceContact.associatedBlockchainIdentity.dashpayUsername stringValue] isEqualToString:self.dashpayUsername]) { @@ -319,13 +327,15 @@ - (NSString *)paymentAddressForBlockchainIdentity:(DSBlockchainIdentity *)blockc return friendshipDerivationPath.receiveAddress; } -- (DSPaymentProtocolRequest *)protocolRequestForBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity onAccount:(DSAccount *)account inContext:(NSManagedObjectContext *)context { - if (!blockchainIdentity || !self.dashpayUsername) { +- (DSPaymentProtocolRequest *)protocolRequestForIdentity:(DSIdentity *)identity + onAccount:(DSAccount *)account + inContext:(NSManagedObjectContext *)context { + if (!identity || !self.dashpayUsername) { return [self protocolRequest]; } __block DSIncomingFundsDerivationPath *friendshipDerivationPath = nil; [context performBlockAndWait:^{ - DSDashpayUserEntity *dashpayUserEntity = [blockchainIdentity matchingDashpayUserInContext:context]; + DSDashpayUserEntity *dashpayUserEntity = [identity matchingDashpayUserInContext:context]; for (DSFriendRequestEntity *friendRequest in dashpayUserEntity.incomingRequests) { if ([[friendRequest.sourceContact.associatedBlockchainIdentity.dashpayUsername stringValue] isEqualToString:self.dashpayUsername]) { diff --git a/DashSync/shared/Models/Persistence/Migration/Internal/DSCoreDataMigrationVersion.h b/DashSync/shared/Models/Persistence/Migration/Internal/DSCoreDataMigrationVersion.h index ac5cb6549..b9590be08 100644 --- a/DashSync/shared/Models/Persistence/Migration/Internal/DSCoreDataMigrationVersion.h +++ b/DashSync/shared/Models/Persistence/Migration/Internal/DSCoreDataMigrationVersion.h @@ -42,6 +42,7 @@ typedef NS_ENUM(NSInteger, DSCoreDataMigrationVersionValue) DSCoreDataMigrationVersionValue_19 = 19, DSCoreDataMigrationVersionValue_20 = 20, DSCoreDataMigrationVersionValue_21 = 21, + DSCoreDataMigrationVersionValue_22 = 22, }; @interface DSCoreDataMigrationVersion : NSObject diff --git a/DashSync/shared/Models/Persistence/Migration/Internal/DSCoreDataMigrationVersion.m b/DashSync/shared/Models/Persistence/Migration/Internal/DSCoreDataMigrationVersion.m index e6526f1f1..941126d1e 100644 --- a/DashSync/shared/Models/Persistence/Migration/Internal/DSCoreDataMigrationVersion.m +++ b/DashSync/shared/Models/Persistence/Migration/Internal/DSCoreDataMigrationVersion.m @@ -20,7 +20,7 @@ @implementation DSCoreDataMigrationVersion + (DSCoreDataMigrationVersionValue)current { - return DSCoreDataMigrationVersionValue_21; + return DSCoreDataMigrationVersionValue_22; } + (NSString *)modelResourceForVersion:(DSCoreDataMigrationVersionValue)version { @@ -46,6 +46,7 @@ + (NSString *)modelResourceForVersion:(DSCoreDataMigrationVersionValue)version { case DSCoreDataMigrationVersionValue_19: return @"DashSync 19"; case DSCoreDataMigrationVersionValue_20: return @"DashSync 20"; case DSCoreDataMigrationVersionValue_21: return @"DashSync 21"; + case DSCoreDataMigrationVersionValue_22: return @"DashSync 22"; default: return [NSString stringWithFormat:@"DashSync %ld", (long)version]; } diff --git a/DashSync/shared/Models/Persistence/Migration/Policies/DSMerkleBlockEntity6To7MigrationPolicy.m b/DashSync/shared/Models/Persistence/Migration/Policies/DSMerkleBlockEntity6To7MigrationPolicy.m index 95b2bc2d1..ed27f87bc 100644 --- a/DashSync/shared/Models/Persistence/Migration/Policies/DSMerkleBlockEntity6To7MigrationPolicy.m +++ b/DashSync/shared/Models/Persistence/Migration/Policies/DSMerkleBlockEntity6To7MigrationPolicy.m @@ -130,7 +130,7 @@ - (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance NSNumber *height = [sInstance valueForKey:@"height"]; NSManagedObject *chainEntity = [sInstance valueForKey:@"chain"]; NSParameterAssert(chainEntity); - if (height != nil && [height intValue] != BLOCK_UNKNOWN_HEIGHT && [[chainEntity valueForKey:@"type"] intValue] == ChainType_MainNet && [height intValue] > self.lastKnownSourceBlockHeight) { + if (height != nil && [height intValue] != BLOCK_UNKNOWN_HEIGHT && [[chainEntity valueForKey:@"type"] intValue] == dash_spv_crypto_network_chain_type_ChainType_MainNet && [height intValue] > self.lastKnownSourceBlockHeight) { self.lastKnownSourceBlockHeight = [height unsignedIntValue]; } @@ -144,7 +144,7 @@ - (BOOL)endEntityMapping:(NSEntityMapping *)mapping manager:(NSMigrationManager self.lastKnownSourceBlockWithCheckpoint = [[DSMerkleBlock alloc] initWithCheckpoint:lastCheckpoint onChain:chain]; } if (self.lastKnownSourceBlockWithCheckpoint) { - DSChainEntity *chainEntity = [self chainEntityForType:ChainType_MainNet inContext:manager.destinationContext]; + DSChainEntity *chainEntity = [self chainEntityForType:dash_spv_crypto_network_chain_type_ChainType_MainNet inContext:manager.destinationContext]; if (chainEntity) { [chainEntity setValue:uint256_data(self.lastKnownSourceBlockWithCheckpoint.blockHash) forKey:@"syncBlockHash"]; [chainEntity setValue:@(self.lastKnownSourceBlockWithCheckpoint.height) forKey:@"syncBlockHeight"]; @@ -176,7 +176,7 @@ - (BOOL)endEntityMapping:(NSEntityMapping *)mapping manager:(NSMigrationManager } -- (DSChainEntity *)chainEntityForType:(ChainType_Tag)type inContext:(NSManagedObjectContext *)context { +- (DSChainEntity *)chainEntityForType:(uint16_t)type inContext:(NSManagedObjectContext *)context { NSFetchRequest *fetchRequest = [DSChainEntity fetchRequest]; fetchRequest.predicate = [NSPredicate predicateWithFormat:@"type = %d", type]; NSError *error = nil; diff --git a/DashSync/shared/Models/Platform/Base/DPBaseObject.m b/DashSync/shared/Models/Platform/Base/DPBaseObject.m index 8d8d103be..34ef047b7 100644 --- a/DashSync/shared/Models/Platform/Base/DPBaseObject.m +++ b/DashSync/shared/Models/Platform/Base/DPBaseObject.m @@ -19,6 +19,7 @@ #import "BigIntTypes.h" +#import "DSChain+Params.h" #import "NSData+Dash.h" #import "NSMutableData+Dash.h" #import diff --git a/DashSync/shared/Models/Platform/Contract/DPContract+Protected.h b/DashSync/shared/Models/Platform/Contract/DPContract+Protected.h index 358d37401..db2d5b927 100644 --- a/DashSync/shared/Models/Platform/Contract/DPContract+Protected.h +++ b/DashSync/shared/Models/Platform/Contract/DPContract+Protected.h @@ -17,15 +17,14 @@ #import "DPContract.h" -@class DSBlockchainIdentity; - NS_ASSUME_NONNULL_BEGIN @interface DPContract () -- (void)setContractState:(DPContractState)contractState inContext:(NSManagedObjectContext *)context; +@property (assign, nonatomic) DPContractState contractState; -- (DSContractTransition *)contractRegistrationTransitionForIdentity:(DSBlockchainIdentity *)blockchainIdentity; +//- (DSContractTransition *)contractRegistrationTransitionForIdentityId:(UInt256)identityId; +- (void)saveAndWaitInContext:(NSManagedObjectContext *)context; @end diff --git a/DashSync/shared/Models/Platform/Contract/DPContract.h b/DashSync/shared/Models/Platform/Contract/DPContract.h index 284881153..8cac385c2 100644 --- a/DashSync/shared/Models/Platform/Contract/DPContract.h +++ b/DashSync/shared/Models/Platform/Contract/DPContract.h @@ -16,6 +16,7 @@ // #import "BigIntTypes.h" +#import "DSKeyManager.h" #import "DPBaseObject.h" NS_ASSUME_NONNULL_BEGIN @@ -23,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN FOUNDATION_EXPORT NSString *const DPContractDidUpdateNotification; FOUNDATION_EXPORT NSString *const DSContractUpdateNotificationKey; -@class DSChain, DSContractTransition, DSBlockchainIdentity; +@class DSChain, DSIdentity; typedef NS_ENUM(NSUInteger, DPContractState) { @@ -36,7 +37,7 @@ typedef NS_ENUM(NSUInteger, DPContractState) @interface DPContract : DPBaseObject @property (readonly, copy, nonatomic) NSString *localContractIdentifier; -@property (readonly, nonatomic) UInt256 registeredBlockchainIdentityUniqueID; +@property (readonly, nonatomic) UInt256 registeredIdentityUniqueID; @property (readonly, copy, nonatomic) NSString *name; @property (readonly, nonatomic) UInt256 contractId; @property (readonly, copy, nonatomic) NSString *base58ContractId; @@ -49,30 +50,32 @@ typedef NS_ENUM(NSUInteger, DPContractState) @property (assign, nonatomic) NSInteger version; @property (copy, nonatomic) NSString *jsonMetaSchema; -@property (copy, nonatomic) NSDictionary *documents; -@property (copy, nonatomic) NSDictionary *definitions; +@property (readonly, nonatomic) dpp_data_contract_DataContract *raw_contract; +@property (readonly, nonatomic) DDocumentTypes *documents; +//@property (copy, nonatomic) NSDictionary *documents; +//@property (copy, nonatomic) NSDictionary *definitions; - (instancetype)initWithLocalContractIdentifier:(NSString *)contractID - documents:(NSDictionary *)documents +// documents:(NSDictionary *)documents + raw_contract:(dpp_data_contract_DataContract *)raw_contract onChain:(DSChain *)chain; - (instancetype)init NS_UNAVAILABLE; - (BOOL)isDocumentDefinedForType:(NSString *)type; -- (void)setDocumentSchema:(DSStringValueDictionary *)schema forType:(NSString *)type; -- (nullable DSStringValueDictionary *)documentSchemaForType:(NSString *)type; +//- (void)setDocumentSchema:(DSStringValueDictionary *)schema forType:(NSString *)type; +//- (nullable DSStringValueDictionary *)documentSchemaForType:(NSString *)type; +//- (nullable NSDictionary *)documentSchemaRefForType:(NSString *)type; -- (nullable NSDictionary *)documentSchemaRefForType:(NSString *)type; - -- (void)registerCreator:(DSBlockchainIdentity *)blockchainIdentity inContext:(NSManagedObjectContext *)context; -- (void)unregisterCreatorInContext:(NSManagedObjectContext *)context; +- (void)registerCreator:(DSIdentity *)identity; +- (void)unregisterCreator; + (DPContract *)localDashpayContractForChain:(DSChain *)chain; + (DPContract *)localDPNSContractForChain:(DSChain *)chain; -+ (DPContract *)localDashThumbnailContractForChain:(DSChain *)chain; +//+ (DPContract *)localDashThumbnailContractForChain:(DSChain *)chain; -- (UInt256)contractIdIfRegisteredByBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity; +- (UInt256)contractIdIfRegisteredByIdentity:(DSIdentity *)identity; @end diff --git a/DashSync/shared/Models/Platform/Contract/DPContract.m b/DashSync/shared/Models/Platform/Contract/DPContract.m index 772e1bee1..0309fd70e 100644 --- a/DashSync/shared/Models/Platform/Contract/DPContract.m +++ b/DashSync/shared/Models/Platform/Contract/DPContract.m @@ -19,8 +19,9 @@ #import "DSAuthenticationKeysDerivationPath.h" #import "DSBlockchainIdentityEntity+CoreDataClass.h" #import "DSChain.h" +#import "DSChain+Params.h" #import "DSContractEntity+CoreDataClass.h" -#import "DSContractTransition.h" +//#import "DSContractTransition.h" #import "DSDashPlatform.h" #import "DSWallet.h" #import "NSData+DSCborDecoding.h" @@ -28,6 +29,7 @@ #import "NSManagedObject+Sugar.h" #import "NSMutableData+Dash.h" #import "NSString+Bitcoin.h" +#import "NSError+Dash.h" NS_ASSUME_NONNULL_BEGIN @@ -37,10 +39,11 @@ @interface DPContract () +@property (assign, nonatomic) dpp_data_contract_DataContract *raw_contract; @property (strong, nonatomic) NSMutableDictionary *mutableDocuments; @property (copy, nonatomic, null_resettable) NSString *localContractIdentifier; @property (assign, nonatomic) UInt256 contractId; -@property (assign, nonatomic) UInt256 registeredBlockchainIdentityUniqueID; +@property (assign, nonatomic) UInt256 registeredIdentityUniqueID; @property (assign, nonatomic) UInt256 entropy; @end @@ -51,18 +54,26 @@ @implementation DPContract #pragma mark - Init +- (void)dealloc { + if (self.raw_contract) { + dpp_data_contract_DataContract_destroy(self.raw_contract); + } +} + - (instancetype)initWithLocalContractIdentifier:(NSString *)localContractIdentifier - documents:(NSDictionary *)documents + raw_contract:(dpp_data_contract_DataContract *)raw_contract +// documents:(NSDictionary *)documents onChain:(DSChain *)chain { NSParameterAssert(localContractIdentifier); - NSParameterAssert(documents); + NSParameterAssert(raw_contract); if (!(self = [super init])) return nil; _version = DEFAULT_VERSION; _localContractIdentifier = localContractIdentifier; _jsonMetaSchema = DEFAULT_SCHEMA; - _mutableDocuments = [documents mutableCopy]; - _definitions = @{}; + _raw_contract = raw_contract; +// _mutableDocuments = [documents mutableCopy]; +// _definitions = @{}; _chain = chain; // [self.chain.chainManagedObjectContext performBlockAndWait:^{ @@ -75,117 +86,27 @@ - (instancetype)initWithLocalContractIdentifier:(NSString *)localContractIdentif return self; } -#pragma mark - Initializer Helpers - -+ (DPContract *)contractWithName:(NSString *)name - withLocalIdentifier:(NSString *)localIdentifier - documents:(NSDictionary *)documents - onChain:(DSChain *)chain { - NSParameterAssert(name); - NSParameterAssert(documents); - - NSDictionary *rawContract = @{ - @"name": name, - @"documents": documents, - }; - DPContract *contract = [self contractFromDictionary:rawContract withLocalIdentifier:localIdentifier onChain:chain]; - - return contract; -} - -+ (nullable DPContract *)contractFromDictionary:(DSStringValueDictionary *)contractDictionary - withLocalIdentifier:(NSString *)localIdentifier - onChain:(DSChain *)chain - error:(NSError *_Nullable __autoreleasing *)error { - return [self contractFromDictionary:contractDictionary withLocalIdentifier:localIdentifier skipValidation:NO onChain:chain error:error]; -} - -+ (nullable DPContract *)contractFromDictionary:(DSStringValueDictionary *)contractDictionary - withLocalIdentifier:(NSString *)localIdentifier - skipValidation:(BOOL)skipValidation - onChain:(DSChain *)chain - error:(NSError *_Nullable __autoreleasing *)error { - NSParameterAssert(contractDictionary); - - // TODO: validate rawContract - - DPContract *contract = [self contractFromDictionary:contractDictionary withLocalIdentifier:localIdentifier onChain:chain]; - - return contract; -} - -+ (nullable DPContract *)contractFromSerialized:(NSData *)data - onChain:(DSChain *)chain - error:(NSError *_Nullable __autoreleasing *)error { - return [self contractFromSerialized:data withLocalIdentifier:[data base64String] skipValidation:NO onChain:chain error:error]; -} - -+ (nullable DPContract *)contractFromSerialized:(NSData *)data - withLocalIdentifier:(NSString *)identifier - skipValidation:(BOOL)skipValidation - onChain:(DSChain *)chain - error:(NSError *_Nullable __autoreleasing *)error { - NSParameterAssert(data); - - DSStringValueDictionary *contractDictionary = [data ds_decodeCborError:error]; - if (!contractDictionary) { - return nil; - } - - return [self contractFromDictionary:contractDictionary - withLocalIdentifier:identifier - skipValidation:skipValidation - onChain:chain - error:error]; -} - -+ (DPContract *)contractFromDictionary:(DSStringValueDictionary *)rawContract withLocalIdentifier:(NSString *)localContractIdentifier onChain:(DSChain *)chain { - NSDictionary *documents = rawContract[@"documents"]; - - DPContract *contract = [[DPContract alloc] initWithLocalContractIdentifier:localContractIdentifier - documents:documents - onChain:chain]; - - NSString *jsonMetaSchema = rawContract[@"$schema"]; - if (jsonMetaSchema) { - contract.jsonMetaSchema = jsonMetaSchema; - } - - NSNumber *version = rawContract[@"version"]; - if (version) { - contract.version = version.integerValue; - } - - NSDictionary *definitions = rawContract[@"definitions"]; - if (definitions) { - contract.definitions = definitions; - } - - - return contract; -} #pragma mark - Contract Info - (UInt256)contractId { if (uint256_is_zero(_contractId)) { - NSAssert(uint256_is_not_zero(self.registeredBlockchainIdentityUniqueID), @"Registered Identity needs to be set"); + NSAssert(uint256_is_not_zero(self.registeredIdentityUniqueID), @"Registered Identity needs to be set"); NSAssert(uint256_is_not_zero(self.entropy), @"Entropy needs to be set"); NSMutableData *mData = [NSMutableData data]; - [mData appendUInt256:self.registeredBlockchainIdentityUniqueID]; + [mData appendUInt256:self.registeredIdentityUniqueID]; [mData appendUInt256:self.entropy]; _contractId = [mData SHA256_2]; } return _contractId; } -- (UInt256)contractIdIfRegisteredByBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity { +- (UInt256)contractIdIfRegisteredByIdentity:(DSIdentity *)identity { NSMutableData *mData = [NSMutableData data]; - [mData appendUInt256:blockchainIdentity.uniqueID]; - DSWallet *wallet = blockchainIdentity.wallet; - DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath blockchainIdentitiesECDSAKeysDerivationPathForWallet:wallet]; + [mData appendUInt256:identity.uniqueID]; + DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath identitiesECDSAKeysDerivationPathForWallet:identity.wallet]; NSMutableData *entropyData = [self.serializedHash mutableCopy]; - [entropyData appendUInt256:blockchainIdentity.uniqueID]; + [entropyData appendUInt256:identity.uniqueID]; [entropyData appendData:[derivationPath publicKeyDataAtIndex:UINT32_MAX - 1]]; //use the last key in 32 bit space (it won't probably ever be used anyways) [mData appendData:uint256_data([entropyData SHA256])]; return [mData SHA256_2]; //this is the contract ID @@ -196,8 +117,8 @@ - (NSString *)base58ContractId { } - (NSString *)base58OwnerId { - NSAssert(uint256_is_not_zero(self.registeredBlockchainIdentityUniqueID), @"Registered Identity can not be 0"); - return uint256_base58(self.registeredBlockchainIdentityUniqueID); + NSAssert(uint256_is_not_zero(self.registeredIdentityUniqueID), @"Registered Identity can not be 0"); + return uint256_base58(self.registeredIdentityUniqueID); } - (NSString *)localContractIdentifier { @@ -222,69 +143,50 @@ - (void)setJsonMetaSchema:(NSString *)jsonMetaSchema { [self resetSerializedValues]; } -- (NSDictionary *)documents { - return [self.mutableDocuments copy]; -} - -- (void)setDocuments:(NSDictionary *)documents { - _mutableDocuments = [documents mutableCopy]; - [self resetSerializedValues]; +- (DDocumentTypes *)documents { + return self.raw_contract->v0->document_types; +// return [self.mutableDocuments copy]; } -- (void)setDefinitions:(NSDictionary *)definitions { - _definitions = [definitions copy]; - [self resetSerializedValues]; -} +//- (void)setDocuments:(NSDictionary *)documents { +// _mutableDocuments = [documents mutableCopy]; +// [self resetSerializedValues]; +//} +// +//- (void)setDefinitions:(NSDictionary *)definitions { +// _definitions = [definitions copy]; +// [self resetSerializedValues]; +//} - (BOOL)isDocumentDefinedForType:(NSString *)type { NSParameterAssert(type); - if (!type) { - return NO; - } - - return (self.mutableDocuments[type] != nil); + return dash_spv_platform_contract_manager_is_document_defined_for_type(self.raw_contract, (char *) [type UTF8String]); } -- (void)setDocumentSchema:(DSStringValueDictionary *)schema forType:(NSString *)type { - NSParameterAssert(schema); - NSParameterAssert(type); - if (!schema || !type) { - return; - } - - self.mutableDocuments[type] = schema; -} - -- (nullable DSStringValueDictionary *)documentSchemaForType:(NSString *)type { - NSParameterAssert(type); - if (!type) { - return nil; - } - - return self.mutableDocuments[type]; -} - -- (nullable NSDictionary *)documentSchemaRefForType:(NSString *)type { - NSParameterAssert(type); - if (!type) { - return nil; - } - - if (![self isDocumentDefinedForType:type]) { - return nil; - } - - NSString *refValue = [NSString stringWithFormat:@"%@#/documents/%@", - self.jsonSchemaId, type]; - NSDictionary *dpObjectSchemaRef = @{@"$ref": refValue}; - - return dpObjectSchemaRef; -} - -- (void)resetSerializedValues { - [super resetSerializedValues]; - _keyValueDictionary = nil; -} +//- (void)setDocumentSchema:(DSStringValueDictionary *)schema forType:(NSString *)type { +// NSParameterAssert(schema); +// NSParameterAssert(type); +// if (!schema || !type) return; +// self.mutableDocuments[type] = schema; +//} +// +//- (nullable DSStringValueDictionary *)documentSchemaForType:(NSString *)type { +// NSParameterAssert(type); +// return type ? self.mutableDocuments[type] : nil; +//} +// +//- (nullable NSDictionary *)documentSchemaRefForType:(NSString *)type { +// NSParameterAssert(type); +// return type && [self isDocumentDefinedForType:type] +// ? @{@"$ref": [NSString stringWithFormat:@"%@#/documents/%@", self.jsonSchemaId, type]} +// : nil; +//} + +//- (void)resetSerializedValues { +// [super resetSerializedValues]; +// +// _keyValueDictionary = nil; +//} - (NSString *)name { return [DSDashPlatform nameForContractWithIdentifier:self.localContractIdentifier]; @@ -304,36 +206,31 @@ - (NSString *)statusString { return @"Other State"; } -- (void)unregisterCreatorInContext:(NSManagedObjectContext *)context { - self.registeredBlockchainIdentityUniqueID = UINT256_ZERO; +- (void)unregisterCreator { + self.registeredIdentityUniqueID = UINT256_ZERO; self.contractId = UINT256_ZERO; //will be lazy loaded self.entropy = UINT256_ZERO; - [self saveAndWaitInContext:context]; } -- (void)registerCreator:(DSBlockchainIdentity *)blockchainIdentity inContext:(NSManagedObjectContext *)context { - NSParameterAssert(blockchainIdentity); - self.registeredBlockchainIdentityUniqueID = blockchainIdentity ? blockchainIdentity.uniqueID : UINT256_ZERO; +- (void)registerCreator:(DSIdentity *)identity { + NSParameterAssert(identity); + self.registeredIdentityUniqueID = identity ? identity.uniqueID : UINT256_ZERO; self.contractId = UINT256_ZERO; //will be lazy loaded - DSWallet *wallet = blockchainIdentity.wallet; - DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath blockchainIdentitiesECDSAKeysDerivationPathForWallet:wallet]; + DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath identitiesECDSAKeysDerivationPathForWallet:identity.wallet]; NSMutableData *entropyData = [self.serializedHash mutableCopy]; - [entropyData appendUInt256:blockchainIdentity.uniqueID]; + [entropyData appendUInt256:identity.uniqueID]; [entropyData appendData:[derivationPath publicKeyDataAtIndex:UINT32_MAX - 1]]; //use the last key in 32 bit space (it won't probably ever be used anyways) self.entropy = [entropyData SHA256]; - [self saveAndWaitInContext:context]; -} - -- (void)setContractState:(DPContractState)contractState inContext:(NSManagedObjectContext *)context { - _contractState = contractState; - [self saveAndWaitInContext:context]; } #pragma mark - Transitions -- (DSContractTransition *)contractRegistrationTransitionForIdentity:(DSBlockchainIdentity *)blockchainIdentity { - return [[DSContractTransition alloc] initWithContract:self withTransitionVersion:1 blockchainIdentityUniqueId:blockchainIdentity.uniqueID onChain:self.chain]; -} +//- (DSContractTransition *)contractRegistrationTransitionForIdentityId:(UInt256)identityId { +// return [[DSContractTransition alloc] initWithContract:self +// withTransitionVersion:1 +// identityUniqueId:identityId +// onChain:self.chain]; +//} #pragma mark - Saving @@ -341,7 +238,7 @@ - (DSContractTransition *)contractRegistrationTransitionForIdentity:(DSBlockchai - (DSContractEntity *)contractEntityInContext:(NSManagedObjectContext *)context { __block DSContractEntity *entity = nil; [context performBlockAndWait:^{ - entity = [DSContractEntity anyObjectInContext:context matching:@"localContractIdentifier == %@ && chain == %@", self.localContractIdentifier, [self.chain chainEntityInContext:context]]; + entity = [DSContractEntity entityWithLocalContractIdentifier:self.localContractIdentifier onChain:self.chain inContext:context]; }]; return entity; } @@ -354,18 +251,16 @@ - (void)saveAndWaitInContext:(NSManagedObjectContext *)context { entity = [DSContractEntity managedObjectInBlockedContext:context]; entity.chain = [self.chain chainEntityInContext:context]; entity.localContractIdentifier = self.localContractIdentifier; - if (uint256_is_not_zero(self.registeredBlockchainIdentityUniqueID)) { - entity.registeredBlockchainIdentityUniqueID = uint256_data(self.registeredBlockchainIdentityUniqueID); - } - if (uint256_is_not_zero(self.entropy)) { + if (uint256_is_not_zero(self.registeredIdentityUniqueID)) + entity.registeredBlockchainIdentityUniqueID = uint256_data(self.registeredIdentityUniqueID); + if (uint256_is_not_zero(self.entropy)) entity.entropy = uint256_data(self.entropy); - } hasChange = YES; } - if (uint256_is_not_zero(self.registeredBlockchainIdentityUniqueID) && (!entity.registeredBlockchainIdentityUniqueID || !uint256_eq(entity.registeredBlockchainIdentityUniqueID.UInt256, self.registeredBlockchainIdentityUniqueID))) { - entity.registeredBlockchainIdentityUniqueID = uint256_data(self.registeredBlockchainIdentityUniqueID); + if (uint256_is_not_zero(self.registeredIdentityUniqueID) && (!entity.registeredBlockchainIdentityUniqueID || !uint256_eq(entity.registeredBlockchainIdentityUniqueID.UInt256, self.registeredIdentityUniqueID))) { + entity.registeredBlockchainIdentityUniqueID = uint256_data(self.registeredIdentityUniqueID); hasChange = YES; - } else if (uint256_is_zero(self.registeredBlockchainIdentityUniqueID) && entity.registeredBlockchainIdentityUniqueID) { + } else if (uint256_is_zero(self.registeredIdentityUniqueID) && entity.registeredBlockchainIdentityUniqueID) { entity.registeredBlockchainIdentityUniqueID = nil; hasChange = YES; } @@ -386,7 +281,9 @@ - (void)saveAndWaitInContext:(NSManagedObjectContext *)context { if (hasChange) { [context ds_save]; dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:DPContractDidUpdateNotification object:nil userInfo:@{DSContractUpdateNotificationKey: self}]; + [[NSNotificationCenter defaultCenter] postNotificationName:DPContractDidUpdateNotification + object:nil + userInfo:@{DSContractUpdateNotificationKey: self}]; }); } }]; @@ -395,69 +292,94 @@ - (void)saveAndWaitInContext:(NSManagedObjectContext *)context { #pragma mark - Special Contracts -+ (DPContract *)contractAtPath:(NSString *)resource ofType:(NSString *)type identifier:(NSString *)identifier forChain:(DSChain *)chain { - // TODO: read async'ly - NSString *bundlePath = [[NSBundle bundleForClass:self.class] pathForResource:@"DashSync" ofType:@"bundle"]; - NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; - NSString *path = [bundle pathForResource:resource ofType:type]; - NSError *error = nil; - NSData *data = [NSData dataWithContentsOfFile:path options:NSDataReadingUncached error:&error]; - NSAssert(error == nil, @"Failed reading contract json"); - DSStringValueDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; - NSAssert(error == nil, @"Failed parsing json"); - - NSString *localIdentifier = [NSString stringWithFormat:@"%@-%@", identifier, chain.uniqueID]; - - DPContract *contract = [self contractFromDictionary:jsonObject withLocalIdentifier:localIdentifier onChain:chain error:&error]; - NSAssert(error == nil, @"Failed building DPContract"); - return contract; -} +//+ (DPContract *)contractAtPath:(NSString *)resource +// ofType:(NSString *)type +// identifier:(NSString *)identifier +// forChain:(DSChain *)chain { +// // TODO: read async'ly +// NSString *bundlePath = [[NSBundle bundleForClass:self.class] pathForResource:@"DashSync" ofType:@"bundle"]; +// NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; +// NSString *path = [bundle pathForResource:resource ofType:type]; +// NSError *error = nil; +// NSData *data = [NSData dataWithContentsOfFile:path options:NSDataReadingUncached error:&error]; +// NSAssert(error == nil, @"Failed reading contract json"); +// DSStringValueDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; +// NSAssert(error == nil, @"Failed parsing json"); +// NSString *localIdentifier = [NSString stringWithFormat:@"%@-%@", identifier, chain.uniqueID]; +// +// NSDictionary *documents = jsonObject[@"documents"]; +// +// +// DPContract *contract = [[DPContract alloc] initWithLocalContractIdentifier:localIdentifier +// raw_contract: +// onChain:chain]; +// NSString *jsonMetaSchema = jsonObject[@"$schema"]; +// if (jsonMetaSchema) +// contract.jsonMetaSchema = jsonMetaSchema; +// NSNumber *version = jsonObject[@"version"]; +// if (version) +// contract.version = version.integerValue; +// NSDictionary *definitions = jsonObject[@"definitions"]; +// if (definitions) +// contract.definitions = definitions; +// return contract; +//} + (DPContract *)localDashpayContractForChain:(DSChain *)chain { - DPContract *contract = [self contractAtPath:@"dashpay-contract" ofType:@"json" identifier:DASHPAY_CONTRACT forChain:chain]; + dpp_data_contract_DataContract *raw_contract = dash_spv_platform_contract_manager_ContractsManager_load_dashpay_contract(chain.shareCore.contractsManager->obj); + DPContract *contract = [[DPContract alloc] initWithLocalContractIdentifier:[NSString stringWithFormat:@"%@-%@", DASHPAY_CONTRACT, chain.uniqueID] + raw_contract:raw_contract + onChain:chain]; + + +// DPContract *contract = [self contractAtPath:@"dashpay-contract" ofType:@"json" identifier:DASHPAY_CONTRACT forChain:chain]; if (uint256_is_not_zero(chain.dashpayContractID) && contract.contractState == DPContractState_Unknown) { - [contract setContractState:DPContractState_Registered inContext:[NSManagedObjectContext platformContext]]; + contract.contractState = DPContractState_Registered; contract.contractId = chain.dashpayContractID; [contract saveAndWaitInContext:[NSManagedObjectContext platformContext]]; } - return contract; } + (DPContract *)localDPNSContractForChain:(DSChain *)chain { - DPContract *contract = [self contractAtPath:@"dpns-contract" ofType:@"json" identifier:DPNS_CONTRACT forChain:chain]; + dpp_data_contract_DataContract *raw_contract = dash_spv_platform_contract_manager_ContractsManager_load_dpns_contract(chain.shareCore.contractsManager->obj); + DPContract *contract = [[DPContract alloc] initWithLocalContractIdentifier:[NSString stringWithFormat:@"%@-%@", DPNS_CONTRACT, chain.uniqueID] + raw_contract:raw_contract + onChain:chain]; + +// DPContract *contract = [self /*contractAtPath*/:@"dpns-contract" ofType:@"json" identifier:DPNS_CONTRACT forChain:chain]; if (uint256_is_not_zero(chain.dpnsContractID) && contract.contractState == DPContractState_Unknown) { - [contract setContractState:DPContractState_Registered inContext:[NSManagedObjectContext platformContext]]; + contract.contractState = DPContractState_Registered; contract.contractId = chain.dpnsContractID; [contract saveAndWaitInContext:[NSManagedObjectContext platformContext]]; } return contract; } - -+ (DPContract *)localDashThumbnailContractForChain:(DSChain *)chain { - DPContract *contract = [self contractAtPath:@"dashthumbnail-contract" ofType:@"json" identifier:DASHTHUMBNAIL_CONTRACT forChain:chain]; - return contract; -} +// +//+ (DPContract *)localDashThumbnailContractForChain:(DSChain *)chain { +// DPContract *contract = [self contractAtPath:@"dashthumbnail-contract" ofType:@"json" identifier:DASHTHUMBNAIL_CONTRACT forChain:chain]; +// return contract; +//} #pragma mark - DPPSerializableObject @synthesize keyValueDictionary = _keyValueDictionary; - -- (DSMutableStringValueDictionary *)objectDictionary { - if (_keyValueDictionary == nil) { - DSMutableStringValueDictionary *json = [[DSMutableStringValueDictionary alloc] init]; - json[@"$schema"] = self.jsonMetaSchema; - json[@"ownerId"] = uint256_data(self.registeredBlockchainIdentityUniqueID); - json[@"$id"] = uint256_data(self.contractId); - json[@"documents"] = self.documents; - json[@"protocolVersion"] = @(0); - if (self.definitions.count > 0) { - json[@"definitions"] = self.definitions; - } - _keyValueDictionary = json; - } - return _keyValueDictionary; -} +// +//- (DSMutableStringValueDictionary *)objectDictionary { +// if (_keyValueDictionary == nil) { +// DSMutableStringValueDictionary *json = [[DSMutableStringValueDictionary alloc] init]; +// json[@"$schema"] = self.jsonMetaSchema; +// json[@"ownerId"] = uint256_data(self.registeredIdentityUniqueID); +// json[@"$id"] = uint256_data(self.contractId); +// json[@"documents"] = self.documents; +// json[@"protocolVersion"] = @(0); +// if (self.definitions.count > 0) { +// json[@"definitions"] = self.definitions; +// } +// _keyValueDictionary = json; +// } +// return _keyValueDictionary; +//} @end diff --git a/DashSync/shared/Models/Platform/DSDashPlatform.h b/DashSync/shared/Models/Platform/DSDashPlatform.h index 38a107b7e..6601106b1 100644 --- a/DashSync/shared/Models/Platform/DSDashPlatform.h +++ b/DashSync/shared/Models/Platform/DSDashPlatform.h @@ -15,7 +15,7 @@ // limitations under the License. // -#import "DPDocumentFactory.h" +//#import "DPDocumentFactory.h" #import #define DPNS_CONTRACT @"DPNS_CONTRACT" @@ -30,14 +30,15 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly, strong, nonatomic) DPContract *dashPayContract; @property (readonly, strong, nonatomic) DPContract *dpnsContract; -@property (readonly, strong, nonatomic) DPContract *dashThumbnailContract; +//@property (readonly, strong, nonatomic) DPContract *dashThumbnailContract; @property (readonly, strong, nonatomic) NSMutableDictionary *knownContracts; @property (readonly, strong, nonatomic) DSChain *chain; - (instancetype)init NS_UNAVAILABLE; -- (DPDocumentFactory *)documentFactoryForBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity forContract:(DPContract *)contract; +//- (DPDocumentFactory *)documentFactoryForIdentity:(DSIdentity *)identity +// forContract:(DPContract *)contract; + (NSString *)nameForContractWithIdentifier:(NSString *)identifier; diff --git a/DashSync/shared/Models/Platform/DSDashPlatform.m b/DashSync/shared/Models/Platform/DSDashPlatform.m index 3c44c0aab..dff8baa52 100644 --- a/DashSync/shared/Models/Platform/DSDashPlatform.m +++ b/DashSync/shared/Models/Platform/DSDashPlatform.m @@ -15,6 +15,7 @@ // limitations under the License. // +#import "DSChain+Params.h" #import "DSDashPlatform.h" #import "DPContract.h" #import "DSChain.h" @@ -26,7 +27,7 @@ @interface DSDashPlatform () @property (strong, nonatomic, null_resettable) NSMutableDictionary *knownContracts; @property (strong, nonatomic) DPContract *dashPayContract; @property (strong, nonatomic) DPContract *dpnsContract; -@property (strong, nonatomic) DPContract *dashThumbnailContract; +//@property (strong, nonatomic) DPContract *dashThumbnailContract; @end @@ -61,10 +62,9 @@ + (instancetype)sharedInstanceForChain:(DSChain *)chain { return platformForChain; } -- (DPDocumentFactory *)documentFactoryForBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity forContract:(DPContract *)contract { - DPDocumentFactory *documentFactory = [[DPDocumentFactory alloc] initWithBlockchainIdentity:blockchainIdentity contract:contract onChain:self.chain]; - return documentFactory; -} +//- (DPDocumentFactory *)documentFactoryForIdentity:(DSIdentity *)identity forContract:(DPContract *)contract { +// return [[DPDocumentFactory alloc] initWithIdentity:identity contract:contract onChain:self.chain]; +//} + (NSString *)nameForContractWithIdentifier:(NSString *)identifier { if ([identifier hasPrefix:DASHPAY_CONTRACT]) { @@ -79,7 +79,7 @@ + (NSString *)nameForContractWithIdentifier:(NSString *)identifier { - (NSMutableDictionary *)knownContracts { if (!_knownContracts) { - _knownContracts = [NSMutableDictionary dictionaryWithObjects:@[[self dashPayContract], [self dpnsContract], [self dashThumbnailContract]] forKeys:@[DASHPAY_CONTRACT, DPNS_CONTRACT, DASHTHUMBNAIL_CONTRACT]]; + _knownContracts = [NSMutableDictionary dictionaryWithObjects:@[[self dashPayContract], [self dpnsContract]/*, [self dashThumbnailContract]*/] forKeys:@[DASHPAY_CONTRACT, DPNS_CONTRACT/*, DASHTHUMBNAIL_CONTRACT*/]]; } return _knownContracts; } @@ -98,11 +98,11 @@ - (DPContract *)dpnsContract { return _dpnsContract; } -- (DPContract *)dashThumbnailContract { - if (!_dashThumbnailContract) { - _dashThumbnailContract = [DPContract localDashThumbnailContractForChain:self.chain]; - } - return _dashThumbnailContract; -} +//- (DPContract *)dashThumbnailContract { +// if (!_dashThumbnailContract) { +// _dashThumbnailContract = [DPContract localDashThumbnailContractForChain:self.chain]; +// } +// return _dashThumbnailContract; +//} @end diff --git a/DashSync/shared/Models/Platform/Document/DPDocumentFactory.h b/DashSync/shared/Models/Platform/Document/DPDocumentFactory.h index ee2de5ade..44f1ce72e 100644 --- a/DashSync/shared/Models/Platform/Document/DPDocumentFactory.h +++ b/DashSync/shared/Models/Platform/Document/DPDocumentFactory.h @@ -19,7 +19,7 @@ #import "DPContract.h" #import "DPDocumentProtocol.h" -#import "DSBlockchainIdentity.h" +#import "DSIdentity.h" NS_ASSUME_NONNULL_BEGIN @@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN @interface DPDocumentFactory : NSObject -- (instancetype)initWithBlockchainIdentity:(DSBlockchainIdentity *)identity +- (instancetype)initWithIdentity:(DSIdentity *)identity contract:(DPContract *)contract onChain:(DSChain *)chain; diff --git a/DashSync/shared/Models/Platform/Document/DPDocumentFactory.m b/DashSync/shared/Models/Platform/Document/DPDocumentFactory.m index 812914987..b97b25fc4 100644 --- a/DashSync/shared/Models/Platform/Document/DPDocumentFactory.m +++ b/DashSync/shared/Models/Platform/Document/DPDocumentFactory.m @@ -37,7 +37,7 @@ @interface DPDocumentFactory () @implementation DPDocumentFactory -- (instancetype)initWithBlockchainIdentity:(DSBlockchainIdentity *)identity +- (instancetype)initWithIdentity:(DSIdentity *)identity contract:(DPContract *)contract onChain:(DSChain *)chain { NSParameterAssert(identity); @@ -64,7 +64,7 @@ - (nullable DPDocument *)documentOnTable:(NSString *)tableName dataDictionary = @{}; } - if (uint256_is_zero(self.contract.contractId) && uint256_is_zero(self.contract.registeredBlockchainIdentityUniqueID)) { + if (uint256_is_zero(self.contract.contractId) && uint256_is_zero(self.contract.registeredIdentityUniqueID)) { if (error != NULL) { *error = [NSError errorWithDomain:DPErrorDomain code:DPErrorCode_InvalidDocumentType @@ -107,7 +107,7 @@ - (nullable DPDocument *)documentOnTable:(NSString *)tableName dataDictionary = @{}; } - if (uint256_is_zero(self.contract.contractId) && uint256_is_zero(self.contract.registeredBlockchainIdentityUniqueID)) { + if (uint256_is_zero(self.contract.contractId) && uint256_is_zero(self.contract.registeredIdentityUniqueID)) { if (error != NULL) { *error = [NSError errorWithDomain:DPErrorDomain code:DPErrorCode_InvalidDocumentType diff --git a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityCloseTransition.h b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityCloseTransition.h deleted file mode 100644 index 848a7dc4b..000000000 --- a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityCloseTransition.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// DSBlockchainIdentityCloseTransition.h -// DashSync -// -// Created by Sam Westrich on 8/13/18. -// - -#import "DSTransition.h" - -@interface DSBlockchainIdentityCloseTransition : DSTransition - -//@property (nonatomic,assign) uint16_t blockchainIdentityCloseTransactionVersion; -//@property (nonatomic,assign) UInt256 registrationTransactionHash; -//@property (nonatomic,assign) UInt256 previousBlockchainIdentityTransactionHash; -//@property (nonatomic,assign) uint64_t creditFee; -//@property (nonatomic,strong) NSData * payloadSignature; -// -//- (instancetype)initWithInputHashes:(NSArray *)hashes inputIndexes:(NSArray *)indexes inputScripts:(NSArray *)scripts inputSequences:(NSArray*)inputSequences outputAddresses:(NSArray *)addresses outputAmounts:(NSArray *)amounts blockchainIdentityCloseTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousBlockchainIdentityTransactionHash:(UInt256)previousBlockchainIdentityTransactionHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain; -// -//-(instancetype)initWithBlockchainIdentityCloseTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousBlockchainIdentityTransactionHash:(UInt256)previousBlockchainIdentityTransactionHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain; - -@end diff --git a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityRegistrationTransition.h b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityRegistrationTransition.h deleted file mode 100644 index c59e1b2f3..000000000 --- a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityRegistrationTransition.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// DSBlockchainIdentityRegistrationTransition.h -// DashSync -// -// Created by Sam Westrich on 7/12/18. -// - -#import "BigIntTypes.h" -#import "DSBlockchainIdentity.h" -#import "DSTransition.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface DSBlockchainIdentityRegistrationTransition : DSTransition - -@property (nonatomic, readonly) NSDictionary *publicKeys; -@property (nonatomic, readonly) DSUTXO lockedOutpoint; - -- (instancetype)initWithVersion:(uint16_t)version registeringPublicKeys:(NSDictionary *)publicKeys usingCreditFundingTransaction:(DSCreditFundingTransaction *)creditFundingTransaction onChain:(DSChain *)chain; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityUpdateTransition.h b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityUpdateTransition.h deleted file mode 100644 index 665501d7c..000000000 --- a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityUpdateTransition.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// DSBlockchainIdentityResetUserKeyTransaction.h -// DashSync -// -// Created by Sam Westrich on 8/13/18. -// - -#import "BigIntTypes.h" -#import "DSTransition.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface DSBlockchainIdentityUpdateTransition : DSTransition - -//@property (nonatomic,assign) uint16_t blockchainIdentityResetTransactionVersion; -//@property (nonatomic,assign) UInt256 registrationTransactionHash; -//@property (nonatomic,assign) UInt256 previousBlockchainIdentityTransactionHash; -//@property (nonatomic,assign) uint64_t creditFee; -//@property (nonatomic,assign) UInt160 replacementPublicKeyHash; //we will get rid of this and do next line later -//@property (nullable, nonatomic,readonly) NSString * replacementAddress; // TODO: replacementAddress is not initialized -////@property (nonatomic,strong) NSData * replacementPublicKey; -//@property (nonatomic,strong) NSData * oldPublicKeyPayloadSignature; -// -//- (instancetype)initWithInputHashes:(NSArray *)hashes inputIndexes:(NSArray *)indexes inputScripts:(NSArray *)scripts inputSequences:(NSArray*)inputSequences outputAddresses:(NSArray *)addresses outputAmounts:(NSArray *)amounts blockchainIdentityResetTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousBlockchainIdentityTransactionHash:(UInt256)previousBlockchainIdentityTransactionHash replacementPublicKeyHash:(UInt160)replacementPublicKeyHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain; -// -////this is what we will eventually go to (right below) -// -////- (instancetype)initWithInputHashes:(NSArray *)hashes inputIndexes:(NSArray *)indexes inputScripts:(NSArray *)scripts inputSequences:(NSArray*)inputSequences outputAddresses:(NSArray *)addresses outputAmounts:(NSArray *)amounts blockchainIdentityResetTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousBlockchainIdentityTransactionHash:(UInt256)previousBlockchainIdentityTransactionHash replacementPublicKey:(NSData*)replacementPublicKey creditFee:(uint64_t)creditFee onChain:(DSChain *)chain; -// -//-(instancetype)initWithBlockchainIdentityResetTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousBlockchainIdentityTransactionHash:(UInt256)previousBlockchainIdentityTransactionHash replacementPublicKeyHash:(UInt160)replacementPublicKeyHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain; -// -//-(void)signPayloadWithKey:(DSECDSAKey*)privateKey; -// -//-(BOOL)checkTransitionSignatureIsSignedByPublicKeyWithHash:(UInt160)oldPublicKeyHash; - -@end - -NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityCloseTransition.h b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityCloseTransition.h new file mode 100644 index 000000000..95f92fa7c --- /dev/null +++ b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityCloseTransition.h @@ -0,0 +1,22 @@ +// +// DSIdentityCloseTransition.h +// DashSync +// +// Created by Sam Westrich on 8/13/18. +// + +#import "DSTransition.h" + +@interface DSIdentityCloseTransition : DSTransition + +//@property (nonatomic,assign) uint16_t identityCloseTransactionVersion; +//@property (nonatomic,assign) UInt256 registrationTransactionHash; +//@property (nonatomic,assign) UInt256 previousIdentityTransactionHash; +//@property (nonatomic,assign) uint64_t creditFee; +//@property (nonatomic,strong) NSData * payloadSignature; +// +//- (instancetype)initWithInputHashes:(NSArray *)hashes inputIndexes:(NSArray *)indexes inputScripts:(NSArray *)scripts inputSequences:(NSArray*)inputSequences outputAddresses:(NSArray *)addresses outputAmounts:(NSArray *)amounts identityCloseTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousIdentityTransactionHash:(UInt256)previousIdentityTransactionHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain; +// +//-(instancetype)initWithIdentityCloseTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousIdentityTransactionHash:(UInt256)previousIdentityTransactionHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain; + +@end diff --git a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityCloseTransition.m b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityCloseTransition.m similarity index 70% rename from DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityCloseTransition.m rename to DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityCloseTransition.m index 63612fedb..66ddf7121 100644 --- a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityCloseTransition.m +++ b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityCloseTransition.m @@ -1,17 +1,17 @@ // -// DSBlockchainIdentityCloseTransition.m +// DSIdentityCloseTransition.m // DashSync // // Created by Sam Westrich on 8/13/18. // -#import "DSBlockchainIdentityCloseTransition.h" +#import "DSIdentityCloseTransition.h" #import "DSTransactionFactory.h" #import "NSData+Dash.h" #import "NSMutableData+Dash.h" #import "NSString+Bitcoin.h" -@implementation DSBlockchainIdentityCloseTransition +@implementation DSIdentityCloseTransition //- (instancetype)initWithMessage:(NSData *)message onChain:(DSChain *)chain //{ @@ -26,7 +26,7 @@ @implementation DSBlockchainIdentityCloseTransition // off += payloadLengthSize.unsignedLongValue; // // if (length - off < 2) return nil; -// self.blockchainIdentityCloseTransactionVersion = [message UInt16AtOffset:off]; +// self.identityCloseTransactionVersion = [message UInt16AtOffset:off]; // off += 2; // // if (length - off < 32) return nil; @@ -34,7 +34,7 @@ @implementation DSBlockchainIdentityCloseTransition // off += 32; // // if (length - off < 32) return nil; -// self.previousBlockchainIdentityTransactionHash = [message UInt256AtOffset:off]; +// self.previousIdentityTransactionHash = [message UInt256AtOffset:off]; // off += 32; // // if (length - off < 8) return nil; @@ -54,33 +54,33 @@ @implementation DSBlockchainIdentityCloseTransition // return self; //} // -//- (instancetype)initWithInputHashes:(NSArray *)hashes inputIndexes:(NSArray *)indexes inputScripts:(NSArray *)scripts inputSequences:(NSArray*)inputSequences outputAddresses:(NSArray *)addresses outputAmounts:(NSArray *)amounts blockchainIdentityCloseTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousBlockchainIdentityTransactionHash:(UInt256)previousBlockchainIdentityTransactionHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain { +//- (instancetype)initWithInputHashes:(NSArray *)hashes inputIndexes:(NSArray *)indexes inputScripts:(NSArray *)scripts inputSequences:(NSArray*)inputSequences outputAddresses:(NSArray *)addresses outputAmounts:(NSArray *)amounts identityCloseTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousIdentityTransactionHash:(UInt256)previousIdentityTransactionHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain { // if (!(self = [super initWithInputHashes:hashes inputIndexes:indexes inputScripts:scripts inputSequences:inputSequences outputAddresses:addresses outputAmounts:amounts onChain:chain])) return nil; // self.type = DSTransactionType_SubscriptionCloseAccount; // self.version = SPECIAL_TX_VERSION; -// self.blockchainIdentityCloseTransactionVersion = version; +// self.identityCloseTransactionVersion = version; // self.registrationTransactionHash = registrationTransactionHash; -// self.previousBlockchainIdentityTransactionHash = previousBlockchainIdentityTransactionHash; +// self.previousIdentityTransactionHash = previousIdentityTransactionHash; // self.creditFee = creditFee; // return self; //} // -//-(instancetype)initWithBlockchainIdentityCloseTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousBlockchainIdentityTransactionHash:(UInt256)previousBlockchainIdentityTransactionHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain { +//-(instancetype)initWithIdentityCloseTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousIdentityTransactionHash:(UInt256)previousIdentityTransactionHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain { // if (!(self = [super initOnChain:chain])) return nil; // self.type = DSTransactionType_SubscriptionCloseAccount; // self.version = SPECIAL_TX_VERSION; -// self.blockchainIdentityCloseTransactionVersion = version; +// self.identityCloseTransactionVersion = version; // self.registrationTransactionHash = registrationTransactionHash; -// self.previousBlockchainIdentityTransactionHash = previousBlockchainIdentityTransactionHash; +// self.previousIdentityTransactionHash = previousIdentityTransactionHash; // self.creditFee = creditFee; // return self; //} // //-(NSData*)payloadData { // NSMutableData * data = [NSMutableData data]; -// [data appendUInt16:self.blockchainIdentityCloseTransactionVersion]; +// [data appendUInt16:self.identityCloseTransactionVersion]; // [data appendUInt256:self.registrationTransactionHash]; -// [data appendUInt256:self.previousBlockchainIdentityTransactionHash]; +// [data appendUInt256:self.previousIdentityTransactionHash]; // [data appendUInt64:self.creditFee]; // [data appendVarInt:self.payloadSignature.length]; // [data appendData:self.payloadSignature]; diff --git a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityRegistrationTransition.h b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityRegistrationTransition.h new file mode 100644 index 000000000..f22a87ba4 --- /dev/null +++ b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityRegistrationTransition.h @@ -0,0 +1,26 @@ +// +// DSIdentityRegistrationTransition.h +// DashSync +// +// Created by Sam Westrich on 7/12/18. +// + +#import "BigIntTypes.h" +#import "DSIdentity.h" +#import "DSTransition.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSIdentityRegistrationTransition : DSTransition + +@property (nonatomic, readonly) NSDictionary *publicKeys; +@property (nonatomic, readonly) DSUTXO lockedOutpoint; + +- (instancetype)initWithVersion:(uint16_t)version + registeringPublicKeys:(NSDictionary *)publicKeys + usingAssetLockTransaction:(DSAssetLockTransaction *)assetLockTransaction + onChain:(DSChain *)chain; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityRegistrationTransition.m b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityRegistrationTransition.m similarity index 60% rename from DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityRegistrationTransition.m rename to DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityRegistrationTransition.m index fd6a9ea15..3d23e7ea0 100644 --- a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityRegistrationTransition.m +++ b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityRegistrationTransition.m @@ -1,13 +1,13 @@ // -// DSBlockchainIdentityRegistrationTransition.m +// DSIdentityRegistrationTransition.m // DashSync // // Created by Sam Westrich on 7/12/18. // -#import "DSBlockchainIdentityRegistrationTransition.h" +#import "DSIdentityRegistrationTransition.h" #import "BigIntTypes.h" -#import "DSCreditFundingTransaction.h" +#import "DSAssetLockTransaction.h" #import "DSInstantSendTransactionLock.h" #import "DSKeyManager.h" #import "DSTransaction+Protected.h" @@ -17,14 +17,14 @@ #import "NSMutableData+Dash.h" #import "NSString+Bitcoin.h" -@interface DSBlockchainIdentityRegistrationTransition () +@interface DSIdentityRegistrationTransition () @property (nonatomic, strong) NSDictionary *publicKeys; -@property (nonatomic, strong) DSCreditFundingTransaction *creditFundingTransaction; +@property (nonatomic, strong) DSAssetLockTransaction *assetLockTransaction; @end -@implementation DSBlockchainIdentityRegistrationTransition +@implementation DSIdentityRegistrationTransition - (instancetype)initOnChain:(DSChain *)chain { if (!(self = [super initOnChain:chain])) return nil; @@ -32,15 +32,18 @@ - (instancetype)initOnChain:(DSChain *)chain { return self; } -- (instancetype)initWithVersion:(uint16_t)version registeringPublicKeys:(NSDictionary *)publicKeys usingCreditFundingTransaction:(DSCreditFundingTransaction *)creditFundingTransaction onChain:(DSChain *)chain { +- (instancetype)initWithVersion:(uint16_t)version + registeringPublicKeys:(NSDictionary *)publicKeys + usingAssetLockTransaction:(DSAssetLockTransaction *)assetLockTransaction + onChain:(DSChain *)chain { NSParameterAssert(chain); NSParameterAssert(publicKeys); NSAssert(publicKeys.count, @"There must be at least one key when registering a user"); if (!(self = [self initOnChain:chain])) return nil; self.version = version; - self.creditFundingTransaction = creditFundingTransaction; - self.blockchainIdentityUniqueId = [dsutxo_data(creditFundingTransaction.lockedOutpoint) SHA256_2]; + self.assetLockTransaction = assetLockTransaction; + self.identityUniqueId = [dsutxo_data(assetLockTransaction.lockedOutpoint) SHA256_2]; self.publicKeys = publicKeys; return self; } @@ -48,14 +51,14 @@ - (instancetype)initWithVersion:(uint16_t)version registeringPublicKeys:(NSDicti - (NSMutableArray *)platformKeyDictionaries { NSMutableArray *platformKeys = [NSMutableArray array]; for (NSNumber *indexIdentifier in self.publicKeys) { - OpaqueKey *key = self.publicKeys[indexIdentifier].pointerValue; + DMaybeOpaqueKey *key = self.publicKeys[indexIdentifier].pointerValue; DSMutableStringValueDictionary *platformKeyDictionary = [[DSMutableStringValueDictionary alloc] init]; platformKeyDictionary[@"id"] = @([indexIdentifier unsignedIntValue]); platformKeyDictionary[@"purpose"] = @(DWIdentityPublicKeyPurposeAuthentication); platformKeyDictionary[@"securityLevel"] = @(DWIdentityPublicKeySecurityLevelMaster); platformKeyDictionary[@"readOnly"] = @NO; - platformKeyDictionary[@"type"] = @(key->tag); - platformKeyDictionary[@"data"] = [DSKeyManager publicKeyData:key]; + platformKeyDictionary[@"type"] = @(key->ok->tag); + platformKeyDictionary[@"data"] = [DSKeyManager publicKeyData:key->ok]; [platformKeys addObject:platformKeyDictionary]; } return platformKeys; @@ -63,15 +66,15 @@ - (NSMutableArray *)platformKeyDictionaries { - (DSMutableStringValueDictionary *)assetLockProofDictionary { DSMutableStringValueDictionary *assetLockDictionary = [DSMutableStringValueDictionary dictionary]; - if (self.creditFundingTransaction.instantSendLockAwaitingProcessing) { + if (self.assetLockTransaction.instantSendLockAwaitingProcessing) { assetLockDictionary[@"type"] = @(0); - assetLockDictionary[@"instantLock"] = self.creditFundingTransaction.instantSendLockAwaitingProcessing.toData; - assetLockDictionary[@"outputIndex"] = @(self.creditFundingTransaction.lockedOutpoint.n); - assetLockDictionary[@"transaction"] = [self.creditFundingTransaction toData]; + assetLockDictionary[@"instantLock"] = self.assetLockTransaction.instantSendLockAwaitingProcessing.toData; + assetLockDictionary[@"outputIndex"] = @(self.assetLockTransaction.lockedOutpoint.n); + assetLockDictionary[@"transaction"] = [self.assetLockTransaction toData]; } else { assetLockDictionary[@"type"] = @(1); - assetLockDictionary[@"coreChainLockedHeight"] = @(self.creditFundingTransaction.blockHeight); - assetLockDictionary[@"outPoint"] = dsutxo_data(self.creditFundingTransaction.lockedOutpoint); + assetLockDictionary[@"coreChainLockedHeight"] = @(self.assetLockTransaction.blockHeight); + assetLockDictionary[@"outPoint"] = dsutxo_data(self.assetLockTransaction.lockedOutpoint); } return assetLockDictionary; @@ -87,24 +90,21 @@ - (DSMutableStringValueDictionary *)baseKeyValueDictionary { - (void)applyKeyValueDictionary:(DSMutableStringValueDictionary *)keyValueDictionary { [super applyKeyValueDictionary:keyValueDictionary]; NSDictionary *assetLockDictionary = keyValueDictionary[@"assetLock"]; - self.creditFundingTransaction = [DSCreditFundingTransaction transactionWithMessage:assetLockDictionary[@"transaction"] onChain:self.chain]; + self.assetLockTransaction = [DSAssetLockTransaction transactionWithMessage:assetLockDictionary[@"transaction"] onChain:self.chain]; NSDictionary *proofDictionary = keyValueDictionary[@"proof"]; NSNumber *proofType = proofDictionary[@"type"]; if ([proofType integerValue] == 0) { - //this is an instant send proof - NSData *instantSendLockData = proofDictionary[@"instantLock"]; - //todo: v18 make this deterministic - self.creditFundingTransaction.instantSendLockAwaitingProcessing = [DSInstantSendTransactionLock instantSendTransactionLockWithNonDeterministicMessage:instantSendLockData onChain:self.chain]; + self.assetLockTransaction.instantSendLockAwaitingProcessing = [DSInstantSendTransactionLock instantSendTransactionLockWithDeterministicMessage:proofDictionary[@"instantLock"] onChain:self.chain]; } - self.blockchainIdentityUniqueId = [dsutxo_data(self.lockedOutpoint) SHA256_2]; + self.identityUniqueId = [dsutxo_data(self.lockedOutpoint) SHA256_2]; NSArray *publicKeysDictionariesArray = keyValueDictionary[@"publicKeys"]; NSMutableDictionary *platformKeys = [NSMutableDictionary dictionary]; for (DSMutableStringValueDictionary *platformKeyDictionary in publicKeysDictionariesArray) { - KeyKind keyType = [platformKeyDictionary[@"type"] unsignedIntValue]; + DKeyKind keyType = [platformKeyDictionary[@"type"] unsignedIntValue]; NSUInteger identifier = [platformKeyDictionary[@"id"] unsignedIntValue]; NSData *keyData = platformKeyDictionary[@"data"]; - OpaqueKey *key = [DSKeyManager keyWithPublicKeyData:keyData ofType:keyType]; + DMaybeOpaqueKey *key = [DSKeyManager keyWithPublicKeyData:keyData ofType:&keyType]; platformKeys[@(identifier)] = [NSValue valueWithPointer:key]; } self.publicKeys = [platformKeys copy]; diff --git a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityTopupTransition.h b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityTopupTransition.h similarity index 50% rename from DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityTopupTransition.h rename to DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityTopupTransition.h index 0483fd895..266304af7 100644 --- a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityTopupTransition.h +++ b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityTopupTransition.h @@ -1,5 +1,5 @@ // -// DSBlockchainIdentityTopupTransition.h +// DSIdentityTopupTransition.h // DashSync // // Created by Sam Westrich on 7/30/18. @@ -10,9 +10,9 @@ @class DSChain; -@interface DSBlockchainIdentityTopupTransition : DSTransition +@interface DSIdentityTopupTransition : DSTransition -@property (nonatomic, assign) uint16_t blockchainIdentityTopupTransactionVersion; +@property (nonatomic, assign) uint16_t identityTopupTransactionVersion; @property (nonatomic, readonly) uint64_t topupAmount; diff --git a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityTopupTransition.m b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityTopupTransition.m similarity index 50% rename from DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityTopupTransition.m rename to DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityTopupTransition.m index cd647ca71..5bec0d0d0 100644 --- a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityTopupTransition.m +++ b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityTopupTransition.m @@ -1,21 +1,21 @@ // -// DSBlockchainIdentityTopupTransition.m +// DSIdentityTopupTransition.m // DashSync // // Created by Sam Westrich on 7/30/18. // -#import "DSBlockchainIdentityTopupTransition.h" +#import "DSIdentityTopupTransition.h" #import "DSTransactionFactory.h" #import "NSData+Dash.h" #import "NSMutableData+Dash.h" #import "NSString+Bitcoin.h" -@interface DSBlockchainIdentityTopupTransition () +@interface DSIdentityTopupTransition () @end -@implementation DSBlockchainIdentityTopupTransition +@implementation DSIdentityTopupTransition @end diff --git a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityUpdateTransition.h b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityUpdateTransition.h new file mode 100644 index 000000000..ea0569970 --- /dev/null +++ b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityUpdateTransition.h @@ -0,0 +1,38 @@ +// +// DSIdentityUpdateTransition.h +// DashSync +// +// Created by Sam Westrich on 8/13/18. +// + +#import "BigIntTypes.h" +#import "DSTransition.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSIdentityUpdateTransition : DSTransition + +//@property (nonatomic,assign) uint16_t identityResetTransactionVersion; +//@property (nonatomic,assign) UInt256 registrationTransactionHash; +//@property (nonatomic,assign) UInt256 previousIdentityTransactionHash; +//@property (nonatomic,assign) uint64_t creditFee; +//@property (nonatomic,assign) UInt160 replacementPublicKeyHash; //we will get rid of this and do next line later +//@property (nullable, nonatomic,readonly) NSString * replacementAddress; // TODO: replacementAddress is not initialized +////@property (nonatomic,strong) NSData * replacementPublicKey; +//@property (nonatomic,strong) NSData * oldPublicKeyPayloadSignature; +// +//- (instancetype)initWithInputHashes:(NSArray *)hashes inputIndexes:(NSArray *)indexes inputScripts:(NSArray *)scripts inputSequences:(NSArray*)inputSequences outputAddresses:(NSArray *)addresses outputAmounts:(NSArray *)amounts identityResetTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousIdentityTransactionHash:(UInt256)previousIdentityTransactionHash replacementPublicKeyHash:(UInt160)replacementPublicKeyHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain; +// +////this is what we will eventually go to (right below) +// +////- (instancetype)initWithInputHashes:(NSArray *)hashes inputIndexes:(NSArray *)indexes inputScripts:(NSArray *)scripts inputSequences:(NSArray*)inputSequences outputAddresses:(NSArray *)addresses outputAmounts:(NSArray *)amounts identityResetTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousIdentityTransactionHash:(UInt256)previousIdentityTransactionHash replacementPublicKey:(NSData*)replacementPublicKey creditFee:(uint64_t)creditFee onChain:(DSChain *)chain; +// +//-(instancetype)initWithIdentityResetTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousIdentityTransactionHash:(UInt256)previousIdentityTransactionHash replacementPublicKeyHash:(UInt160)replacementPublicKeyHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain; +// +//-(void)signPayloadWithKey:(DSECDSAKey*)privateKey; +// +//-(BOOL)checkTransitionSignatureIsSignedByPublicKeyWithHash:(UInt160)oldPublicKeyHash; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityUpdateTransition.m b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityUpdateTransition.m similarity index 72% rename from DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityUpdateTransition.m rename to DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityUpdateTransition.m index 4701614c4..59ea5c50e 100644 --- a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityUpdateTransition.m +++ b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSIdentityUpdateTransition.m @@ -1,17 +1,17 @@ // -// DSBlockchainIdentityResetTransition.m +// DSIdentityResetTransition.m // DashSync // // Created by Sam Westrich on 8/13/18. // -#import "DSBlockchainIdentityUpdateTransition.h" +#import "DSIdentityUpdateTransition.h" #import "DSTransactionFactory.h" #import "NSData+Dash.h" #import "NSMutableData+Dash.h" #import "NSString+Bitcoin.h" -@implementation DSBlockchainIdentityUpdateTransition +@implementation DSIdentityUpdateTransition //- (instancetype)initWithMessage:(NSData *)message onChain:(DSChain *)chain //{ @@ -29,7 +29,7 @@ @implementation DSBlockchainIdentityUpdateTransition // off += payloadLengthSize.unsignedLongValue; // // if (length - off < 2) return nil; -// self.blockchainIdentityResetTransactionVersion = [message UInt16AtOffset:off]; +// self.identityResetTransactionVersion = [message UInt16AtOffset:off]; // off += 2; // // if (length - off < 32) return nil; @@ -37,7 +37,7 @@ @implementation DSBlockchainIdentityUpdateTransition // off += 32; // // if (length - off < 32) return nil; -// self.previousBlockchainIdentityTransactionHash = [message UInt256AtOffset:off]; +// self.previousIdentityTransactionHash = [message UInt256AtOffset:off]; // off += 32; // // if (length - off < 8) return nil; @@ -66,7 +66,7 @@ @implementation DSBlockchainIdentityUpdateTransition // return self; //} // -//- (instancetype)initWithInputHashes:(NSArray *)hashes inputIndexes:(NSArray *)indexes inputScripts:(NSArray *)scripts inputSequences:(NSArray*)inputSequences outputAddresses:(NSArray *)addresses outputAmounts:(NSArray *)amounts blockchainIdentityResetTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousBlockchainIdentityTransactionHash:(UInt256)previousBlockchainIdentityTransactionHash replacementPublicKeyHash:(UInt160)replacementPublicKeyHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain { +//- (instancetype)initWithInputHashes:(NSArray *)hashes inputIndexes:(NSArray *)indexes inputScripts:(NSArray *)scripts inputSequences:(NSArray*)inputSequences outputAddresses:(NSArray *)addresses outputAmounts:(NSArray *)amounts identityResetTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousIdentityTransactionHash:(UInt256)previousIdentityTransactionHash replacementPublicKeyHash:(UInt160)replacementPublicKeyHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain { // NSParameterAssert(indexes); // NSParameterAssert(scripts); // NSParameterAssert(inputSequences); @@ -77,23 +77,23 @@ @implementation DSBlockchainIdentityUpdateTransition // if (!(self = [super initWithInputHashes:hashes inputIndexes:indexes inputScripts:scripts inputSequences:inputSequences outputAddresses:addresses outputAmounts:amounts onChain:chain])) return nil; // self.type = DSTransactionType_SubscriptionResetKey; // self.version = SPECIAL_TX_VERSION; -// self.blockchainIdentityResetTransactionVersion = version; +// self.identityResetTransactionVersion = version; // self.registrationTransactionHash = registrationTransactionHash; -// self.previousBlockchainIdentityTransactionHash = previousBlockchainIdentityTransactionHash; +// self.previousIdentityTransactionHash = previousIdentityTransactionHash; // self.creditFee = creditFee; // self.replacementPublicKeyHash = replacementPublicKeyHash; // return self; //} // -//-(instancetype)initWithBlockchainIdentityResetTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousBlockchainIdentityTransactionHash:(UInt256)previousBlockchainIdentityTransactionHash replacementPublicKeyHash:(UInt160)replacementPublicKeyHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain { +//-(instancetype)initWithIdentityResetTransactionVersion:(uint16_t)version registrationTransactionHash:(UInt256)registrationTransactionHash previousIdentityTransactionHash:(UInt256)previousIdentityTransactionHash replacementPublicKeyHash:(UInt160)replacementPublicKeyHash creditFee:(uint64_t)creditFee onChain:(DSChain *)chain { // NSParameterAssert(chain); // // if (!(self = [super initOnChain:chain])) return nil; // self.type = DSTransactionType_SubscriptionResetKey; // self.version = SPECIAL_TX_VERSION; -// self.blockchainIdentityResetTransactionVersion = version; +// self.identityResetTransactionVersion = version; // self.registrationTransactionHash = registrationTransactionHash; -// self.previousBlockchainIdentityTransactionHash = previousBlockchainIdentityTransactionHash; +// self.previousIdentityTransactionHash = previousIdentityTransactionHash; // self.creditFee = creditFee; // self.replacementPublicKeyHash = replacementPublicKeyHash; // return self; @@ -101,9 +101,9 @@ @implementation DSBlockchainIdentityUpdateTransition // ////-(NSData*)payloadData { //// NSMutableData * data = [NSMutableData data]; -//// [data appendUInt16:self.blockchainIdentityResetTransactionVersion]; +//// [data appendUInt16:self.identityResetTransactionVersion]; //// [data appendUInt256:self.registrationTransitionHash]; -//// [data appendUInt256:self.previousBlockchainIdentityTransactionHash]; +//// [data appendUInt256:self.previousIdentityTransactionHash]; //// [data appendUInt64:self.creditFee]; //// [data appendVarInt:self.replacementPublicKey.length]; //// [data appendData:self.replacementPublicKey]; @@ -114,9 +114,9 @@ @implementation DSBlockchainIdentityUpdateTransition //// ////-(NSData*)payloadDataForHash { //// NSMutableData * data = [NSMutableData data]; -//// [data appendUInt16:self.blockchainIdentityResetTransactionVersion]; +//// [data appendUInt16:self.identityResetTransactionVersion]; //// [data appendUInt256:self.registrationTransitionHash]; -//// [data appendUInt256:self.previousBlockchainIdentityTransactionHash]; +//// [data appendUInt256:self.previousIdentityTransactionHash]; //// [data appendUInt64:self.creditFee]; //// [data appendVarInt:self.replacementPublicKey.length]; //// [data appendData:self.replacementPublicKey]; @@ -126,9 +126,9 @@ @implementation DSBlockchainIdentityUpdateTransition // //-(NSData*)payloadData { // NSMutableData * data = [NSMutableData data]; -// [data appendUInt16:self.blockchainIdentityResetTransactionVersion]; +// [data appendUInt16:self.identityResetTransactionVersion]; // [data appendUInt256:self.registrationTransactionHash]; -// [data appendUInt256:self.previousBlockchainIdentityTransactionHash]; +// [data appendUInt256:self.previousIdentityTransactionHash]; // [data appendUInt64:self.creditFee]; // [data appendUInt160:self.replacementPublicKeyHash]; // [data appendVarInt:self.oldPublicKeyPayloadSignature.length]; @@ -138,9 +138,9 @@ @implementation DSBlockchainIdentityUpdateTransition // //-(NSData*)payloadDataForHash { // NSMutableData * data = [NSMutableData data]; -// [data appendUInt16:self.blockchainIdentityResetTransactionVersion]; +// [data appendUInt16:self.identityResetTransactionVersion]; // [data appendUInt256:self.registrationTransactionHash]; -// [data appendUInt256:self.previousBlockchainIdentityTransactionHash]; +// [data appendUInt256:self.previousIdentityTransactionHash]; // [data appendUInt64:self.creditFee]; // [data appendUInt160:self.replacementPublicKeyHash]; // [data appendUInt8:0]; @@ -152,8 +152,8 @@ @implementation DSBlockchainIdentityUpdateTransition //} // //-(BOOL)checkTransitionSignatureIsSignedByPublicKeyWithHash:(UInt160)oldPublicKeyHash { -// DSECDSAKey * blockchainIdentityPublicKey = [DSECDSAKey keyRecoveredFromCompactSig:self.oldPublicKeyPayloadSignature andMessageDigest:[self payloadHash]]; -// return uint160_eq([blockchainIdentityPublicKey hash160], oldPublicKeyHash); +// DSECDSAKey * identityPublicKey = [DSECDSAKey keyRecoveredFromCompactSig:self.oldPublicKeyPayloadSignature andMessageDigest:[self payloadHash]]; +// return uint160_eq([dentityPublicKey hash160], oldPublicKeyHash); //} // //-(void)signPayloadWithKey:(DSECDSAKey*)privateKey { diff --git a/DashSync/shared/Models/Platform/Transitions/DSContractTransition.h b/DashSync/shared/Models/Platform/Transitions/DSContractTransition.h index ebe8d19bf..49551bf9b 100644 --- a/DashSync/shared/Models/Platform/Transitions/DSContractTransition.h +++ b/DashSync/shared/Models/Platform/Transitions/DSContractTransition.h @@ -15,18 +15,21 @@ // limitations under the License. // -#import "DSTransition.h" +//#import "DSTransition.h" NS_ASSUME_NONNULL_BEGIN -@class DPContract; - -@interface DSContractTransition : DSTransition - -@property (nonatomic, readonly) DPContract *contract; - -- (instancetype)initWithContract:(DPContract *)contract withTransitionVersion:(uint16_t)version blockchainIdentityUniqueId:(UInt256)blockchainIdentityUniqueId onChain:(DSChain *)chain; - -@end +//@class DPContract; +// +//@interface DSContractTransition : DSTransition +// +//@property (nonatomic, readonly) DPContract *contract; +// +//- (instancetype)initWithContract:(DPContract *)contract +// withTransitionVersion:(uint16_t)version +// identityUniqueId:(UInt256)identityUniqueId +// onChain:(DSChain *)chain; +// +//@end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Platform/Transitions/DSContractTransition.m b/DashSync/shared/Models/Platform/Transitions/DSContractTransition.m index e7265e0b8..701c1643d 100644 --- a/DashSync/shared/Models/Platform/Transitions/DSContractTransition.m +++ b/DashSync/shared/Models/Platform/Transitions/DSContractTransition.m @@ -19,30 +19,36 @@ #import "DPContract+Protected.h" #import "DPDocument.h" #import "DSTransition+Protected.h" +#import "NSData+Dash.h" #import "NSString+Dash.h" -@interface DSContractTransition () - -@property (nonatomic, strong) DPContract *contract; - -@end - -@implementation DSContractTransition - -- (DSMutableStringValueDictionary *)baseKeyValueDictionary { - DSMutableStringValueDictionary *json = [super baseKeyValueDictionary]; - json[@"dataContract"] = self.contract.objectDictionary; - json[@"entropy"] = uint256_data(self.contract.entropy); - return json; -} - -- (instancetype)initWithContract:(DPContract *)contract withTransitionVersion:(uint16_t)version blockchainIdentityUniqueId:(UInt256)blockchainIdentityUniqueId onChain:(DSChain *)chain { - if (self = [super initWithTransitionVersion:version blockchainIdentityUniqueId:blockchainIdentityUniqueId onChain:chain]) { - self.contract = contract; - } - self.type = DSTransitionType_DataContract; - - return self; -} - -@end +//@interface DSContractTransition () +// +//@property (nonatomic, strong) DPContract *contract; +// +//@end +// +//@implementation DSContractTransition +// +//- (DSMutableStringValueDictionary *)baseKeyValueDictionary { +// DSMutableStringValueDictionary *json = [super baseKeyValueDictionary]; +//// json[@"dataContract"] = self.contract.objectDictionary; +// json[@"entropy"] = uint256_data(self.contract.entropy); +// return json; +//} +// +//- (instancetype)initWithContract:(DPContract *)contract +// withTransitionVersion:(uint16_t)version +// identityUniqueId:(UInt256)identityUniqueId +// onChain:(DSChain *)chain { +// if (self = [super initWithTransitionVersion:version +// identityUniqueId:identityUniqueId +// onChain:chain]) { +// self.contract = contract; +// } +// self.type = DSTransitionType_DataContract; +// +// return self; +//} +// +//@end diff --git a/DashSync/shared/Models/Platform/Transitions/DSDocumentTransition.h b/DashSync/shared/Models/Platform/Transitions/DSDocumentTransition.h index c772c2083..48516ac4e 100644 --- a/DashSync/shared/Models/Platform/Transitions/DSDocumentTransition.h +++ b/DashSync/shared/Models/Platform/Transitions/DSDocumentTransition.h @@ -15,6 +15,7 @@ // limitations under the License. // +#import "DPDocument.h" #import "DSTransition.h" NS_ASSUME_NONNULL_BEGIN @@ -33,7 +34,10 @@ typedef NS_ENUM(NSUInteger, DSDocumentTransitionType) @property (nonatomic, readonly) NSArray *documents; @property (nonatomic, readonly) DSPlatformQuery *expectedResponseQuery; -- (instancetype)initForDocuments:(NSArray *)documents withTransitionVersion:(uint16_t)version blockchainIdentityUniqueId:(UInt256)blockchainIdentityUniqueId onChain:(DSChain *)chain; +- (instancetype)initForDocuments:(NSArray *)documents + withTransitionVersion:(uint16_t)version + identityUniqueId:(UInt256)identityUniqueId + onChain:(DSChain *)chain; @end diff --git a/DashSync/shared/Models/Platform/Transitions/DSDocumentTransition.m b/DashSync/shared/Models/Platform/Transitions/DSDocumentTransition.m index 242e3a334..4f5bd3fa4 100644 --- a/DashSync/shared/Models/Platform/Transitions/DSDocumentTransition.m +++ b/DashSync/shared/Models/Platform/Transitions/DSDocumentTransition.m @@ -16,11 +16,11 @@ // #import "DSDocumentTransition.h" -#import "DPDocument.h" #import "DPDocumentState.h" #import "DSPlatformQuery.h" #import "DSPlatformTreeQuery.h" #import "DSTransition+Protected.h" +#import "NSData+Dash.h" @interface DSDocumentTransition () @@ -42,16 +42,19 @@ - (NSArray *)documentsAsArrayOfDictionaries { - (DSMutableStringValueDictionary *)baseKeyValueDictionary { DSMutableStringValueDictionary *json = [super baseKeyValueDictionary]; json[@"transitions"] = [self documentsAsArrayOfDictionaries]; - json[@"ownerId"] = uint256_data(self.blockchainIdentityUniqueId); + json[@"ownerId"] = uint256_data(self.identityUniqueId); return json; } -- (instancetype)initForDocuments:(NSArray *)documents withTransitionVersion:(uint16_t)version blockchainIdentityUniqueId:(UInt256)blockchainIdentityUniqueId onChain:(DSChain *)chain { - if (!(self = [super initWithTransitionVersion:version blockchainIdentityUniqueId:blockchainIdentityUniqueId onChain:chain])) return nil; +- (instancetype)initForDocuments:(NSArray *)documents + withTransitionVersion:(uint16_t)version + identityUniqueId:(UInt256)identityUniqueId + onChain:(DSChain *)chain { + if (!(self = [super initWithTransitionVersion:version identityUniqueId:identityUniqueId onChain:chain])) return nil; self.documents = documents; self.type = DSTransitionType_Documents; - self.blockchainIdentityUniqueId = blockchainIdentityUniqueId; + self.identityUniqueId = identityUniqueId; return self; } diff --git a/DashSync/shared/Models/Platform/Transitions/DSTransition+Protected.h b/DashSync/shared/Models/Platform/Transitions/DSTransition+Protected.h index d24b64c44..5741f6313 100644 --- a/DashSync/shared/Models/Platform/Transitions/DSTransition+Protected.h +++ b/DashSync/shared/Models/Platform/Transitions/DSTransition+Protected.h @@ -30,13 +30,13 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) uint64_t creditFee; @property (nonatomic, assign) UInt256 transitionHash; -@property (nonatomic, assign) UInt256 blockchainIdentityUniqueId; +@property (nonatomic, assign) UInt256 identityUniqueId; @property (nonatomic, assign) NSTimeInterval createdTimestamp; @property (nonatomic, assign) NSTimeInterval registeredTimestamp; @property (nonatomic, copy) NSData *signatureData; -@property (nonatomic, assign) KeyKind signatureType; +//@property (nonatomic, assign) KeyKind signatureType; @property (nonatomic, assign) uint32_t signaturePublicKeyId; @property (nonatomic, readonly) DSMutableStringValueDictionary *keyValueDictionary; diff --git a/DashSync/shared/Models/Platform/Transitions/DSTransition.h b/DashSync/shared/Models/Platform/Transitions/DSTransition.h index a99c0ce29..c85c30704 100644 --- a/DashSync/shared/Models/Platform/Transitions/DSTransition.h +++ b/DashSync/shared/Models/Platform/Transitions/DSTransition.h @@ -6,9 +6,9 @@ // #import "BigIntTypes.h" -#import "dash_shared_core.h" #import "DPBaseObject.h" -#import "DSBlockchainIdentity.h" +//#import "DSIdentity.h" +#import "DSKeyManager.h" NS_ASSUME_NONNULL_BEGIN @@ -27,13 +27,13 @@ typedef NS_ENUM(NSUInteger, DSTransitionType) #define TS_VERSION 0x00000001u -@class DSBlockchainIdentity; +@class DSIdentity; @interface DSTransition : DPBaseObject @property (nonatomic, readonly) uint16_t version; @property (nonatomic, readonly) DSTransitionType type; -@property (nonatomic, readonly) UInt256 blockchainIdentityUniqueId; +@property (nonatomic, readonly) UInt256 identityUniqueId; @property (nonatomic, readonly) uint64_t creditFee; @property (nonatomic, readonly) UInt256 transitionHash; @@ -42,15 +42,20 @@ typedef NS_ENUM(NSUInteger, DSTransitionType) @property (nonatomic, readonly) NSTimeInterval createdTimestamp; @property (nonatomic, readonly) NSTimeInterval registeredTimestamp; -@property (nonatomic, readonly) KeyKind signatureType; +//@property (nonatomic, readonly) DKeyKind signatureType; @property (nonatomic, readonly) NSData *signatureData; @property (nonatomic, readonly) uint32_t signaturePublicKeyId; -- (instancetype)initWithTransitionVersion:(uint16_t)version blockchainIdentityUniqueId:(UInt256)blockchainIdentityUniqueId onChain:(DSChain *_Nonnull)chain; //local creation +- (instancetype)initWithTransitionVersion:(uint16_t)version + identityUniqueId:(UInt256)identityUniqueId + onChain:(DSChain *_Nonnull)chain; //local creation -- (instancetype)initWithData:(NSData *)data onChain:(DSChain *)chain; +- (instancetype)initWithData:(NSData *)data + onChain:(DSChain *)chain; -- (void)signWithKey:(OpaqueKey *)privateKey atIndex:(uint32_t)index fromIdentity:(DSBlockchainIdentity *_Nullable)blockchainIdentity; +- (void)signWithKey:(DMaybeOpaqueKey *)privateKey + atIndex:(uint32_t)index + fromIdentity:(DSIdentity *_Nullable)identity; @end diff --git a/DashSync/shared/Models/Platform/Transitions/DSTransition.m b/DashSync/shared/Models/Platform/Transitions/DSTransition.m index ade916806..4df1c0aa5 100644 --- a/DashSync/shared/Models/Platform/Transitions/DSTransition.m +++ b/DashSync/shared/Models/Platform/Transitions/DSTransition.m @@ -7,8 +7,8 @@ #import "DSTransition.h" #import "BigIntTypes.h" -#import "DSBlockchainIdentity.h" -#import "DSBlockchainIdentityRegistrationTransition.h" +#import "DSIdentity.h" +#import "DSIdentityRegistrationTransition.h" #import "DSChainEntity+CoreDataClass.h" #import "DSKeyManager.h" #import "DSTransition+Protected.h" @@ -21,7 +21,7 @@ @interface DSTransition () -@property (nonatomic, strong) DSBlockchainIdentityRegistrationTransition *blockchainIdentityRegistrationTransaction; +@property (nonatomic, strong) DSIdentityRegistrationTransition *identityRegistrationTransaction; @end @@ -43,34 +43,36 @@ - (instancetype)initWithData:(NSData *)data onChain:(DSChain *)chain { if (!(self = [self initOnChain:chain])) return nil; NSError *error = nil; _keyValueDictionary = [data ds_decodeCborError:&error]; - if (error || !_keyValueDictionary) { - return nil; - } + if (error || !_keyValueDictionary) return nil; [self applyKeyValueDictionary:_keyValueDictionary]; return self; } -- (instancetype)initWithTransitionVersion:(uint16_t)version blockchainIdentityUniqueId:(UInt256)blockchainIdentityUniqueId onChain:(DSChain *_Nonnull)chain { +- (instancetype)initWithTransitionVersion:(uint16_t)version identityUniqueId:(UInt256)identityUniqueId onChain:(DSChain *_Nonnull)chain { NSParameterAssert(chain); if (!(self = [self initOnChain:chain])) return nil; self.type = DSTransitionType_Documents; self.version = version; - self.blockchainIdentityUniqueId = blockchainIdentityUniqueId; + self.identityUniqueId = identityUniqueId; return self; } -- (BOOL)checkTransitionSignature:(OpaqueKey *)key { +- (BOOL)checkTransitionSignature:(DOpaqueKey *)key { return [DSKeyManager verifyMessageDigest:key digest:[self serializedBaseDataHash].UInt256 signature:self.signatureData]; } -- (BOOL)checkTransitionSignedByBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity { - return [blockchainIdentity verifySignature:self.signatureData ofType:KeyKind_ECDSA forMessageDigest:[self serializedBaseDataHash].UInt256]; +- (BOOL)checkTransitionSignedByIdentity:(DSIdentity *)identity { + return [identity verifySignature:self.signatureData + ofType:dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor() + forMessageDigest:[self serializedBaseDataHash].UInt256]; } -- (void)signWithKey:(OpaqueKey *)privateKey atIndex:(uint32_t)index fromIdentity:(DSBlockchainIdentity *)blockchainIdentity { +- (void)signWithKey:(DMaybeOpaqueKey *)privateKey + atIndex:(uint32_t)index + fromIdentity:(DSIdentity *)identity { NSParameterAssert(privateKey); - if ([self isKindOfClass:[DSBlockchainIdentityRegistrationTransition class]]) { + if ([self isKindOfClass:[DSIdentityRegistrationTransition class]]) { NSAssert(index == UINT32_MAX, @"index must not exist"); } else { NSAssert(index != UINT32_MAX, @"index must exist"); @@ -78,8 +80,9 @@ - (void)signWithKey:(OpaqueKey *)privateKey atIndex:(uint32_t)index fromIdentity //ATTENTION If this ever changes from ECDSA, change the max signature size defined above // DSLogPrivate(@"Private Key is %@", [privateKey serializedPrivateKeyForChain:self.chain]); // DSLogPrivate(@"Signing %@ with key %@", [self serializedBaseDataHash].hexString, privateKey.publicKeyData.hexString); - self.signatureType = (KeyKind) privateKey->tag; - self.signatureData = [DSKeyManager signMesasageDigest:privateKey digest:[self serializedBaseDataHash].UInt256]; +// dash_spv_crypto_keys_key_OpaqueKey_kind(privateKey); +// self.signatureType = (KeyKind) privateKey->tag; + self.signatureData = [DSKeyManager signMesasageDigest:privateKey->ok digest:[self serializedBaseDataHash].UInt256]; self.signaturePublicKeyId = index; self.transitionHash = self.data.SHA256; } diff --git a/DashSync/shared/Models/Spork/DSSpork.m b/DashSync/shared/Models/Spork/DSSpork.m index 29dc17b60..baa85abc6 100644 --- a/DashSync/shared/Models/Spork/DSSpork.m +++ b/DashSync/shared/Models/Spork/DSSpork.m @@ -8,6 +8,7 @@ #import "DSSpork.h" #import "DSChain.h" +#import "DSChain+Params.h" #import "DSChainManager.h" #import "DSKeyManager.h" #import "DSPeerManager.h" @@ -78,12 +79,44 @@ - (BOOL)checkSignature70208Method:(NSData *)signature { [stringMessageData appendString:DASH_MESSAGE_MAGIC]; [stringMessageData appendString:stringMessage]; UInt256 messageDigest = stringMessageData.SHA256_2; + SLICE *compact_sig = slice_ctor(signature); + u256 *message_digest = Arr_u8_32_ctor(32, messageDigest.u8); + + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError *msg_public_key = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_key_with_compact_sig(compact_sig, message_digest); +// slice_dtor(compact_sig); +// u256_dtor(message_digest); + if (!msg_public_key) { + return NO; + } + if (!msg_public_key->ok) { + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError_destroy(msg_public_key); + return NO; + } + NSData *publicKeyData = [NSData dataFromHexString:[self sporkKey]]; + SLICE *public_key_data = slice_ctor(publicKeyData); - OpaqueKey *messagePublicKey = key_ecdsa_recovered_from_compact_sig(signature.bytes, signature.length, messageDigest.u8); - OpaqueKey *sporkPublicKey = [DSKeyManager keyWithPublicKeyData:[NSData dataFromHexString:[self sporkKey]] ofType:KeyKind_ECDSA]; - BOOL isEqual = [DSKeyManager keysPublicKeyDataIsEqual:sporkPublicKey key2:messagePublicKey]; - processor_destroy_opaque_key(messagePublicKey); - processor_destroy_opaque_key(sporkPublicKey); + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError *spork_public_key = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_key_with_public_key_data(public_key_data); +// slice_dtor(public_key_data); + if (!spork_public_key) { + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError_destroy(msg_public_key); + return NO; + } + if (!spork_public_key->ok) { + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError_destroy(msg_public_key); + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError_destroy(spork_public_key); + return NO; + } + BYTES *spork_public_key_data = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_public_key_data(spork_public_key->ok); + BOOL isEqual = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_public_key_data_equal_to(msg_public_key->ok, spork_public_key_data); + bytes_dtor(spork_public_key_data); + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError_destroy(msg_public_key); + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError_destroy(spork_public_key); + +// DOpaqueKey *messagePublicKey = key_ecdsa_recovered_from_compact_sig(signature.bytes, signature.length, messageDigest.u8); +// DOpaqueKey *sporkPublicKey = [DSKeyManager keyWithPublicKeyData:[NSData dataFromHexString:[self sporkKey]] ofType:KeyKind_ECDSA]; +// BOOL isEqual = [DSKeyManager keysPublicKeyDataIsEqual:sporkPublicKey key2:messagePublicKey]; +// processor_destroy_opaque_key(messagePublicKey); +// processor_destroy_opaque_key(sporkPublicKey); return isEqual; } @@ -91,9 +124,21 @@ - (BOOL)checkSignature:(NSData *)signature { if (self.chain.protocolVersion < 70209) { return [self checkSignature70208Method:signature]; } else { - OpaqueKey *messagePublicKey = key_ecdsa_recovered_from_compact_sig(signature.bytes, signature.length, self.sporkHash.u8); - NSString *sporkAddress = [DSKeyManager addressForKey:messagePublicKey forChainType:self.chain.chainType]; - processor_destroy_opaque_key(messagePublicKey); + SLICE *compact_sig = slice_ctor(signature); + u256 *message_digest = Arr_u8_32_ctor(32, self.sporkHash.u8); + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError *result = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_key_with_compact_sig(compact_sig, message_digest); + if (!result) return NO; + if (!result->ok) { + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError_destroy(result); + return NO; + } + char *addr = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_address_with_public_key_data(result->ok, self.chain.chainType); + NSString *sporkAddress = [DSKeyManager NSStringFrom:addr]; + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError_destroy(result); + +// DOpaqueKey *messagePublicKey = key_ecdsa_recovered_from_compact_sig(signature.bytes, signature.length, self.sporkHash.u8); +// NSString *sporkAddress = [DSKeyManager addressForKey:messagePublicKey forChainType:self.chain.chainType]; +// processor_destroy_opaque_key(messagePublicKey); DSSporkManager *sporkManager = self.chain.chainManager.sporkManager; return [[self sporkAddress] isEqualToString:sporkAddress] || (![sporkManager sporksUpdatedSignatures] && [self checkSignature70208Method:signature]); } @@ -101,10 +146,16 @@ - (BOOL)checkSignature:(NSData *)signature { - (NSString *)sporkKey { if (self.chain.sporkPublicKeyHexString) return self.chain.sporkPublicKeyHexString; + NSString *key = NULL; if (self.chain.sporkPrivateKeyBase58String) { - return [DSKeyManager NSDataFrom:key_ecdsa_public_key_data_for_private_key([self.chain.sporkPrivateKeyBase58String UTF8String], self.chain.chainType)].hexString; + DMaybeKeyData *result = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_public_key_data_for_private_key((char *) [self.chain.sporkPrivateKeyBase58String UTF8String], self.chain.chainType); + if (result) { + if (result->ok) + key = [NSData dataWithBytes:(const void *)result->ok->values length:result->ok->count].hexString; + DMaybeKeyDataDtor(result); + } } - return nil; + return key; } //starting in 12.3 sporks use addresses instead of public keys diff --git a/DashSync/shared/Models/System/DSEnvironment.m b/DashSync/shared/Models/System/DSEnvironment.m index f4d64dfba..0b50051e1 100644 --- a/DashSync/shared/Models/System/DSEnvironment.m +++ b/DashSync/shared/Models/System/DSEnvironment.m @@ -8,6 +8,7 @@ #import "DSEnvironment.h" #import "DSAccount.h" #import "DSChainsManager.h" +#import "DSChain+Wallet.h" #import "DSWallet.h" @implementation DSEnvironment diff --git a/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.h b/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.h index 241518775..47c4f00a5 100644 --- a/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.h +++ b/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.h @@ -25,6 +25,14 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) uint8_t specialTransactionVersion; @property (nonatomic, strong) NSMutableArray *creditOutputs; +@property (nonatomic, readonly) UInt160 creditBurnPublicKeyHash; +@property (nonatomic, readonly) DSUTXO lockedOutpoint; + +- (BOOL)checkInvitationDerivationPathIndexForWallet:(DSWallet *)wallet isIndex:(uint32_t)index; +- (BOOL)checkDerivationPathIndexForWallet:(DSWallet *)wallet isIndex:(uint32_t)index; + +- (void)markInvitationAddressAsUsedInWallet:(DSWallet *)wallet; +- (void)markAddressAsUsedInWallet:(DSWallet *)wallet; @end diff --git a/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m b/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m index 1ce845e2a..35b13e7cb 100644 --- a/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m @@ -17,8 +17,11 @@ #import "DSAssetLockTransaction.h" #import "DSChain.h" +#import "DSAssetLockDerivationPath.h" +#import "DSDerivationPathFactory.h" #import "DSTransactionFactory.h" #import "NSData+Dash.h" +#import "NSMutableData+Dash.h" @implementation DSAssetLockTransaction @@ -58,4 +61,82 @@ - (instancetype)initWithMessage:(NSData *)message onChain:(DSChain *)chain { return self; } + +- (NSData *)payloadData { + return [self basePayloadData]; +} + +- (NSData *)basePayloadData { + NSMutableData *data = [NSMutableData data]; + [data appendUInt8:self.specialTransactionVersion]; + NSUInteger creditOutputsCount = self.creditOutputs.count; + [data appendVarInt:creditOutputsCount]; + for (NSUInteger i = 0; i < creditOutputsCount; i++) { + DSTransactionOutput *output = self.creditOutputs[i]; + [data appendUInt64:output.amount]; + [data appendCountedData:output.outScript]; + } + return data; +} + +- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { + @synchronized(self) { + NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex] mutableCopy]; + [data appendCountedData:[self payloadData]]; + if (subscriptIndex != NSNotFound) [data appendUInt32:SIGHASH_ALL]; + return data; + } +} +- (size_t)size { + return [super size] + [self payloadData].length; +} + +- (DSUTXO)lockedOutpoint { + for (int i = 0; i < self.creditOutputs.count; i++) { + DSTransactionOutput *output = self.outputs[i]; + NSData *script = output.outScript; + if ([script UInt8AtOffset:0] == OP_RETURN && script.length == 22) { + DSUTXO outpoint = {.hash = uint256_reverse(self.txHash), .n = i}; //!OCLINT + return outpoint; + } + } + return DSUTXO_ZERO; +} + +- (UInt160)creditBurnPublicKeyHash { + for (DSTransactionOutput *output in self.creditOutputs) { + NSData *script = output.outScript; + if ([script UInt8AtOffset:0] == OP_RETURN && script.length == 22) { + return [script subdataWithRange:NSMakeRange(2, 20)].UInt160; + } + } + return UINT160_ZERO; +} +- (BOOL)checkInvitationDerivationPathIndexForWallet:(DSWallet *)wallet isIndex:(uint32_t)index { + DSAssetLockDerivationPath *registrationFundingDerivationPath = [[DSDerivationPathFactory sharedInstance] identityInvitationFundingDerivationPathForWallet:wallet]; + NSString *address = [DSKeyManager addressFromHash160:[self creditBurnPublicKeyHash] forChain:self.chain]; + return [[registrationFundingDerivationPath addressAtIndex:index] isEqualToString:address]; +} + +- (BOOL)checkDerivationPathIndexForWallet:(DSWallet *)wallet isIndex:(uint32_t)index { + DSAssetLockDerivationPath *registrationFundingDerivationPath = [[DSDerivationPathFactory sharedInstance] identityRegistrationFundingDerivationPathForWallet:wallet]; + NSString *address = [DSKeyManager addressFromHash160:[self creditBurnPublicKeyHash] forChain:self.chain]; + return [[registrationFundingDerivationPath addressAtIndex:index] isEqualToString:address]; +} + + +- (void)markInvitationAddressAsUsedInWallet:(DSWallet *)wallet { + DSAssetLockDerivationPath *path = [[DSDerivationPathFactory sharedInstance] identityInvitationFundingDerivationPathForWallet:wallet]; + NSString *address = [DSKeyManager addressFromHash160:[self creditBurnPublicKeyHash] forChain:self.chain]; + [path registerTransactionAddress:address]; + [path registerAddressesWithGapLimit:10 error:nil]; +} + +- (void)markAddressAsUsedInWallet:(DSWallet *)wallet { + DSAssetLockDerivationPath *path = [[DSDerivationPathFactory sharedInstance] identityRegistrationFundingDerivationPathForWallet:wallet]; + NSString *address = [DSKeyManager addressFromHash160:[self creditBurnPublicKeyHash] forChain:self.chain]; + [path registerTransactionAddress:address]; + [path registerAddressesWithGapLimit:10 error:nil]; +} + @end diff --git a/DashSync/shared/Models/Transactions/Base/DSCreditFundingTransaction.m b/DashSync/shared/Models/Transactions/Base/DSCreditFundingTransaction.m index 91cadd9c3..5f54b6659 100644 --- a/DashSync/shared/Models/Transactions/Base/DSCreditFundingTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSCreditFundingTransaction.m @@ -17,7 +17,7 @@ #import "DSCreditFundingTransaction.h" #import "DSAccount.h" -#import "DSCreditFundingDerivationPath.h" +#import "DSAssetLockDerivationPath.h" #import "DSCreditFundingTransactionEntity+CoreDataClass.h" #import "DSDerivationPathFactory.h" #import "DSInstantSendTransactionLock.h" @@ -57,32 +57,33 @@ - (UInt160)creditBurnPublicKeyHash { } - (uint32_t)usedDerivationPathIndexForWallet:(DSWallet *)wallet { - DSCreditFundingDerivationPath *registrationFundingDerivationPath = [[DSDerivationPathFactory sharedInstance] blockchainIdentityRegistrationFundingDerivationPathForWallet:wallet]; - NSString *address = [DSKeyManager addressFromHash160:[self creditBurnPublicKeyHash] forChain:self.chain]; - return (uint32_t)[registrationFundingDerivationPath indexOfKnownAddress:address]; + DSAssetLockDerivationPath *registrationFundingDerivationPath = [[DSDerivationPathFactory sharedInstance] identityRegistrationFundingDerivationPathForWallet:wallet]; + return (uint32_t)[registrationFundingDerivationPath indexOfKnownAddressHash:[self creditBurnPublicKeyHash]]; } -- (BOOL)checkDerivationPathIndexForWallet:(DSWallet *)wallet isIndex:(uint32_t)index { - DSCreditFundingDerivationPath *registrationFundingDerivationPath = [[DSDerivationPathFactory sharedInstance] blockchainIdentityRegistrationFundingDerivationPathForWallet:wallet]; +- (BOOL)checkDerivationPathIndexForWallet:(DSWallet *)wallet + isIndex:(uint32_t)index { + DSAssetLockDerivationPath *registrationFundingDerivationPath = [[DSDerivationPathFactory sharedInstance] identityRegistrationFundingDerivationPathForWallet:wallet]; NSString *address = [DSKeyManager addressFromHash160:[self creditBurnPublicKeyHash] forChain:self.chain]; return [[registrationFundingDerivationPath addressAtIndex:index] isEqualToString:address]; } -- (BOOL)checkInvitationDerivationPathIndexForWallet:(DSWallet *)wallet isIndex:(uint32_t)index { - DSCreditFundingDerivationPath *registrationFundingDerivationPath = [[DSDerivationPathFactory sharedInstance] blockchainIdentityInvitationFundingDerivationPathForWallet:wallet]; +- (BOOL)checkInvitationDerivationPathIndexForWallet:(DSWallet *)wallet + isIndex:(uint32_t)index { + DSAssetLockDerivationPath *registrationFundingDerivationPath = [[DSDerivationPathFactory sharedInstance] identityInvitationFundingDerivationPathForWallet:wallet]; NSString *address = [DSKeyManager addressFromHash160:[self creditBurnPublicKeyHash] forChain:self.chain]; return [[registrationFundingDerivationPath addressAtIndex:index] isEqualToString:address]; } - (void)markAddressAsUsedInWallet:(DSWallet *)wallet { - DSCreditFundingDerivationPath *registrationFundingDerivationPath = [[DSDerivationPathFactory sharedInstance] blockchainIdentityRegistrationFundingDerivationPathForWallet:wallet]; + DSAssetLockDerivationPath *registrationFundingDerivationPath = [[DSDerivationPathFactory sharedInstance] identityRegistrationFundingDerivationPathForWallet:wallet]; NSString *address = [DSKeyManager addressFromHash160:[self creditBurnPublicKeyHash] forChain:self.chain]; [registrationFundingDerivationPath registerTransactionAddress:address]; [registrationFundingDerivationPath registerAddressesWithGapLimit:10 error:nil]; } - (void)markInvitationAddressAsUsedInWallet:(DSWallet *)wallet { - DSCreditFundingDerivationPath *registrationFundingDerivationPath = [[DSDerivationPathFactory sharedInstance] blockchainIdentityInvitationFundingDerivationPathForWallet:wallet]; + DSAssetLockDerivationPath *registrationFundingDerivationPath = [[DSDerivationPathFactory sharedInstance] identityInvitationFundingDerivationPathForWallet:wallet]; NSString *address = [DSKeyManager addressFromHash160:[self creditBurnPublicKeyHash] forChain:self.chain]; [registrationFundingDerivationPath registerTransactionAddress:address]; [registrationFundingDerivationPath registerAddressesWithGapLimit:10 error:nil]; diff --git a/DashSync/shared/Models/Transactions/Base/DSInstantSendTransactionLock.h b/DashSync/shared/Models/Transactions/Base/DSInstantSendTransactionLock.h index ed1b6e57c..da4a43735 100644 --- a/DashSync/shared/Models/Transactions/Base/DSInstantSendTransactionLock.h +++ b/DashSync/shared/Models/Transactions/Base/DSInstantSendTransactionLock.h @@ -7,11 +7,12 @@ #import "BigIntTypes.h" #import "DSChain.h" +#import "DSKeyManager.h" #import NS_ASSUME_NONNULL_BEGIN -@class DSChain, DSSimplifiedMasternodeEntry, DSQuorumEntry, DSMasternodeList; +@class DSChain; @interface DSInstantSendTransactionLock : NSObject @@ -22,7 +23,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) NSArray *inputOutpoints; @property (nonatomic, readonly) UInt256 cycleHash; @property (nonatomic, readonly) BOOL signatureVerified; //verifies the signature and quorum together -@property (nonatomic, readonly) DSQuorumEntry *intendedQuorum; +//@property (nonatomic, readonly) DLLMQEntry *intendedQuorum; +@property (nonatomic, readonly, assign) u384 *intendedQuorumPublicKey; +//@property (nonatomic, readonly) DSQuorumEntry *intendedQuorum; @property (nonatomic, readonly) BOOL saved; @property (nonatomic, readonly) UInt256 requestID; @@ -41,7 +44,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithTransactionHash:(UInt256)transactionHash withInputOutpoints:(NSArray *)inputOutpoints signature:(UInt768)signature signatureVerified:(BOOL)signatureVerified quorumVerified:(BOOL)quorumVerified onChain:(DSChain *)chain; -- (DSQuorumEntry *_Nullable)findSigningQuorumReturnMasternodeList:(DSMasternodeList *_Nullable *_Nullable)returnMasternodeList; +//- (DSQuorumEntry *_Nullable)findSigningQuorumReturnMasternodeList:(DSMasternodeList *_Nullable *_Nullable)returnMasternodeList; @end diff --git a/DashSync/shared/Models/Transactions/Base/DSInstantSendTransactionLock.m b/DashSync/shared/Models/Transactions/Base/DSInstantSendTransactionLock.m index 5a2fb3cfd..900dd1738 100644 --- a/DashSync/shared/Models/Transactions/Base/DSInstantSendTransactionLock.m +++ b/DashSync/shared/Models/Transactions/Base/DSInstantSendTransactionLock.m @@ -7,13 +7,11 @@ #import "DSInstantSendTransactionLock.h" #import "DSChain.h" +#import "DSChain+Params.h" #import "DSChainEntity+CoreDataClass.h" #import "DSChainManager.h" #import "DSInstantSendLockEntity+CoreDataClass.h" -#import "DSMasternodeList.h" #import "DSMasternodeManager.h" -#import "DSQuorumEntry.h" -#import "DSSimplifiedMasternodeEntry.h" #import "DSSporkManager.h" #import "DSTransactionEntity+CoreDataClass.h" #import "DSTransactionHashEntity+CoreDataClass.h" @@ -31,7 +29,9 @@ @interface DSInstantSendTransactionLock () @property (nonatomic, strong) NSArray *inputOutpoints; @property (nonatomic, assign) BOOL signatureVerified; @property (nonatomic, assign) BOOL quorumVerified; -@property (nonatomic, strong) DSQuorumEntry *intendedQuorum; +//@property (nonatomic, strong) DSQuorumEntry *intendedQuorum; +//@property (nonatomic, assign) DLLMQEntry *intendedQuorum; +@property (nonatomic, assign) u384 *intendedQuorumPublicKey; @property (nonatomic, assign) BOOL saved; @property (nonatomic, assign) UInt768 signature; @@ -39,6 +39,12 @@ @interface DSInstantSendTransactionLock () @implementation DSInstantSendTransactionLock +- (void)dealloc { + if (self.intendedQuorumPublicKey) { + u384_dtor(self.intendedQuorumPublicKey); + } +} + + (instancetype)instantSendTransactionLockWithNonDeterministicMessage:(NSData *)message onChain:(DSChain *)chain { return [[self alloc] initWithNonDeterministicMessage:message onChain:chain]; } @@ -141,11 +147,17 @@ - (NSData *)toData { [mData appendUTXO:inputOutpoints.transactionOutpoint]; } [mData appendUInt256:self.transactionHash]; + [mData appendUInt256:self.cycleHash]; [mData appendUInt768:self.signature]; return [mData copy]; } -- (instancetype)initWithTransactionHash:(UInt256)transactionHash withInputOutpoints:(NSArray *)inputOutpoints signature:(UInt768)signature signatureVerified:(BOOL)signatureVerified quorumVerified:(BOOL)quorumVerified onChain:(DSChain *)chain { +- (instancetype)initWithTransactionHash:(UInt256)transactionHash + withInputOutpoints:(NSArray *)inputOutpoints + signature:(UInt768)signature + signatureVerified:(BOOL)signatureVerified + quorumVerified:(BOOL)quorumVerified + onChain:(DSChain *)chain { if (!(self = [self initOnChain:chain])) return nil; self.transactionHash = transactionHash; self.inputOutpoints = inputOutpoints; @@ -170,59 +182,76 @@ - (UInt256)requestID { return _requestID; } -- (UInt256)signIDForQuorumEntry:(DSQuorumEntry *)quorumEntry { - NSMutableData *data = [NSMutableData data]; - [data appendVarInt:quorum_type_for_is_locks(self.chain.chainType)]; - [data appendUInt256:quorumEntry.quorumHash]; - [data appendUInt256:self.requestID]; - [data appendUInt256:self.transactionHash]; - return [data SHA256_2]; -} - -- (BOOL)verifySignatureAgainstQuorum:(DSQuorumEntry *)quorumEntry { - UInt256 signId = [self signIDForQuorumEntry:quorumEntry]; - return key_bls_verify(quorumEntry.quorumPublicKey.u8, quorumEntry.useLegacyBLSScheme, signId.u8, self.signature.u8); +- (UInt256)signIDForQuorumEntry:(DLLMQEntry *)quorumEntry { + u256 *request_id = u256_ctor_u(self.requestID); + u256 *tx_hash = u256_ctor_u(self.transactionHash); + u256 *result = DLLMQEntrySignID(quorumEntry, request_id, tx_hash); + UInt256 signId = u256_cast(result); + u256_dtor(result); + return signId; +// NSMutableData *data = [NSMutableData data]; +// +// struct dash_spv_crypto_crypto_byte_util_UInt256 *llmq_hash = dash_spv_crypto_llmq_entry_LLMQEntry_get_llmq_hash(quorumEntry); +// +// [data appendVarInt:quorum_type_for_is_locks(self.chain.chainType)]; +// [data appendUInt256:quorumEntry.quorumHash]; +// [data appendUInt256:self.requestID]; +// [data appendUInt256:self.transactionHash]; +// return [data SHA256_2]; } -- (DSQuorumEntry *)findSigningQuorumReturnMasternodeList:(DSMasternodeList **)returnMasternodeList { - DSQuorumEntry *foundQuorum = nil; - LLMQType ISLockQuorumType = quorum_type_for_is_locks(self.chain.chainType); - for (DSMasternodeList *masternodeList in [self.chain.chainManager.masternodeManager.recentMasternodeLists copy]) { - for (DSQuorumEntry *quorumEntry in [[masternodeList quorumsOfType:ISLockQuorumType] allValues]) { - BOOL signatureVerified = [self verifySignatureAgainstQuorum:quorumEntry]; - if (signatureVerified) { - foundQuorum = quorumEntry; - if (returnMasternodeList) *returnMasternodeList = masternodeList; - break; - } - } - if (foundQuorum) break; - } - return foundQuorum; +- (BOOL)verifySignatureAgainstQuorum:(DLLMQEntry *)quorumEntry { + u256 *request_id = u256_ctor_u(self.requestID); + u256 *tx_hash = u256_ctor_u(self.transactionHash); + u768 *sig = u768_ctor_u(self.signature); + u256 *sign_id = DLLMQEntrySignID(quorumEntry, request_id, tx_hash); + bool verified = DLLMQEntryVerifySignature(quorumEntry, sign_id, sig); + u256_dtor(sign_id); + return verified; +// UInt256 signId = [self signIDForQuorumEntry:quorumEntry]; +// return key_bls_verify(quorumEntry.quorumPublicKey.u8, quorumEntry.useLegacyBLSScheme, signId.u8, self.signature.u8); } +//- (DSQuorumEntry *)findSigningQuorumReturnMasternodeList:(DSMasternodeList **)returnMasternodeList { +// DSQuorumEntry *foundQuorum = nil; +// DLLMQType ISLockQuorumType = quorum_type_for_is_locks(self.chain.chainType); +// for (DSMasternodeList *masternodeList in [self.chain.chainManager.masternodeManager.recentMasternodeLists copy]) { +// for (DSQuorumEntry *quorumEntry in [[masternodeList quorumsOfType:ISLockQuorumType] allValues]) { +// BOOL signatureVerified = [self verifySignatureAgainstQuorum:quorumEntry]; +// if (signatureVerified) { +// foundQuorum = quorumEntry; +// if (returnMasternodeList) *returnMasternodeList = masternodeList; +// break; +// } +// } +// if (foundQuorum) break; +// } +// return foundQuorum; +//} +// - (BOOL)verifySignatureWithQuorumOffset:(uint32_t)offset { - DSQuorumEntry *quorumEntry = [self.chain.chainManager.masternodeManager quorumEntryForInstantSendRequestID:[self requestID] withBlockHeightOffset:offset]; - if (quorumEntry && quorumEntry.verified) { - self.signatureVerified = [self verifySignatureAgainstQuorum:quorumEntry]; - if (!self.signatureVerified) { - DSLog(@"[%@] unable to verify IS signature with offset %d", self.chain.name, offset); + DLLMQEntry *quorumEntry = [self.chain.chainManager.masternodeManager quorumEntryForInstantSendRequestID:[self requestID] withBlockHeightOffset:offset]; + if (quorumEntry) { + if (quorumEntry->verified) { + self.signatureVerified = [self verifySignatureAgainstQuorum:quorumEntry]; + if (!self.signatureVerified) { + DSLog(@"[%@] unable to verify IS signature with offset %d", self.chain.name, offset); + } else { + DSLog(@"[%@] IS signature verified with offset %d", self.chain.name, offset); + } } else { - DSLog(@"[%@] IS signature verified with offset %d", self.chain.name, offset); + DSLog(@"[%@] quorum entry %@ found but is not yet verified", self.chain.name, [DSKeyManager NSStringFrom:DLLMQEntryHashHex(quorumEntry)]); } - - } else if (quorumEntry) { - DSLog(@"[%@] quorum entry %@ found but is not yet verified", self.chain.name, uint256_hex(quorumEntry.quorumHash)); } else { DSLog(@"[%@] no quorum entry found", self.chain.name); } if (self.signatureVerified) { - self.intendedQuorum = quorumEntry; - } else if (quorumEntry.verified && offset == 8) { + self.intendedQuorumPublicKey = quorumEntry->public_key; + } else if (quorumEntry->verified && offset == 8) { //try again a few blocks more in the past DSLog(@"[%@] trying with offset 0", self.chain.name); return [self verifySignatureWithQuorumOffset:0]; - } else if (quorumEntry.verified && offset == 0) { + } else if (quorumEntry->verified && offset == 0) { //try again a few blocks more in the future DSLog(@"[%@] trying with offset 16", self.chain.name); return [self verifySignatureWithQuorumOffset:16]; diff --git a/DashSync/shared/Models/Transactions/Base/DSTransaction.h b/DashSync/shared/Models/Transactions/Base/DSTransaction.h index 66fc56c01..f072a41e1 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransaction.h +++ b/DashSync/shared/Models/Transactions/Base/DSTransaction.h @@ -29,10 +29,13 @@ #import "DSShapeshiftEntity+CoreDataClass.h" #import #import "DSChain.h" +#import "DSKeyManager.h" +#import "DSTransactionInput.h" +#import "DSTransactionOutput.h" NS_ASSUME_NONNULL_BEGIN -@class DSChain, DSAccount, DSWallet, DSTransactionLockVote, DSTransactionEntity, DSInstantSendTransactionLock, DSBlockchainIdentity, DSDerivationPath, DSTransactionInput, DSTransactionOutput; +@class DSChain, DSAccount, DSWallet, DSTransactionLockVote, DSTransactionEntity, DSInstantSendTransactionLock, DSIdentity, DSDerivationPath, DSTransactionInput, DSTransactionOutput; #define TX_FEE_PER_B 1ULL // standard tx fee per b of tx size #define TX_FEE_PER_INPUT 10000ULL // standard ix fee per input @@ -65,6 +68,14 @@ typedef NS_ENUM(NSInteger, DSTransactionSortType) DSTransactionSortType_BIP69, }; +typedef NS_ENUM(NSUInteger, DSTransactionDirection) +{ + DSTransactionDirection_Sent, + DSTransactionDirection_Received, + DSTransactionDirection_Moved, + DSTransactionDirection_NotAccountFunds, +}; + @interface DSTransaction : NSObject @property (nonatomic, readonly) NSArray *inputs; @@ -73,8 +84,8 @@ typedef NS_ENUM(NSInteger, DSTransactionSortType) @property (nonatomic, readonly) NSArray *inputAddresses; @property (nonatomic, readonly) NSArray *outputAddresses; -@property (nonatomic, readonly) NSSet *sourceBlockchainIdentities; -@property (nonatomic, readonly) NSSet *destinationBlockchainIdentities; +@property (nonatomic, readonly) NSSet *sourceIdentities; +@property (nonatomic, readonly) NSSet *destinationIdentities; @property (nonatomic, readonly) BOOL instantSendReceived; @property (nonatomic, readonly) BOOL confirmed; @@ -104,7 +115,7 @@ typedef NS_ENUM(NSInteger, DSTransactionSortType) @property (nonatomic, readonly) NSString *longDescription; @property (nonatomic, readonly) BOOL isCoinbaseClassicTransaction; -@property (nonatomic, readonly) BOOL isCreditFundingTransaction; +//@property (nonatomic, readonly) BOOL isCreditFundingTransaction; @property (nonatomic, readonly) UInt256 creditBurnIdentityIdentifier; @property (nonatomic, strong) DSShapeshiftEntity *associatedShapeshift; @@ -116,7 +127,7 @@ typedef NS_ENUM(NSInteger, DSTransactionSortType) @property (nonatomic, readonly) BOOL transactionTypeRequiresInputs; + (instancetype)transactionWithMessage:(NSData *)message onChain:(DSChain *)chain; -+ (UInt256)devnetGenesisCoinbaseTxHash:(DevnetType)devnetType onProtocolVersion:(uint32_t)protocolVersion forChain:(DSChain *)chain; ++ (UInt256)devnetGenesisCoinbaseTxHash:(dash_spv_crypto_network_chain_type_DevnetType *)devnetType onProtocolVersion:(uint32_t)protocolVersion forChain:(DSChain *)chain; - (instancetype)initOnChain:(DSChain *)chain; - (instancetype)initWithMessage:(NSData *)message onChain:(DSChain *)chain; @@ -143,6 +154,8 @@ typedef NS_ENUM(NSInteger, DSTransactionSortType) - (void)hasSetInputsAndOutputs; - (BOOL)signWithSerializedPrivateKeys:(NSArray *)privateKeys; - (BOOL)signWithPrivateKeys:(NSArray *)keys; +// TMP method to handle specific c structures +- (BOOL)signWithMaybePrivateKeys:(NSArray *)keys; - (BOOL)signWithPreorderedPrivateKeys:(NSArray *)keys; - (NSString *_Nullable)shapeshiftOutboundAddress; @@ -168,9 +181,10 @@ typedef NS_ENUM(NSInteger, DSTransactionSortType) - (void)setInstantSendReceivedWithInstantSendLock:(DSInstantSendTransactionLock *)instantSendLock; -- (void)loadBlockchainIdentitiesFromDerivationPaths:(NSArray *)derivationPaths; +- (void)loadIdentitiesFromDerivationPaths:(NSArray *)derivationPaths; @end +//typedef NSUInteger DSTransactionDirection; @interface DSTransaction (Extensions) - (DSTransactionDirection)direction; diff --git a/DashSync/shared/Models/Transactions/Base/DSTransaction.m b/DashSync/shared/Models/Transactions/Base/DSTransaction.m index 428853fb9..1d8a9c647 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSTransaction.m @@ -28,11 +28,15 @@ #import "DSAccount.h" #import "DSAddressEntity+CoreDataClass.h" +#import "DSAssetLockTransaction.h" #import "DSAssetUnlockTransaction.h" #import "DSChain.h" +#import "DSChain+Identity.h" +#import "DSChain+Params.h" +#import "DSChain+Transaction.h" +#import "DSChain+Wallet.h" #import "DSChainEntity+CoreDataClass.h" #import "DSChainManager.h" -#import "DSCreditFundingTransaction.h" #import "DSIdentitiesManager.h" #import "DSInstantSendTransactionLock.h" #import "DSMasternodeManager.h" @@ -40,8 +44,6 @@ #import "DSTransactionEntity+CoreDataClass.h" #import "DSTransactionFactory.h" #import "DSTransactionHashEntity+CoreDataClass.h" -#import "DSTransactionInput.h" -#import "DSTransactionOutput.h" #import "DSWallet.h" #import "NSData+DSHash.h" #import "NSData+Dash.h" @@ -54,8 +56,8 @@ @interface DSTransaction () @property (nonatomic, strong) DSChain *chain; @property (nonatomic, assign) BOOL confirmed; -@property (nonatomic, strong) NSSet *sourceBlockchainIdentities; -@property (nonatomic, strong) NSSet *destinationBlockchainIdentities; +@property (nonatomic, strong) NSSet *sourceIdentities; +@property (nonatomic, strong) NSSet *destinationIdentities; @property (nonatomic, strong) NSMutableArray *mInputs; @property (nonatomic, strong) NSMutableArray *mOutputs; @@ -69,9 +71,11 @@ + (instancetype)transactionWithMessage:(NSData *)message onChain:(DSChain *)chai return [[self alloc] initWithMessage:message onChain:chain]; } -+ (UInt256)devnetGenesisCoinbaseTxHash:(DevnetType)devnetType onProtocolVersion:(uint32_t)protocolVersion forChain:(DSChain *)chain { ++ (UInt256)devnetGenesisCoinbaseTxHash:(dash_spv_crypto_network_chain_type_DevnetType *)devnetType + onProtocolVersion:(uint32_t)protocolVersion + forChain:(DSChain *)chain { DSTransaction *transaction = [[self alloc] initOnChain:chain]; - NSData *coinbaseData = [DSKeyManager NSDataFrom:devnet_genesis_coinbase_message(devnetType, protocolVersion)]; + NSData *coinbaseData = [DSKeyManager NSDataFrom:dash_spv_crypto_devnet_genesis_coinbase_message(devnetType, protocolVersion)]; [transaction addInputHash:UINT256_ZERO index:UINT32_MAX script:nil signature:coinbaseData sequence:UINT32_MAX]; [transaction addOutputScript:[NSData dataWithUInt8:OP_RETURN] amount:chain.baseReward]; return transaction.toData.SHA256_2; @@ -94,8 +98,8 @@ - (instancetype)initOnChain:(DSChain *)chain { self.hasUnverifiedInstantSendLock = NO; _lockTime = TX_LOCKTIME; self.blockHeight = TX_UNCONFIRMED; - self.sourceBlockchainIdentities = [NSSet set]; - self.destinationBlockchainIdentities = [NSSet set]; + self.sourceIdentities = [NSSet set]; + self.destinationIdentities = [NSSet set]; return self; } @@ -380,15 +384,15 @@ - (BOOL)isCoinbaseClassicTransaction { } } -- (BOOL)isCreditFundingTransaction { - for (DSTransactionOutput *output in self.outputs) { - NSData *script = output.outScript; - if ([script UInt8AtOffset:0] == OP_RETURN && script.length == 22) { - return YES; - } - } - return NO; -} +//- (BOOL)isCreditFundingTransaction { +// for (DSTransactionOutput *output in self.outputs) { +// NSData *script = output.outScript; +// if ([script UInt8AtOffset:0] == OP_RETURN && script.length == 22) { +// return YES; +// } +// } +// return NO; +//} - (NSUInteger)hash { if (uint256_is_zero(_txHash)) return super.hash; @@ -409,7 +413,7 @@ - (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { NSArray *outputs = self.outputs; NSUInteger inputsCount = inputs.count; NSUInteger outputsCount = outputs.count; - BOOL forSigHash = ([self isMemberOfClass:[DSTransaction class]] || [self isMemberOfClass:[DSCreditFundingTransaction class]] || [self isMemberOfClass:[DSAssetUnlockTransaction class]]) && subscriptIndex != NSNotFound; + BOOL forSigHash = ([self isMemberOfClass:[DSTransaction class]] || [self isMemberOfClass:[DSAssetLockTransaction class]] || [self isMemberOfClass:[DSAssetUnlockTransaction class]]) && subscriptIndex != NSNotFound; NSUInteger dataSize = 8 + [NSMutableData sizeOfVarInt:inputsCount] + [NSMutableData sizeOfVarInt:outputsCount] + TX_INPUT_SIZE * inputsCount + TX_OUTPUT_SIZE * outputsCount + (forSigHash ? 4 : 0); NSMutableData *d = [NSMutableData dataWithCapacity:dataSize]; @@ -563,7 +567,8 @@ - (BOOL)signWithSerializedPrivateKeys:(NSArray *)privateKeys { NSMutableArray *keys = [NSMutableArray arrayWithCapacity:privateKeys.count]; for (NSString *pk in privateKeys) { - OpaqueKey *key = [DSKeyManager keyWithPrivateKeyString:pk ofKeyType:KeyKind_ECDSA forChainType:self.chain.chainType]; + DKeyKind *kind = dash_spv_crypto_keys_key_KeyKind_ECDSA_ctor(); + DMaybeOpaqueKey *key = [DSKeyManager keyWithPrivateKeyString:pk ofKeyType:kind forChainType:self.chain.chainType]; if (!key) continue; [keys addObject:[NSValue valueWithPointer:key]]; } @@ -575,34 +580,49 @@ - (BOOL)signWithPrivateKeys:(NSArray *)keys { NSMutableArray *addresses = [NSMutableArray arrayWithCapacity:keys.count]; // TODO: avoid double looping: defer getting address into signWithPrivateKeys key <-> address - for (NSValue *key in keys) { - [addresses addObject:[DSKeyManager addressForKey:key.pointerValue forChainType:self.chain.chainType]]; + for (NSValue *keyValue in keys) { + DMaybeOpaqueKey *key = (DMaybeOpaqueKey *) keyValue.pointerValue; + [addresses addObject:[DSKeyManager addressForKey:key->ok forChainType:self.chain.chainType]]; } @synchronized (self) { - for (NSUInteger i = 0; i < self.mInputs.count; i++) { - DSTransactionInput *transactionInput = self.mInputs[i]; - + for (NSUInteger i = 0; i < self.mInputs.count; i++) {DSTransactionInput *transactionInput = self.mInputs[i]; NSString *addr = [DSKeyManager addressWithScriptPubKey:transactionInput.inScript forChain:self.chain]; NSUInteger keyIdx = (addr) ? [addresses indexOfObject:addr] : NSNotFound; if (keyIdx == NSNotFound) continue; NSData *data = [self toDataWithSubscriptIndex:i]; - NSMutableData *sig = [NSMutableData data]; - NSValue *keyValue = keys[keyIdx]; - OpaqueKey *key = ((OpaqueKey *) keyValue.pointerValue); - UInt256 hash = data.SHA256_2; - NSData *signedData = [DSKeyManager NSDataFrom:key_ecdsa_sign(key->ecdsa, hash.u8, 32)]; - - NSMutableData *s = [NSMutableData dataWithData:signedData]; - [s appendUInt8:SIGHASH_ALL]; - [sig appendScriptPushData:s]; - NSArray *elem = [transactionInput.inScript scriptElements]; - if (elem.count >= 2 && [elem[elem.count - 2] intValue] == OP_EQUALVERIFY) { // pay-to-pubkey-hash scriptSig - [sig appendScriptPushData:[DSKeyManager publicKeyData:key]]; - } - + NSData *inScript = transactionInput.inScript; + NSData *sig = [DSTransaction signInput:data inputScript:inScript withOpaqueKeyValue:keys[keyIdx]]; transactionInput.signature = sig; } + if (!self.isSigned) return NO; + _txHash = self.data.SHA256_2; + return YES; + } +} + +- (BOOL)signWithMaybePrivateKeys:(NSArray *)keys { + NSMutableArray *addresses = [NSMutableArray arrayWithCapacity:keys.count]; + for (NSValue *keyValue in keys) { + DMaybeOpaqueKeys *maybe_key_set = (DMaybeOpaqueKeys *) keyValue.pointerValue; + if (maybe_key_set && maybe_key_set->ok) { + for (int i = 0; i < maybe_key_set->ok->count; i++) { + DOpaqueKey *key = maybe_key_set->ok->values[i]; + [addresses addObject:[DSKeyManager addressForKey:key forChainType:self.chain.chainType]]; + } + } + + } + @synchronized (self) { + for (NSUInteger i = 0; i < self.mInputs.count; i++) {DSTransactionInput *transactionInput = self.mInputs[i]; + NSString *addr = [DSKeyManager addressWithScriptPubKey:transactionInput.inScript forChain:self.chain]; + NSUInteger keyIdx = (addr) ? [addresses indexOfObject:addr] : NSNotFound; + if (keyIdx == NSNotFound) continue; + NSData *data = [self toDataWithSubscriptIndex:i]; + NSData *inScript = transactionInput.inScript; + NSData *sig = [DSTransaction signInput:data inputScript:inScript withOpaqueKeyValue:keys[keyIdx]]; + transactionInput.signature = sig; + } if (!self.isSigned) return NO; _txHash = self.data.SHA256_2; return YES; @@ -613,31 +633,28 @@ - (BOOL)signWithPreorderedPrivateKeys:(NSArray *)keys { @synchronized (self) { for (NSUInteger i = 0; i < self.mInputs.count; i++) { DSTransactionInput *transactionInput = self.mInputs[i]; - NSMutableData *sig = [NSMutableData data]; - NSData *data = [self toDataWithSubscriptIndex:i]; - UInt256 hash = data.SHA256_2; - NSValue *keyValue = keys[i]; - OpaqueKey *key = ((OpaqueKey *) keyValue.pointerValue); - NSData *signedData = [DSKeyManager NSDataFrom:key_ecdsa_sign(key->ecdsa, hash.u8, 32)]; - NSMutableData *s = [NSMutableData dataWithData:signedData]; - NSArray *elem = [transactionInput.inScript scriptElements]; - - [s appendUInt8:SIGHASH_ALL]; - [sig appendScriptPushData:s]; - - if (elem.count >= 2 && [elem[elem.count - 2] intValue] == OP_EQUALVERIFY) { // pay-to-pubkey-hash scriptSig - [sig appendScriptPushData:[DSKeyManager publicKeyData:key]]; - } - + NSData *sig = [DSTransaction signInput:[self toDataWithSubscriptIndex:i] inputScript:transactionInput.inScript withOpaqueKeyValue:keys[i]]; transactionInput.signature = sig; } - if (!self.isSigned) return NO; _txHash = self.data.SHA256_2; return YES; } } ++ (NSData *)signInput:(NSData *)data + inputScript:(NSData *)inputScript + withOpaqueKeyValue:(NSValue *)keyValue { + DMaybeOpaqueKey *key = ((DMaybeOpaqueKey *) keyValue.pointerValue); + SLICE *input = slice_ctor(data); + BYTES *tx_input_script = bytes_ctor(inputScript); + BYTES *tx_sig = dash_spv_crypto_keys_key_OpaqueKey_create_tx_signature(key->ok, input, tx_input_script); + NSData *result = [DSKeyManager NSDataFrom:tx_sig]; +// bytes_dtor(tx_input_script); +// slice_dtor(input); + return result; +} + // MARK: - Priority (Deprecated) // priority = sum(input_amount_in_satoshis*input_age_in_blocks)/size_in_bytes @@ -715,27 +732,34 @@ - (BOOL)confirmed { // MARK: - Blockchain Identities -- (void)loadBlockchainIdentitiesFromDerivationPaths:(NSArray *)derivationPaths { - NSMutableSet *destinationBlockchainIdentities = [NSMutableSet set]; - NSMutableSet *sourceBlockchainIdentities = [NSMutableSet set]; +- (void)loadIdentitiesFromDerivationPaths:(NSArray *)derivationPaths { + NSMutableSet *destinationIdentities = [NSMutableSet set]; + NSMutableSet *sourceIdentities = [NSMutableSet set]; for (DSTransactionOutput *output in self.outputs) { for (DSFundsDerivationPath *derivationPath in derivationPaths) { if ([derivationPath isKindOfClass:[DSIncomingFundsDerivationPath class]] && [derivationPath containsAddress:output.address]) { DSIncomingFundsDerivationPath *incomingFundsDerivationPath = ((DSIncomingFundsDerivationPath *)derivationPath); - DSBlockchainIdentity *destinationBlockchainIdentity = [incomingFundsDerivationPath contactDestinationBlockchainIdentity]; - DSBlockchainIdentity *sourceBlockchainIdentity = [incomingFundsDerivationPath contactSourceBlockchainIdentity]; - if (sourceBlockchainIdentity) { - [destinationBlockchainIdentities addObject:sourceBlockchainIdentity]; //these need to be inverted since the derivation path is incoming + DSIdentity *destinationIdentity = [self.chain identityForUniqueId:incomingFundsDerivationPath.contactDestinationIdentityUniqueId + foundInWallet:nil + includeForeignIdentities:YES]; + + DSIdentity *sourceIdentity = [self.chain identityForUniqueId:incomingFundsDerivationPath.contactSourceIdentityUniqueId + foundInWallet:nil + includeForeignIdentities:YES]; + + + if (sourceIdentity) { + [destinationIdentities addObject:sourceIdentity]; //these need to be inverted since the derivation path is incoming } - if (destinationBlockchainIdentity) { - [sourceBlockchainIdentities addObject:destinationBlockchainIdentity]; //these need to be inverted since the derivation path is incoming + if (destinationIdentity) { + [sourceIdentities addObject:destinationIdentity]; //these need to be inverted since the derivation path is incoming } } } } - self.sourceBlockchainIdentities = [self.sourceBlockchainIdentities setByAddingObjectsFromSet:[sourceBlockchainIdentities copy]]; - self.destinationBlockchainIdentities = [self.destinationBlockchainIdentities setByAddingObjectsFromSet:[destinationBlockchainIdentities copy]]; + self.sourceIdentities = [self.sourceIdentities setByAddingObjectsFromSet:[sourceIdentities copy]]; + self.destinationIdentities = [self.destinationIdentities setByAddingObjectsFromSet:[destinationIdentities copy]]; } // MARK: - Polymorphic data @@ -880,6 +904,24 @@ - (BOOL)saveInitialInContext:(NSManagedObjectContext *)context { @implementation DSTransaction (Extensions) - (DSTransactionDirection)direction { - return [_chain directionOfTransaction: self]; + const uint64_t sent = [_chain amountSentByTransaction:self]; + const uint64_t received = [_chain amountReceivedFromTransaction:self]; + const uint64_t fee = self.feeUsed; + if (sent > 0 && (received + fee) == sent) { + // moved + return DSTransactionDirection_Moved; + } else if (sent > 0) { + // sent + return DSTransactionDirection_Sent; + } else if (received > 0) { + // received + return DSTransactionDirection_Received; + } else { + // no funds moved on this account + return DSTransactionDirection_NotAccountFunds; + } +// +// +// return [_chain directionOfTransaction: self]; } @end diff --git a/DashSync/shared/Models/Transactions/Base/DSTransactionInput.h b/DashSync/shared/Models/Transactions/Base/DSTransactionInput.h index 337686faf..f87289775 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransactionInput.h +++ b/DashSync/shared/Models/Transactions/Base/DSTransactionInput.h @@ -29,7 +29,11 @@ typedef union _UInt256 UInt256; @property (nonatomic, strong, nullable) NSData *signature; @property (nonatomic, assign) uint32_t sequence; -+ (instancetype)transactionInputWithHash:(UInt256)inputHash index:(uint32_t)index inScript:(NSData *)inScript signature:(NSData *_Nullable)signature sequence:(uint32_t)sequence; ++ (instancetype)transactionInputWithHash:(UInt256)inputHash + index:(uint32_t)index + inScript:(NSData *)inScript + signature:(NSData *_Nullable)signature + sequence:(uint32_t)sequence; - (NSComparisonResult)compare:(DSTransactionInput *)obj; diff --git a/DashSync/shared/Models/Transactions/Coinbase/DSCoinbaseTransaction.m b/DashSync/shared/Models/Transactions/Coinbase/DSCoinbaseTransaction.m index 3c2370ba6..6658e4f37 100644 --- a/DashSync/shared/Models/Transactions/Coinbase/DSCoinbaseTransaction.m +++ b/DashSync/shared/Models/Transactions/Coinbase/DSCoinbaseTransaction.m @@ -5,6 +5,7 @@ // Created by Sam Westrich on 7/12/18. // +#import "DSChain+Params.h" #import "DSCoinbaseTransaction.h" #import "DSCoinbaseTransactionEntity+CoreDataClass.h" #import "DSTransactionFactory.h" diff --git a/DashSync/shared/Models/Transactions/DSTransactionFactory.m b/DashSync/shared/Models/Transactions/DSTransactionFactory.m index 4bc66e4d9..ee872b361 100644 --- a/DashSync/shared/Models/Transactions/DSTransactionFactory.m +++ b/DashSync/shared/Models/Transactions/DSTransactionFactory.m @@ -8,12 +8,11 @@ #import "DSAssetLockTransaction.h" #import "DSAssetUnlockTransaction.h" #import "DSTransactionFactory.h" -#import "DSBlockchainIdentityCloseTransition.h" -#import "DSBlockchainIdentityRegistrationTransition.h" -#import "DSBlockchainIdentityTopupTransition.h" -#import "DSBlockchainIdentityUpdateTransition.h" +#import "DSIdentityCloseTransition.h" +#import "DSIdentityRegistrationTransition.h" +#import "DSIdentityTopupTransition.h" +#import "DSIdentityUpdateTransition.h" #import "DSCoinbaseTransaction.h" -#import "DSCreditFundingTransaction.h" #import "DSProviderRegistrationTransaction.h" #import "DSProviderUpdateRegistrarTransaction.h" #import "DSProviderUpdateRevocationTransaction.h" @@ -40,14 +39,8 @@ + (DSTransaction *)transactionWithMessage:(NSData *)message onChain:(DSChain *)c type = [message UInt16AtOffset:2]; } switch (type) { - case DSTransactionType_Classic: { - DSTransaction *transaction = [DSTransaction transactionWithMessage:message onChain:chain]; - if ([transaction isCreditFundingTransaction]) { - //replace with credit funding transaction - transaction = [DSCreditFundingTransaction transactionWithMessage:message onChain:chain]; - } - return transaction; - } + case DSTransactionType_Classic: + return [DSTransaction transactionWithMessage:message onChain:chain]; case DSTransactionType_Coinbase: return [DSCoinbaseTransaction transactionWithMessage:message onChain:chain]; case DSTransactionType_ProviderRegistration: diff --git a/DashSync/shared/Models/Transactions/Provider/DSProviderRegistrationTransaction.m b/DashSync/shared/Models/Transactions/Provider/DSProviderRegistrationTransaction.m index b49269d17..59ee52a5d 100644 --- a/DashSync/shared/Models/Transactions/Provider/DSProviderRegistrationTransaction.m +++ b/DashSync/shared/Models/Transactions/Provider/DSProviderRegistrationTransaction.m @@ -6,7 +6,9 @@ // #import "DSProviderRegistrationTransaction.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" +#import "DSChain+Wallet.h" #import "DSChainManager+Protected.h" #import "DSMasternodeManager+LocalMasternode.h" #import "DSProviderRegistrationTransactionEntity+CoreDataClass.h" @@ -177,12 +179,14 @@ - (NSString *)payloadCollateralString { } - (UInt256)payloadCollateralDigest { - return [DSKeyManager proRegTXPayloadCollateralDigest:[self payloadDataForHash] - scriptPayout:self.scriptPayout - reward:self.operatorReward - ownerKeyHash:self.ownerKeyHash - voterKeyHash:self.votingKeyHash - chainType:self.chain.chainType].UInt256; + SLICE *pld = slice_ctor([self payloadDataForHash]); + SLICE *sp = slice_ctor(self.scriptPayout); + u160 *owner_hash = u160_ctor_u(self.ownerKeyHash); + u160 *voter_hash = u160_ctor_u(self.votingKeyHash); + u256 *result = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_pro_reg_tx_payload_collateral_digest(pld, sp, self.operatorReward, owner_hash, voter_hash, self.chain.chainType); + UInt256 digest = u256_cast(result); + u256_dtor(result); + return digest; } - (BOOL)checkPayloadSignature { @@ -212,6 +216,7 @@ - (NSData *)basePayloadData { [data appendUInt16:CFSwapInt16BigToHost(self.platformP2PPort)]; [data appendUInt16:CFSwapInt16BigToHost(self.platformHTTPPort)]; } +// NSLog(@"basePayloadData: %@", data.hexString); return data; } @@ -226,14 +231,19 @@ - (NSData *)payloadData { [data appendData:[self basePayloadData]]; [data appendUInt8:self.payloadSignature.length]; [data appendData:self.payloadSignature]; +// NSLog(@"payloadData: %@", data.hexString); return data; } - (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { @synchronized(self) { NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex] mutableCopy]; + NSLog(@"[PRO_REG_TX] toDataWithSubscriptIndex [%lul] %@", subscriptIndex, data.hexString); + NSLog(@"[PRO_REG_TX] base payload: [%lul] %@", subscriptIndex, [self basePayloadData].hexString); + NSLog(@"[PRO_REG_TX] signature: [%lul] %@", subscriptIndex, self.payloadSignature.hexString); [data appendCountedData:[self payloadData]]; if (subscriptIndex != NSNotFound) [data appendUInt32:SIGHASH_ALL]; + NSLog(@"[PRO_REG_TX] toDataWithSubscriptIndex [%lul] %@", subscriptIndex, data.hexString); return data; } } diff --git a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRegistrarTransaction.h b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRegistrarTransaction.h index 7ab4fc1ff..c0e32413e 100644 --- a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRegistrarTransaction.h +++ b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRegistrarTransaction.h @@ -56,11 +56,11 @@ providerUpdateRegistrarTransactionVersion:(uint16_t)version - (void)updateInputsHash; -- (void)signPayloadWithKey:(OpaqueKey *_Nonnull)privateKey; +- (void)signPayloadWithKey:(DOpaqueKey *_Nonnull)privateKey; - (BOOL)checkPayloadSignature; -- (BOOL)checkPayloadSignature:(OpaqueKey *_Nonnull)publicKey; +- (BOOL)checkPayloadSignature:(DOpaqueKey *_Nonnull)publicKey; @end diff --git a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRegistrarTransaction.m b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRegistrarTransaction.m index 07a340f71..7561a4aff 100644 --- a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRegistrarTransaction.m +++ b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRegistrarTransaction.m @@ -6,6 +6,7 @@ // #import "DSProviderUpdateRegistrarTransaction.h" +#import "DSChain+Transaction.h" #import "DSChainManager.h" #import "DSLocalMasternode.h" #import "DSMasternodeManager.h" @@ -136,20 +137,36 @@ - (UInt256)payloadHash { return [self payloadDataForHash].SHA256_2; } -- (BOOL)checkPayloadSignature:(OpaqueKey *)providerOwnerPublicKey { - return key_check_payload_signature(providerOwnerPublicKey, self.providerRegistrationTransaction.ownerKeyHash.u8); +- (BOOL)checkPayloadSignature:(DOpaqueKey *)providerOwnerPublicKey { + u160 *owner_key_hash = u160_ctor_u(self.providerRegistrationTransaction.ownerKeyHash); + BOOL result = dash_spv_crypto_keys_key_OpaqueKey_check_payload_signature(providerOwnerPublicKey, owner_key_hash); +// u160_dtor(owner_key_hash); + return result; } - (BOOL)checkPayloadSignature { NSData *signature = self.payloadSignature; NSData *payload = [self payloadDataForHash]; - return key_ecdsa_verify_compact_sig(signature.bytes, signature.length, payload.bytes, payload.length, self.providerRegistrationTransaction.ownerKeyHash.u8); + SLICE *slice = slice_ctor(signature); + u256 *digest = u256_ctor(payload); + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError *result = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_key_with_compact_sig(slice, digest); + if (!result) return NO; + if (!result->ok) { + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError_destroy(result); + return NO; + } + u160 *hashed = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_hash160(result->ok); + BOOL verified = uint160_eq(self.providerRegistrationTransaction.ownerKeyHash, u160_cast(hashed)); + return verified; } -- (void)signPayloadWithKey:(OpaqueKey *)privateKey { +- (void)signPayloadWithKey:(DOpaqueKey *)privateKey { //ATTENTION If this ever changes from ECDSA, change the max signature size defined above //DSLogPrivate(@"Private Key is %@", [privateKey serializedPrivateKeyForChain:self.chain]); - self.payloadSignature = [DSKeyManager NSDataFrom:key_ecdsa_compact_sign(privateKey->ecdsa, [self payloadHash].u8)];; + u256 *digest = u256_ctor_u([self payloadHash]); + Arr_u8_65 *sig = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_compact_sign(privateKey->ecdsa, digest); + self.payloadSignature = NSDataFromPtr(sig); + Arr_u8_65_destroy(sig); } - (NSData *)basePayloadData { diff --git a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRevocationTransaction.h b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRevocationTransaction.h index 4ea6e8d19..a68f0b992 100644 --- a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRevocationTransaction.h +++ b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRevocationTransaction.h @@ -30,11 +30,11 @@ NS_ASSUME_NONNULL_BEGIN - (void)updateInputsHash; -- (void)signPayloadWithKey:(OpaqueKey *_Nonnull)privateKey; +- (void)signPayloadWithKey:(DOpaqueKey *_Nonnull)privateKey; - (BOOL)checkPayloadSignature; -- (BOOL)checkPayloadSignature:(OpaqueKey *)publicKey; +- (BOOL)checkPayloadSignature:(DOpaqueKey *)publicKey; @end diff --git a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRevocationTransaction.m b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRevocationTransaction.m index 7de1b9d2c..c3050f91b 100644 --- a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRevocationTransaction.m +++ b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRevocationTransaction.m @@ -6,6 +6,7 @@ // #import "DSProviderUpdateRevocationTransaction.h" +#import "DSChain+Transaction.h" #import "DSChainManager.h" #import "DSLocalMasternode.h" #import "DSMasternodeManager.h" @@ -104,25 +105,23 @@ - (UInt256)payloadHash { - (BOOL)checkPayloadSignature { NSAssert(self.providerRegistrationTransaction, @"We need a provider registration transaction"); - return key_bls_verify(self.providerRegistrationTransaction.operatorKey.u8, - ![self.providerRegistrationTransaction usesBasicBLS], - [self payloadHash].u8, - [self payloadSignature].bytes); + u384 *pubkey = u384_ctor_u(self.providerRegistrationTransaction.operatorKey); + SLICE *digest = slice_u256_ctor_u([self payloadHash]); + u768 *sig = u768_ctor([self payloadSignature]); + BOOL verified = dash_spv_crypto_keys_bls_key_BLSKey_verify_signature(pubkey, ![self.providerRegistrationTransaction usesBasicBLS], digest, sig); + return verified; } -- (BOOL)checkPayloadSignature:(OpaqueKey *)publicKey { +- (BOOL)checkPayloadSignature:(DOpaqueKey *)publicKey { return [DSKeyManager verifyMessageDigest:publicKey digest:[self payloadHash] signature:[self payloadSignature]]; } -- (void)signPayloadWithKey:(OpaqueKey *)privateKey { +- (void)signPayloadWithKey:(DOpaqueKey *)privateKey { NSData *payload = [self payloadDataForHash]; - BLSKey *bls; - if (privateKey->tag == OpaqueKey_BLSBasic) - bls = privateKey->bls_basic; - else - bls = privateKey->bls_legacy; - - self.payloadSignature = [DSKeyManager NSDataFrom:key_bls_sign_data(bls, payload.bytes, payload.length)]; + SLICE *p = slice_ctor(payload); + u768 *signed_data = dash_spv_crypto_keys_bls_key_BLSKey_sign_data(privateKey->bls, p); + self.payloadSignature = NSDataFromPtr(signed_data); + u768_dtor(signed_data); } - (NSData *)basePayloadData { diff --git a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateServiceTransaction.h b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateServiceTransaction.h index fd46f5085..d07a114ea 100644 --- a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateServiceTransaction.h +++ b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateServiceTransaction.h @@ -6,7 +6,8 @@ // #import "BigIntTypes.h" -#import "dash_shared_core.h" +//#import "dash_shared_core.h" +#import "DSKeyManager.h" #import "DSTransaction.h" NS_ASSUME_NONNULL_BEGIN @@ -53,11 +54,11 @@ providerUpdateServiceTransactionVersion:(uint16_t)version - (void)updateInputsHash; -- (void)signPayloadWithKey:(OpaqueKey *_Nonnull)privateKey; +- (void)signPayloadWithKey:(DOpaqueKey *_Nonnull)privateKey; - (BOOL)checkPayloadSignature; -- (BOOL)checkPayloadSignature:(OpaqueKey *)publicKey; +- (BOOL)checkPayloadSignature:(DOpaqueKey *)publicKey; @end diff --git a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateServiceTransaction.m b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateServiceTransaction.m index cbd2ee993..d2130cab7 100644 --- a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateServiceTransaction.m +++ b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateServiceTransaction.m @@ -6,6 +6,7 @@ // #import "DSProviderUpdateServiceTransaction.h" +#import "DSChain+Transaction.h" #import "DSChainManager.h" #import "DSLocalMasternode.h" #import "DSMasternodeManager.h" @@ -134,25 +135,23 @@ - (UInt256)payloadHash { - (BOOL)checkPayloadSignature { NSAssert(self.providerRegistrationTransaction, @"We need a provider registration transaction"); - return key_bls_verify(self.providerRegistrationTransaction.operatorKey.u8, - ![self.providerRegistrationTransaction usesBasicBLS], - [self payloadHash].u8, - [self payloadSignature].bytes); + u384 *pub_key = u384_ctor_u(self.providerRegistrationTransaction.operatorKey); + SLICE *digest = slice_u256_ctor_u([self payloadHash]); + u768 *sig = u768_ctor([self payloadSignature]); + BOOL verified = dash_spv_crypto_keys_bls_key_BLSKey_verify_signature(pub_key, ![self.providerRegistrationTransaction usesBasicBLS], digest, sig); + return verified; } -- (BOOL)checkPayloadSignature:(OpaqueKey *)publicKey { +- (BOOL)checkPayloadSignature:(DOpaqueKey *)publicKey { return [DSKeyManager verifyMessageDigest:publicKey digest:[self payloadHash] signature:[self payloadSignature]]; } -- (void)signPayloadWithKey:(OpaqueKey *)privateKey { +- (void)signPayloadWithKey:(DOpaqueKey *)privateKey { NSData *data = [self payloadDataForHash]; - BLSKey *bls; - if (privateKey->tag == OpaqueKey_BLSBasic) - bls = privateKey->bls_basic; - else - bls = privateKey->bls_legacy; - - self.payloadSignature = [DSKeyManager NSDataFrom:key_bls_sign_data(bls, data.bytes, data.length)]; + SLICE *slice = slice_ctor(data); + u768 *sig = dash_spv_crypto_keys_bls_key_BLSKey_sign_data(privateKey->bls, slice); + self.payloadSignature = [NSData dataWithBytes:sig->values length:sig->count]; + u768_dtor(sig); } - (NSString *_Nullable)payoutAddress { diff --git a/DashSync/shared/Models/Wallet/DSAccount.h b/DashSync/shared/Models/Wallet/DSAccount.h index 3db72ad65..6f1c9db26 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.h +++ b/DashSync/shared/Models/Wallet/DSAccount.h @@ -23,6 +23,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#import "DSAssetLockTransaction.h" #import "DSFundsDerivationPath.h" #import "DSIncomingFundsDerivationPath.h" #import "DSTransaction.h" @@ -35,141 +36,127 @@ NS_ASSUME_NONNULL_BEGIN FOUNDATION_EXPORT NSString *_Nonnull const DSAccountNewAccountFromTransactionNotification; FOUNDATION_EXPORT NSString *_Nonnull const DSAccountNewAccountShouldBeAddedFromTransactionNotification; -@class DSFundsDerivationPath, DSIncomingFundsDerivationPathDSWallet, DSBlockchainIdentityRegistrationTransition, DSBlockchainIdentityUpdateTransition, DSCreditFundingTransaction; -@class DSCoinbaseTransaction, DSPotentialOneWayFriendship; +@class DSFundsDerivationPath, DSIncomingFundsDerivationPathDSWallet, DSIdentityRegistrationTransition, DSIdentityUpdateTransition, DSCoinbaseTransaction, DSPotentialOneWayFriendship; @interface DSAccount : NSObject // BIP 43 derivation paths @property (nullable, nonatomic, readonly) NSArray *fundDerivationPaths; - @property (nullable, nonatomic, readonly) NSArray *outgoingFundDerivationPaths; - @property (nullable, nonatomic, strong) DSFundsDerivationPath *defaultDerivationPath; - @property (nullable, nonatomic, readonly) DSFundsDerivationPath *bip44DerivationPath; - @property (nullable, nonatomic, readonly) DSFundsDerivationPath *bip32DerivationPath; - @property (nullable, nonatomic, readonly) DSDerivationPath *masterContactsDerivationPath; - @property (nullable, nonatomic, weak) DSWallet *wallet; - @property (nonatomic, readonly) NSString *uniqueID; - @property (nonatomic, readonly) uint32_t accountNumber; - @property (nonatomic, readonly) NSManagedObjectContext *managedObjectContext; - // current wallet balance excluding transactions known to be invalid @property (nonatomic, readonly) uint64_t balance; - // NSValue objects containing UTXO structs @property (nonatomic, readonly) NSArray *unspentOutputs; - // latest 100 transactions sorted by date, most recent first @property (atomic, readonly) NSArray *recentTransactions; - // latest 100 transactions sorted by date, most recent first @property (atomic, readonly) NSArray *recentTransactionsWithInternalOutput; - // all wallet transactions sorted by date, most recent first @property (atomic, readonly) NSArray *allTransactions; - // all wallet transactions sorted by date, most recent first @property (atomic, readonly) NSArray *coinbaseTransactions; - // Does this account have any coinbase rewards @property (nonatomic, readonly) BOOL hasCoinbaseTransaction; - // returns the first unused external address @property (nullable, nonatomic, readonly) NSString *receiveAddress; - // returns the first unused internal address @property (nullable, nonatomic, readonly) NSString *changeAddress; - // all previously generated external addresses @property (nonatomic, readonly) NSArray *externalAddresses; - // all previously generated internal addresses @property (nonatomic, readonly) NSArray *internalAddresses; - // all the contacts for an account @property (nonatomic, readonly) NSArray *_Nonnull contacts; - // has an extended public key missing in one of the account derivation paths @property (nonatomic, readonly) BOOL hasAnExtendedPublicKeyMissing; -- (NSArray *_Nullable)registerAddressesWithGapLimit:(NSUInteger)gapLimit unusedAccountGapLimit:(NSUInteger)unusedAccountGapLimit dashpayGapLimit:(NSUInteger)dashpayGapLimit internal:(BOOL)internal error:(NSError **)error; - -+ (DSAccount *)accountWithAccountNumber:(uint32_t)accountNumber withDerivationPaths:(NSArray *)derivationPaths inContext:(NSManagedObjectContext *_Nullable)context; - -+ (NSArray *)standardAccountsToAccountNumber:(uint32_t)accountNumber onChain:(DSChain *)chain inContext:(NSManagedObjectContext *_Nullable)context; - -- (instancetype)initWithAccountNumber:(uint32_t)accountNumber withDerivationPaths:(NSArray *)derivationPaths inContext:(NSManagedObjectContext *_Nullable)context; - -- (instancetype)initAsViewOnlyWithAccountNumber:(uint32_t)accountNumber withDerivationPaths:(NSArray *)derivationPaths inContext:(NSManagedObjectContext *_Nullable)context; - +- (NSArray *_Nullable)registerAddressesWithGapLimit:(NSUInteger)gapLimit + unusedAccountGapLimit:(NSUInteger)unusedAccountGapLimit + dashpayGapLimit:(NSUInteger)dashpayGapLimit + internal:(BOOL)internal + error:(NSError **)error; + ++ (DSAccount *)accountWithAccountNumber:(uint32_t)accountNumber + withDerivationPaths:(NSArray *)derivationPaths + inContext:(NSManagedObjectContext *_Nullable)context; + ++ (NSArray *)standardAccountsToAccountNumber:(uint32_t)accountNumber + onChain:(DSChain *)chain + inContext:(NSManagedObjectContext *_Nullable)context; +- (instancetype)initWithAccountNumber:(uint32_t)accountNumber + withDerivationPaths:(NSArray *)derivationPaths + inContext:(NSManagedObjectContext *_Nullable)context; +- (instancetype)initAsViewOnlyWithAccountNumber:(uint32_t)accountNumber + withDerivationPaths:(NSArray *)derivationPaths + inContext:(NSManagedObjectContext *_Nullable)context; - (void)removeDerivationPath:(DSDerivationPath *)derivationPath; - - (DSIncomingFundsDerivationPath *)derivationPathForFriendshipWithIdentifier:(NSData *)friendshipIdentifier; - - (void)removeIncomingDerivationPathForFriendshipWithIdentifier:(NSData *)friendshipIdentifier; - - (void)addDerivationPath:(DSDerivationPath *)derivationPath; +- (void)addIncomingDerivationPath:(DSIncomingFundsDerivationPath *)derivationPath + forFriendshipIdentifier:(NSData *)friendshipIdentifier + inContext:(NSManagedObjectContext *)context; -- (void)addIncomingDerivationPath:(DSIncomingFundsDerivationPath *)derivationPath forFriendshipIdentifier:(NSData *)friendshipIdentifier inContext:(NSManagedObjectContext *)context; - -- (void)addOutgoingDerivationPath:(DSIncomingFundsDerivationPath *)derivationPath forFriendshipIdentifier:(NSData *)friendshipIdentifier inContext:(NSManagedObjectContext *)context; - +- (void)addOutgoingDerivationPath:(DSIncomingFundsDerivationPath *)derivationPath + forFriendshipIdentifier:(NSData *)friendshipIdentifier + inContext:(NSManagedObjectContext *)context; - (void)addDerivationPathsFromArray:(NSArray *)derivationPaths; // largest amount that can be sent from the account after fees @property (nonatomic, readonly) uint64_t maxOutputAmount; -- (uint64_t)maxOutputAmountWithConfirmationCount:(uint64_t)confirmationCount returnInputCount:(uint32_t *_Nullable)rInputCount; - +- (uint64_t)maxOutputAmountWithConfirmationCount:(uint64_t)confirmationCount + returnInputCount:(uint32_t *_Nullable)rInputCount; // true if the address is controlled by the wallet - (BOOL)containsAddress:(NSString *)address; - // true if the address is internal and is controlled by the wallet - (BOOL)containsInternalAddress:(NSString *)address; - // true if the address is external and is controlled by the wallet - (BOOL)containsExternalAddress:(NSString *)address; - // true if the address is controlled by the wallet except for evolution addresses - (BOOL)baseDerivationPathsContainAddress:(NSString *)address; - // the high level (hardened) derivation path containing the address - (DSDerivationPath *_Nullable)derivationPathContainingAddress:(NSString *)address; - // the high level (hardened) derivation path containing the address that is external to the wallet, basically a friend's address - (DSIncomingFundsDerivationPath *_Nullable)externalDerivationPathContainingAddress:(NSString *)address; - - (BOOL)transactionAddressAlreadySeenInOutputs:(NSString *)address; - // true if the address was previously used as an input or output in any wallet transaction (from this wallet only) - (BOOL)addressIsUsed:(NSString *)address; - // returns an unsigned transaction that sends the specified amount from the wallet to the given address -- (DSTransaction *_Nullable)transactionFor:(uint64_t)amount to:(NSString *)address withFee:(BOOL)fee; - +- (DSTransaction *_Nullable)transactionFor:(uint64_t)amount + to:(NSString *)address + withFee:(BOOL)fee; // returns an unsigned transaction that sends the specified amount from the wallet to the given address intended for conversion to L2 credits -- (DSCreditFundingTransaction *_Nullable)creditFundingTransactionFor:(uint64_t)amount to:(NSString *)address withFee:(BOOL)fee; +- (DSAssetLockTransaction *)assetLockTransactionFor:(uint64_t)amount + to:(NSString *)address + withFee:(BOOL)fee; // returns an unsigned transaction that sends the specified amounts from the wallet to the specified output scripts - (DSTransaction *_Nullable)transactionForAmounts:(NSArray *)amounts toOutputScripts:(NSArray *)scripts withFee:(BOOL)fee; - // returns an unsigned transaction that sends the specified amounts from the wallet to the specified output scripts -- (DSTransaction *_Nullable)transactionForAmounts:(NSArray *)amounts toOutputScripts:(NSArray *)scripts withFee:(BOOL)fee toShapeshiftAddress:(NSString *_Nullable)shapeshiftAddress; - -- (DSTransaction *)updateTransaction:(DSTransaction *)transaction forAmounts:(NSArray *)amounts toOutputScripts:(NSArray *)scripts withFee:(BOOL)fee; - - -- (DSTransaction *)updateTransaction:(DSTransaction *)transaction forAmounts:(NSArray *)amounts toOutputScripts:(NSArray *)scripts withFee:(BOOL)fee sortType:(DSTransactionSortType)sortType; +- (DSTransaction *_Nullable)transactionForAmounts:(NSArray *)amounts + toOutputScripts:(NSArray *)scripts + withFee:(BOOL)fee + toShapeshiftAddress:(NSString *_Nullable)shapeshiftAddress; +- (DSTransaction *)updateTransaction:(DSTransaction *)transaction + forAmounts:(NSArray *)amounts + toOutputScripts:(NSArray *)scripts + withFee:(BOOL)fee; +- (DSTransaction *)updateTransaction:(DSTransaction *)transaction + forAmounts:(NSArray *)amounts + toOutputScripts:(NSArray *)scripts + withFee:(BOOL)fee + sortType:(DSTransactionSortType)sortType; /// Sign any inputs in the given transaction that can be signed using private keys from the wallet /// @@ -179,7 +166,8 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSAccountNewAccountShouldBeAddedFromT /// /// - Note: Using this method to sign a tx doesn't present pin controller, use this method carefully from UI /// -- (void)signTransaction:(DSTransaction *)transaction completion:(_Nonnull TransactionValidityCompletionBlock)completion; +- (void)signTransaction:(DSTransaction *)transaction + completion:(_Nonnull TransactionValidityCompletionBlock)completion; /// Sign any inputs in the given transaction that can be signed using private keys from the wallet /// @@ -189,25 +177,31 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSAccountNewAccountShouldBeAddedFromT /// /// - Note: Using this method to sign a tx presents pin controller for auth purpose /// -- (void)signTransaction:(DSTransaction *)transaction withPrompt:(NSString *_Nullable)authprompt completion:(_Nonnull TransactionValidityCompletionBlock)completion; +- (void)signTransaction:(DSTransaction *)transaction + withPrompt:(NSString *_Nullable)authprompt + completion:(_Nonnull TransactionValidityCompletionBlock)completion; // true if the given transaction is associated with the account (even if it hasn't been registered), false otherwise - (BOOL)canContainTransaction:(DSTransaction *)transaction; // adds a transaction to the account, or returns false if it isn't associated with the account -- (BOOL)registerTransaction:(DSTransaction *)transaction saveImmediately:(BOOL)saveImmediately; +- (BOOL)registerTransaction:(DSTransaction *)transaction + saveImmediately:(BOOL)saveImmediately; // this is used to save transactions atomically with the block, needs to be called before switching threads to save the block - (void)prepareForIncomingTransactionPersistenceForBlockSaveWithNumber:(uint32_t)blockNumber; // this is used to save transactions atomically with the block -- (void)persistIncomingTransactionsAttributesForBlockSaveWithNumber:(uint32_t)blockNumber inContext:(NSManagedObjectContext *)context; +- (void)persistIncomingTransactionsAttributesForBlockSaveWithNumber:(uint32_t)blockNumber + inContext:(NSManagedObjectContext *)context; // removes a transaction from the account along with any transactions that depend on its outputs, returns TRUE if a transaction was removed -- (BOOL)removeTransaction:(DSTransaction *)transaction saveImmediately:(BOOL)saveImmediately; +- (BOOL)removeTransaction:(DSTransaction *)transaction + saveImmediately:(BOOL)saveImmediately; // removes a transaction by hash from the account along with any transactions that depend on its outputs, returns TRUE if a transaction was removed -- (BOOL)removeTransactionWithHash:(UInt256)txHash saveImmediately:(BOOL)saveImmediately; +- (BOOL)removeTransactionWithHash:(UInt256)txHash + saveImmediately:(BOOL)saveImmediately; // returns the transaction with the given hash if it's been registered in the account (might also return non-registered) - (DSTransaction *_Nullable)transactionForHash:(UInt256)txHash; @@ -253,7 +247,9 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSAccountNewAccountShouldBeAddedFromT - (void)chainUpdatedBlockHeight:(int32_t)height; -- (NSArray *)setBlockHeight:(int32_t)height andTimestamp:(NSTimeInterval)timestamp forTransactionHashes:(NSArray *)txHashes; +- (NSArray *)setBlockHeight:(int32_t)height + andTimestamp:(NSTimeInterval)timestamp + forTransactionHashes:(NSArray *)txHashes; // This loads the derivation paths addresses once the account is set to a wallet - (void)loadDerivationPaths; @@ -269,7 +265,8 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSAccountNewAccountShouldBeAddedFromT // given a private key, queries api.dashwallet.com for unspent outputs and calls the completion block with a signed // transaction that will sweep the balance into wallet (doesn't publish the tx) -- (void)sweepPrivateKey:(NSString *)privKey withFee:(BOOL)fee +- (void)sweepPrivateKey:(NSString *)privKey + withFee:(BOOL)fee completion:(void (^_Nonnull)(DSTransaction *_Nonnull tx, uint64_t fee, NSError *_Null_unspecified error))completion; @end diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index b0a9fe375..1d6a8c5b4 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -24,14 +24,16 @@ // THE SOFTWARE. #import "DSAccount.h" +#import "DSChain+Identity.h" +#import "DSChain+Params.h" #import "DSChain+Protected.h" #import "DSFundsDerivationPath.h" #import "DSWallet+Protected.h" -#import "DSBlockchainIdentityCloseTransition.h" -#import "DSBlockchainIdentityRegistrationTransition.h" -#import "DSBlockchainIdentityTopupTransition.h" -#import "DSBlockchainIdentityUpdateTransition.h" +#import "DSIdentityCloseTransition.h" +#import "DSIdentityRegistrationTransition.h" +#import "DSIdentityTopupTransition.h" +#import "DSIdentityUpdateTransition.h" #import "DSProviderRegistrationTransaction.h" #import "DSProviderUpdateRegistrarTransaction.h" #import "DSProviderUpdateRevocationTransaction.h" @@ -49,8 +51,8 @@ #import "DSBIP39Mnemonic.h" #import "DSChainEntity+CoreDataClass.h" #import "DSCoinbaseTransaction.h" -#import "DSCreditFundingTransaction.h" #import "DSDerivationPathEntity+CoreDataClass.h" +#import "DSDerivationPathFactory.h" #import "DSGovernanceSyncManager.h" #import "DSIncomingFundsDerivationPath.h" #import "DSInsightManager.h" @@ -73,6 +75,12 @@ #define AUTH_SWEEP_KEY @"AUTH_SWEEP_KEY" #define AUTH_SWEEP_FEE @"AUTH_SWEEP_FEE" +#define ERROR_CANT_CREATE_KEY [NSError errorWithCode:187 localizedDescriptionKey:@"Can't create key"] +#define ERROR_INVALID_PRIVATE_KEY [NSError errorWithCode:187 localizedDescriptionKey:@"Not a valid private key"] +#define ERROR_PRIVATE_KEY_ALREADY_IN_WALLET [NSError errorWithCode:187 localizedDescriptionKey:@"This private key is already in your wallet"] +#define ERROR_EMPTY_PRIVATE_KEY [NSError errorWithCode:417 localizedDescriptionKey:@"This private key is empty"] +#define ERROR_NO_FUNDS_FOR_FEE [NSError errorWithCode:417 localizedDescriptionKey: @"Transaction fees would cost more than the funds available on this private key (due to tiny \"dust\" deposits)"] +#define ERROR_SIGNING [NSError errorWithCode:401 localizedDescriptionKey:@"Error signing transaction"] @class DSFundsDerivationPath, DSIncomingFundsDerivationPath, DSAccount; @@ -120,11 +128,17 @@ @implementation DSAccount : NSObject // MARK: - Initiation -+ (DSAccount *)accountWithAccountNumber:(uint32_t)accountNumber withDerivationPaths:(NSArray *)derivationPaths inContext:(NSManagedObjectContext *_Nullable)context { - return [[self alloc] initWithAccountNumber:accountNumber withDerivationPaths:derivationPaths inContext:context]; ++ (DSAccount *)accountWithAccountNumber:(uint32_t)accountNumber + withDerivationPaths:(NSArray *)derivationPaths + inContext:(NSManagedObjectContext *_Nullable)context { + return [[self alloc] initWithAccountNumber:accountNumber + withDerivationPaths:derivationPaths + inContext:context]; } -+ (NSArray *)standardAccountsToAccountNumber:(uint32_t)accountNumber onChain:(DSChain *)chain inContext:(NSManagedObjectContext *_Nullable)context { ++ (NSArray *)standardAccountsToAccountNumber:(uint32_t)accountNumber + onChain:(DSChain *)chain + inContext:(NSManagedObjectContext *_Nullable)context { NSMutableArray *accounts = [NSMutableArray array]; for (uint32_t i = 0; i < accountNumber + 1; i++) { [accounts addObject:[self accountWithAccountNumber:i withDerivationPaths:[chain standardDerivationPathsForAccountNumber:i] inContext:context]]; @@ -172,7 +186,9 @@ - (void)verifyAndAssignAddedDerivationPaths:(NSArray *)deriv } } -- (instancetype)initWithAccountNumber:(uint32_t)accountNumber withDerivationPaths:(NSArray *)derivationPaths inContext:(NSManagedObjectContext *)context { +- (instancetype)initWithAccountNumber:(uint32_t)accountNumber + withDerivationPaths:(NSArray *)derivationPaths + inContext:(NSManagedObjectContext *)context { NSParameterAssert(derivationPaths); if (!(self = [super init])) return nil; @@ -323,7 +339,6 @@ - (NSString *)uniqueID { - (uint32_t)blockHeight { static uint32_t height = 0; uint32_t h = self.wallet.chain.lastSyncBlockHeight; - if (h > height) height = h; return height; } @@ -380,7 +395,9 @@ - (void)addDerivationPath:(DSDerivationPath *)derivationPath { } } -- (void)addIncomingDerivationPath:(DSIncomingFundsDerivationPath *)derivationPath forFriendshipIdentifier:(NSData *)friendshipIdentifier inContext:(NSManagedObjectContext *)context { +- (void)addIncomingDerivationPath:(DSIncomingFundsDerivationPath *)derivationPath + forFriendshipIdentifier:(NSData *)friendshipIdentifier + inContext:(NSManagedObjectContext *)context { NSParameterAssert(derivationPath); NSParameterAssert(friendshipIdentifier); NSAssert(derivationPath.length, @"derivation path must have a length"); @@ -396,7 +413,9 @@ - (void)addIncomingDerivationPath:(DSIncomingFundsDerivationPath *)derivationPat - (void)addOutgoingDerivationPath:(DSIncomingFundsDerivationPath *)derivationPath forFriendshipIdentifier:(NSData *)friendshipIdentifier inContext:(NSManagedObjectContext *)context { NSParameterAssert(derivationPath); NSParameterAssert(friendshipIdentifier); - NSAssert(derivationPath.sourceIsLocal || !derivationPath.length, @"derivation path must not have a length unless it is on device"); + + BOOL sourceIsLocal = !![derivationPath.chain identityForUniqueId:derivationPath.contactSourceIdentityUniqueId]; + NSAssert(sourceIsLocal || !derivationPath.length, @"derivation path must not have a length unless it is on device"); derivationPath.account = self; [self.mContactOutgoingFundDerivationPathsDictionary setObject:derivationPath forKey:friendshipIdentifier]; if ([derivationPath hasExtendedPublicKey]) { @@ -446,7 +465,11 @@ - (BOOL)hasAnExtendedPublicKeyMissing { return NO; } -- (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit unusedAccountGapLimit:(NSUInteger)unusedAccountGapLimit dashpayGapLimit:(NSUInteger)dashpayGapLimit internal:(BOOL)internal error:(NSError **)error { +- (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit + unusedAccountGapLimit:(NSUInteger)unusedAccountGapLimit + dashpayGapLimit:(NSUInteger)dashpayGapLimit + internal:(BOOL)internal + error:(NSError **)error { NSMutableArray *mArray = [NSMutableArray array]; for (DSDerivationPath *derivationPath in self.fundDerivationPaths) { if ([derivationPath isKindOfClass:[DSFundsDerivationPath class]]) { @@ -948,14 +971,14 @@ - (BOOL)canContainTransaction:(DSTransaction *)transaction { - (BOOL)checkIsFirstTransaction:(DSTransaction *)transaction { NSParameterAssert(transaction); - for (DSDerivationPath *derivationPath in self.fundDerivationPaths) { if ([derivationPath type] & DSDerivationPathType_IsForFunds) { NSString *firstAddress; if ([derivationPath isKindOfClass:[DSFundsDerivationPath class]]) { firstAddress = [(DSFundsDerivationPath *)derivationPath addressAtIndex:0 internal:NO]; } else if ([derivationPath isKindOfClass:[DSIncomingFundsDerivationPath class]]) { - firstAddress = [(DSIncomingFundsDerivationPath *)derivationPath addressAtIndex:0]; + NSData *pubKey = [(DSIncomingFundsDerivationPath *)derivationPath publicKeyDataAtIndex:0]; + firstAddress = [DSKeyManager ecdsaKeyAddressFromPublicKeyData:pubKey forChainType:derivationPath.chain.chainType]; } if ([transaction.outputs indexOfObjectPassingTest:^BOOL(DSTransactionOutput *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { return [obj.address isEqual:firstAddress]; @@ -970,54 +993,79 @@ - (BOOL)checkIsFirstTransaction:(DSTransaction *)transaction { // MARK: = Creation // returns an unsigned transaction that sends the specified amount from the wallet to the given address -- (DSTransaction *)transactionFor:(uint64_t)amount to:(NSString *)address withFee:(BOOL)fee { +- (DSTransaction *)transactionFor:(uint64_t)amount + to:(NSString *)address + withFee:(BOOL)fee { NSParameterAssert(address); NSData *script = [DSKeyManager scriptPubKeyForAddress:address forChain:self.wallet.chain]; - return [self transactionForAmounts:@[@(amount)] toOutputScripts:@[script] withFee:fee]; + return [self transactionForAmounts:@[@(amount)] + toOutputScripts:@[script] + withFee:fee]; } // returns an unsigned transaction that sends the specified amount from the wallet to the given address -- (DSCreditFundingTransaction *)creditFundingTransactionFor:(uint64_t)amount to:(NSString *)address withFee:(BOOL)fee { +- (DSAssetLockTransaction *)assetLockTransactionFor:(uint64_t)amount + to:(NSString *)address + withFee:(BOOL)fee { NSParameterAssert(address); - NSMutableData *script = [NSMutableData data]; - [script appendCreditBurnScriptPubKeyForAddress:address forChain:self.wallet.chain]; - - DSCreditFundingTransaction *transaction = [[DSCreditFundingTransaction alloc] initOnChain:self.wallet.chain]; - return (DSCreditFundingTransaction *)[self updateTransaction:transaction forAmounts:@[@(amount)] toOutputScripts:@[script] withFee:fee sortType:DSTransactionSortType_BIP69]; + DSAssetLockTransaction *transaction = [[DSAssetLockTransaction alloc] initOnChain:self.wallet.chain]; + return (DSAssetLockTransaction *)[self updateTransaction:transaction + forAmounts:@[@(amount)] + toOutputScripts:@[script] + withFee:fee + sortType:DSTransactionSortType_BIP69]; } -// returns an unsigned transaction that sends the specified amounts from the wallet to the specified output scripts -- (DSTransaction *)transactionForAmounts:(NSArray *)amounts toOutputScripts:(NSArray *)scripts withFee:(BOOL)fee { - return [self transactionForAmounts:amounts toOutputScripts:scripts withFee:fee toShapeshiftAddress:nil]; -} -- (DSTransaction *)transactionForAmounts:(NSArray *)amounts toOutputScripts:(NSArray *)scripts withFee:(BOOL)fee toShapeshiftAddress:(NSString *)shapeshiftAddress { +// returns an unsigned transaction that sends the specified amounts from the wallet to the specified output scripts +- (DSTransaction *)transactionForAmounts:(NSArray *)amounts + toOutputScripts:(NSArray *)scripts + withFee:(BOOL)fee { + return [self transactionForAmounts:amounts + toOutputScripts:scripts + withFee:fee + toShapeshiftAddress:nil]; +} + +- (DSTransaction *)transactionForAmounts:(NSArray *)amounts + toOutputScripts:(NSArray *)scripts + withFee:(BOOL)fee + toShapeshiftAddress:(NSString *)shapeshiftAddress { NSParameterAssert(amounts); NSParameterAssert(scripts); DSTransaction *transaction = [[DSTransaction alloc] initOnChain:self.wallet.chain]; - return [self updateTransaction:transaction forAmounts:amounts toOutputScripts:scripts withFee:fee toShapeshiftAddress:shapeshiftAddress sortType:DSTransactionSortType_BIP69]; + return [self updateTransaction:transaction + forAmounts:amounts + toOutputScripts:scripts + withFee:fee + toShapeshiftAddress:shapeshiftAddress + sortType:DSTransactionSortType_BIP69]; } // MARK: == Proposal Transaction Creation - (DSTransaction *)proposalCollateralTransactionWithData:(NSData *)data { NSParameterAssert(data); - NSMutableData *script = [NSMutableData data]; - [script appendProposalInfo:data]; - return [self transactionForAmounts:@[@(PROPOSAL_COST)] toOutputScripts:@[script] withFee:TRUE]; } // MARK: = Update // returns an unsigned transaction that sends the specified amounts from the wallet to the specified output scripts -- (DSTransaction *)updateTransaction:(DSTransaction *)transaction forAmounts:(NSArray *)amounts toOutputScripts:(NSArray *)scripts withFee:(BOOL)fee { - return [self updateTransaction:transaction forAmounts:amounts toOutputScripts:scripts withFee:fee sortType:DSTransactionSortType_BIP69]; +- (DSTransaction *)updateTransaction:(DSTransaction *)transaction + forAmounts:(NSArray *)amounts + toOutputScripts:(NSArray *)scripts + withFee:(BOOL)fee { + return [self updateTransaction:transaction + forAmounts:amounts + toOutputScripts:scripts + withFee:fee + sortType:DSTransactionSortType_BIP69]; } - (DSTransaction *)updateTransaction:(DSTransaction *)transaction @@ -1025,7 +1073,12 @@ - (DSTransaction *)updateTransaction:(DSTransaction *)transaction toOutputScripts:(NSArray *)scripts withFee:(BOOL)fee sortType:(DSTransactionSortType)sortType { - return [self updateTransaction:transaction forAmounts:amounts toOutputScripts:scripts withFee:fee toShapeshiftAddress:nil sortType:sortType]; + return [self updateTransaction:transaction + forAmounts:amounts + toOutputScripts:scripts + withFee:fee + toShapeshiftAddress:nil + sortType:sortType]; } // returns an unsigned transaction that sends the specified amounts from the wallet to the specified output scripts @@ -1153,7 +1206,9 @@ - (void)chainUpdatedBlockHeight:(int32_t)height { // set the block heights and timestamps for the given transactions, use a height of TX_UNCONFIRMED and timestamp of 0 to // indicate a transaction and it's dependents should remain marked as unverified (not 0-conf safe) -- (NSArray *)setBlockHeight:(int32_t)height andTimestamp:(NSTimeInterval)timestamp forTransactionHashes:(NSArray *)txHashes { +- (NSArray *)setBlockHeight:(int32_t)height + andTimestamp:(NSTimeInterval)timestamp + forTransactionHashes:(NSArray *)txHashes { @synchronized (self) { NSMutableArray *hashes = [NSMutableArray array], *updated = [NSMutableArray array]; BOOL needsUpdate = NO; @@ -1199,7 +1254,8 @@ - (NSArray *)setBlockHeight:(int32_t)height andTimestamp:(NSTimeInterval)timesta // MARK: = Removal // removes a transaction from the wallet along with any transactions that depend on its outputs -- (BOOL)removeTransactionWithHash:(UInt256)txHash saveImmediately:(BOOL)saveImmediately { +- (BOOL)removeTransactionWithHash:(UInt256)txHash + saveImmediately:(BOOL)saveImmediately { @synchronized (self) { DSTransaction *transaction = self.allTx[uint256_obj(txHash)]; if (!transaction) return FALSE; @@ -1207,7 +1263,8 @@ - (BOOL)removeTransactionWithHash:(UInt256)txHash saveImmediately:(BOOL)saveImme } } -- (BOOL)removeTransaction:(DSTransaction *)baseTransaction saveImmediately:(BOOL)saveImmediately { +- (BOOL)removeTransaction:(DSTransaction *)baseTransaction + saveImmediately:(BOOL)saveImmediately { NSParameterAssert(baseTransaction); @synchronized (self) { NSMutableSet *dependentTransactions = [NSMutableSet set]; @@ -1272,7 +1329,8 @@ - (NSArray *)usedDerivationPathsForTransaction:(DSTransaction *)transaction { return usedDerivationPaths; } -- (void)signTransaction:(DSTransaction *)transaction completion:(_Nonnull TransactionValidityCompletionBlock)completion { +- (void)signTransaction:(DSTransaction *)transaction + completion:(_Nonnull TransactionValidityCompletionBlock)completion { NSParameterAssert(transaction); if (_isViewOnlyAccount) return; @@ -1286,24 +1344,8 @@ - (void)signTransaction:(DSTransaction *)transaction completion:(_Nonnull Transa if (!seed) { if (completion) completion(NO, YES); } else { - NSMutableArray *privkeys = [NSMutableArray array]; - for (NSDictionary *dictionary in usedDerivationPaths) { - DSDerivationPath *derivationPath = dictionary[@"derivationPath"]; - NSMutableOrderedSet *externalIndexes = dictionary[@"externalIndexes"], - *internalIndexes = dictionary[@"internalIndexes"]; - if ([derivationPath isKindOfClass:[DSFundsDerivationPath class]]) { - DSFundsDerivationPath *fundsDerivationPath = (DSFundsDerivationPath *)derivationPath; - [privkeys addObjectsFromArray:[fundsDerivationPath privateKeys:externalIndexes.array internal:NO fromSeed:seed]]; - [privkeys addObjectsFromArray:[fundsDerivationPath privateKeys:internalIndexes.array internal:YES fromSeed:seed]]; - } else if ([derivationPath isKindOfClass:[DSIncomingFundsDerivationPath class]]) { - DSIncomingFundsDerivationPath *incomingFundsDerivationPath = (DSIncomingFundsDerivationPath *)derivationPath; - [privkeys addObjectsFromArray:[incomingFundsDerivationPath privateKeys:externalIndexes.array fromSeed:seed]]; - } else { - NSAssert(FALSE, @"The derivation path must be a normal or incoming funds derivation path"); - } - } - - BOOL signedSuccessfully = [transaction signWithPrivateKeys:privkeys]; + NSArray *privkeys = [self collectPrivateKeys:usedDerivationPaths fromSeed:seed]; + BOOL signedSuccessfully = [transaction signWithMaybePrivateKeys:privkeys]; if (completion) completion(signedSuccessfully, NO); } }); @@ -1311,7 +1353,9 @@ - (void)signTransaction:(DSTransaction *)transaction completion:(_Nonnull Transa } // sign any inputs in the given transaction that can be signed using private keys from the wallet -- (void)signTransaction:(DSTransaction *)transaction withPrompt:(NSString *_Nullable)authprompt completion:(TransactionValidityCompletionBlock)completion { +- (void)signTransaction:(DSTransaction *)transaction + withPrompt:(NSString *_Nullable)authprompt + completion:(TransactionValidityCompletionBlock)completion { NSParameterAssert(transaction); if (_isViewOnlyAccount) return; @@ -1324,32 +1368,70 @@ - (void)signTransaction:(DSTransaction *)transaction withPrompt:(NSString *_Null if (!seed) { if (completion) completion(NO, YES); } else { - NSMutableArray *privkeys = [NSMutableArray array]; - for (NSDictionary *dictionary in usedDerivationPaths) { - DSDerivationPath *derivationPath = dictionary[@"derivationPath"]; - NSMutableOrderedSet *externalIndexes = dictionary[@"externalIndexes"], - *internalIndexes = dictionary[@"internalIndexes"]; - if ([derivationPath isKindOfClass:[DSFundsDerivationPath class]]) { - DSFundsDerivationPath *fundsDerivationPath = (DSFundsDerivationPath *)derivationPath; - [privkeys addObjectsFromArray:[fundsDerivationPath privateKeys:externalIndexes.array internal:NO fromSeed:seed]]; - [privkeys addObjectsFromArray:[fundsDerivationPath privateKeys:internalIndexes.array internal:YES fromSeed:seed]]; - } else if ([derivationPath isKindOfClass:[DSIncomingFundsDerivationPath class]]) { - DSIncomingFundsDerivationPath *incomingFundsDerivationPath = (DSIncomingFundsDerivationPath *)derivationPath; - [privkeys addObjectsFromArray:[incomingFundsDerivationPath privateKeys:externalIndexes.array fromSeed:seed]]; - } else { - NSAssert(FALSE, @"The derivation path must be a normal or incoming funds derivation path"); - } - } - - BOOL signedSuccessfully = [transaction signWithPrivateKeys:privkeys]; + NSArray *privkeys = [self collectPrivateKeys:usedDerivationPaths fromSeed:seed]; + BOOL signedSuccessfully = [transaction signWithMaybePrivateKeys:privkeys]; if (completion) completion(signedSuccessfully, NO); } }); } } +- (NSArray *)collectPrivateKeys:(NSArray *)usedDerivationPaths + fromSeed:(NSData *)seed { + NSMutableArray *privkeys = [NSMutableArray array]; + for (NSDictionary *dictionary in usedDerivationPaths) { + DSDerivationPath *derivationPath = dictionary[@"derivationPath"]; + NSMutableOrderedSet *externalIndexes = dictionary[@"externalIndexes"], + *internalIndexes = dictionary[@"internalIndexes"]; + if ([derivationPath isKindOfClass:[DSFundsDerivationPath class]]) { + // TODO: before we migrated high-level models for derivation paths into rust + // TODO: in order to avoid mess while dealing with nested c-structures + // TODO: we wrap DMaybeOpaqueKeys into NSValue + + DSFundsDerivationPath *fundsDerivationPath = (DSFundsDerivationPath *)derivationPath; + + NSMutableArray *externalArray = [NSMutableArray array]; + for (NSNumber *index in externalIndexes.array) { + NSUInteger indexes[] = {0, index.unsignedIntValue}; + [externalArray addObject:[NSIndexPath indexPathWithIndexes:indexes length:2]]; + } + DMaybeOpaqueKeys *external = [DSDerivationPathFactory privateKeysAtIndexPaths:externalArray + fromSeed:seed + derivationPath:fundsDerivationPath]; + NSMutableArray *internalArray = [NSMutableArray array]; + for (NSNumber *index in internalIndexes.array) { + NSUInteger indexes[] = {1, index.unsignedIntValue}; + [internalArray addObject:[NSIndexPath indexPathWithIndexes:indexes length:2]]; + } + DMaybeOpaqueKeys *internal = [DSDerivationPathFactory privateKeysAtIndexPaths:internalArray + fromSeed:seed + derivationPath:fundsDerivationPath]; + + [privkeys addObject:[NSValue valueWithPointer:external]]; + [privkeys addObject:[NSValue valueWithPointer:internal]]; + } else if ([derivationPath isKindOfClass:[DSIncomingFundsDerivationPath class]]) { + DSIncomingFundsDerivationPath *incomingFundsDerivationPath = (DSIncomingFundsDerivationPath *)derivationPath; + + NSMutableArray *mArray = [NSMutableArray array]; + for (NSNumber *index in externalIndexes.array) { + NSUInteger indexes[] = {index.unsignedIntValue}; + [mArray addObject:[NSIndexPath indexPathWithIndexes:indexes length:1]]; + } + DMaybeOpaqueKeys *keys = [DSDerivationPathFactory privateKeysAtIndexPaths:mArray + fromSeed:seed + derivationPath:incomingFundsDerivationPath]; + [privkeys addObject:[NSValue valueWithPointer:keys]]; + } else { + NSAssert(FALSE, @"The derivation path must be a normal or incoming funds derivation path"); + } + } + return privkeys; +} + // sign any inputs in the given transaction that can be signed using private keys from the wallet -- (void)signTransactions:(NSArray *)transactions withPrompt:(NSString *)authprompt completion:(TransactionValidityCompletionBlock)completion { +- (void)signTransactions:(NSArray *)transactions + withPrompt:(NSString *)authprompt + completion:(TransactionValidityCompletionBlock)completion { if (_isViewOnlyAccount) return; int64_t amount = 0; @@ -1388,8 +1470,20 @@ - (void)signTransactions:(NSArray *)transactions withPrompt:(NS DSFundsDerivationPath *derivationPath = dictionary[@"derivationPath"]; NSMutableOrderedSet *externalIndexes = dictionary[@"externalIndexes"], *internalIndexes = dictionary[@"internalIndexes"]; - [privkeys addObjectsFromArray:[derivationPath serializedPrivateKeys:externalIndexes.array internal:NO fromSeed:seed]]; - [privkeys addObjectsFromArray:[derivationPath serializedPrivateKeys:internalIndexes.array internal:YES fromSeed:seed]]; + + NSMutableArray *externalArray = [NSMutableArray array]; + NSMutableArray *internalArray = [NSMutableArray array]; + for (NSNumber *index in externalIndexes) { + NSUInteger indexes[] = {0, index.unsignedIntValue}; + [externalArray addObject:[NSIndexPath indexPathWithIndexes:indexes length:2]]; + } + for (NSNumber *index in internalIndexes) { + NSUInteger indexes[] = {1, index.unsignedIntValue}; + [internalArray addObject:[NSIndexPath indexPathWithIndexes:indexes length:2]]; + } + + [privkeys addObjectsFromArray:[DSDerivationPathFactory serializedPrivateKeysAtIndexPaths:externalArray fromSeed:seed derivationPath:derivationPath]]; + [privkeys addObjectsFromArray:[DSDerivationPathFactory serializedPrivateKeysAtIndexPaths:internalArray fromSeed:seed derivationPath:derivationPath]]; } BOOL signedSuccessfully = [transaction signWithSerializedPrivateKeys:privkeys]; @@ -1403,7 +1497,8 @@ - (void)signTransactions:(NSArray *)transactions withPrompt:(NS // MARK: = Registration // records the transaction in the account, or returns false if it isn't associated with the wallet -- (BOOL)registerTransaction:(DSTransaction *)transaction saveImmediately:(BOOL)saveImmediately { +- (BOOL)registerTransaction:(DSTransaction *)transaction + saveImmediately:(BOOL)saveImmediately { NSParameterAssert(transaction); #if DEBUG DSLogPrivate(@"[%@] [DSAccount] registering transaction %@", self.wallet.chain.name, transaction); @@ -1449,8 +1544,8 @@ - (BOOL)registerTransaction:(DSTransaction *)transaction saveImmediately:(BOOL)s [derivationPath registerTransactionAddress:output.address]; //only will register if derivation path contains address } } - [transaction loadBlockchainIdentitiesFromDerivationPaths:self.fundDerivationPaths]; - [transaction loadBlockchainIdentitiesFromDerivationPaths:self.outgoingFundDerivationPaths]; + [transaction loadIdentitiesFromDerivationPaths:self.fundDerivationPaths]; + [transaction loadIdentitiesFromDerivationPaths:self.outgoingFundDerivationPaths]; [self updateBalance]; if (saveImmediately) { if (!self.wallet.isTransient) { @@ -1468,7 +1563,8 @@ - (void)prepareForIncomingTransactionPersistenceForBlockSaveWithNumber:(uint32_t [self.transactionsToSave removeAllObjects]; } -- (void)persistIncomingTransactionsAttributesForBlockSaveWithNumber:(uint32_t)blockNumber inContext:(NSManagedObjectContext *)context { +- (void)persistIncomingTransactionsAttributesForBlockSaveWithNumber:(uint32_t)blockNumber + inContext:(NSManagedObjectContext *)context { for (DSTransaction *transaction in self.transactionsToSaveInBlockSave[@(blockNumber)]) { [transaction setInitialPersistentAttributesInContext:context]; } @@ -1782,18 +1878,35 @@ - (void)sweepPrivateKey:(NSString *)privKey withFee:(BOOL)fee } cancel:^{}]; return; } - - ECDSAKey *key = key_ecdsa_with_private_key([privKey UTF8String], self.wallet.chain.chainType); - NSString *address = [DSKeyManager NSStringFrom:address_for_ecdsa_key(key, self.wallet.chain.chainType)]; - NSData *publicKeyData = [DSKeyManager NSDataFrom:key_ecdsa_public_key_data(key)]; - processor_destroy_ecdsa_key(key); + DChainType *chain_type = self.wallet.chain.chainType; + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError *result = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_key_with_private_key((char *)[privKey UTF8String], chain_type); + if (!result) { + completion(nil, 0, ERROR_CANT_CREATE_KEY); + return; + } + if (!result->ok) { + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError_destroy(result); + completion(nil, 0, ERROR_CANT_CREATE_KEY); + return; + } + + char *addr = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_address_with_public_key_data(result->ok, self.wallet.chain.chainType); + NSString *address = [DSKeyManager NSStringFrom:addr]; + BYTES *public_key_data = dash_spv_crypto_keys_ecdsa_key_ECDSAKey_public_key_data(result->ok); + NSData *publicKeyData = [DSKeyManager NSDataFrom:public_key_data]; + Result_ok_dash_spv_crypto_keys_ecdsa_key_ECDSAKey_err_dash_spv_crypto_keys_KeyError_destroy(result); + +// ECDSAKey *key = key_ecdsa_with_private_key([privKey UTF8String], chain_type); +// NSString *address = [DSKeyManager NSStringFrom:address_for_ecdsa_key(key, chain_type)]; +// NSData *publicKeyData = [DSKeyManager NSDataFrom:key_ecdsa_public_key_data(key)]; +// processor_destroy_ecdsa_key(key); if (!address) { - completion(nil, 0, [NSError errorWithCode:187 localizedDescriptionKey:@"Not a valid private key"]); + completion(nil, 0, ERROR_INVALID_PRIVATE_KEY); return; } if ([self.wallet containsAddress:address]) { - completion(nil, 0, [NSError errorWithCode:187 localizedDescriptionKey:@"This private key is already in your wallet"]); + completion(nil, 0, ERROR_PRIVATE_KEY_ALREADY_IN_WALLET); return; } @@ -1819,7 +1932,7 @@ - (void)sweepPrivateKey:(NSString *)privKey withFee:(BOOL)fee } if (balance == 0) { - completion(nil, 0, [NSError errorWithCode:417 localizedDescriptionKey:@"This private key is empty"]); + completion(nil, 0, ERROR_EMPTY_PRIVATE_KEY); return; } @@ -1827,16 +1940,14 @@ - (void)sweepPrivateKey:(NSString *)privKey withFee:(BOOL)fee if (fee) feeAmount = [self.wallet.chain feeForTxSize:tx.size + 34 + (publicKeyData.length - 33) * tx.inputs.count]; //input count doesn't matter for non instant transactions if (feeAmount + self.wallet.chain.minOutputAmount > balance) { - completion(nil, 0, [NSError errorWithCode:417 localizedDescriptionKey: - @"Transaction fees would cost more than the funds available on this " - "private key (due to tiny \"dust\" deposits)"]); + completion(nil, 0, ERROR_NO_FUNDS_FOR_FEE); return; } [tx addOutputAddress:self.receiveAddress amount:balance - feeAmount]; if (![tx signWithSerializedPrivateKeys:@[privKey]]) { - completion(nil, 0, [NSError errorWithCode:401 localizedDescriptionKey:@"Error signing transaction"]); + completion(nil, 0, ERROR_SIGNING); return; } diff --git a/DashSync/shared/Models/Wallet/DSSpecialTransactionsWalletHolder.h b/DashSync/shared/Models/Wallet/DSSpecialTransactionsWalletHolder.h index b3ffdda15..4ee5a4201 100644 --- a/DashSync/shared/Models/Wallet/DSSpecialTransactionsWalletHolder.h +++ b/DashSync/shared/Models/Wallet/DSSpecialTransactionsWalletHolder.h @@ -9,7 +9,7 @@ #import -@class DSWallet, DSTransaction, DSCreditFundingTransaction, DSBlockchainIdentityRegistrationTransition, DSBlockchainIdentityUpdateTransition; +@class DSWallet, DSTransaction, DSIdentityRegistrationTransition, DSIdentityUpdateTransition; NS_ASSUME_NONNULL_BEGIN @@ -25,17 +25,18 @@ NS_ASSUME_NONNULL_BEGIN - (void)removeAllTransactions; -- (DSCreditFundingTransaction *)creditFundingTransactionForBlockchainIdentityUniqueId:(UInt256)blockchainIdentityUniqueId; + +//- (DSCreditFundingTransaction *)creditFundingTransactionForIdentityUniqueId:(UInt256)identityUniqueId; //// This gets a blockchain user registration transaction that has a specific public key hash (will change to BLS pub key) -//- (DSBlockchainIdentityRegistrationTransition*)blockchainIdentityRegistrationTransactionForPublicKeyHash:(UInt160)publicKeyHash; +//- (DSIdentityRegistrationTransition*)identityRegistrationTransactionForPublicKeyHash:(UInt160)publicKeyHash; // //// This gets a blockchain user reset transaction that has a specific public key hash (will change to BLS pub key) -//- (DSBlockchainIdentityUpdateTransition*)blockchainIdentityResetTransactionForPublicKeyHash:(UInt160)publicKeyHash; +//- (DSIdentityUpdateTransition*)identityResetTransactionForPublicKeyHash:(UInt160)publicKeyHash; // -//- (NSArray*)identityTransitionsForRegistrationTransitionHash:(UInt256)blockchainIdentityRegistrationTransactionHash; +//- (NSArray*)identityTransitionsForRegistrationTransitionHash:(UInt256)identityRegistrationTransactionHash; // -//- (UInt256)lastSubscriptionTransactionHashForRegistrationTransactionHash:(UInt256)blockchainIdentityRegistrationTransactionHash; +//- (UInt256)lastSubscriptionTransactionHashForRegistrationTransactionHash:(UInt256)identityRegistrationTransactionHash; // this is used to save transactions atomically with the block, needs to be called before switching threads to save the block - (void)prepareForIncomingTransactionPersistenceForBlockSaveWithNumber:(uint32_t)blockNumber; diff --git a/DashSync/shared/Models/Wallet/DSSpecialTransactionsWalletHolder.m b/DashSync/shared/Models/Wallet/DSSpecialTransactionsWalletHolder.m index 31a6a65a1..ee5278bb4 100644 --- a/DashSync/shared/Models/Wallet/DSSpecialTransactionsWalletHolder.m +++ b/DashSync/shared/Models/Wallet/DSSpecialTransactionsWalletHolder.m @@ -10,7 +10,7 @@ #import "DSSpecialTransactionsWalletHolder.h" #import "DSAddressEntity+CoreDataClass.h" #import "DSChain.h" -#import "DSCreditFundingTransaction.h" +#import "DSChain+Params.h" #import "DSDerivationPath.h" #import "DSDerivationPathEntity+CoreDataClass.h" #import "DSDerivationPathFactory.h" @@ -38,7 +38,6 @@ @interface DSSpecialTransactionsWalletHolder () @property (nonatomic, strong) NSMutableDictionary *providerUpdateServiceTransactions; @property (nonatomic, strong) NSMutableDictionary *providerUpdateRegistrarTransactions; @property (nonatomic, strong) NSMutableDictionary *providerUpdateRevocationTransactions; -@property (nonatomic, strong) NSMutableDictionary *creditFundingTransactions; @property (nonatomic, strong) NSMutableDictionary *assetLockTransactions; @property (nonatomic, strong) NSMutableDictionary *assetUnlockTransactions; @property (nonatomic, strong) NSMutableArray *transactionsToSave; @@ -59,7 +58,6 @@ - (instancetype)initWithWallet:(DSWallet *)wallet inContext:(NSManagedObjectCont self.providerUpdateRevocationTransactions = [NSMutableDictionary dictionary]; self.assetLockTransactions = [NSMutableDictionary dictionary]; self.assetUnlockTransactions = [NSMutableDictionary dictionary]; - self.creditFundingTransactions = [NSMutableDictionary dictionary]; self.managedObjectContext = [NSManagedObjectContext chainContext]; self.wallet = wallet; self.transactionsToSave = [NSMutableArray array]; @@ -68,7 +66,14 @@ - (instancetype)initWithWallet:(DSWallet *)wallet inContext:(NSManagedObjectCont } - (NSArray *)transactionDictionaries { - return @[self.providerRegistrationTransactions, self.providerUpdateServiceTransactions, self.providerUpdateRegistrarTransactions, self.providerUpdateRevocationTransactions, self.creditFundingTransactions, self.assetLockTransactions, self.assetUnlockTransactions]; + return @[ + self.providerRegistrationTransactions, + self.providerUpdateServiceTransactions, + self.providerUpdateRegistrarTransactions, + self.providerUpdateRevocationTransactions, + self.assetLockTransactions, + self.assetUnlockTransactions + ]; } @@ -154,12 +159,6 @@ - (BOOL)registerTransaction:(DSTransaction *)transaction saveImmediately:(BOOL)s [self.providerUpdateRevocationTransactions setObject:transaction forKey:uint256_data(transaction.txHash)]; added = TRUE; } - } else if ([transaction isMemberOfClass:[DSCreditFundingTransaction class]]) { - DSCreditFundingTransaction *creditFundingTransaction = (DSCreditFundingTransaction *)transaction; - if (![self.creditFundingTransactions objectForKey:uint256_data(creditFundingTransaction.creditBurnIdentityIdentifier)]) { - [self.creditFundingTransactions setObject:transaction forKey:uint256_data(creditFundingTransaction.creditBurnIdentityIdentifier)]; - added = TRUE; - } } else if ([transaction isMemberOfClass:[DSAssetLockTransaction class]]) { DSAssetLockTransaction *assetLockTransaction = (DSAssetLockTransaction *)transaction; if (![self.assetLockTransactions objectForKey:uint256_data(assetLockTransaction.txHash)]) { @@ -213,15 +212,10 @@ - (void)loadTransactions { [self.providerUpdateServiceTransactions setObject:transaction forKey:uint256_data(transaction.txHash)]; } else if ([transaction isMemberOfClass:[DSProviderUpdateRegistrarTransaction class]]) { [self.providerUpdateRegistrarTransactions setObject:transaction forKey:uint256_data(transaction.txHash)]; - } else if ([transaction isMemberOfClass:[DSCreditFundingTransaction class]]) { - DSCreditFundingTransaction *creditFundingTransaction = (DSCreditFundingTransaction *)transaction; - [self.creditFundingTransactions setObject:transaction forKey:uint256_data(creditFundingTransaction.creditBurnIdentityIdentifier)]; } else if ([transaction isMemberOfClass:[DSAssetLockTransaction class]]) { - DSAssetLockTransaction *assetLockTransaction = (DSAssetLockTransaction *)transaction; - [self.assetLockTransactions setObject:transaction forKey:uint256_data(assetLockTransaction.txHash)]; + [self.assetLockTransactions setObject:transaction forKey:uint256_data(transaction.txHash)]; } else if ([transaction isMemberOfClass:[DSAssetUnlockTransaction class]]) { - DSAssetUnlockTransaction *assetUnlockTransaction = (DSAssetUnlockTransaction *)transaction; - [self.assetUnlockTransactions setObject:transaction forKey:uint256_data(assetUnlockTransaction.txHash)]; + [self.assetUnlockTransactions setObject:transaction forKey:uint256_data(transaction.txHash)]; } else { //the other ones don't have addresses in payload NSAssert(FALSE, @"Unknown special transaction type"); } @@ -253,33 +247,33 @@ - (void)loadTransactions { } } - // NSArray * blockchainIdentityRegistrationTransactions = [self.blockchainIdentityRegistrationTransactions allValues]; + // NSArray * identityRegistrationTransactions = [self.identityRegistrationTransactions allValues]; // - // for (DSBlockchainIdentityRegistrationTransition * blockchainIdentityRegistrationTransaction in blockchainIdentityRegistrationTransactions) { - // NSArray* blockchainIdentityResetTransactions = [DSBlockchainIdentityResetTransactionEntity objectsInContext:context matching:@"registrationTransactionHash == %@",uint256_data(blockchainIdentityRegistrationTransaction.txHash)]; - // for (DSBlockchainIdentityResetTransitionEntity *e in blockchainIdentityResetTransactions) { + // for (DSIdentityRegistrationTransition * identityRegistrationTransaction in identityRegistrationTransactions) { + // NSArray* identityResetTransactions = [DSBlockchainIdentityResetTransactionEntity objectsInContext:context matching:@"registrationTransactionHash == %@",uint256_data(identityRegistrationTransaction.txHash)]; + // for (DSBlockchainIdentityResetTransitionEntity *e in identityResetTransactions) { // DSTransaction *transaction = [e transactionForChain:self.wallet.chain]; // // if (! transaction) continue; - // [self.blockchainIdentityResetTransactions setObject:transaction forKey:uint256_data(transaction.txHash)]; + // [self.identityResetTransactions setObject:transaction forKey:uint256_data(transaction.txHash)]; // } // - // NSArray* blockchainIdentityCloseTransactions = [DSBlockchainIdentityCloseTransactionEntity objectsInContext:context matching:@"registrationTransactionHash == %@",uint256_data(blockchainIdentityRegistrationTransaction.txHash)]; - // for (DSBlockchainIdentityCloseTransitionEntity *e in blockchainIdentityCloseTransactions) { + // NSArray* identityCloseTransactions = [DSBlockchainIdentityCloseTransactionEntity objectsInContext:context matching:@"registrationTransactionHash == %@",uint256_data(identityRegistrationTransaction.txHash)]; + // for (DSBlockchainIdentityCloseTransitionEntity *e in identityCloseTransactions) { // DSTransaction *transaction = [e transactionForChain:self.wallet.chain]; // // if (! transaction) continue; - // [self.blockchainIdentityCloseTransactions setObject:transaction forKey:uint256_data(transaction.txHash)]; + // [self.identityCloseTransactions setObject:transaction forKey:uint256_data(transaction.txHash)]; // } // - // NSArray* blockchainIdentityTopupTransactions = [DSBlockchainIdentityTopupTransitionEntity objectsInContext:context matching:@"registrationTransactionHash == %@",uint256_data(blockchainIdentityRegistrationTransaction.txHash)]; - // for (DSBlockchainIdentityTopupTransitionEntity *e in blockchainIdentityTopupTransactions) { + // NSArray* identityTopupTransactions = [DSBlockchainIdentityTopupTransitionEntity objectsInContext:context matching:@"registrationTransactionHash == %@",uint256_data(identityRegistrationTransaction.txHash)]; + // for (DSBlockchainIdentityTopupTransitionEntity *e in identityTopupTransactions) { // DSTransaction *transaction = [e transactionForChain:self.wallet.chain]; // // if (! transaction) continue; - // [self.blockchainIdentityTopupTransactions setObject:transaction forKey:uint256_data(transaction.txHash)]; + // [self.identityTopupTransactions setObject:transaction forKey:uint256_data(transaction.txHash)]; // } - // NSArray* transitions = [DSTransitionEntity objectsInContext:context matching:@"registrationTransactionHash == %@",uint256_data(blockchainIdentityRegistrationTransaction.txHash)]; + // NSArray* transitions = [DSTransitionEntity objectsInContext:context matching:@"registrationTransactionHash == %@",uint256_data(identityRegistrationTransaction.txHash)]; // for (DSTransitionEntity *e in transitions) { // DSTransaction *transaction = [e transactionForChain:self.wallet.chain]; // @@ -290,78 +284,78 @@ - (void)loadTransactions { }]; } -- (DSCreditFundingTransaction *)creditFundingTransactionForBlockchainIdentityUniqueId:(UInt256)blockchainIdentityUniqueId { - return [self.creditFundingTransactions objectForKey:uint256_data(blockchainIdentityUniqueId)]; -} - +//- (DSCreditFundingTransaction *)creditFundingTransactionForIdentityUniqueId:(UInt256)identityUniqueId { +// return [self.creditFundingTransactions objectForKey:uint256_data(identityUniqueId)]; +//} +// //// MARK: == Blockchain Identities Transaction Retrieval // -//-(DSBlockchainIdentityRegistrationTransition*)blockchainIdentityRegistrationTransactionForPublicKeyHash:(UInt160)publicKeyHash { -// for (DSBlockchainIdentityRegistrationTransition * blockchainIdentityRegistrationTransaction in [self.blockchainIdentityRegistrationTransactions allValues]) { -// if (uint160_eq(blockchainIdentityRegistrationTransaction.pubkeyHash, publicKeyHash)) { -// return blockchainIdentityRegistrationTransaction; +//-(DSIdentityRegistrationTransition*)identityRegistrationTransactionForPublicKeyHash:(UInt160)publicKeyHash { +// for (DSIdentityRegistrationTransition * identityRegistrationTransaction in [self.identityRegistrationTransactions allValues]) { +// if (uint160_eq(identityRegistrationTransaction.pubkeyHash, publicKeyHash)) { +// return identityRegistrationTransaction; // } // } // return nil; //} // -//- (DSBlockchainIdentityUpdateTransition*)blockchainIdentityResetTransactionForPublicKeyHash:(UInt160)publicKeyHash { -// for (DSBlockchainIdentityResetTransition * blockchainIdentityResetTransaction in [self.blockchainIdentityResetTransactions allValues]) { -// if (uint160_eq(blockchainIdentityResetTransaction.replacementPublicKeyHash, publicKeyHash)) { -// return blockchainIdentityResetTransaction; +//- (DSIdentityUpdateTransition*)identityResetTransactionForPublicKeyHash:(UInt160)publicKeyHash { +// for (DSIdentityResetTransition * identityResetTransaction in [self.identityResetTransactions allValues]) { +// if (uint160_eq(identityResetTransaction.replacementPublicKeyHash, publicKeyHash)) { +// return identityResetTransaction; // } // } // return nil; //} // -//-(NSArray*)identityTransitionsForRegistrationTransitionHash:(UInt256)blockchainIdentityRegistrationTransactionHash { -// NSLog(@"blockchainIdentityRegistrationTransactionHash %@",uint256_hex(blockchainIdentityRegistrationTransactionHash)); +//-(NSArray*)identityTransitionsForRegistrationTransitionHash:(UInt256)identityRegistrationTransactionHash { +// NSLog(@"identityRegistrationTransactionHash %@",uint256_hex(identityRegistrationTransactionHash)); // NSMutableArray * subscriptionTransactions = [NSMutableArray array]; -// for (DSBlockchainIdentityTopupTransition * blockchainIdentityTopupTransaction in [self.blockchainIdentityTopupTransactions allValues]) { -// if (uint256_eq(blockchainIdentityTopupTransaction.registrationTransactionHash, blockchainIdentityRegistrationTransactionHash)) { -// [subscriptionTransactions addObject:blockchainIdentityTopupTransaction]; +// for (DSIdentityTopupTransition * identityTopupTransaction in [self.identityTopupTransactions allValues]) { +// if (uint256_eq(identityTopupTransaction.registrationTransactionHash, identityRegistrationTransactionHash)) { +// [subscriptionTransactions addObject:identityTopupTransaction]; // } // } -// for (DSBlockchainIdentityResetTransition * blockchainIdentityResetTransaction in [self.blockchainIdentityResetTransactions allValues]) { -// if (uint256_eq(blockchainIdentityResetTransaction.registrationTransactionHash, blockchainIdentityRegistrationTransactionHash)) { -// [subscriptionTransactions addObject:blockchainIdentityResetTransaction]; +// for (DSIdentityResetTransition * identityResetTransaction in [self.identityResetTransactions allValues]) { +// if (uint256_eq(identityResetTransaction.registrationTransactionHash, identityRegistrationTransactionHash)) { +// [subscriptionTransactions addObject:identityResetTransaction]; // } // } -// for (DSBlockchainIdentityCloseTransition * blockchainIdentityCloseTransaction in [self.blockchainIdentityCloseTransactions allValues]) { -// if (uint256_eq(blockchainIdentityCloseTransaction.registrationTransactionHash, blockchainIdentityRegistrationTransactionHash)) { -// [subscriptionTransactions addObject:blockchainIdentityCloseTransaction]; +// for (DSIdentityCloseTransition * identityCloseTransaction in [self.identityCloseTransactions allValues]) { +// if (uint256_eq(identityCloseTransaction.registrationTransactionHash, identityRegistrationTransactionHash)) { +// [subscriptionTransactions addObject:identityCloseTransaction]; // } // } // for (DSTransition * transition in [self.transitions allValues]) { -// NSLog(@"transition blockchainIdentityRegistrationTransactionHash %@",uint256_hex(transition.registrationTransactionHash)); -// if (uint256_eq(transition.registrationTransactionHash, blockchainIdentityRegistrationTransactionHash)) { +// NSLog(@"transition identityRegistrationTransactionHash %@",uint256_hex(transition.registrationTransactionHash)); +// if (uint256_eq(transition.registrationTransactionHash, identityRegistrationTransactionHash)) { // [subscriptionTransactions addObject:transition]; // } // } // return [subscriptionTransactions copy]; //} // -//-(UInt256)lastSubscriptionTransactionHashForRegistrationTransactionHash:(UInt256)blockchainIdentityRegistrationTransactionHash { -// NSMutableOrderedSet * subscriptionTransactions = [NSMutableOrderedSet orderedSetWithArray:[self identityTransitionsForRegistrationTransitionHash:blockchainIdentityRegistrationTransactionHash]]; -// UInt256 lastSubscriptionTransactionHash = blockchainIdentityRegistrationTransactionHash; +//-(UInt256)lastSubscriptionTransactionHashForRegistrationTransactionHash:(UInt256)identityRegistrationTransactionHash { +// NSMutableOrderedSet * subscriptionTransactions = [NSMutableOrderedSet orderedSetWithArray:[self identityTransitionsForRegistrationTransitionHash:identityRegistrationTransactionHash]]; +// UInt256 lastSubscriptionTransactionHash = identityRegistrationTransactionHash; // while ([subscriptionTransactions count]) { // BOOL found = FALSE; // for (DSTransaction * transaction in [subscriptionTransactions copy]) { -// if ([transaction isKindOfClass:[DSBlockchainIdentityTopupTransition class]]) { +// if ([transaction isKindOfClass:[DSIdentityTopupTransition class]]) { // [subscriptionTransactions removeObject:transaction]; //remove topups -// } else if ([transaction isKindOfClass:[DSBlockchainIdentityUpdateTransition class]]) { -// DSBlockchainIdentityUpdateTransition * blockchainIdentityResetTransaction = (DSBlockchainIdentityUpdateTransition*)transaction; -// if (uint256_eq(blockchainIdentityResetTransaction.previousBlockchainIdentityTransactionHash, lastSubscriptionTransactionHash)) { -// lastSubscriptionTransactionHash = blockchainIdentityResetTransaction.txHash; +// } else if ([transaction isKindOfClass:[DSIdentityUpdateTransition class]]) { +// DSIdentityUpdateTransition * transition = (DSIdentityUpdateTransition*)transaction; +// if (uint256_eq(transition.previousIdentityTransactionHash, lastSubscriptionTransactionHash)) { +// lastSubscriptionTransactionHash = transition.txHash; // found = TRUE; -// [subscriptionTransactions removeObject:blockchainIdentityResetTransaction]; +// [subscriptionTransactions removeObject:transition]; // } -// } else if ([transaction isKindOfClass:[DSBlockchainIdentityCloseTransition class]]) { -// DSBlockchainIdentityCloseTransition * blockchainIdentityCloseTransaction = (DSBlockchainIdentityCloseTransition*)transaction; -// if (uint256_eq(blockchainIdentityCloseTransaction.previousBlockchainIdentityTransactionHash, lastSubscriptionTransactionHash)) { -// lastSubscriptionTransactionHash = blockchainIdentityCloseTransaction.txHash; +// } else if ([transaction isKindOfClass:[DSIdentityCloseTransition class]]) { +// DSIdentityCloseTransition * transition = (DSIdentityCloseTransition*)transaction; +// if (uint256_eq(transition.previousIdentityTransactionHash, lastSubscriptionTransactionHash)) { +// lastSubscriptionTransactionHash = transition.txHash; // found = TRUE; -// [subscriptionTransactions removeObject:blockchainIdentityCloseTransaction]; +// [subscriptionTransactions removeObject:transition]; // } // } else if ([transaction isKindOfClass:[DSTransition class]]) { // DSTransition * transition = (DSTransition*)transaction; diff --git a/DashSync/shared/Models/Wallet/DSWallet+Identity.h b/DashSync/shared/Models/Wallet/DSWallet+Identity.h new file mode 100644 index 000000000..4e5493aa0 --- /dev/null +++ b/DashSync/shared/Models/Wallet/DSWallet+Identity.h @@ -0,0 +1,66 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "DSIdentity.h" +#import "DSWallet.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface DSWallet (Identity) + +@property (nonatomic, readonly) NSDictionary *identities; +@property (nonatomic, readonly, nullable) DSIdentity *defaultIdentity; +@property (nonatomic, readonly) NSArray *identityAddresses; +// the first unused index for blockchain identity registration funding +@property (nonatomic, readonly) uint32_t unusedIdentityIndex; +// the amount of known blockchain identities +@property (nonatomic, readonly) uint32_t identitiesCount; + +- (void)setup; +- (void)setupIdentities; +- (void)loadIdentities; + +- (void)unregisterIdentity:(DSIdentity *)identity; +- (void)addIdentities:(NSArray *)identities; +- (void)addIdentity:(DSIdentity *)identity; + +// Verify makes sure the keys for the blockchain identity are good +- (BOOL)registerIdentities:(NSArray *)identities verify:(BOOL)verify; +- (BOOL)registerIdentity:(DSIdentity *)identity verify:(BOOL)verify; +- (BOOL)registerIdentity:(DSIdentity *)identity; +- (BOOL)containsIdentity:(DSIdentity *)identity; + +- (DSIdentity *)createIdentity; +- (DSIdentity *)createIdentityUsingDerivationIndex:(uint32_t)index; +- (DSIdentity *)createIdentityForUsername:(NSString *_Nullable)username; +- (DSIdentity *)createIdentityForUsername:(NSString *_Nullable)username usingDerivationIndex:(uint32_t)index; +- (DSIdentity *_Nullable)identityThatCreatedContract:(DPContract *)contract withContractId:(UInt256)contractId; +- (DSIdentity *_Nullable)identityForUniqueId:(UInt256)uniqueId; + +- (NSUInteger)indexOfIdentityAuthenticationHash:(UInt160)hash; +- (NSUInteger)indexOfIdentityAssetLockRegistrationHash:(UInt160)hash; +- (NSUInteger)indexOfIdentityAssetLockTopupHash:(UInt160)hash; +- (NSUInteger)indexOfIdentityAssetLockInvitationHash:(UInt160)hash; + +// Protected +- (void)wipeIdentitiesInContext:(NSManagedObjectContext *)context; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Wallet/DSWallet+Identity.m b/DashSync/shared/Models/Wallet/DSWallet+Identity.m new file mode 100644 index 000000000..a7b06cb5e --- /dev/null +++ b/DashSync/shared/Models/Wallet/DSWallet+Identity.m @@ -0,0 +1,378 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DPContract.h" +#import "DSAccount.h" +#import "DSAccountEntity+CoreDataClass.h" +#import "DSAuthenticationKeysDerivationPath.h" +#import "DSAssetLockDerivationPath.h" +#import "DSIdentity+Protected.h" +#import "DSIdentity+Username.h" +#import "DSBlockchainIdentityEntity+CoreDataClass.h" +#import "DSChain+Params.h" +#import "DSDashpayUserEntity+CoreDataClass.h" +#import "DSDerivationPathFactory.h" +#import "DSDerivationPathEntity+CoreDataClass.h" +#import "DSFriendRequestEntity+CoreDataClass.h" +#import "DSIncomingFundsDerivationPath.h" +#import "DSTransactionEntity+CoreDataClass.h" +#import "DSWallet+Identity.h" +#import "NSData+Dash.h" +#import "NSManagedObject+Sugar.h" +#import + +#define WALLET_BLOCKCHAIN_USERS_KEY @"WALLET_BLOCKCHAIN_USERS_KEY" +#define IDENTITY_INDEX_KEY @"IDENTITY_INDEX_KEY" +#define IDENTITY_LOCKED_OUTPUT_KEY @"IDENTITY_LOCKED_OUTPUT_KEY" + +NSString const *mIdentitiesDictionaryKey = @"mIdentitiesDictionaryKey"; +NSString const *defaultIdentityKey = @"defaultIdentityKey"; + +@implementation DSWallet (Identity) + +- (void)setMIdentities:(NSMutableDictionary *)dictionary { + objc_setAssociatedObject(self, &mIdentitiesDictionaryKey, dictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSMutableDictionary *)mIdentities { + return objc_getAssociatedObject(self, &mIdentitiesDictionaryKey); +} + +- (DSIdentity *)defaultIdentity { + return objc_getAssociatedObject(self, &defaultIdentityKey); +} + +- (void)setDefaultIdentity:(DSIdentity *)defaultIdentity { + objc_setAssociatedObject(self, &defaultIdentityKey, defaultIdentity, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void)setup { + self.mIdentities = nil; + self.mIdentities = [NSMutableDictionary dictionary]; +} + +- (void)setupIdentities { + self.mIdentities = nil; + [self identities]; +} + +- (NSString *)walletIdentitiesKey { + return [NSString stringWithFormat:@"%@_%@", WALLET_BLOCKCHAIN_USERS_KEY, [self uniqueIDString]]; +} + +- (NSString *)walletIdentitiesDefaultIndexKey { + return [NSString stringWithFormat:@"%@_%@_DEFAULT_INDEX", WALLET_BLOCKCHAIN_USERS_KEY, [self uniqueIDString]]; +} + +- (void)loadIdentities { + [self.chain.chainManagedObjectContext performBlockAndWait:^{ + NSMutableArray *usedFriendshipIdentifiers = [NSMutableArray array]; + for (NSData *identityData in self.mIdentities) { + DSIdentity *identity = [self.mIdentities objectForKey:identityData]; + NSSet *outgoingRequests = [identity matchingDashpayUserInContext:self.chain.chainManagedObjectContext].outgoingRequests; + for (DSFriendRequestEntity *request in outgoingRequests) { + DSAccount *account = [self accountWithNumber:request.account.index]; + UInt256 destinationIdentityID = request.destinationContact.associatedBlockchainIdentity.uniqueID.UInt256; + UInt256 sourceIdentityID = identity.uniqueID; + DSIncomingFundsDerivationPath *path = [DSIncomingFundsDerivationPath contactBasedDerivationPathWithDestinationIdentityUniqueId:destinationIdentityID + sourceIdentityUniqueId:sourceIdentityID + forAccount:account + onChain:self.chain]; + path.standaloneExtendedPublicKeyUniqueID = request.derivationPath.publicKeyIdentifier; + path.wallet = self; + [account addIncomingDerivationPath:path forFriendshipIdentifier:request.friendshipIdentifier inContext:self.chain.chainManagedObjectContext]; + [usedFriendshipIdentifiers addObject:request.friendshipIdentifier]; + } + } + + for (NSData *identityUniqueIdData in self.mIdentities) { + DSIdentity *identity = [self.mIdentities objectForKey:identityUniqueIdData]; + NSSet *incomingRequests = [identity matchingDashpayUserInContext:self.chain.chainManagedObjectContext].incomingRequests; + for (DSFriendRequestEntity *request in incomingRequests) { + DSAccount *account = [self accountWithNumber:request.account.index]; + DSIncomingFundsDerivationPath *fundsDerivationPath = [account derivationPathForFriendshipWithIdentifier:request.friendshipIdentifier]; + if (fundsDerivationPath) { + //both contacts are on device + [account addOutgoingDerivationPath:fundsDerivationPath + forFriendshipIdentifier:request.friendshipIdentifier + inContext:self.chain.chainManagedObjectContext]; + } else { + NSString *derivationPathPublicKeyIdentifier = request.derivationPath.publicKeyIdentifier; + UInt256 destinationIdentityID = request.destinationContact.associatedBlockchainIdentity.uniqueID.UInt256; + UInt256 sourceIdentityID = request.sourceContact.associatedBlockchainIdentity.uniqueID.UInt256; + DSIncomingFundsDerivationPath *path = [DSIncomingFundsDerivationPath externalDerivationPathWithExtendedPublicKeyUniqueID:derivationPathPublicKeyIdentifier + withDestinationIdentityUniqueId:destinationIdentityID + sourceIdentityUniqueId:sourceIdentityID + onChain:self.chain]; + path.wallet = self; + path.account = account; + [account addOutgoingDerivationPath:path + forFriendshipIdentifier:request.friendshipIdentifier + inContext:self.chain.chainManagedObjectContext]; + } + } + } + + //this adds the extra information to the transaction and must come after loading all blockchain identities. + for (DSAccount *account in self.accounts) { + for (DSTransaction *transaction in account.allTransactions) { + [transaction loadIdentitiesFromDerivationPaths:account.fundDerivationPaths]; + [transaction loadIdentitiesFromDerivationPaths:account.outgoingFundDerivationPaths]; + } + } + }]; +} +// MARK: - Identities + +- (NSArray *)identityAddresses { + DSAuthenticationKeysDerivationPath *derivationPath = [[DSDerivationPathFactory sharedInstance] identityBLSKeysDerivationPathForWallet:self]; + return derivationPath.hasExtendedPublicKey ? [derivationPath addressesToIndex:[self unusedIdentityIndex] + 10 useCache:YES addToCache:YES] : @[]; +} + +- (void)unregisterIdentity:(DSIdentity *)identity { + NSParameterAssert(identity); + NSAssert(identity.wallet == self, @"the identity you are trying to remove is not in this wallet"); + [self.mIdentities removeObjectForKey:identity.uniqueIDData]; + NSError *error = nil; + NSMutableDictionary *keyChainDictionary = [getKeychainDict(self.walletIdentitiesKey, @[[NSNumber class], [NSData class]], &error) mutableCopy]; + if (keyChainDictionary) + [keyChainDictionary removeObjectForKey:identity.uniqueIDData]; + else + keyChainDictionary = [NSMutableDictionary dictionary]; + setKeychainDict(keyChainDictionary, self.walletIdentitiesKey, NO); +} + +- (void)addIdentities:(NSArray *)identities { + for (DSIdentity *identity in identities) { + [self addIdentity:identity]; + } +} + +- (void)addIdentity:(DSIdentity *)identity { + NSParameterAssert(identity); + NSAssert(uint256_is_not_zero(identity.uniqueID), @"The identity unique ID must be set"); + [self.mIdentities setObject:identity forKey:identity.uniqueIDData]; +} + +- (BOOL)containsIdentity:(DSIdentity *)identity { + return identity.lockedOutpointData && ([self.mIdentities objectForKey:identity.uniqueIDData] != nil); +} + +- (BOOL)registerIdentities:(NSArray *)identities + verify:(BOOL)verify { + for (DSIdentity *identity in identities) { + if (![self registerIdentity:identity verify:verify]) + return FALSE; + } + return TRUE; +} + +- (BOOL)registerIdentity:(DSIdentity *)identity { + return [self registerIdentity:identity verify:NO]; +} + +- (BOOL)registerIdentity:(DSIdentity *)identity + verify:(BOOL)verify { + NSParameterAssert(identity); + if (verify) { + if (![identity verifyKeysForWallet:self]) { + identity.isLocal = FALSE; + return FALSE; + } + } + if ([self.mIdentities objectForKey:identity.uniqueIDData] == nil) + [self addIdentity:identity]; + NSError *error = nil; + NSMutableDictionary *keyChainDictionary = [getKeychainDict(self.walletIdentitiesKey, @[[NSNumber class], [NSData class]], &error) mutableCopy]; + if (error) return FALSE; + if (!keyChainDictionary) + keyChainDictionary = [NSMutableDictionary dictionary]; + NSAssert(uint256_is_not_zero(identity.uniqueID), @"registrationTransactionHashData must not be null"); + keyChainDictionary[identity.uniqueIDData] = uint256_is_zero(identity.lockedOutpointData.transactionOutpoint.hash) + ? @{IDENTITY_INDEX_KEY: @(identity.index)} + : @{IDENTITY_INDEX_KEY: @(identity.index), IDENTITY_LOCKED_OUTPUT_KEY: identity.lockedOutpointData}; + setKeychainDict(keyChainDictionary, self.walletIdentitiesKey, NO); + if (!self.defaultIdentity && (identity.index == 0)) + self.defaultIdentity = identity; + return TRUE; +} + +- (void)wipeIdentitiesInContext:(NSManagedObjectContext *)context { + for (DSIdentity *identity in [self.mIdentities allValues]) { + [self unregisterIdentity:identity]; + [identity deletePersistentObjectAndSave:NO inContext:context]; + } + self.defaultIdentity = nil; +} + +- (DSIdentity *_Nullable)identityThatCreatedContract:(DPContract *)contract + withContractId:(UInt256)contractId { + NSParameterAssert(contract); + NSAssert(uint256_is_not_zero(contractId), @"contractId must not be null"); + DSIdentity *foundIdentity = nil; + for (DSIdentity *identity in [self.mIdentities allValues]) { + if (uint256_eq([contract contractIdIfRegisteredByIdentity:identity], contractId)) + foundIdentity = identity; + } + return foundIdentity; +} + +- (DSIdentity *)identityForUniqueId:(UInt256)uniqueId { + NSAssert(uint256_is_not_zero(uniqueId), @"uniqueId must not be null"); + DSIdentity *foundIdentity = nil; + for (DSIdentity *identity in [self.mIdentities allValues]) { + if (uint256_eq([identity uniqueID], uniqueId)) + foundIdentity = identity; + } + return foundIdentity; +} + +- (uint32_t)identitiesCount { + return (uint32_t)[self.mIdentities count]; +} + +- (BOOL)upgradeIdentityKeyChain { + NSError *error = nil; + NSMutableDictionary *keyChainDictionary = [getKeychainDict(self.walletIdentitiesKey, @[[NSNumber class], [NSData class]], &error) mutableCopy]; + NSAssert(error == nil, @"There should be no error during upgrade"); + if (error) return FALSE; + NSMutableDictionary *updated = [NSMutableDictionary dictionary]; + for (NSData *identityLockedOutpoint in keyChainDictionary) { + [updated setObject:@{IDENTITY_INDEX_KEY: keyChainDictionary[identityLockedOutpoint], IDENTITY_LOCKED_OUTPUT_KEY: identityLockedOutpoint} + forKey:uint256_data([identityLockedOutpoint SHA256_2])]; + } + setKeychainDict(updated, self.walletIdentitiesKey, NO); + return TRUE; +} + + +//This loads all the identities that the wallet knows about. If the app was deleted and reinstalled the identity information will remain from the keychain but must be reaquired from the network. +- (NSMutableDictionary *)identities { + //setKeychainDict(@{}, self.walletIdentitiesKey, NO); + if (self.mIdentities) return self.mIdentities; + NSError *error = nil; + NSMutableDictionary *keyChainDictionary = [getKeychainDict(self.walletIdentitiesKey, @[[NSNumber class], [NSData class]], &error) mutableCopy]; + if (error) return nil; + uint64_t defaultIndex = getKeychainInt(self.walletIdentitiesDefaultIndexKey, &error); + if (error) return nil; + NSMutableDictionary *rDictionary = [NSMutableDictionary dictionary]; + if (keyChainDictionary && keyChainDictionary.count) { + if ([[[keyChainDictionary allValues] firstObject] isKindOfClass:[NSNumber class]]) + return [self upgradeIdentityKeyChain] ? (NSMutableDictionary *) [self identities] : nil; + for (NSData *uniqueIdData in keyChainDictionary) { + NSDictionary *dict = keyChainDictionary[uniqueIdData]; + uint32_t index = [[dict objectForKey:IDENTITY_INDEX_KEY] unsignedIntValue]; + // either the identity is known in core data (and we can pull it) or the wallet has been wiped and we need to get it from DAPI (the unique Id was saved in the keychain, so we don't need to resync) + //TODO: get the identity from core data + + NSManagedObjectContext *context = [NSManagedObjectContext chainContext]; //shouldn't matter what context is used + + [context performBlockAndWait:^{ + NSUInteger identityEntitiesCount = [DSBlockchainIdentityEntity countObjectsInContext:context matching:@"chain == %@ && isLocal == TRUE", [self.chain chainEntityInContext:context]]; + if (identityEntitiesCount != keyChainDictionary.count) + DSLog(@"[%@] Unmatching blockchain entities count", self.chain.name); + DSBlockchainIdentityEntity *entity = [DSBlockchainIdentityEntity anyObjectInContext:context matching:@"uniqueID == %@", uniqueIdData]; + DSIdentity *identity = nil; + NSDictionary *dict = keyChainDictionary[uniqueIdData]; + NSData *lockedOutpointData = [dict objectForKey:IDENTITY_LOCKED_OUTPUT_KEY]; + if (entity) { + if (lockedOutpointData) { + identity = [[DSIdentity alloc] initAtIndex:index withLockedOutpoint:lockedOutpointData.transactionOutpoint inWallet:self withIdentityEntity:entity]; + } else { + identity = [[DSIdentity alloc] initAtIndex:index withUniqueId:uniqueIdData.UInt256 inWallet:self withIdentityEntity:entity]; + } + } else if (lockedOutpointData) { + //No blockchain identity is known in core data + NSData *transactionHashData = uint256_data(uint256_reverse(lockedOutpointData.transactionOutpoint.hash)); + DSTransactionEntity *creditRegitrationTransactionEntity = [DSTransactionEntity anyObjectInContext:context matching:@"transactionHash.txHash == %@", transactionHashData]; + if (creditRegitrationTransactionEntity) { + // The registration funding transaction exists + // Weird but we should recover in this situation + DSAssetLockTransaction *assetLockTransaction = (DSAssetLockTransaction *)[creditRegitrationTransactionEntity transactionForChain:self.chain]; + BOOL correctIndex = [assetLockTransaction checkDerivationPathIndexForWallet:self isIndex:index]; + if (!correctIndex) { + NSAssert(FALSE, @"We should implement this"); + } else { + identity = [[DSIdentity alloc] initAtIndex:index withAssetLockTransaction:assetLockTransaction withUsernameDictionary:nil inWallet:self]; + [identity registerInWallet]; + } + } else { + // We also don't have the registration funding transaction + identity = [[DSIdentity alloc] initAtIndex:index withUniqueId:uniqueIdData.UInt256 inWallet:self]; + [identity registerInWalletForIdentityUniqueId:uniqueIdData.UInt256]; + } + } else { + identity = [[DSIdentity alloc] initAtIndex:index withUniqueId:uniqueIdData.UInt256 inWallet:self]; + [identity registerInWalletForIdentityUniqueId:uniqueIdData.UInt256]; + } + if (identity) { + rDictionary[uniqueIdData] = identity; + if (index == defaultIndex) + self.defaultIdentity = identity; + } + }]; + } + } + self.mIdentities = rDictionary; + return self.mIdentities; +} + +- (uint32_t)unusedIdentityIndex { + NSArray *identities = [self.mIdentities allValues]; + NSNumber *max = [identities valueForKeyPath:@"index.@max.intValue"]; + return max != nil ? ([max unsignedIntValue] + 1) : 0; +} + +- (DSIdentity *)createIdentity { + return [[DSIdentity alloc] initAtIndex:[self unusedIdentityIndex] inWallet:self]; +} + +- (DSIdentity *)createIdentityUsingDerivationIndex:(uint32_t)index { + return [[DSIdentity alloc] initAtIndex:index inWallet:self]; +} + +- (DSIdentity *)createIdentityForUsername:(NSString *)username { + DSIdentity *identity = [self createIdentity]; + [identity addDashpayUsername:username save:NO]; + return identity; +} + +- (DSIdentity *)createIdentityForUsername:(NSString *)username + usingDerivationIndex:(uint32_t)index { + DSIdentity *identity = [self createIdentityUsingDerivationIndex:index]; + [identity addDashpayUsername:username save:NO]; + return identity; +} + + +- (NSUInteger)indexOfIdentityAuthenticationHash:(UInt160)hash { + return [[DSAuthenticationKeysDerivationPath identitiesBLSKeysDerivationPathForWallet:self] indexOfKnownAddressHash:hash]; +} + +- (NSUInteger)indexOfIdentityAssetLockRegistrationHash:(UInt160)hash { + return [[DSAssetLockDerivationPath identityRegistrationFundingDerivationPathForWallet:self] indexOfKnownAddressHash:hash]; +} + +- (NSUInteger)indexOfIdentityAssetLockTopupHash:(UInt160)hash { + return [[DSAssetLockDerivationPath identityTopupFundingDerivationPathForWallet:self] indexOfKnownAddressHash:hash]; +} + +- (NSUInteger)indexOfIdentityAssetLockInvitationHash:(UInt160)hash { + return [[DSAssetLockDerivationPath identityInvitationFundingDerivationPathForWallet:self] indexOfKnownAddressHash:hash]; +} + +@end diff --git a/DashSync/shared/Models/Wallet/DSWallet+Invitation.h b/DashSync/shared/Models/Wallet/DSWallet+Invitation.h new file mode 100644 index 000000000..d9cfcc697 --- /dev/null +++ b/DashSync/shared/Models/Wallet/DSWallet+Invitation.h @@ -0,0 +1,45 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "DSInvitation.h" +#import "DSWallet.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSWallet (Invitation) + +@property (nonatomic, readonly) NSDictionary *invitations; +// the first unused index for invitations +@property (nonatomic, readonly) uint32_t unusedInvitationIndex; +// the amount of known blockchain invitations +@property (nonatomic, readonly) uint32_t invitationsCount; + +- (void)setupInvitations; + +- (void)unregisterInvitation:(DSInvitation *)invitation; +- (void)addInvitation:(DSInvitation *)invitation; +- (void)registerInvitation:(DSInvitation *)invitation; +- (BOOL)containsInvitation:(DSInvitation *)invitation; +- (DSInvitation *)createInvitation; +- (DSInvitation *)createInvitationUsingDerivationIndex:(uint32_t)index; +- (DSInvitation *_Nullable)invitationForUniqueId:(UInt256)uniqueId; +- (void)wipeInvitationsInContext:(NSManagedObjectContext *)context; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Wallet/DSWallet+Invitation.m b/DashSync/shared/Models/Wallet/DSWallet+Invitation.m new file mode 100644 index 000000000..b2a6af49b --- /dev/null +++ b/DashSync/shared/Models/Wallet/DSWallet+Invitation.m @@ -0,0 +1,182 @@ +// +// Created by Vladimir Pirogov +// Copyright © 2024 Dash Core Group. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "DSInvitation+Protected.h" +#import "DSChain+Params.h" +#import "DSTransactionEntity+CoreDataClass.h" +#import "DSWallet+Invitation.h" +#import "NSManagedObject+Sugar.h" +#import + +#define WALLET_BLOCKCHAIN_INVITATIONS_KEY @"WALLET_BLOCKCHAIN_INVITATIONS_KEY" +NSString const *mInvitationsDictionaryKey = @"mInvitationsDictionaryKey"; + +@interface DSWallet () + +@property (nonatomic, strong) NSMutableDictionary *mInvitations; + +@end + +@implementation DSWallet (Invitation) + +- (void)setMInvitations:(NSMutableDictionary *)dictionary { + objc_setAssociatedObject(self, &mInvitationsDictionaryKey, dictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSMutableDictionary *)mInvitations { + return objc_getAssociatedObject(self, &mInvitationsDictionaryKey); +} + +- (void)setupInvitations { + self.mInvitations = nil; + [self invitations]; +} + +- (NSString *)walletInvitationsKey { + return [NSString stringWithFormat:@"%@_%@", WALLET_BLOCKCHAIN_INVITATIONS_KEY, [self uniqueIDString]]; +} + +// MARK: - Invitations + + +- (uint32_t)invitationsCount { + return (uint32_t)[self.mInvitations count]; +} + + +//This loads all the identities that the wallet knows about. If the app was deleted and reinstalled the identity information will remain from the keychain but must be reaquired from the network. +- (NSMutableDictionary *)invitations { + //setKeychainDict(@{}, self.walletInvitationsKey, NO); + if (self.mInvitations) return self.mInvitations; + NSError *error = nil; + NSMutableDictionary *keyChainDictionary = [getKeychainDict(self.walletInvitationsKey, @[[NSNumber class], [NSData class]], &error) mutableCopy]; + if (error) return nil; + NSMutableDictionary *rDictionary = [NSMutableDictionary dictionary]; + if (keyChainDictionary) { + for (NSData *invitationLockedOutpointData in keyChainDictionary) { + uint32_t index = [keyChainDictionary[invitationLockedOutpointData] unsignedIntValue]; + DSUTXO invitationLockedOutpoint = invitationLockedOutpointData.transactionOutpoint; + //either the identity is known in core data (and we can pull it) or the wallet has been wiped and we need to get it from DAPI (the unique Id was saved in the keychain, so we don't need to resync) + //TODO: get the identity from core data + NSManagedObjectContext *context = [NSManagedObjectContext chainContext]; //shouldn't matter what context is used + [context performBlockAndWait:^{ + NSUInteger invitationEntitiesCount = [DSBlockchainInvitationEntity countObjectsInContext:context matching:@"chain == %@", [self.chain chainEntityInContext:context]]; + if (invitationEntitiesCount != keyChainDictionary.count) + DSLog(@"[%@] Unmatching blockchain invitations count", self.chain.name); + NSData *identityID = uint256_data([dsutxo_data(invitationLockedOutpoint) SHA256_2]); + DSBlockchainInvitationEntity *invitationEntity = [DSBlockchainInvitationEntity anyObjectInContext:context matching:@"blockchainIdentity.uniqueID == %@", identityID]; + DSInvitation *invitation = nil; + if (invitationEntity) { + invitation = [[DSInvitation alloc] initAtIndex:index withLockedOutpoint:invitationLockedOutpoint inWallet:self withInvitationEntity:invitationEntity]; + } else { + //No blockchain identity is known in core data + NSData *transactionHashData = uint256_data(uint256_reverse(invitationLockedOutpoint.hash)); + DSTransactionEntity *creditRegitrationTransactionEntity = [DSTransactionEntity anyObjectInContext:context matching:@"transactionHash.txHash == %@", transactionHashData]; + if (creditRegitrationTransactionEntity) { + //The registration funding transaction exists + //Weird but we should recover in this situation + DSAssetLockTransaction *registrationTransaction = (DSAssetLockTransaction *)[creditRegitrationTransactionEntity transactionForChain:self.chain]; + + BOOL correctIndex = [registrationTransaction checkInvitationDerivationPathIndexForWallet:self isIndex:index]; + if (!correctIndex) { + NSAssert(FALSE, @"We should implement this"); + } else { + invitation = [[DSInvitation alloc] initAtIndex:index withAssetLockTransaction:registrationTransaction inWallet:self]; + [invitation registerInWallet]; + } + } else { + //We also don't have the registration funding transaction + invitation = [[DSInvitation alloc] initAtIndex:index withLockedOutpoint:invitationLockedOutpoint inWallet:self]; + [invitation registerInWalletForIdentityUniqueId:[dsutxo_data(invitationLockedOutpoint) SHA256_2]]; + } + } + if (invitation) + rDictionary[invitationLockedOutpointData] = invitation; + }]; + } + } + self.mInvitations = rDictionary; + return self.mInvitations; +} + +- (DSInvitation *)invitationForUniqueId:(UInt256)uniqueId { + NSAssert(uint256_is_not_zero(uniqueId), @"uniqueId must not be null"); + DSInvitation *foundInvitation = nil; + for (DSInvitation *invitation in [self.mInvitations allValues]) { + if (uint256_eq([invitation.identity uniqueID], uniqueId)) + foundInvitation = invitation; + } + return foundInvitation; +} + +- (uint32_t)unusedInvitationIndex { + NSArray *invitations = [self.mInvitations allValues]; + NSNumber *max = [invitations valueForKeyPath:@"identity.index.@max.intValue"]; + return max != nil ? ([max unsignedIntValue] + 1) : 0; +} + +- (DSInvitation *)createInvitation { + return [[DSInvitation alloc] initAtIndex:[self unusedInvitationIndex] inWallet:self]; +} + +- (DSInvitation *)createInvitationUsingDerivationIndex:(uint32_t)index { + return [[DSInvitation alloc] initAtIndex:index inWallet:self]; +} + +- (void)unregisterInvitation:(DSInvitation *)invitation { + NSParameterAssert(invitation); + NSAssert(invitation.wallet == self, @"the invitation you are trying to remove is not in this wallet"); + NSAssert(invitation.identity != nil, @"the invitation you are trying to remove has no identity"); + [self.mInvitations removeObjectForKey:invitation.identity.lockedOutpointData]; + NSError *error = nil; + NSMutableDictionary *keyChainDictionary = [getKeychainDict(self.walletInvitationsKey, @[[NSNumber class], [NSData class]], &error) mutableCopy]; + if (!keyChainDictionary) keyChainDictionary = [NSMutableDictionary dictionary]; + [keyChainDictionary removeObjectForKey:invitation.identity.lockedOutpointData]; + setKeychainDict(keyChainDictionary, self.walletInvitationsKey, NO); +} + +- (void)addInvitation:(DSInvitation *)invitation { + NSParameterAssert(invitation); + [self.mInvitations setObject:invitation forKey:invitation.identity.lockedOutpointData]; +} + +- (void)registerInvitation:(DSInvitation *)invitation { + NSParameterAssert(invitation); + NSAssert(invitation.identity != nil, @"the invitation you are trying to remove has no identity"); + if ([self.mInvitations objectForKey:invitation.identity.lockedOutpointData] == nil) + [self addInvitation:invitation]; + NSError *error = nil; + NSMutableDictionary *keyChainDictionary = [getKeychainDict(self.walletInvitationsKey, @[[NSNumber class], [NSData class]], &error) mutableCopy]; + if (!keyChainDictionary) + keyChainDictionary = [NSMutableDictionary dictionary]; + NSAssert(uint256_is_not_zero(invitation.identity.uniqueID), @"registrationTransactionHashData must not be null"); + keyChainDictionary[invitation.identity.lockedOutpointData] = @(invitation.identity.index); + setKeychainDict(keyChainDictionary, self.walletInvitationsKey, NO); +} + +- (BOOL)containsInvitation:(DSInvitation *)invitation { + return invitation.identity.lockedOutpointData && ([self.mInvitations objectForKey:invitation.identity.lockedOutpointData] != nil); +} + +- (void)wipeInvitationsInContext:(NSManagedObjectContext *)context { + for (DSInvitation *invitation in [self.mInvitations allValues]) { + [self unregisterInvitation:invitation]; + [invitation deletePersistentObjectAndSave:NO inContext:context]; + } +} + +@end diff --git a/DashSync/shared/Models/Wallet/DSWallet+Protected.h b/DashSync/shared/Models/Wallet/DSWallet+Protected.h index db2561f98..ad7a7158c 100644 --- a/DashSync/shared/Models/Wallet/DSWallet+Protected.h +++ b/DashSync/shared/Models/Wallet/DSWallet+Protected.h @@ -46,8 +46,6 @@ NS_ASSUME_NONNULL_BEGIN + (NSData *)chainSynchronizationFingerprintForBlockZones:(NSOrderedSet *)blockHeightZones forChainHeight:(uint32_t)chainHeight; -- (void)loadBlockchainIdentities; - @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Wallet/DSWallet.h b/DashSync/shared/Models/Wallet/DSWallet.h index f6a3d6993..23d0d74e5 100644 --- a/DashSync/shared/Models/Wallet/DSWallet.h +++ b/DashSync/shared/Models/Wallet/DSWallet.h @@ -24,7 +24,7 @@ #import "BigIntTypes.h" #import "DSBIP39Mnemonic.h" -#import "DSBlockchainIdentity.h" +#import "DSIdentity.h" #import NS_ASSUME_NONNULL_BEGIN @@ -38,34 +38,17 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSWalletBalanceDidChangeNotification; #define DUFFS 100000000LL #define MAX_MONEY (21000000LL * DUFFS) -@class DSChain, DSAccount, DSTransaction, DSDerivationPath, DSLocalMasternode, DSSpecialTransactionsWalletHolder, DSBlockchainInvitation; +@class DSChain, DSAccount, DSTransaction, DSDerivationPath, DSLocalMasternode, DSSpecialTransactionsWalletHolder, DSInvitation; @interface DSWallet : NSObject @property (nonatomic, readonly) NSDictionary *orderedAccounts; - @property (nonatomic, readonly) uint32_t lastAccountNumber; - @property (nonatomic, readonly) NSArray *accounts; - @property (nonatomic, readonly) DSSpecialTransactionsWalletHolder *specialTransactionsHolder; - -@property (nonatomic, readonly) NSDictionary *blockchainIdentities; - -@property (nonatomic, readonly) NSDictionary *blockchainInvitations; - -@property (nonatomic, readonly, nullable) DSBlockchainIdentity *defaultBlockchainIdentity; - -- (void)setDefaultBlockchainIdentity:(DSBlockchainIdentity *)defaultBlockchainIdentity; - -@property (nonatomic, readonly) NSArray *blockchainIdentityAddresses; - @property (nonatomic, readonly) NSArray *providerOwnerAddresses; - @property (nonatomic, readonly) NSArray *providerVotingAddresses; - @property (nonatomic, readonly) NSArray *providerOperatorAddresses; - @property (nonatomic, readonly) NSArray *platformNodeAddresses; //This is unique among all wallets and all chains @@ -103,29 +86,28 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSWalletBalanceDidChangeNotification; // the total amount received by the wallet (excluding change) @property (nonatomic, readonly) uint64_t totalReceived; -// the first unused index for blockchain identity registration funding -@property (nonatomic, readonly) uint32_t unusedBlockchainIdentityIndex; - -// the first unused index for invitations -@property (nonatomic, readonly) uint32_t unusedBlockchainInvitationIndex; - -// the amount of known blockchain identities -@property (nonatomic, readonly) uint32_t blockchainIdentitiesCount; - -// the amount of known blockchain invitations -@property (nonatomic, readonly) uint32_t blockchainInvitationsCount; - // The fingerprint for currentTransactions @property (nonatomic, readonly) NSData *chainSynchronizationFingerprint; - (void)authPrivateKey:(void (^_Nullable)(NSString *_Nullable authKey))completion; -+ (DSWallet *_Nullable)standardWalletWithSeedPhrase:(NSString *)seedPhrase setCreationDate:(NSTimeInterval)creationDate forChain:(DSChain *)chain storeSeedPhrase:(BOOL)storeSeedPhrase isTransient:(BOOL)isTransient; -+ (DSWallet *_Nullable)standardWalletWithRandomSeedPhraseForChain:(DSChain *)chain storeSeedPhrase:(BOOL)store isTransient:(BOOL)isTransient; -+ (DSWallet *_Nullable)standardWalletWithRandomSeedPhraseInLanguage:(DSBIP39Language)language forChain:(DSChain *)chain storeSeedPhrase:(BOOL)store isTransient:(BOOL)isTransient; -+ (DSWallet *_Nullable)transientWalletWithDerivedKeyData:(NSData *)derivedData forChain:(DSChain *)chain; - -- (instancetype)initWithUniqueID:(NSString *_Nonnull)uniqueID forChain:(DSChain *_Nonnull)chain; ++ (DSWallet *_Nullable)standardWalletWithSeedPhrase:(NSString *)seedPhrase + setCreationDate:(NSTimeInterval)creationDate + forChain:(DSChain *)chain + storeSeedPhrase:(BOOL)storeSeedPhrase + isTransient:(BOOL)isTransient; ++ (DSWallet *_Nullable)standardWalletWithRandomSeedPhraseForChain:(DSChain *)chain + storeSeedPhrase:(BOOL)store + isTransient:(BOOL)isTransient; ++ (DSWallet *_Nullable)standardWalletWithRandomSeedPhraseInLanguage:(DSBIP39Language)language + forChain:(DSChain *)chain + storeSeedPhrase:(BOOL)store + isTransient:(BOOL)isTransient; ++ (DSWallet *_Nullable)transientWalletWithDerivedKeyData:(NSData *)derivedData + forChain:(DSChain *)chain; + +- (instancetype)initWithUniqueID:(NSString *_Nonnull)uniqueID + forChain:(DSChain *_Nonnull)chain; // true if the address is controlled by the wallet - (BOOL)containsAddress:(NSString *)address; @@ -134,14 +116,10 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSWalletBalanceDidChangeNotification; - (BOOL)accountsBaseDerivationPathsContainAddress:(NSString *)address; - (DSAccount *_Nullable)accountForAddress:(NSString *)address; - - (DSAccount *_Nullable)accountForDashpayExternalDerivationPathAddress:(NSString *)address; - // true if the address was previously used as an input or output for this wallet - (BOOL)addressIsUsed:(NSString *)address; - - (BOOL)transactionAddressAlreadySeenInOutputs:(NSString *)address; - - (void)chainUpdatedBlockHeight:(int32_t)height; // sets the block heights and timestamps for the given transactions, and returns an array of hashes of the updated tx @@ -170,12 +148,17 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSWalletBalanceDidChangeNotification; - (NSArray *)accountsThatCanContainTransaction:(DSTransaction *)transaction; // returns an account to which the given transaction hash is associated with, no account if the transaction hash is not associated with the wallet -- (DSAccount *_Nullable)accountForTransactionHash:(UInt256)txHash transaction:(DSTransaction *_Nullable __autoreleasing *_Nullable)transaction; +- (DSAccount *_Nullable)accountForTransactionHash:(UInt256)txHash + transaction:(DSTransaction *_Nullable __autoreleasing *_Nullable)transaction; // returns the transaction with the given hash if it's been registered in the wallet (might also return non-registered) - (DSTransaction *_Nullable)transactionForHash:(UInt256)txHash; -- (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit unusedAccountGapLimit:(NSUInteger)unusedAccountGapLimit dashpayGapLimit:(NSUInteger)dashpayGapLimit internal:(BOOL)internal error:(NSError *_Nullable *_Nullable)error; +- (NSArray *)registerAddressesWithGapLimit:(NSUInteger)gapLimit + unusedAccountGapLimit:(NSUInteger)unusedAccountGapLimit + dashpayGapLimit:(NSUInteger)dashpayGapLimit + internal:(BOOL)internal + error:(NSError *_Nullable *_Nullable)error; // returns the amount received by the wallet from the transaction (total outputs to change and/or receive addresses) - (uint64_t)amountReceivedFromTransaction:(DSTransaction *)transaction; @@ -193,16 +176,20 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSWalletBalanceDidChangeNotification; - (void)prepareForIncomingTransactionPersistenceForBlockSaveWithNumber:(uint32_t)blockNumber; // this is used to save transactions atomically with the block -- (void)persistIncomingTransactionsAttributesForBlockSaveWithNumber:(uint32_t)blockNumber inContext:(NSManagedObjectContext *)context; +- (void)persistIncomingTransactionsAttributesForBlockSaveWithNumber:(uint32_t)blockNumber + inContext:(NSManagedObjectContext *)context; //returns the seed phrase after authenticating - (void)seedPhraseAfterAuthentication:(void (^_Nullable)(NSString *_Nullable seedPhrase))completion; -- (void)seedPhraseAfterAuthenticationWithPrompt:(NSString *_Nullable)authprompt completion:(void (^_Nullable)(NSString *_Nullable seedPhrase))completion; +- (void)seedPhraseAfterAuthenticationWithPrompt:(NSString *_Nullable)authprompt + completion:(void (^_Nullable)(NSString *_Nullable seedPhrase))completion; - (NSString *_Nullable)seedPhraseIfAuthenticated; -- (OpaqueKey *_Nullable)privateKeyForAddress:(NSString *_Nonnull)address fromSeed:(NSData *_Nonnull)seed; -- (NSString *_Nullable)privateKeyAddressForAddress:(NSString *)address fromSeed:(NSData *)seed; +- (DMaybeOpaqueKey *_Nullable)privateKeyForAddress:(NSString *_Nonnull)address + fromSeed:(NSData *_Nonnull)seed; +- (NSString *_Nullable)privateKeyAddressForAddress:(NSString *)address + fromSeed:(NSData *)seed; //generate a random Mnemonic seed + (NSString *_Nullable)generateRandomSeedPhrase; @@ -222,66 +209,41 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSWalletBalanceDidChangeNotification; //Recreate derivation paths and addresses - (void)reloadDerivationPaths; -- (void)unregisterBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity; -- (void)addBlockchainIdentities:(NSArray *)blockchainIdentities; -- (void)addBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity; - -//Verify makes sure the keys for the blockchain identity are good -- (BOOL)registerBlockchainIdentities:(NSArray *)blockchainIdentities verify:(BOOL)verify; -- (BOOL)registerBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity verify:(BOOL)verify; -- (BOOL)registerBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity; -- (BOOL)containsBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity; - -- (void)unregisterBlockchainInvitation:(DSBlockchainInvitation *)blockchainInvitation; -- (void)addBlockchainInvitation:(DSBlockchainInvitation *)blockchainInvitation; -- (void)registerBlockchainInvitation:(DSBlockchainInvitation *)blockchainInvitation; -- (BOOL)containsBlockchainInvitation:(DSBlockchainInvitation *)blockchainInvitation; - -- (DSBlockchainIdentity *)createBlockchainIdentity; -- (DSBlockchainIdentity *)createBlockchainIdentityUsingDerivationIndex:(uint32_t)index; -- (DSBlockchainIdentity *)createBlockchainIdentityForUsername:(NSString *_Nullable)username; -- (DSBlockchainIdentity *)createBlockchainIdentityForUsername:(NSString *_Nullable)username usingDerivationIndex:(uint32_t)index; - -- (DSBlockchainInvitation *)createBlockchainInvitation; -- (DSBlockchainInvitation *)createBlockchainInvitationUsingDerivationIndex:(uint32_t)index; - -- (DSBlockchainIdentity *_Nullable)blockchainIdentityThatCreatedContract:(DPContract *)contract withContractId:(UInt256)contractId; - -- (DSBlockchainIdentity *_Nullable)blockchainIdentityForUniqueId:(UInt256)uniqueId; - -- (DSBlockchainInvitation *_Nullable)blockchainInvitationForUniqueId:(UInt256)uniqueId; -- (void)seedWithPrompt:(NSString *_Nullable)authprompt forAmount:(uint64_t)amount completion:(_Nullable SeedCompletionBlock)completion; +- (void)seedWithPrompt:(NSString *_Nullable)authprompt + forAmount:(uint64_t)amount + completion:(_Nullable SeedCompletionBlock)completion; -- (void)copyForChain:(DSChain *)chain completion:(void (^_Nonnull)(DSWallet *_Nullable copiedWallet))completion; +- (void)copyForChain:(DSChain *)chain + completion:(void (^_Nonnull)(DSWallet *_Nullable copiedWallet))completion; - (void)registerMasternodeOperator:(DSLocalMasternode *)masternode; //will use indexes -- (void)registerMasternodeOperator:(DSLocalMasternode *)masternode withOperatorPublicKey:(OpaqueKey *)operatorKey; //will use defined key +- (void)registerMasternodeOperator:(DSLocalMasternode *)masternode + withOperatorPublicKey:(DOpaqueKey *)operatorKey; //will use defined key - (void)registerMasternodeOwner:(DSLocalMasternode *)masternode; -- (void)registerMasternodeOwner:(DSLocalMasternode *)masternode withOwnerPrivateKey:(OpaqueKey *)ownerKey; //will use defined key +- (void)registerMasternodeOwner:(DSLocalMasternode *)masternode + withOwnerPrivateKey:(DOpaqueKey *)ownerKey; //will use defined key - (void)registerMasternodeVoter:(DSLocalMasternode *)masternode; -- (void)registerMasternodeVoter:(DSLocalMasternode *)masternode withVotingKey:(OpaqueKey *)votingKey; //will use defined key +- (void)registerMasternodeVoter:(DSLocalMasternode *)masternode + withVotingKey:(DOpaqueKey *)votingKey; //will use defined key - (void)registerPlatformNode:(DSLocalMasternode *)masternode; -- (void)registerPlatformNode:(DSLocalMasternode *)masternode withKey:(OpaqueKey *)key; //will use defined key +- (void)registerPlatformNode:(DSLocalMasternode *)masternode + withKey:(DOpaqueKey *)key; //will use defined key -- (BOOL)containsProviderVotingAuthenticationHash:(UInt160)votingAuthenticationHash; -- (BOOL)containsProviderOwningAuthenticationHash:(UInt160)owningAuthenticationHash; +- (BOOL)containsProviderVotingAuthenticationHash:(UInt160)hash; +- (BOOL)containsProviderOwningAuthenticationHash:(UInt160)hash; - (BOOL)containsProviderOperatorAuthenticationKey:(UInt384)providerOperatorAuthenticationKey; -- (BOOL)containsBlockchainIdentityBLSAuthenticationHash:(UInt160)blockchainIdentityAuthenticationHash; +- (BOOL)containsIdentityBLSAuthenticationHash:(UInt160)hash; - (BOOL)containsHoldingAddress:(NSString *)holdingAddress; -- (NSUInteger)indexOfProviderVotingAuthenticationHash:(UInt160)votingAuthenticationHash; -- (NSUInteger)indexOfProviderOwningAuthenticationHash:(UInt160)owningAuthenticationHash; +- (NSUInteger)indexOfProviderVotingAuthenticationHash:(UInt160)hash; +- (NSUInteger)indexOfProviderOwningAuthenticationHash:(UInt160)hash; - (NSUInteger)indexOfProviderOperatorAuthenticationKey:(UInt384)providerOperatorAuthenticationKey; -- (NSUInteger)indexOfPlatformNodeAuthenticationHash:(UInt160)platformNodeAuthenticationHash; +- (NSUInteger)indexOfPlatformNodeAuthenticationHash:(UInt160)hash; - (NSUInteger)indexOfHoldingAddress:(NSString *)holdingAddress; -- (NSUInteger)indexOfBlockchainIdentityAuthenticationHash:(UInt160)blockchainIdentityAuthenticationHash; -- (NSUInteger)indexOfBlockchainIdentityCreditFundingRegistrationHash:(UInt160)creditFundingRegistrationHash; -- (NSUInteger)indexOfBlockchainIdentityCreditFundingTopupHash:(UInt160)creditFundingTopupHash; -- (NSUInteger)indexOfBlockchainIdentityCreditFundingInvitationHash:(UInt160)creditFundingInvitationHash; @end diff --git a/DashSync/shared/Models/Wallet/DSWallet.m b/DashSync/shared/Models/Wallet/DSWallet.m index 9eee22251..e50cb2bbf 100644 --- a/DashSync/shared/Models/Wallet/DSWallet.m +++ b/DashSync/shared/Models/Wallet/DSWallet.m @@ -23,41 +23,22 @@ // THE SOFTWARE. #import "DSAccount.h" -#import "DSAccountEntity+CoreDataClass.h" -#import "DSAddressEntity+CoreDataProperties.h" +#import "DSAssetLockDerivationPath+Protected.h" #import "DSAuthenticationKeysDerivationPath+Protected.h" #import "DSAuthenticationManager+Private.h" -#import "DSAuthenticationManager.h" -#import "DSBIP39Mnemonic.h" -#import "DSBlockchainIdentity+Protected.h" -#import "DSBlockchainIdentityEntity+CoreDataClass.h" -#import "DSBlockchainIdentityKeyPathEntity+CoreDataClass.h" -#import "DSBlockchainIdentityRegistrationTransition.h" -#import "DSBlockchainIdentityUpdateTransition.h" -#import "DSBlockchainIdentityUsernameEntity+CoreDataClass.h" -#import "DSBlockchainInvitation+Protected.h" -#import "DSChain+Protected.h" +#import "DSChain+Params.h" +#import "DSChain+Wallet.h" #import "DSChainsManager.h" -#import "DSCreditFundingDerivationPath+Protected.h" -#import "DSCreditFundingTransaction.h" -#import "DSCreditFundingTransactionEntity+CoreDataClass.h" -#import "DSDashpayUserEntity+CoreDataClass.h" -#import "DSDerivationPathEntity+CoreDataClass.h" #import "DSDerivationPathFactory.h" -#import "DSEnvironment.h" -#import "DSFriendRequestEntity+CoreDataClass.h" -#import "DSIncomingFundsDerivationPath.h" #import "DSLocalMasternode.h" #import "DSMasternodeHoldingsDerivationPath+Protected.h" #import "DSOptionsManager.h" -#import "DSPriceManager.h" #import "DSProviderRegistrationTransaction.h" #import "DSSpecialTransactionsWalletHolder.h" -#import "DSTransactionEntity+CoreDataProperties.h" +#import "DSWallet+Identity.h" +#import "DSWallet+Invitation.h" #import "DSWallet+Protected.h" -#import "NSData+Dash.h" #import "NSDate+Utils.h" -#import "NSManagedObject+Sugar.h" #import "NSMutableData+Dash.h" #define SEED_ENTROPY_LENGTH (128 / 8) @@ -66,8 +47,6 @@ #define AUTH_PRIVKEY_KEY @"authprivkey" #define WALLET_MNEMONIC_KEY @"WALLET_MNEMONIC_KEY" #define WALLET_MASTER_PUBLIC_KEY @"WALLET_MASTER_PUBLIC_KEY" -#define WALLET_BLOCKCHAIN_USERS_KEY @"WALLET_BLOCKCHAIN_USERS_KEY" -#define WALLET_BLOCKCHAIN_INVITATIONS_KEY @"WALLET_BLOCKCHAIN_INVITATIONS_KEY" #define WALLET_ACCOUNTS_KNOWN_KEY @"WALLET_ACCOUNTS_KNOWN_KEY" @@ -79,9 +58,6 @@ #define VERIFIED_WALLET_CREATION_TIME_KEY @"VERIFIED_WALLET_CREATION_TIME" #define REFERENCE_DATE_2001 978307200 -#define IDENTITY_INDEX_KEY @"IDENTITY_INDEX_KEY" -#define IDENTITY_LOCKED_OUTPUT_KEY @"IDENTITY_LOCKED_OUTPUT_KEY" - @interface DSWallet () { NSTimeInterval _lGuessedWalletCreationTime; } @@ -105,8 +81,6 @@ @interface DSWallet () { @property (nonatomic, strong) NSMutableDictionary *mPlatformNodeKeyLocations; @property (nonatomic, assign, getter=isTransient) BOOL transient; -@property (nonatomic, strong) NSMutableDictionary *mBlockchainIdentities; -@property (nonatomic, strong) NSMutableDictionary *mBlockchainInvitations; @end @@ -163,7 +137,7 @@ - (instancetype)initWithChain:(DSChain *)chain { self.transient = FALSE; self.mAccounts = [NSMutableDictionary dictionary]; self.chain = chain; - self.mBlockchainIdentities = [NSMutableDictionary dictionary]; +// self.mIdentities = [NSMutableDictionary dictionary]; self.mMasternodeOwnerIndexes = [NSMutableDictionary dictionary]; self.mMasternodeVoterIndexes = [NSMutableDictionary dictionary]; self.mMasternodeOperatorIndexes = [NSMutableDictionary dictionary]; @@ -173,6 +147,7 @@ - (instancetype)initWithChain:(DSChain *)chain { self.checkedWalletCreationTime = NO; self.checkedGuessedWalletCreationTime = NO; self.checkedVerifyWalletCreationTime = NO; + [self setup]; return self; } @@ -209,11 +184,8 @@ - (instancetype)initWithUniqueID:(NSString *)uniqueID andAccounts:(NSArrayok) { + char *c_string = dash_spv_crypto_keys_key_OpaqueKey_address_with_public_key_data(result->ok, self.chain.chainType); + if (c_string) { + keyAddress = [NSString stringWithCString:(const char *)c_string encoding:NSUTF8StringEncoding]; + str_destroy(c_string); + } + } + DMaybeOpaqueKeyDtor(result); + } + return keyAddress; } - (void)reloadDerivationPaths { @@ -1132,8 +1027,8 @@ - (void)wipeBlockchainInfoInContext:(NSManagedObjectContext *)context { [account wipeBlockchainInfo]; } [self.specialTransactionsHolder removeAllTransactions]; - [self wipeBlockchainIdentitiesInContext:context]; - [self wipeBlockchainInvitationsInContext:context]; + [self wipeIdentitiesInContext:context]; + [self wipeInvitationsInContext:context]; } - (void)wipeBlockchainExtraAccountsInContext:(NSManagedObjectContext *)context { @@ -1147,416 +1042,6 @@ - (void)wipeBlockchainExtraAccountsInContext:(NSManagedObjectContext *)context { } } -// MARK: - Blockchain Identities - -- (NSArray *)blockchainIdentityAddresses { - DSAuthenticationKeysDerivationPath *derivationPath = [[DSDerivationPathFactory sharedInstance] blockchainIdentityBLSKeysDerivationPathForWallet:self]; - if (!derivationPath.hasExtendedPublicKey) return @[]; - return [derivationPath addressesToIndex:[self unusedBlockchainIdentityIndex] + 10 useCache:YES addToCache:YES]; -} - -- (void)unregisterBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity { - NSParameterAssert(blockchainIdentity); - NSAssert(blockchainIdentity.wallet == self, @"the blockchainIdentity you are trying to remove is not in this wallet"); - - [self.mBlockchainIdentities removeObjectForKey:blockchainIdentity.uniqueIDData]; - NSError *error = nil; - NSMutableDictionary *keyChainDictionary = [getKeychainDict(self.walletBlockchainIdentitiesKey, @[[NSNumber class], [NSData class]], &error) mutableCopy]; - if (!keyChainDictionary) keyChainDictionary = [NSMutableDictionary dictionary]; - [keyChainDictionary removeObjectForKey:blockchainIdentity.uniqueIDData]; - setKeychainDict(keyChainDictionary, self.walletBlockchainIdentitiesKey, NO); -} - -- (void)addBlockchainIdentities:(NSArray *)blockchainIdentities { - for (DSBlockchainIdentity *identity in blockchainIdentities) { - [self addBlockchainIdentity:identity]; - } -} - -- (void)addBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity { - NSParameterAssert(blockchainIdentity); - NSAssert(uint256_is_not_zero(blockchainIdentity.uniqueID), @"The blockchain identity unique ID must be set"); - [self.mBlockchainIdentities setObject:blockchainIdentity forKey:blockchainIdentity.uniqueIDData]; -} - -- (BOOL)containsBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity { - if (blockchainIdentity.lockedOutpointData) { - return ([self.mBlockchainIdentities objectForKey:blockchainIdentity.uniqueIDData] != nil); - } else { - return FALSE; - } -} - -- (BOOL)registerBlockchainIdentities:(NSArray *)blockchainIdentities verify:(BOOL)verify { - for (DSBlockchainIdentity *identity in blockchainIdentities) { - BOOL success = [self registerBlockchainIdentity:identity verify:verify]; - if (!success) { - return FALSE; - } - } - return TRUE; -} - -- (BOOL)registerBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity { - return [self registerBlockchainIdentity:blockchainIdentity verify:NO]; -} - -- (BOOL)registerBlockchainIdentity:(DSBlockchainIdentity *)blockchainIdentity verify:(BOOL)verify { - NSParameterAssert(blockchainIdentity); - if (verify) { - BOOL verified = [blockchainIdentity verifyKeysForWallet:self]; - if (!verified) { - blockchainIdentity.isLocal = FALSE; - return FALSE; - } - } - - if ([self.mBlockchainIdentities objectForKey:blockchainIdentity.uniqueIDData] == nil) { - [self addBlockchainIdentity:blockchainIdentity]; - } - NSError *error = nil; - NSMutableDictionary *keyChainDictionary = [getKeychainDict(self.walletBlockchainIdentitiesKey, @[[NSNumber class], [NSData class]], &error) mutableCopy]; - - if (error) return FALSE; - - if (!keyChainDictionary) keyChainDictionary = [NSMutableDictionary dictionary]; - - NSAssert(uint256_is_not_zero(blockchainIdentity.uniqueID), @"registrationTransactionHashData must not be null"); - if (uint256_is_zero(blockchainIdentity.lockedOutpointData.transactionOutpoint.hash)) { - keyChainDictionary[blockchainIdentity.uniqueIDData] = @{IDENTITY_INDEX_KEY: @(blockchainIdentity.index)}; - } else { - keyChainDictionary[blockchainIdentity.uniqueIDData] = @{IDENTITY_INDEX_KEY: @(blockchainIdentity.index), IDENTITY_LOCKED_OUTPUT_KEY: blockchainIdentity.lockedOutpointData}; - } - setKeychainDict(keyChainDictionary, self.walletBlockchainIdentitiesKey, NO); - - if (!_defaultBlockchainIdentity && (blockchainIdentity.index == 0)) { - _defaultBlockchainIdentity = blockchainIdentity; - } - return TRUE; -} - -- (void)wipeBlockchainIdentitiesInContext:(NSManagedObjectContext *)context { - for (DSBlockchainIdentity *blockchainIdentity in [_mBlockchainIdentities allValues]) { - [self unregisterBlockchainIdentity:blockchainIdentity]; - [blockchainIdentity deletePersistentObjectAndSave:NO inContext:context]; - } - _defaultBlockchainIdentity = nil; -} - -- (DSBlockchainIdentity *_Nullable)blockchainIdentityThatCreatedContract:(DPContract *)contract withContractId:(UInt256)contractId { - NSParameterAssert(contract); - NSAssert(uint256_is_not_zero(contractId), @"contractId must not be null"); - DSBlockchainIdentity *foundBlockchainIdentity = nil; - for (DSBlockchainIdentity *blockchainIdentity in [_mBlockchainIdentities allValues]) { - if (uint256_eq([contract contractIdIfRegisteredByBlockchainIdentity:blockchainIdentity], contractId)) { - foundBlockchainIdentity = blockchainIdentity; - } - } - return foundBlockchainIdentity; -} - -- (DSBlockchainIdentity *)blockchainIdentityForUniqueId:(UInt256)uniqueId { - NSAssert(uint256_is_not_zero(uniqueId), @"uniqueId must not be null"); - DSBlockchainIdentity *foundBlockchainIdentity = nil; - for (DSBlockchainIdentity *blockchainIdentity in [_mBlockchainIdentities allValues]) { - if (uint256_eq([blockchainIdentity uniqueID], uniqueId)) { - foundBlockchainIdentity = blockchainIdentity; - } - } - return foundBlockchainIdentity; -} - -- (uint32_t)blockchainIdentitiesCount { - return (uint32_t)[self.mBlockchainIdentities count]; -} - -- (BOOL)upgradeIdentityKeyChain { - NSError *error = nil; - NSMutableDictionary *keyChainDictionary = [getKeychainDict(self.walletBlockchainIdentitiesKey, @[[NSNumber class], [NSData class]], &error) mutableCopy]; - NSAssert(error == nil, @"There should be no error during upgrade"); - if (error) return FALSE; - NSMutableDictionary *updatedKeyChainDictionary = [NSMutableDictionary dictionary]; - for (NSData *blockchainIdentityLockedOutpoint in keyChainDictionary) { - NSData *uniqueIdData = uint256_data([blockchainIdentityLockedOutpoint SHA256_2]); - [updatedKeyChainDictionary setObject:@{IDENTITY_INDEX_KEY: keyChainDictionary[blockchainIdentityLockedOutpoint], IDENTITY_LOCKED_OUTPUT_KEY: blockchainIdentityLockedOutpoint} forKey:uniqueIdData]; - } - setKeychainDict(updatedKeyChainDictionary, self.walletBlockchainIdentitiesKey, NO); - return TRUE; -} - - -//This loads all the identities that the wallet knows about. If the app was deleted and reinstalled the identity information will remain from the keychain but must be reaquired from the network. -- (NSMutableDictionary *)blockchainIdentities { - //setKeychainDict(@{}, self.walletBlockchainIdentitiesKey, NO); - if (_mBlockchainIdentities) return _mBlockchainIdentities; - NSError *error = nil; - NSMutableDictionary *keyChainDictionary = [getKeychainDict(self.walletBlockchainIdentitiesKey, @[[NSNumber class], [NSData class]], &error) mutableCopy]; - if (error) { - return nil; - } - uint64_t defaultIndex = getKeychainInt(self.walletBlockchainIdentitiesDefaultIndexKey, &error); - if (error) { - return nil; - } - NSMutableDictionary *rDictionary = [NSMutableDictionary dictionary]; - - if (keyChainDictionary && keyChainDictionary.count) { - if ([[[keyChainDictionary allValues] firstObject] isKindOfClass:[NSNumber class]]) { - BOOL upgraded = [self upgradeIdentityKeyChain]; - if (!upgraded) { - return nil; - } else { - return (NSMutableDictionary *) [self blockchainIdentities]; - } - } - for (NSData *uniqueIdData in keyChainDictionary) { - uint32_t index = [[keyChainDictionary[uniqueIdData] objectForKey:IDENTITY_INDEX_KEY] unsignedIntValue]; - //DSLogPrivate(@"Blockchain identity unique Id is %@",uint256_hex(blockchainIdentityUniqueId)); - // UInt256 lastTransitionHash = [self.specialTransactionsHolder lastSubscriptionTransactionHashForRegistrationTransactionHash:registrationTransactionHash]; - // DSLogPrivate(@"reg %@ last %@",uint256_hex(registrationTransactionHash),uint256_hex(lastTransitionHash)); - // DSBlockchainIdentityRegistrationTransition * blockchainIdentityRegistrationTransaction = [self blockchainIdentityRegistrationTransactionForIndex:index]; - - //either the identity is known in core data (and we can pull it) or the wallet has been wiped and we need to get it from DAPI (the unique Id was saved in the keychain, so we don't need to resync) - //TODO: get the identity from core data - - NSManagedObjectContext *context = [NSManagedObjectContext chainContext]; //shouldn't matter what context is used - - [context performBlockAndWait:^{ - NSUInteger blockchainIdentityEntitiesCount = [DSBlockchainIdentityEntity countObjectsInContext:context matching:@"chain == %@ && isLocal == TRUE", [self.chain chainEntityInContext:context]]; - if (blockchainIdentityEntitiesCount != keyChainDictionary.count) { - DSLog(@"[%@] Unmatching blockchain entities count", self.chain.name); - } - DSBlockchainIdentityEntity *blockchainIdentityEntity = [DSBlockchainIdentityEntity anyObjectInContext:context matching:@"uniqueID == %@", uniqueIdData]; - DSBlockchainIdentity *blockchainIdentity = nil; - NSData *lockedOutpointData = [keyChainDictionary[uniqueIdData] objectForKey:IDENTITY_LOCKED_OUTPUT_KEY]; - if (blockchainIdentityEntity) { - if (lockedOutpointData) { - blockchainIdentity = [[DSBlockchainIdentity alloc] initAtIndex:index withLockedOutpoint:lockedOutpointData.transactionOutpoint inWallet:self withBlockchainIdentityEntity:blockchainIdentityEntity]; - } else { - blockchainIdentity = [[DSBlockchainIdentity alloc] initAtIndex:index withUniqueId:uniqueIdData.UInt256 inWallet:self withBlockchainIdentityEntity:blockchainIdentityEntity]; - } - } else if (lockedOutpointData) { - //No blockchain identity is known in core data - NSData *transactionHashData = uint256_data(uint256_reverse(lockedOutpointData.transactionOutpoint.hash)); - DSTransactionEntity *creditRegitrationTransactionEntity = [DSTransactionEntity anyObjectInContext:context matching:@"transactionHash.txHash == %@", transactionHashData]; - if (creditRegitrationTransactionEntity) { - //The registration funding transaction exists - //Weird but we should recover in this situation - DSCreditFundingTransaction *registrationTransaction = (DSCreditFundingTransaction *)[creditRegitrationTransactionEntity transactionForChain:self.chain]; - - BOOL correctIndex = [registrationTransaction checkDerivationPathIndexForWallet:self isIndex:index]; - if (!correctIndex) { - NSAssert(FALSE, @"We should implement this"); - } else { - blockchainIdentity = [[DSBlockchainIdentity alloc] initAtIndex:index withFundingTransaction:registrationTransaction withUsernameDictionary:nil inWallet:self]; - [blockchainIdentity registerInWallet]; - } - } else { - //We also don't have the registration funding transaction - blockchainIdentity = [[DSBlockchainIdentity alloc] initAtIndex:index withUniqueId:uniqueIdData.UInt256 inWallet:self]; - [blockchainIdentity registerInWalletForBlockchainIdentityUniqueId:uniqueIdData.UInt256]; - } - } else { - blockchainIdentity = [[DSBlockchainIdentity alloc] initAtIndex:index withUniqueId:uniqueIdData.UInt256 inWallet:self]; - [blockchainIdentity registerInWalletForBlockchainIdentityUniqueId:uniqueIdData.UInt256]; - } - if (blockchainIdentity) { - rDictionary[uniqueIdData] = blockchainIdentity; - if (index == defaultIndex) { - _defaultBlockchainIdentity = blockchainIdentity; - } - } - }]; - } - } - _mBlockchainIdentities = rDictionary; - return _mBlockchainIdentities; -} - -- (void)setDefaultBlockchainIdentity:(DSBlockchainIdentity *)defaultBlockchainIdentity { - if (![[self.blockchainIdentities allValues] containsObject:defaultBlockchainIdentity]) return; - _defaultBlockchainIdentity = defaultBlockchainIdentity; - setKeychainInt(defaultBlockchainIdentity.index, self.walletBlockchainIdentitiesDefaultIndexKey, NO); -} - -- (uint32_t)unusedBlockchainIdentityIndex { - NSArray *blockchainIdentities = [_mBlockchainIdentities allValues]; - NSNumber *max = [blockchainIdentities valueForKeyPath:@"index.@max.intValue"]; - return max != nil ? ([max unsignedIntValue] + 1) : 0; -} - -- (DSBlockchainIdentity *)createBlockchainIdentity { - DSBlockchainIdentity *blockchainIdentity = [[DSBlockchainIdentity alloc] initAtIndex:[self unusedBlockchainIdentityIndex] inWallet:self]; - return blockchainIdentity; -} - -- (DSBlockchainIdentity *)createBlockchainIdentityUsingDerivationIndex:(uint32_t)index { - DSBlockchainIdentity *blockchainIdentity = [[DSBlockchainIdentity alloc] initAtIndex:index inWallet:self]; - return blockchainIdentity; -} - -- (DSBlockchainIdentity *)createBlockchainIdentityForUsername:(NSString *)username { - DSBlockchainIdentity *blockchainIdentity = [self createBlockchainIdentity]; - [blockchainIdentity addDashpayUsername:username save:NO]; - return blockchainIdentity; -} - -- (DSBlockchainIdentity *)createBlockchainIdentityForUsername:(NSString *)username usingDerivationIndex:(uint32_t)index { - DSBlockchainIdentity *blockchainIdentity = [self createBlockchainIdentityUsingDerivationIndex:index]; - [blockchainIdentity addDashpayUsername:username save:NO]; - return blockchainIdentity; -} - -// MARK: - Invitations - - -- (uint32_t)blockchainInvitationsCount { - return (uint32_t)[self.mBlockchainInvitations count]; -} - - -//This loads all the identities that the wallet knows about. If the app was deleted and reinstalled the identity information will remain from the keychain but must be reaquired from the network. -- (NSMutableDictionary *)blockchainInvitations { - //setKeychainDict(@{}, self.walletBlockchainInvitationsKey, NO); - if (_mBlockchainInvitations) return _mBlockchainInvitations; - NSError *error = nil; - NSMutableDictionary *keyChainDictionary = [getKeychainDict(self.walletBlockchainInvitationsKey, @[[NSNumber class], [NSData class]], &error) mutableCopy]; - if (error) { - return nil; - } - NSMutableDictionary *rDictionary = [NSMutableDictionary dictionary]; - - if (keyChainDictionary) { - for (NSData *blockchainInvitationLockedOutpointData in keyChainDictionary) { - uint32_t index = [keyChainDictionary[blockchainInvitationLockedOutpointData] unsignedIntValue]; - DSUTXO blockchainInvitationLockedOutpoint = blockchainInvitationLockedOutpointData.transactionOutpoint; - //DSLogPrivate(@"Blockchain identity unique Id is %@",uint256_hex(blockchainInvitationUniqueId)); - // UInt256 lastTransitionHash = [self.specialTransactionsHolder lastSubscriptionTransactionHashForRegistrationTransactionHash:registrationTransactionHash]; - // DSLogPrivate(@"reg %@ last %@",uint256_hex(registrationTransactionHash),uint256_hex(lastTransitionHash)); - // DSBlockchainInvitationRegistrationTransition * blockchainInvitationRegistrationTransaction = [self blockchainInvitationRegistrationTransactionForIndex:index]; - - //either the identity is known in core data (and we can pull it) or the wallet has been wiped and we need to get it from DAPI (the unique Id was saved in the keychain, so we don't need to resync) - //TODO: get the identity from core data - - NSManagedObjectContext *context = [NSManagedObjectContext chainContext]; //shouldn't matter what context is used - - [context performBlockAndWait:^{ - NSUInteger blockchainInvitationEntitiesCount = [DSBlockchainInvitationEntity countObjectsInContext:context matching:@"chain == %@", [self.chain chainEntityInContext:context]]; - if (blockchainInvitationEntitiesCount != keyChainDictionary.count) { - DSLog(@"[%@] Unmatching blockchain invitations count", self.chain.name); - } - DSBlockchainInvitationEntity *blockchainInvitationEntity = [DSBlockchainInvitationEntity anyObjectInContext:context matching:@"blockchainIdentity.uniqueID == %@", uint256_data([dsutxo_data(blockchainInvitationLockedOutpoint) SHA256_2])]; - DSBlockchainInvitation *blockchainInvitation = nil; - if (blockchainInvitationEntity) { - blockchainInvitation = [[DSBlockchainInvitation alloc] initAtIndex:index withLockedOutpoint:blockchainInvitationLockedOutpoint inWallet:self withBlockchainInvitationEntity:blockchainInvitationEntity]; - } else { - //No blockchain identity is known in core data - NSData *transactionHashData = uint256_data(uint256_reverse(blockchainInvitationLockedOutpoint.hash)); - DSTransactionEntity *creditRegitrationTransactionEntity = [DSTransactionEntity anyObjectInContext:context matching:@"transactionHash.txHash == %@", transactionHashData]; - if (creditRegitrationTransactionEntity) { - //The registration funding transaction exists - //Weird but we should recover in this situation - DSCreditFundingTransaction *registrationTransaction = (DSCreditFundingTransaction *)[creditRegitrationTransactionEntity transactionForChain:self.chain]; - - BOOL correctIndex = [registrationTransaction checkInvitationDerivationPathIndexForWallet:self isIndex:index]; - if (!correctIndex) { - NSAssert(FALSE, @"We should implement this"); - } else { - blockchainInvitation = [[DSBlockchainInvitation alloc] initAtIndex:index withFundingTransaction:registrationTransaction inWallet:self]; - [blockchainInvitation registerInWallet]; - } - } else { - //We also don't have the registration funding transaction - blockchainInvitation = [[DSBlockchainInvitation alloc] initAtIndex:index withLockedOutpoint:blockchainInvitationLockedOutpoint inWallet:self]; - [blockchainInvitation registerInWalletForBlockchainIdentityUniqueId:[dsutxo_data(blockchainInvitationLockedOutpoint) SHA256_2]]; - } - } - if (blockchainInvitation) { - rDictionary[blockchainInvitationLockedOutpointData] = blockchainInvitation; - } - }]; - } - } - _mBlockchainInvitations = rDictionary; - return _mBlockchainInvitations; -} - -- (DSBlockchainInvitation *)blockchainInvitationForUniqueId:(UInt256)uniqueId { - NSAssert(uint256_is_not_zero(uniqueId), @"uniqueId must not be null"); - DSBlockchainInvitation *foundBlockchainInvitation = nil; - for (DSBlockchainInvitation *blockchainInvitation in [_mBlockchainInvitations allValues]) { - if (uint256_eq([blockchainInvitation.identity uniqueID], uniqueId)) { - foundBlockchainInvitation = blockchainInvitation; - } - } - return foundBlockchainInvitation; -} - -- (uint32_t)unusedBlockchainInvitationIndex { - NSArray *blockchainInvitations = [_mBlockchainInvitations allValues]; - NSNumber *max = [blockchainInvitations valueForKeyPath:@"identity.index.@max.intValue"]; - return max != nil ? ([max unsignedIntValue] + 1) : 0; -} - -- (DSBlockchainInvitation *)createBlockchainInvitation { - DSBlockchainInvitation *blockchainInvitation = [[DSBlockchainInvitation alloc] initAtIndex:[self unusedBlockchainInvitationIndex] inWallet:self]; - return blockchainInvitation; -} - -- (DSBlockchainInvitation *)createBlockchainInvitationUsingDerivationIndex:(uint32_t)index { - DSBlockchainInvitation *blockchainInvitation = [[DSBlockchainInvitation alloc] initAtIndex:index inWallet:self]; - return blockchainInvitation; -} - -- (void)unregisterBlockchainInvitation:(DSBlockchainInvitation *)blockchainInvitation { - NSParameterAssert(blockchainInvitation); - NSAssert(blockchainInvitation.wallet == self, @"the blockchainInvitation you are trying to remove is not in this wallet"); - NSAssert(blockchainInvitation.identity != nil, @"the blockchainInvitation you are trying to remove has no identity"); - - [self.mBlockchainInvitations removeObjectForKey:blockchainInvitation.identity.lockedOutpointData]; - NSError *error = nil; - NSMutableDictionary *keyChainDictionary = [getKeychainDict(self.walletBlockchainInvitationsKey, @[[NSNumber class], [NSData class]], &error) mutableCopy]; - if (!keyChainDictionary) keyChainDictionary = [NSMutableDictionary dictionary]; - [keyChainDictionary removeObjectForKey:blockchainInvitation.identity.lockedOutpointData]; - setKeychainDict(keyChainDictionary, self.walletBlockchainInvitationsKey, NO); -} - -- (void)addBlockchainInvitation:(DSBlockchainInvitation *)blockchainInvitation { - NSParameterAssert(blockchainInvitation); - [self.mBlockchainInvitations setObject:blockchainInvitation forKey:blockchainInvitation.identity.lockedOutpointData]; -} - -- (void)registerBlockchainInvitation:(DSBlockchainInvitation *)blockchainInvitation { - NSParameterAssert(blockchainInvitation); - NSAssert(blockchainInvitation.identity != nil, @"the blockchainInvitation you are trying to remove has no identity"); - - if ([self.mBlockchainInvitations objectForKey:blockchainInvitation.identity.lockedOutpointData] == nil) { - [self addBlockchainInvitation:blockchainInvitation]; - } - NSError *error = nil; - NSMutableDictionary *keyChainDictionary = [getKeychainDict(self.walletBlockchainInvitationsKey, @[[NSNumber class], [NSData class]], &error) mutableCopy]; - if (!keyChainDictionary) keyChainDictionary = [NSMutableDictionary dictionary]; - - NSAssert(uint256_is_not_zero(blockchainInvitation.identity.uniqueID), @"registrationTransactionHashData must not be null"); - keyChainDictionary[blockchainInvitation.identity.lockedOutpointData] = @(blockchainInvitation.identity.index); - setKeychainDict(keyChainDictionary, self.walletBlockchainInvitationsKey, NO); -} - -- (BOOL)containsBlockchainInvitation:(DSBlockchainInvitation *)blockchainInvitation { - if (blockchainInvitation.identity.lockedOutpointData) { - return ([self.mBlockchainInvitations objectForKey:blockchainInvitation.identity.lockedOutpointData] != nil); - } else { - return FALSE; - } -} - -- (void)wipeBlockchainInvitationsInContext:(NSManagedObjectContext *)context { - for (DSBlockchainInvitation *blockchainInvitation in [_mBlockchainInvitations allValues]) { - [self unregisterBlockchainInvitation:blockchainInvitation]; - [blockchainInvitation deletePersistentObjectAndSave:NO inContext:context]; - } -} - // MARK: - Masternodes (Providers) @@ -1620,7 +1105,7 @@ - (void)registerMasternodeOperator:(DSLocalMasternode *)masternode { } } -- (void)registerMasternodeOperator:(DSLocalMasternode *)masternode withOperatorPublicKey:(OpaqueKey *)operatorKey { +- (void)registerMasternodeOperator:(DSLocalMasternode *)masternode withOperatorPublicKey:(DOpaqueKey *)operatorKey { NSParameterAssert(masternode); if ([self.mMasternodeOperatorPublicKeyLocations objectForKey:uint256_data(masternode.providerRegistrationTransaction.txHash)] == nil) { NSData *publicKeyData = [DSKeyManager publicKeyData:operatorKey]; @@ -1648,7 +1133,7 @@ - (void)registerMasternodeOwner:(DSLocalMasternode *)masternode { } } -- (void)registerMasternodeOwner:(DSLocalMasternode *)masternode withOwnerPrivateKey:(OpaqueKey *)ownerKey { +- (void)registerMasternodeOwner:(DSLocalMasternode *)masternode withOwnerPrivateKey:(DOpaqueKey *)ownerKey { NSParameterAssert(masternode); if ([self.mMasternodeOwnerPrivateKeyLocations objectForKey:uint256_data(masternode.providerRegistrationTransaction.txHash)] == nil) { @@ -1677,7 +1162,7 @@ - (void)registerMasternodeVoter:(DSLocalMasternode *)masternode { } } -- (void)registerMasternodeVoter:(DSLocalMasternode *)masternode withVotingKey:(OpaqueKey *)votingKey { +- (void)registerMasternodeVoter:(DSLocalMasternode *)masternode withVotingKey:(DOpaqueKey *)votingKey { if ([self.mMasternodeVoterKeyLocations objectForKey:uint256_data(masternode.providerRegistrationTransaction.txHash)] == nil) { NSData *publicKeyData = [DSKeyManager publicKeyData:votingKey]; NSData *hashedVoterKey = [NSData dataWithUInt256:publicKeyData.SHA256]; @@ -1708,7 +1193,7 @@ - (void)registerPlatformNode:(DSLocalMasternode *)masternode { } } -- (void)registerPlatformNode:(DSLocalMasternode *)masternode withKey:(OpaqueKey *)key { +- (void)registerPlatformNode:(DSLocalMasternode *)masternode withKey:(DOpaqueKey *)key { NSParameterAssert(masternode); if ([self.mPlatformNodeKeyLocations objectForKey:uint256_data(masternode.providerRegistrationTransaction.txHash)] == nil) { @@ -1726,95 +1211,51 @@ - (void)registerPlatformNode:(DSLocalMasternode *)masternode withKey:(OpaqueKey } } -- (BOOL)containsProviderVotingAuthenticationHash:(UInt160)votingAuthenticationHash { - DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath providerVotingKeysDerivationPathForWallet:self]; - NSString *address = [DSKeyManager addressFromHash160:votingAuthenticationHash forChain:self.chain]; - return [derivationPath containsAddress:address]; +- (BOOL)containsProviderVotingAuthenticationHash:(UInt160)hash { + return [[DSAuthenticationKeysDerivationPath providerVotingKeysDerivationPathForWallet:self] containsAddressHash:hash]; } -- (BOOL)containsProviderOwningAuthenticationHash:(UInt160)owningAuthenticationHash { - DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath providerOwnerKeysDerivationPathForWallet:self]; - NSString *address = [DSKeyManager addressFromHash160:owningAuthenticationHash forChain:self.chain]; - return [derivationPath containsAddress:address]; +- (BOOL)containsProviderOwningAuthenticationHash:(UInt160)hash { + return [[DSAuthenticationKeysDerivationPath providerOwnerKeysDerivationPathForWallet:self] containsAddressHash:hash]; } - (BOOL)containsProviderOperatorAuthenticationKey:(UInt384)providerOperatorAuthenticationKey { - DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath providerOperatorKeysDerivationPathForWallet:self]; - NSString *address = [DSKeyManager addressFromHash160:[[NSData dataWithUInt384:providerOperatorAuthenticationKey] hash160] forChain:self.chain]; - return [derivationPath containsAddress:address]; + UInt160 hash = [[NSData dataWithUInt384:providerOperatorAuthenticationKey] hash160]; + return [[DSAuthenticationKeysDerivationPath providerOperatorKeysDerivationPathForWallet:self] containsAddressHash:hash]; } -- (BOOL)containsPlatformNodeAuthenticationHash:(UInt160)platformNodeAuthenticationHash { - DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath platformNodeKeysDerivationPathForWallet:self]; - NSString *address = [DSKeyManager addressFromHash160:platformNodeAuthenticationHash forChain:self.chain]; - return [derivationPath containsAddress:address]; +- (BOOL)containsPlatformNodeAuthenticationHash:(UInt160)hash { + return [[DSAuthenticationKeysDerivationPath platformNodeKeysDerivationPathForWallet:self] containsAddressHash:hash]; } -- (BOOL)containsBlockchainIdentityBLSAuthenticationHash:(UInt160)blockchainIdentityAuthenticationHash { - DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath blockchainIdentitiesBLSKeysDerivationPathForWallet:self]; - NSString *address = [DSKeyManager addressFromHash160:blockchainIdentityAuthenticationHash forChain:self.chain]; - return [derivationPath containsAddress:address]; +- (BOOL)containsIdentityBLSAuthenticationHash:(UInt160)hash { + return [[DSAuthenticationKeysDerivationPath identitiesBLSKeysDerivationPathForWallet:self] containsAddressHash:hash]; } - (BOOL)containsHoldingAddress:(NSString *)holdingAddress { NSParameterAssert(holdingAddress); - - DSMasternodeHoldingsDerivationPath *derivationPath = [DSMasternodeHoldingsDerivationPath providerFundsDerivationPathForWallet:self]; - return [derivationPath containsAddress:holdingAddress]; + return [[DSMasternodeHoldingsDerivationPath providerFundsDerivationPathForWallet:self] containsAddress:holdingAddress]; } -- (NSUInteger)indexOfProviderVotingAuthenticationHash:(UInt160)votingAuthenticationHash { - DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath providerVotingKeysDerivationPathForWallet:self]; - NSString *address = [DSKeyManager addressFromHash160:votingAuthenticationHash forChain:self.chain]; - return [derivationPath indexOfKnownAddress:address]; +- (NSUInteger)indexOfProviderVotingAuthenticationHash:(UInt160)hash { + return [[DSAuthenticationKeysDerivationPath providerVotingKeysDerivationPathForWallet:self] indexOfKnownAddressHash:hash]; } -- (NSUInteger)indexOfProviderOwningAuthenticationHash:(UInt160)owningAuthenticationHash { - DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath providerOwnerKeysDerivationPathForWallet:self]; - NSString *address = [DSKeyManager addressFromHash160:owningAuthenticationHash forChain:self.chain]; - return [derivationPath indexOfKnownAddress:address]; +- (NSUInteger)indexOfProviderOwningAuthenticationHash:(UInt160)hash { + return [[DSAuthenticationKeysDerivationPath providerOwnerKeysDerivationPathForWallet:self] indexOfKnownAddressHash:hash]; } - (NSUInteger)indexOfProviderOperatorAuthenticationKey:(UInt384)providerOperatorAuthenticationKey { - DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath providerOperatorKeysDerivationPathForWallet:self]; - NSString *address = [DSKeyManager addressFromHash160:[[NSData dataWithUInt384:providerOperatorAuthenticationKey] hash160] forChain:self.chain]; - return [derivationPath indexOfKnownAddress:address]; + return [[DSAuthenticationKeysDerivationPath providerOperatorKeysDerivationPathForWallet:self] indexOfKnownAddressHash:[[NSData dataWithUInt384:providerOperatorAuthenticationKey] hash160]]; } -- (NSUInteger)indexOfPlatformNodeAuthenticationHash:(UInt160)platformNodeAuthenticationHash { - DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath platformNodeKeysDerivationPathForWallet:self]; - NSString *address = [DSKeyManager addressFromHash160:platformNodeAuthenticationHash forChain:self.chain]; - return [derivationPath indexOfKnownAddress:address]; +- (NSUInteger)indexOfPlatformNodeAuthenticationHash:(UInt160)hash { + return [[DSAuthenticationKeysDerivationPath platformNodeKeysDerivationPathForWallet:self] indexOfKnownAddressHash:hash]; } - (NSUInteger)indexOfHoldingAddress:(NSString *)holdingAddress { NSParameterAssert(holdingAddress); - DSMasternodeHoldingsDerivationPath *derivationPath = [DSMasternodeHoldingsDerivationPath providerFundsDerivationPathForWallet:self]; - return [derivationPath indexOfKnownAddress:holdingAddress]; -} - -- (NSUInteger)indexOfBlockchainIdentityAuthenticationHash:(UInt160)blockchainIdentityAuthenticationHash { - DSAuthenticationKeysDerivationPath *derivationPath = [DSAuthenticationKeysDerivationPath blockchainIdentitiesBLSKeysDerivationPathForWallet:self]; - NSString *address = [DSKeyManager addressFromHash160:blockchainIdentityAuthenticationHash forChain:self.chain]; - return [derivationPath indexOfKnownAddress:address]; -} - -- (NSUInteger)indexOfBlockchainIdentityCreditFundingRegistrationHash:(UInt160)creditFundingRegistrationHash { - DSCreditFundingDerivationPath *derivationPath = [DSCreditFundingDerivationPath blockchainIdentityRegistrationFundingDerivationPathForWallet:self]; - NSString *address = [DSKeyManager addressFromHash160:creditFundingRegistrationHash forChain:self.chain]; - return [derivationPath indexOfKnownAddress:address]; -} - -- (NSUInteger)indexOfBlockchainIdentityCreditFundingTopupHash:(UInt160)creditFundingTopupHash { - DSCreditFundingDerivationPath *derivationPath = [DSCreditFundingDerivationPath blockchainIdentityTopupFundingDerivationPathForWallet:self]; - NSString *address = [DSKeyManager addressFromHash160:creditFundingTopupHash forChain:self.chain]; - return [derivationPath indexOfKnownAddress:address]; -} - -- (NSUInteger)indexOfBlockchainIdentityCreditFundingInvitationHash:(UInt160)creditFundingInvitationHash { - DSCreditFundingDerivationPath *derivationPath = [DSCreditFundingDerivationPath blockchainIdentityInvitationFundingDerivationPathForWallet:self]; - NSString *address = [DSKeyManager addressFromHash160:creditFundingInvitationHash forChain:self.chain]; - return [derivationPath indexOfKnownAddress:address]; + return [[DSMasternodeHoldingsDerivationPath providerFundsDerivationPathForWallet:self] indexOfKnownAddress:holdingAddress]; } @end diff --git a/DashSync/shared/Models/Wallet/DSWalletConstants.m b/DashSync/shared/Models/Wallet/DSWalletConstants.m index 8392923de..5582cc93d 100644 --- a/DashSync/shared/Models/Wallet/DSWalletConstants.m +++ b/DashSync/shared/Models/Wallet/DSWalletConstants.m @@ -79,30 +79,30 @@ NSString *const DSContractUpdateNotificationKey = @"DSContractUpdateNotificationKey"; -NSString *const DSBlockchainIdentityDidUpdateNotification = @"DSBlockchainIdentitiesDidUpdateNotification"; +NSString *const DSIdentityDidUpdateNotification = @"DSIdentitiesDidUpdateNotification"; -NSString *const DSBlockchainIdentityDidUpdateUsernameStatusNotification = @"DSBlockchainIdentityDidUpdateUsernameStatusNotification"; +NSString *const DSIdentityDidUpdateUsernameStatusNotification = @"DSIdentityDidUpdateUsernameStatusNotification"; -NSString *const DSBlockchainIdentityKey = @"DSBlockchainIdentityKey"; +NSString *const DSIdentityKey = @"DSIdentityKey"; -NSString *const DSBlockchainIdentityUsernameKey = @"DSBlockchainIdentityUsernameKey"; +NSString *const DSIdentityUsernameKey = @"DSIdentityUsernameKey"; -NSString *const DSBlockchainIdentityUsernameDomainKey = @"DSBlockchainIdentityUsernameDomainKey"; +NSString *const DSIdentityUsernameDomainKey = @"DSIdentityUsernameDomainKey"; -NSString *const DSBlockchainIdentityUpdateEvents = @"DSBlockchainIdentityUpdateEvents"; +NSString *const DSIdentityUpdateEvents = @"DSIdentityUpdateEvents"; -NSString *const DSBlockchainIdentityUpdateEventKeyUpdate = @"DSBlockchainIdentityUpdateEventKeyUpdate"; +NSString *const DSIdentityUpdateEventKeyUpdate = @"DSIdentityUpdateEventKeyUpdate"; -NSString *const DSBlockchainIdentityUpdateEventRegistration = @"DSBlockchainIdentityUpdateEventRegistration"; +NSString *const DSIdentityUpdateEventRegistration = @"DSIdentityUpdateEventRegistration"; -NSString *const DSBlockchainIdentityUpdateEventCreditBalance = @"DSBlockchainIdentityUpdateEventCreditBalance"; +NSString *const DSIdentityUpdateEventCreditBalance = @"DSIdentityUpdateEventCreditBalance"; -NSString *const DSBlockchainIdentityUpdateEventDashpaySyncronizationBlockHash = @"DSBlockchainIdentityUpdateEventDashpaySyncronizationBlockHash"; +NSString *const DSIdentityUpdateEventDashpaySyncronizationBlockHash = @"DSIdentityUpdateEventDashpaySyncronizationBlockHash"; -NSString *const DSBlockchainInvitationDidUpdateNotification = @"DSBlockchainInvitationDidUpdateNotification"; +NSString *const DSInvitationDidUpdateNotification = @"DSInvitationDidUpdateNotification"; -NSString *const DSBlockchainInvitationKey = @"DSBlockchainInvitationKey"; +NSString *const DSInvitationKey = @"DSInvitationKey"; -NSString *const DSBlockchainInvitationUpdateEvents = @"DSBlockchainInvitationUpdateEvents"; +NSString *const DSInvitationUpdateEvents = @"DSInvitationUpdateEvents"; -NSString *const DSBlockchainInvitationUpdateEventLink = @"DSBlockchainInvitationUpdateEventLink"; +NSString *const DSInvitationUpdateEventLink = @"DSInvitationUpdateEventLink"; diff --git a/Example/DashSync.xcodeproj/project.pbxproj b/Example/DashSync.xcodeproj/project.pbxproj index dc8216820..ff6ceeb6b 100644 --- a/Example/DashSync.xcodeproj/project.pbxproj +++ b/Example/DashSync.xcodeproj/project.pbxproj @@ -69,6 +69,7 @@ 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; 93099D7228E7275400160709 /* MNLIST_1746460.dat in Resources */ = {isa = PBXBuildFile; fileRef = 93099D7028E7275300160709 /* MNLIST_1746460.dat */; }; 93099D7328E7275400160709 /* MNL_1746460_1746516.dat in Resources */ = {isa = PBXBuildFile; fileRef = 93099D7128E7275400160709 /* MNL_1746460_1746516.dat */; }; + 9311F51D2D2082DB0039D778 /* DSNetworkActivityView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9311F51C2D2082DB0039D778 /* DSNetworkActivityView.m */; }; 937619A526E22E360023BE64 /* BackgroundTasks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 937619A326E22DCD0023BE64 /* BackgroundTasks.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; B8D9D66B2CB5A33300D289A9 /* libDashSync-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B8D9D66A2CB5A33300D289A9 /* libDashSync-macOS.a */; }; D233D44D64AE2AA230F44AA7 /* libPods-DashSync_Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DE90F593715182564EB9A7E8 /* libPods-DashSync_Tests.a */; }; @@ -77,9 +78,9 @@ FB0F15A521049432000272E6 /* DSTransactionStatusTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FB0F15A421049432000272E6 /* DSTransactionStatusTableViewCell.m */; }; FB0F15A821049505000272E6 /* DSTransactionAmountTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FB0F15A721049505000272E6 /* DSTransactionAmountTableViewCell.m */; }; FB0F15AB21049598000272E6 /* DSTransactionIdentifierTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FB0F15AA21049598000272E6 /* DSTransactionIdentifierTableViewCell.m */; }; - FB0F15B721090445000272E6 /* DSBlockchainIdentitiesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB0F15B621090445000272E6 /* DSBlockchainIdentitiesViewController.m */; }; - FB0F15BE210A0208000272E6 /* DSBlockchainIdentityTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FB0F15BD210A0208000272E6 /* DSBlockchainIdentityTableViewCell.m */; }; - FB0F15C1210ACD52000272E6 /* DSCreateBlockchainIdentityViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB0F15C0210ACD52000272E6 /* DSCreateBlockchainIdentityViewController.m */; }; + FB0F15B721090445000272E6 /* DSIdentitiesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB0F15B621090445000272E6 /* DSIdentitiesViewController.m */; }; + FB0F15BE210A0208000272E6 /* DSIdentityTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FB0F15BD210A0208000272E6 /* DSIdentityTableViewCell.m */; }; + FB0F15C1210ACD52000272E6 /* DSCreateIdentityViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB0F15C0210ACD52000272E6 /* DSCreateIdentityViewController.m */; }; FB0F15C4210AD9A1000272E6 /* DSWalletChooserViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB0F15C3210AD9A0000272E6 /* DSWalletChooserViewController.m */; }; FB12BDBF25239CD70027CD64 /* DSIdentityAuthenticationDerivationPathsAddressesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB12BDBD25239CD60027CD64 /* DSIdentityAuthenticationDerivationPathsAddressesViewController.m */; }; FB12FEE320CD3BD100C1E0F0 /* DSMasternodeTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FB12FEE220CD3BD100C1E0F0 /* DSMasternodeTableViewCell.m */; }; @@ -374,11 +375,11 @@ FB83C2E721B91A8D0057A0EF /* DSTransactionFloodingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB83C2E621B91A8D0057A0EF /* DSTransactionFloodingViewController.m */; }; FB85000D261A75700002E5EA /* DSInvitationDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB85000C261A75700002E5EA /* DSInvitationDetailViewController.m */; }; FB850013261A7B9A0002E5EA /* DSIdentityChooserViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB850012261A7B9A0002E5EA /* DSIdentityChooserViewController.m */; }; - FB850051261AA82F0002E5EA /* DSBlockchainIdentityChooserTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FB850050261AA82F0002E5EA /* DSBlockchainIdentityChooserTableViewCell.m */; }; + FB850051261AA82F0002E5EA /* DSIdentityChooserTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FB850050261AA82F0002E5EA /* DSIdentityChooserTableViewCell.m */; }; FB87A0CE24B7C28700C22DF7 /* DSMiningTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FB87A0CD24B7C28600C22DF7 /* DSMiningTests.m */; }; FB88ECB02649647500CDD987 /* DSInvitationsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FB88ECAF2649647400CDD987 /* DSInvitationsTests.m */; }; FB88ECB6264B8BB800CDD987 /* DSDAPIGetTransactionInformationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB88ECB5264B8BB800CDD987 /* DSDAPIGetTransactionInformationViewController.m */; }; - FB88ECBC264BFC7D00CDD987 /* DSCreateBlockchainIdentityFromInvitationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB88ECBB264BFC7D00CDD987 /* DSCreateBlockchainIdentityFromInvitationViewController.m */; }; + FB88ECBC264BFC7D00CDD987 /* DSCreateIdentityFromInvitationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB88ECBB264BFC7D00CDD987 /* DSCreateIdentityFromInvitationViewController.m */; }; FB89F2D722CC875800CD0232 /* MNL_0_1094976.dat in Resources */ = {isa = PBXBuildFile; fileRef = FB89F2D622CC875800CD0232 /* MNL_0_1094976.dat */; }; FB89F2D922CD82EB00CD0232 /* MNL_0_1095720.dat in Resources */ = {isa = PBXBuildFile; fileRef = FB89F2D822CD82EB00CD0232 /* MNL_0_1095720.dat */; }; FB90D2B420BD2BDD002B7956 /* DSSporksViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB90D2B320BD2BDD002B7956 /* DSSporksViewController.m */; }; @@ -398,8 +399,8 @@ FB97502725B3AFB000A1DCE7 /* DSTestnetSyncTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FB97502625B3AFAF00A1DCE7 /* DSTestnetSyncTests.m */; }; FB97502B25B3BC2D00A1DCE7 /* DSMainnetSyncTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FB97502A25B3BC2D00A1DCE7 /* DSMainnetSyncTests.m */; }; FB9762F724BC4C3E00CF28EC /* DSMiningViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9762F624BC4C3E00CF28EC /* DSMiningViewController.m */; }; - FB988321241D64FA00B39F02 /* DSBlockchainIdentityKeysViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB988320241D64FA00B39F02 /* DSBlockchainIdentityKeysViewController.m */; }; - FB988324241D6C3300B39F02 /* DSBlockchainIdentityKeyTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FB988323241D6C3300B39F02 /* DSBlockchainIdentityKeyTableViewCell.m */; }; + FB988321241D64FA00B39F02 /* DSIdentityKeysViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB988320241D64FA00B39F02 /* DSIdentityKeysViewController.m */; }; + FB988324241D6C3300B39F02 /* DSIdentityKeyTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FB988323241D6C3300B39F02 /* DSIdentityKeyTableViewCell.m */; }; FB995D9A21101C4200AC37D0 /* DSPeersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB995D9921101C4200AC37D0 /* DSPeersViewController.m */; }; FB995D9D21101C9600AC37D0 /* DSPeerTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FB995D9C21101C9600AC37D0 /* DSPeerTableViewCell.m */; }; FB9A106620D7A72D006883E2 /* DSAddressesExporterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9A106520D7A72D006883E2 /* DSAddressesExporterViewController.m */; }; @@ -429,7 +430,7 @@ FBB5D729221DF9390021764A /* DSUpdateMasternodeServiceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB5D728221DF9390021764A /* DSUpdateMasternodeServiceViewController.m */; }; FBB5D73C221FE5F40021764A /* DSUpdateMasternodeRegistrarViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB5D73B221FE5F40021764A /* DSUpdateMasternodeRegistrarViewController.m */; }; FBBDF423226B6E770081D673 /* DSBigNumberTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FBBDF422226B6E770081D673 /* DSBigNumberTests.m */; }; - FBBDF426226C843C0081D673 /* DSBlockchainIdentityTransitionsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBBDF425226C843C0081D673 /* DSBlockchainIdentityTransitionsViewController.m */; }; + FBBDF426226C843C0081D673 /* DSIdentityTransitionsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBBDF425226C843C0081D673 /* DSIdentityTransitionsViewController.m */; }; FBBDF429226C8ECA0081D673 /* DSTransitionTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FBBDF428226C8ECA0081D673 /* DSTransitionTableViewCell.m */; }; FBBDF42C226EDF600081D673 /* DSContactTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FBBDF42B226EDF600081D673 /* DSContactTableViewCell.m */; }; FBC2EAF8260F842B00B53BDB /* Invitations.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FBC2EAF7260F842B00B53BDB /* Invitations.storyboard */; }; @@ -437,12 +438,12 @@ FBC2EB08260F89A900B53BDB /* DSInvitationTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FBC2EB07260F89A900B53BDB /* DSInvitationTableViewCell.m */; }; FBC2EB0E260F8B4600B53BDB /* DSCreateInvitationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBC2EB0D260F8B4600B53BDB /* DSCreateInvitationViewController.m */; }; FBC508C62426F33D00EB3DAB /* SearchIdentity.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FBC508C52426F33D00EB3DAB /* SearchIdentity.storyboard */; }; - FBC508CB2426F42500EB3DAB /* DSSearchBlockchainIdentitiesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBC508CA2426F42500EB3DAB /* DSSearchBlockchainIdentitiesViewController.m */; }; - FBC508CE2427363C00EB3DAB /* DSBlockchainIdentitySearchTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FBC508CD2427363C00EB3DAB /* DSBlockchainIdentitySearchTableViewCell.m */; }; + FBC508CB2426F42500EB3DAB /* DSSearchIdentitiesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBC508CA2426F42500EB3DAB /* DSSearchIdentitiesViewController.m */; }; + FBC508CE2427363C00EB3DAB /* DSIdentitySearchTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FBC508CD2427363C00EB3DAB /* DSIdentitySearchTableViewCell.m */; }; FBC9248E2414EF4000005D0D /* DSAttackTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FBC9248C2414EF0600005D0D /* DSAttackTests.m */; }; FBD696EC2291A1800012FF51 /* DSDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD696EB2291A1800012FF51 /* DSDataTests.m */; }; - FBD967A72125719E005CBE0B /* DSBlockchainIdentityActionsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD967A62125719E005CBE0B /* DSBlockchainIdentityActionsViewController.m */; }; - FBD967AA212571DC005CBE0B /* DSTopupBlockchainIdentityViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD967A9212571DC005CBE0B /* DSTopupBlockchainIdentityViewController.m */; }; + FBD967A72125719E005CBE0B /* DSIdentityActionsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD967A62125719E005CBE0B /* DSIdentityActionsViewController.m */; }; + FBD967AA212571DC005CBE0B /* DSTopupIdentityViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD967A9212571DC005CBE0B /* DSTopupIdentityViewController.m */; }; FBD967AD21270DD6005CBE0B /* DSUsernameTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD967AC21270DD6005CBE0B /* DSUsernameTableViewCell.m */; }; FBDB9B7E20FF7AB900AD61B3 /* DSDeterministicMasternodeListTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FBDB9B7D20FF7AB900AD61B3 /* DSDeterministicMasternodeListTests.m */; }; FBE07DD124673AC200A1079C /* DSMainnetMetricSyncTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FBE07DD024673AC200A1079C /* DSMainnetMetricSyncTests.m */; }; @@ -619,10 +620,11 @@ 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 93099D7028E7275300160709 /* MNLIST_1746460.dat */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = MNLIST_1746460.dat; sourceTree = ""; }; 93099D7128E7275400160709 /* MNL_1746460_1746516.dat */ = {isa = PBXFileReference; lastKnownFileType = file; path = MNL_1746460_1746516.dat; sourceTree = ""; }; + 9311F51B2D2082DB0039D778 /* DSNetworkActivityView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSNetworkActivityView.h; sourceTree = ""; }; + 9311F51C2D2082DB0039D778 /* DSNetworkActivityView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSNetworkActivityView.m; sourceTree = ""; }; 937619A326E22DCD0023BE64 /* BackgroundTasks.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BackgroundTasks.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/BackgroundTasks.framework; sourceTree = DEVELOPER_DIR; }; B8D9D6682CB5A11B00D289A9 /* libDashSync-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libDashSync-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; B8D9D66A2CB5A33300D289A9 /* libDashSync-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libDashSync-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - B8D9D66C2CB5A36600D289A9 /* DashSharedCore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DashSharedCore.xcframework; path = "../../dash-shared-core/dash-spv-apple-bindings/DashSharedCore/framework/DashSharedCore.xcframework"; sourceTree = ""; }; BD44FCB7175424B2BE7993BB /* Pods-DashSync-DashSync_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DashSync-DashSync_Example.release.xcconfig"; path = "Target Support Files/Pods-DashSync-DashSync_Example/Pods-DashSync-DashSync_Example.release.xcconfig"; sourceTree = ""; }; D97E3B2F9438560328BFE431 /* Pods-NetworkInfo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NetworkInfo.release.xcconfig"; path = "Target Support Files/Pods-NetworkInfo/Pods-NetworkInfo.release.xcconfig"; sourceTree = ""; }; DE90F593715182564EB9A7E8 /* libPods-DashSync_Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-DashSync_Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -637,12 +639,12 @@ FB0F15A721049505000272E6 /* DSTransactionAmountTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSTransactionAmountTableViewCell.m; sourceTree = ""; }; FB0F15A921049598000272E6 /* DSTransactionIdentifierTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSTransactionIdentifierTableViewCell.h; sourceTree = ""; }; FB0F15AA21049598000272E6 /* DSTransactionIdentifierTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSTransactionIdentifierTableViewCell.m; sourceTree = ""; }; - FB0F15B521090445000272E6 /* DSBlockchainIdentitiesViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSBlockchainIdentitiesViewController.h; sourceTree = ""; }; - FB0F15B621090445000272E6 /* DSBlockchainIdentitiesViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSBlockchainIdentitiesViewController.m; sourceTree = ""; }; - FB0F15BC210A0208000272E6 /* DSBlockchainIdentityTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSBlockchainIdentityTableViewCell.h; sourceTree = ""; }; - FB0F15BD210A0208000272E6 /* DSBlockchainIdentityTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSBlockchainIdentityTableViewCell.m; sourceTree = ""; }; - FB0F15BF210ACD52000272E6 /* DSCreateBlockchainIdentityViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSCreateBlockchainIdentityViewController.h; sourceTree = ""; }; - FB0F15C0210ACD52000272E6 /* DSCreateBlockchainIdentityViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSCreateBlockchainIdentityViewController.m; sourceTree = ""; }; + FB0F15B521090445000272E6 /* DSIdentitiesViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSIdentitiesViewController.h; sourceTree = ""; }; + FB0F15B621090445000272E6 /* DSIdentitiesViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSIdentitiesViewController.m; sourceTree = ""; }; + FB0F15BC210A0208000272E6 /* DSIdentityTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSIdentityTableViewCell.h; sourceTree = ""; }; + FB0F15BD210A0208000272E6 /* DSIdentityTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSIdentityTableViewCell.m; sourceTree = ""; }; + FB0F15BF210ACD52000272E6 /* DSCreateIdentityViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSCreateIdentityViewController.h; sourceTree = ""; }; + FB0F15C0210ACD52000272E6 /* DSCreateIdentityViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSCreateIdentityViewController.m; sourceTree = ""; }; FB0F15C2210AD9A0000272E6 /* DSWalletChooserViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSWalletChooserViewController.h; sourceTree = ""; }; FB0F15C3210AD9A0000272E6 /* DSWalletChooserViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSWalletChooserViewController.m; sourceTree = ""; }; FB12BDBD25239CD60027CD64 /* DSIdentityAuthenticationDerivationPathsAddressesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DSIdentityAuthenticationDerivationPathsAddressesViewController.m; sourceTree = ""; }; @@ -981,14 +983,14 @@ FB85000C261A75700002E5EA /* DSInvitationDetailViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSInvitationDetailViewController.m; sourceTree = ""; }; FB850011261A7B9A0002E5EA /* DSIdentityChooserViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSIdentityChooserViewController.h; sourceTree = ""; }; FB850012261A7B9A0002E5EA /* DSIdentityChooserViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSIdentityChooserViewController.m; sourceTree = ""; }; - FB85004F261AA82F0002E5EA /* DSBlockchainIdentityChooserTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSBlockchainIdentityChooserTableViewCell.h; sourceTree = ""; }; - FB850050261AA82F0002E5EA /* DSBlockchainIdentityChooserTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSBlockchainIdentityChooserTableViewCell.m; sourceTree = ""; }; + FB85004F261AA82F0002E5EA /* DSIdentityChooserTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSIdentityChooserTableViewCell.h; sourceTree = ""; }; + FB850050261AA82F0002E5EA /* DSIdentityChooserTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSIdentityChooserTableViewCell.m; sourceTree = ""; }; FB87A0CD24B7C28600C22DF7 /* DSMiningTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSMiningTests.m; sourceTree = ""; }; FB88ECAF2649647400CDD987 /* DSInvitationsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSInvitationsTests.m; sourceTree = ""; }; FB88ECB4264B8BB800CDD987 /* DSDAPIGetTransactionInformationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSDAPIGetTransactionInformationViewController.h; sourceTree = ""; }; FB88ECB5264B8BB800CDD987 /* DSDAPIGetTransactionInformationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSDAPIGetTransactionInformationViewController.m; sourceTree = ""; }; - FB88ECBA264BFC7D00CDD987 /* DSCreateBlockchainIdentityFromInvitationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSCreateBlockchainIdentityFromInvitationViewController.h; sourceTree = ""; }; - FB88ECBB264BFC7D00CDD987 /* DSCreateBlockchainIdentityFromInvitationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSCreateBlockchainIdentityFromInvitationViewController.m; sourceTree = ""; }; + FB88ECBA264BFC7D00CDD987 /* DSCreateIdentityFromInvitationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSCreateIdentityFromInvitationViewController.h; sourceTree = ""; }; + FB88ECBB264BFC7D00CDD987 /* DSCreateIdentityFromInvitationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSCreateIdentityFromInvitationViewController.m; sourceTree = ""; }; FB89F2D622CC875800CD0232 /* MNL_0_1094976.dat */ = {isa = PBXFileReference; lastKnownFileType = file; path = MNL_0_1094976.dat; sourceTree = ""; }; FB89F2D822CD82EB00CD0232 /* MNL_0_1095720.dat */ = {isa = PBXFileReference; lastKnownFileType = file; path = MNL_0_1095720.dat; sourceTree = ""; }; FB90D2B220BD2BDD002B7956 /* DSSporksViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSSporksViewController.h; sourceTree = ""; }; @@ -1042,10 +1044,10 @@ FB97502A25B3BC2D00A1DCE7 /* DSMainnetSyncTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DSMainnetSyncTests.m; sourceTree = ""; }; FB9762F524BC4C3E00CF28EC /* DSMiningViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSMiningViewController.h; sourceTree = ""; }; FB9762F624BC4C3E00CF28EC /* DSMiningViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSMiningViewController.m; sourceTree = ""; }; - FB98831F241D64FA00B39F02 /* DSBlockchainIdentityKeysViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSBlockchainIdentityKeysViewController.h; sourceTree = ""; }; - FB988320241D64FA00B39F02 /* DSBlockchainIdentityKeysViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSBlockchainIdentityKeysViewController.m; sourceTree = ""; }; - FB988322241D6C3300B39F02 /* DSBlockchainIdentityKeyTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSBlockchainIdentityKeyTableViewCell.h; sourceTree = ""; }; - FB988323241D6C3300B39F02 /* DSBlockchainIdentityKeyTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSBlockchainIdentityKeyTableViewCell.m; sourceTree = ""; }; + FB98831F241D64FA00B39F02 /* DSIdentityKeysViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSIdentityKeysViewController.h; sourceTree = ""; }; + FB988320241D64FA00B39F02 /* DSIdentityKeysViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSIdentityKeysViewController.m; sourceTree = ""; }; + FB988322241D6C3300B39F02 /* DSIdentityKeyTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSIdentityKeyTableViewCell.h; sourceTree = ""; }; + FB988323241D6C3300B39F02 /* DSIdentityKeyTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSIdentityKeyTableViewCell.m; sourceTree = ""; }; FB995D9821101C4200AC37D0 /* DSPeersViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSPeersViewController.h; sourceTree = ""; }; FB995D9921101C4200AC37D0 /* DSPeersViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSPeersViewController.m; sourceTree = ""; }; FB995D9B21101C9600AC37D0 /* DSPeerTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSPeerTableViewCell.h; sourceTree = ""; }; @@ -1104,8 +1106,8 @@ FBBB464C25C0547F00ACDF35 /* e2eTestsTestnet.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; name = e2eTestsTestnet.yml; path = ../.github/workflows/e2eTestsTestnet.yml; sourceTree = ""; }; FBBB464D25C054DE00ACDF35 /* TestnetE2ETests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = TestnetE2ETests.xctestplan; sourceTree = ""; }; FBBDF422226B6E770081D673 /* DSBigNumberTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSBigNumberTests.m; sourceTree = ""; }; - FBBDF424226C843C0081D673 /* DSBlockchainIdentityTransitionsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSBlockchainIdentityTransitionsViewController.h; sourceTree = ""; }; - FBBDF425226C843C0081D673 /* DSBlockchainIdentityTransitionsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSBlockchainIdentityTransitionsViewController.m; sourceTree = ""; }; + FBBDF424226C843C0081D673 /* DSIdentityTransitionsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSIdentityTransitionsViewController.h; sourceTree = ""; }; + FBBDF425226C843C0081D673 /* DSIdentityTransitionsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSIdentityTransitionsViewController.m; sourceTree = ""; }; FBBDF427226C8ECA0081D673 /* DSTransitionTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSTransitionTableViewCell.h; sourceTree = ""; }; FBBDF428226C8ECA0081D673 /* DSTransitionTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSTransitionTableViewCell.m; sourceTree = ""; }; FBBDF42A226EDF600081D673 /* DSContactTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSContactTableViewCell.h; sourceTree = ""; }; @@ -1118,16 +1120,16 @@ FBC2EB0C260F8B4600B53BDB /* DSCreateInvitationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSCreateInvitationViewController.h; sourceTree = ""; }; FBC2EB0D260F8B4600B53BDB /* DSCreateInvitationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSCreateInvitationViewController.m; sourceTree = ""; }; FBC508C52426F33D00EB3DAB /* SearchIdentity.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SearchIdentity.storyboard; sourceTree = ""; }; - FBC508C92426F42500EB3DAB /* DSSearchBlockchainIdentitiesViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSSearchBlockchainIdentitiesViewController.h; sourceTree = ""; }; - FBC508CA2426F42500EB3DAB /* DSSearchBlockchainIdentitiesViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSSearchBlockchainIdentitiesViewController.m; sourceTree = ""; }; - FBC508CC2427363C00EB3DAB /* DSBlockchainIdentitySearchTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSBlockchainIdentitySearchTableViewCell.h; sourceTree = ""; }; - FBC508CD2427363C00EB3DAB /* DSBlockchainIdentitySearchTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSBlockchainIdentitySearchTableViewCell.m; sourceTree = ""; }; + FBC508C92426F42500EB3DAB /* DSSearchIdentitiesViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSSearchIdentitiesViewController.h; sourceTree = ""; }; + FBC508CA2426F42500EB3DAB /* DSSearchIdentitiesViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSSearchIdentitiesViewController.m; sourceTree = ""; }; + FBC508CC2427363C00EB3DAB /* DSIdentitySearchTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSIdentitySearchTableViewCell.h; sourceTree = ""; }; + FBC508CD2427363C00EB3DAB /* DSIdentitySearchTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSIdentitySearchTableViewCell.m; sourceTree = ""; }; FBC9248C2414EF0600005D0D /* DSAttackTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSAttackTests.m; sourceTree = ""; }; FBD696EB2291A1800012FF51 /* DSDataTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSDataTests.m; sourceTree = ""; }; - FBD967A52125719E005CBE0B /* DSBlockchainIdentityActionsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSBlockchainIdentityActionsViewController.h; sourceTree = ""; }; - FBD967A62125719E005CBE0B /* DSBlockchainIdentityActionsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSBlockchainIdentityActionsViewController.m; sourceTree = ""; }; - FBD967A8212571DC005CBE0B /* DSTopupBlockchainIdentityViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSTopupBlockchainIdentityViewController.h; sourceTree = ""; }; - FBD967A9212571DC005CBE0B /* DSTopupBlockchainIdentityViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSTopupBlockchainIdentityViewController.m; sourceTree = ""; }; + FBD967A52125719E005CBE0B /* DSIdentityActionsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSIdentityActionsViewController.h; sourceTree = ""; }; + FBD967A62125719E005CBE0B /* DSIdentityActionsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSIdentityActionsViewController.m; sourceTree = ""; }; + FBD967A8212571DC005CBE0B /* DSTopupIdentityViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSTopupIdentityViewController.h; sourceTree = ""; }; + FBD967A9212571DC005CBE0B /* DSTopupIdentityViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSTopupIdentityViewController.m; sourceTree = ""; }; FBD967AB21270DD6005CBE0B /* DSUsernameTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSUsernameTableViewCell.h; sourceTree = ""; }; FBD967AC21270DD6005CBE0B /* DSUsernameTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSUsernameTableViewCell.m; sourceTree = ""; }; FBDB9B7D20FF7AB900AD61B3 /* DSDeterministicMasternodeListTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSDeterministicMasternodeListTests.m; sourceTree = ""; }; @@ -1393,7 +1395,6 @@ 6003F58C195388D20070C39A /* Frameworks */ = { isa = PBXGroup; children = ( - B8D9D66C2CB5A36600D289A9 /* DashSharedCore.xcframework */, B8D9D66A2CB5A33300D289A9 /* libDashSync-macOS.a */, B8D9D6682CB5A11B00D289A9 /* libDashSync-macOS.a */, 937619A326E22DCD0023BE64 /* BackgroundTasks.framework */, @@ -1494,20 +1495,20 @@ children = ( FBC508C82426F3DE00EB3DAB /* Searching */, 2A1BDBE7225B855B0028F6DE /* Contracts */, - FB0F15B521090445000272E6 /* DSBlockchainIdentitiesViewController.h */, - FB0F15B621090445000272E6 /* DSBlockchainIdentitiesViewController.m */, - FB0F15BF210ACD52000272E6 /* DSCreateBlockchainIdentityViewController.h */, - FB0F15C0210ACD52000272E6 /* DSCreateBlockchainIdentityViewController.m */, - FB88ECBA264BFC7D00CDD987 /* DSCreateBlockchainIdentityFromInvitationViewController.h */, - FB88ECBB264BFC7D00CDD987 /* DSCreateBlockchainIdentityFromInvitationViewController.m */, - FBD967A52125719E005CBE0B /* DSBlockchainIdentityActionsViewController.h */, - FBD967A62125719E005CBE0B /* DSBlockchainIdentityActionsViewController.m */, - FBD967A8212571DC005CBE0B /* DSTopupBlockchainIdentityViewController.h */, - FBD967A9212571DC005CBE0B /* DSTopupBlockchainIdentityViewController.m */, - FBBDF424226C843C0081D673 /* DSBlockchainIdentityTransitionsViewController.h */, - FBBDF425226C843C0081D673 /* DSBlockchainIdentityTransitionsViewController.m */, - FB98831F241D64FA00B39F02 /* DSBlockchainIdentityKeysViewController.h */, - FB988320241D64FA00B39F02 /* DSBlockchainIdentityKeysViewController.m */, + FB0F15B521090445000272E6 /* DSIdentitiesViewController.h */, + FB0F15B621090445000272E6 /* DSIdentitiesViewController.m */, + FB0F15BF210ACD52000272E6 /* DSCreateIdentityViewController.h */, + FB0F15C0210ACD52000272E6 /* DSCreateIdentityViewController.m */, + FB88ECBA264BFC7D00CDD987 /* DSCreateIdentityFromInvitationViewController.h */, + FB88ECBB264BFC7D00CDD987 /* DSCreateIdentityFromInvitationViewController.m */, + FBD967A52125719E005CBE0B /* DSIdentityActionsViewController.h */, + FBD967A62125719E005CBE0B /* DSIdentityActionsViewController.m */, + FBD967A8212571DC005CBE0B /* DSTopupIdentityViewController.h */, + FBD967A9212571DC005CBE0B /* DSTopupIdentityViewController.m */, + FBBDF424226C843C0081D673 /* DSIdentityTransitionsViewController.h */, + FBBDF425226C843C0081D673 /* DSIdentityTransitionsViewController.m */, + FB98831F241D64FA00B39F02 /* DSIdentityKeysViewController.h */, + FB988320241D64FA00B39F02 /* DSIdentityKeysViewController.m */, ); name = "Blockchain Identities"; sourceTree = ""; @@ -1848,6 +1849,8 @@ FB5252FE20F2377D009A2BF1 /* BRCopyLabel.m */, FB2B21AA20D6FC0B000DF537 /* BRBubbleView.h */, FB2B21A920D6FC0B000DF537 /* BRBubbleView.m */, + 9311F51B2D2082DB0039D778 /* DSNetworkActivityView.h */, + 9311F51C2D2082DB0039D778 /* DSNetworkActivityView.m */, ); name = Views; sourceTree = ""; @@ -1921,8 +1924,8 @@ FB0F15A721049505000272E6 /* DSTransactionAmountTableViewCell.m */, FB0F15A921049598000272E6 /* DSTransactionIdentifierTableViewCell.h */, FB0F15AA21049598000272E6 /* DSTransactionIdentifierTableViewCell.m */, - FB0F15BC210A0208000272E6 /* DSBlockchainIdentityTableViewCell.h */, - FB0F15BD210A0208000272E6 /* DSBlockchainIdentityTableViewCell.m */, + FB0F15BC210A0208000272E6 /* DSIdentityTableViewCell.h */, + FB0F15BD210A0208000272E6 /* DSIdentityTableViewCell.m */, FB995D9B21101C9600AC37D0 /* DSPeerTableViewCell.h */, FB995D9C21101C9600AC37D0 /* DSPeerTableViewCell.m */, FBD967AB21270DD6005CBE0B /* DSUsernameTableViewCell.h */, @@ -1945,14 +1948,14 @@ FBBDF42B226EDF600081D673 /* DSContactTableViewCell.m */, FB702C6323EDC18D0099030C /* DSContractTableViewCell.h */, FB702C6423EDC18D0099030C /* DSContractTableViewCell.m */, - FB988322241D6C3300B39F02 /* DSBlockchainIdentityKeyTableViewCell.h */, - FB988323241D6C3300B39F02 /* DSBlockchainIdentityKeyTableViewCell.m */, - FBC508CC2427363C00EB3DAB /* DSBlockchainIdentitySearchTableViewCell.h */, - FBC508CD2427363C00EB3DAB /* DSBlockchainIdentitySearchTableViewCell.m */, + FB988322241D6C3300B39F02 /* DSIdentityKeyTableViewCell.h */, + FB988323241D6C3300B39F02 /* DSIdentityKeyTableViewCell.m */, + FBC508CC2427363C00EB3DAB /* DSIdentitySearchTableViewCell.h */, + FBC508CD2427363C00EB3DAB /* DSIdentitySearchTableViewCell.m */, FBC2EB06260F89A900B53BDB /* DSInvitationTableViewCell.h */, FBC2EB07260F89A900B53BDB /* DSInvitationTableViewCell.m */, - FB85004F261AA82F0002E5EA /* DSBlockchainIdentityChooserTableViewCell.h */, - FB850050261AA82F0002E5EA /* DSBlockchainIdentityChooserTableViewCell.m */, + FB85004F261AA82F0002E5EA /* DSIdentityChooserTableViewCell.h */, + FB850050261AA82F0002E5EA /* DSIdentityChooserTableViewCell.m */, ); name = Cells; sourceTree = ""; @@ -2232,8 +2235,8 @@ FBC508C82426F3DE00EB3DAB /* Searching */ = { isa = PBXGroup; children = ( - FBC508C92426F42500EB3DAB /* DSSearchBlockchainIdentitiesViewController.h */, - FBC508CA2426F42500EB3DAB /* DSSearchBlockchainIdentitiesViewController.m */, + FBC508C92426F42500EB3DAB /* DSSearchIdentitiesViewController.h */, + FBC508CA2426F42500EB3DAB /* DSSearchIdentitiesViewController.m */, ); name = Searching; sourceTree = ""; @@ -2773,10 +2776,14 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-NetworkInfo/Pods-NetworkInfo-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/DashSync-macOS/DashSync.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf-macOS/Protobuf_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-macOS/gRPCCertificates.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/DashSync.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Protobuf_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -2813,10 +2820,14 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-DashSync-DashSync_Example/Pods-DashSync-DashSync_Example-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/DashSync-iOS/DashSync.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf-iOS/Protobuf_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-iOS/gRPCCertificates.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/DashSync.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Protobuf_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -2893,12 +2904,12 @@ files = ( FB9A107220D836BB006883E2 /* DSAddDevnetAddIPAddressTableViewCell.m in Sources */, FBC2EB0E260F8B4600B53BDB /* DSCreateInvitationViewController.m in Sources */, - FB850051261AA82F0002E5EA /* DSBlockchainIdentityChooserTableViewCell.m in Sources */, + FB850051261AA82F0002E5EA /* DSIdentityChooserTableViewCell.m in Sources */, 2AF8A93C2283539800F8C72D /* DSFetchedResultsTableViewController.m in Sources */, FB763C652089B0A700CDBA57 /* DSAddressTableViewCell.m in Sources */, FB702C6523EDC18D0099030C /* DSContractTableViewCell.m in Sources */, 2A3B3EB820F893F4002962FB /* SwitcherFormTableViewCell.m in Sources */, - FB988321241D64FA00B39F02 /* DSBlockchainIdentityKeysViewController.m in Sources */, + FB988321241D64FA00B39F02 /* DSIdentityKeysViewController.m in Sources */, FB0F15A521049432000272E6 /* DSTransactionStatusTableViewCell.m in Sources */, 6003F59E195388D20070C39A /* DSAppDelegate.m in Sources */, FB5252F620EE159B009A2BF1 /* DSAccountChooserViewController.m in Sources */, @@ -2925,7 +2936,7 @@ FB6B78492278D5210005C9DB /* DSContactSentTransactionsTableViewController.m in Sources */, FBAA7749222B241D001B3756 /* DSProviderUpdateRegistrarTransactionsViewController.m in Sources */, FB0955CC244BBDC400A93675 /* DSContactRelationshipInfoViewController.m in Sources */, - FB88ECBC264BFC7D00CDD987 /* DSCreateBlockchainIdentityFromInvitationViewController.m in Sources */, + FB88ECBC264BFC7D00CDD987 /* DSCreateIdentityFromInvitationViewController.m in Sources */, FBE2B7A524C9E481000F1108 /* DSRegisterTLDViewController.m in Sources */, FB763C622089AF9500CDBA57 /* DSStandaloneAddressesViewController.m in Sources */, 6003F5A7195388D20070C39A /* DSSyncViewController.m in Sources */, @@ -2936,22 +2947,23 @@ FBEB7E8F220EEA7B0013F619 /* DSRegisterMasternodeViewController.m in Sources */, FB0F15A821049505000272E6 /* DSTransactionAmountTableViewCell.m in Sources */, FB6DC62520AB63AC00E35EED /* DSSettingsViewController.m in Sources */, + 9311F51D2D2082DB0039D778 /* DSNetworkActivityView.m in Sources */, FB12FEED20CD520700C1E0F0 /* DSStandaloneDerivationPathKeyInputViewController.m in Sources */, 2A3B3EB420F893F4002962FB /* SelectorFormCellModel.m in Sources */, FBB5D73C221FE5F40021764A /* DSUpdateMasternodeRegistrarViewController.m in Sources */, FBB4E386215057AA008040B7 /* DSAddDAPViewController.m in Sources */, FBB5D729221DF9390021764A /* DSUpdateMasternodeServiceViewController.m in Sources */, - FB0F15BE210A0208000272E6 /* DSBlockchainIdentityTableViewCell.m in Sources */, + FB0F15BE210A0208000272E6 /* DSIdentityTableViewCell.m in Sources */, FBB4E383214BC7C3008040B7 /* DSDAPIGetUserInfoViewController.m in Sources */, FBEB7E92220F0DE50013F619 /* DSAccountChooserTableViewCell.m in Sources */, - FBBDF426226C843C0081D673 /* DSBlockchainIdentityTransitionsViewController.m in Sources */, + FBBDF426226C843C0081D673 /* DSIdentityTransitionsViewController.m in Sources */, FB9A106F20D83683006883E2 /* DSAddDevnetIPAddressTableViewCell.m in Sources */, FBB4E380214A9F01008040B7 /* DSDAPIGetAddressSummaryViewController.m in Sources */, FBB4E3732145B5B3008040B7 /* DSDAPListViewController.m in Sources */, FB85000D261A75700002E5EA /* DSInvitationDetailViewController.m in Sources */, FBB4E37C214A937D008040B7 /* DSDAPICallsViewController.m in Sources */, FB9A106920D833F4006883E2 /* DSAddDevnetViewController.m in Sources */, - FBD967AA212571DC005CBE0B /* DSTopupBlockchainIdentityViewController.m in Sources */, + FBD967AA212571DC005CBE0B /* DSTopupIdentityViewController.m in Sources */, 2A9FFEC622328D0E00956D5F /* DSContactsViewController.m in Sources */, FB2B21AB20D6FC0B000DF537 /* BRBubbleView.m in Sources */, FBB25BF020C5CDC60037F5BE /* DSBlockchainExplorerViewController.m in Sources */, @@ -2969,15 +2981,15 @@ FB5252FC20F232C9009A2BF1 /* DSTransactionDetailViewController.m in Sources */, FB6B78462278D50A0005C9DB /* DSContactReceivedTransactionsTableViewController.m in Sources */, FBF31B68222F784E00393301 /* DSSpecializedDerivationPathsViewController.m in Sources */, - FB988324241D6C3300B39F02 /* DSBlockchainIdentityKeyTableViewCell.m in Sources */, + FB988324241D6C3300B39F02 /* DSIdentityKeyTableViewCell.m in Sources */, FB6B784C2278FB3A0005C9DB /* DSContactSendDashViewController.m in Sources */, - FBD967A72125719E005CBE0B /* DSBlockchainIdentityActionsViewController.m in Sources */, + FBD967A72125719E005CBE0B /* DSIdentityActionsViewController.m in Sources */, FBBDF429226C8ECA0081D673 /* DSTransitionTableViewCell.m in Sources */, FB5252FF20F2377E009A2BF1 /* BRCopyLabel.m in Sources */, 2A3B3EC220F893F4002962FB /* BaseFormCellModel.m in Sources */, 2A3B3EB320F893F4002962FB /* FormTableViewController.m in Sources */, FB0F15AB21049598000272E6 /* DSTransactionIdentifierTableViewCell.m in Sources */, - FB0F15B721090445000272E6 /* DSBlockchainIdentitiesViewController.m in Sources */, + FB0F15B721090445000272E6 /* DSIdentitiesViewController.m in Sources */, FB44D628228C77E400354A4D /* DSQuorumListViewController.m in Sources */, FB12FEE320CD3BD100C1E0F0 /* DSMasternodeTableViewCell.m in Sources */, FB3E970420DC21EB00D9B0CB /* DSAddressesTransactionsViewController.m in Sources */, @@ -2989,7 +3001,7 @@ FB90D2D520C40DD3002B7956 /* DSDoubleDerivationPathsAddressesViewController.m in Sources */, FBF31B65222F74B400393301 /* DSWalletDetailViewController.m in Sources */, 2AF8A95D2284820F00F8C72D /* DSContactProfileAvatarView.m in Sources */, - FB0F15C1210ACD52000272E6 /* DSCreateBlockchainIdentityViewController.m in Sources */, + FB0F15C1210ACD52000272E6 /* DSCreateIdentityViewController.m in Sources */, FBBDF42C226EDF600081D673 /* DSContactTableViewCell.m in Sources */, FB83C2E721B91A8D0057A0EF /* DSTransactionFloodingViewController.m in Sources */, FB12FEEA20CD50AC00C1E0F0 /* DSStandaloneDerivationPathViewController.m in Sources */, @@ -2997,7 +3009,7 @@ FB850013261A7B9A0002E5EA /* DSIdentityChooserViewController.m in Sources */, FB90D2CC20C3F0D3002B7956 /* DSAccountTableViewCell.m in Sources */, FB6DC62920AB64F400E35EED /* DSChainsViewController.m in Sources */, - FBC508CE2427363C00EB3DAB /* DSBlockchainIdentitySearchTableViewCell.m in Sources */, + FBC508CE2427363C00EB3DAB /* DSIdentitySearchTableViewCell.m in Sources */, 2AF8A94F2284745700F8C72D /* TextViewFormTableViewCell.m in Sources */, FB23895B20D3035D00921B89 /* DSProposalTableViewCell.m in Sources */, 6003F59A195388D20070C39A /* main.m in Sources */, @@ -3016,7 +3028,7 @@ FB88ECB6264B8BB800CDD987 /* DSDAPIGetTransactionInformationViewController.m in Sources */, FBC2EB02260F886700B53BDB /* DSInvitationsViewController.m in Sources */, FB83C2E421B7B7560057A0EF /* DSActionsViewController.m in Sources */, - FBC508CB2426F42500EB3DAB /* DSSearchBlockchainIdentitiesViewController.m in Sources */, + FBC508CB2426F42500EB3DAB /* DSSearchIdentitiesViewController.m in Sources */, FBEB7E95220F0DFA0013F619 /* DSWalletChooserTableViewCell.m in Sources */, FB12FEE720CD3EC100C1E0F0 /* DSMasternodeViewController.m in Sources */, 2AE125EE223B9B0600379C6F /* DSOutgoingContactsTableViewController.m in Sources */, @@ -3234,7 +3246,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "DashSync/DashSync-Prefix.pch"; INFOPLIST_FILE = "DashSync/DashSync-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; MODULE_NAME = ExampleApp; OTHER_LDFLAGS = ( "$(inherited)", @@ -3252,7 +3264,6 @@ "-l\"abseil-iOS\"", "-l\"bz2\"", "-l\"c++\"", - "-l\"dash_shared_core_ios\"", "-l\"gRPC-Core-iOS\"", "-l\"gRPC-ProtoRPC-iOS\"", "-l\"gRPC-RxLibrary-iOS\"", @@ -3275,6 +3286,7 @@ "\"SystemConfiguration\"", "-framework", "\"UIKit\"", + "-DPP_STATE_TRANSITIONS", "-ld_classic", ); PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; @@ -3295,7 +3307,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "DashSync/DashSync-Prefix.pch"; INFOPLIST_FILE = "DashSync/DashSync-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; MODULE_NAME = ExampleApp; OTHER_LDFLAGS = ( "$(inherited)", @@ -3313,7 +3325,6 @@ "-l\"abseil-iOS\"", "-l\"bz2\"", "-l\"c++\"", - "-l\"dash_shared_core_ios\"", "-l\"gRPC-Core-iOS\"", "-l\"gRPC-ProtoRPC-iOS\"", "-l\"gRPC-RxLibrary-iOS\"", @@ -3336,6 +3347,7 @@ "\"SystemConfiguration\"", "-framework", "\"UIKit\"", + "-DPP_STATE_TRANSITIONS", "-ld_classic", ); PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; diff --git a/Example/DashSync.xcodeproj/xcshareddata/xcschemes/DashSync-Example.xcscheme b/Example/DashSync.xcodeproj/xcshareddata/xcschemes/DashSync-Example.xcscheme index 88c79c32d..4bdbeb3ea 100644 --- a/Example/DashSync.xcodeproj/xcshareddata/xcschemes/DashSync-Example.xcscheme +++ b/Example/DashSync.xcodeproj/xcshareddata/xcschemes/DashSync-Example.xcscheme @@ -132,6 +132,9 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + enableAddressSanitizer = "YES" + enableASanStackUseAfterReturn = "YES" + enableUBSanitizer = "YES" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" @@ -167,13 +170,8 @@ - - - + - + @@ -121,10 +121,10 @@ - + - + - + - + - + - + - + - + - + - +